{"thread":{"id":"51257","subject":"[PATCH] documentation: add tutorial for revision walking","startedAt":"2019-06-07T01:08:11Z","lastAt":"2019-10-11T23:56:03Z","messageCount":102,"participants":["Emily Shaffer","Eric Sunshine","Jeff Hostetler","Junio C Hamano","Josh Steadmon","Jonathan Tan","Johannes Schindelin","SZEDER Gábor"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"376792","messageId":"20190607010708.46654-1-emilyshaffer@google.com","threadId":"51257","inReplyTo":null,"subject":"[PATCH] documentation: add tutorial for revision walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:07:08Z","receivedAt":"2019-06-07T01:08:11Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Existing documentation on revision walks seems to be primarily intended\nas a reference for those already familiar with the procedure. This\ntutorial attempts to give an entry-level guide to a couple of bare-bones\nrevision walks so that new Git contributors can learn the concepts\nwithout having to wade through options parsing or special casing.\n\nThe target audience is a Git contributor who is just getting started\nwith the concept of revision walking. The goal is to prepare this\ncontributor to be able to understand and modify existing commands which\nperform revision walks more easily, although it will also prepare\ncontributors to create new commands which perform walks.\n\nThe tutorial covers a basic overview of the structs involved during\nrevision walk, setting up a basic commit walk, setting up a basic\nall-object walk, and adding some configuration changes to both walk\ntypes. It intentionally does not cover how to create new commands or\nsearch for options from the command line or gitconfigs.\n\nThere is an associated patchset at\nhttps://github.com/nasamuffin/git/tree/revwalk that contains a reference\nimplementation of the code generated by this tutorial.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n\nThis one is longer than the MyFirstContribution one, thanks in advance\nto anybody with the wherewithal to review this.\n\nI'll also be mailing an RFC patchset In-Reply-To this message; the RFC\npatchset should not be merged to Git, as I intend to host it in my own\nmirror as an example. I hosted a similar example for the\nMyFirstContribution tutorial; it's visible at\nhttps://github.com/nasamuffin/git/tree/psuh. There might be a better\nplace to host these so I don't \"own\" them but I'm not sure what it is;\nkeeping them as a live branch somewhere struck me as an okay way to keep\nthem from getting stale.\n\nLooking forward to hearing everyone's comments!\n - Emily\n\n Documentation/.gitignore         |   1 +\n Documentation/Makefile           |   1 +\n Documentation/MyFirstRevWalk.txt | 826 +++++++++++++++++++++++++++++++\n 3 files changed, 828 insertions(+)\n create mode 100644 Documentation/MyFirstRevWalk.txt\n\ndiff --git a/Documentation/.gitignore b/Documentation/.gitignore\nindex 9022d48355..0e3df737c5 100644\n--- a/Documentation/.gitignore\n+++ b/Documentation/.gitignore\n@@ -12,6 +12,7 @@ cmds-*.txt\n mergetools-*.txt\n manpage-base-url.xsl\n SubmittingPatches.txt\n+MyFirstRevWalk.txt\n tmp-doc-diff/\n GIT-ASCIIDOCFLAGS\n /GIT-EXCLUDED-PROGRAMS\ndiff --git a/Documentation/Makefile b/Documentation/Makefile\nindex dbf5a0f276..d57b80962f 100644\n--- a/Documentation/Makefile\n+++ b/Documentation/Makefile\n@@ -77,6 +77,7 @@ API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technica\n SP_ARTICLES += $(API_DOCS)\n \n TECH_DOCS += SubmittingPatches\n+TECH_DOCS += MyFirstRevWalk\n TECH_DOCS += technical/hash-function-transition\n TECH_DOCS += technical/http-protocol\n TECH_DOCS += technical/index-format\ndiff --git a/Documentation/MyFirstRevWalk.txt b/Documentation/MyFirstRevWalk.txt\nnew file mode 100644\nindex 0000000000..494c09d1fa\n--- /dev/null\n+++ b/Documentation/MyFirstRevWalk.txt\n@@ -0,0 +1,826 @@\n+My First Revision Walk\n+======================\n+\n+== What's a Revision Walk?\n+\n+The revision walk is a key concept in Git - this is the process that underpins\n+operations like `git log`, `git blame`, and `git reflog`. Beginning at HEAD, the\n+list of objects is found by walking parent relationships between objects. The\n+revision walk can also be usedto determine whether or not a given object is\n+reachable from the current HEAD pointer.\n+\n+=== Related Reading\n+\n+- `Documentation/user-manual.txt` under \"Hacking Git\" contains some coverage of\n+  the revision walker in its various incarnations.\n+- `Documentation/technical/api-revision-walking.txt`\n+- https://eagain.net/articles/git-for-computer-scientists/[Git for Computer Scientists]\n+  gives a good overview of the types of objects in Git and what your revision\n+  walk is really describing.\n+\n+== Setting Up\n+\n+Create a new branch from `master`.\n+\n+----\n+git checkout -b revwalk origin/master\n+----\n+\n+We'll put our fiddling into a new command. For fun, let's name it `git walken`.\n+Open up a new file `builtin/walken.c` and set up the command handler:\n+\n+----\n+/*\n+ * \"git walken\"\n+ *\n+ * Part of the \"My First Revision Walk\" tutorial.\n+ */\n+\n+#include <stdio.h>\n+#include \"builtin.h\"\n+\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+        printf(_(\"cmd_walken incoming...\\n\"));\n+        return 0;\n+}\n+----\n+\n+Add usage text and `-h` handling, in order to pass the test suite:\n+\n+----\n+static const char * const walken_usage[] = {\n+\tN_(\"git walken\"),\n+\tNULL,\n+}\n+\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n+\n+\t...\n+}\n+----\n+\n+Also add the relevant line in builtin.h near `cmd_whatchanged()`:\n+\n+----\n+extern int cmd_walken(int argc, const char **argv, const char *prefix);\n+----\n+\n+Include the command in `git.c` in `commands[]` near the entry for `whatchanged`:\n+\n+----\n+{ \"walken\", cmd_walken, RUN_SETUP },\n+----\n+\n+Add it to the `Makefile` near the line for `builtin\\worktree.o`:\n+\n+----\n+BUILTIN_OBJS += builtin/walken.o\n+----\n+\n+Build and test out your command, without forgetting to ensure the `DEVELOPER`\n+flag is set:\n+\n+----\n+echo DEVELOPER=1 >config.mak\n+make\n+./bin-wrappers/git walken\n+----\n+\n+NOTE: For a more exhaustive overview of the new command process, take a look at\n+`Documentation/MyFirstContribution`.\n+\n+NOTE: A reference implementation can be found at TODO LINK.\n+\n+=== `struct rev_cmdline_info`\n+\n+The definition of `struct rev_cmdline_info` can be found in `revision.h`.\n+\n+This struct is contained within the `rev_info` struct and is used to reflect\n+parameters provided by the user over the CLI.\n+\n+`nr` represents the number of `rev_cmdline_entry` present in the array.\n+\n+`alloc` is used by the `ALLOC_GROW` macro. Check\n+`Documentation/technical/api-allocation-growing.txt` - this variable is used to\n+track the allocated size of the list.\n+\n+Per entry, we find:\n+\n+`item` is the object provided upon which to base the revision walk. Items in Git\n+can be blobs, trees, commits, or tags. (See `Documentation/gittutorial-2.txt`.)\n+\n+`name` is the SHA-1 of the object - a 40-digit hex string you may be familiar\n+with from using Git to organize your source in the past. Check the tutorial\n+mentioned above towards the top for a discussion of where the SHA-1 can come\n+from.\n+\n+`whence` indicates some information about what to do with the parents of the\n+specified object. We'll explore this flag more later on; take a look at\n+`Documentation/revisions.txt` to get an idea of what could set the `whence`\n+value.\n+\n+`flags` are used to hint the beginning of the revision walk and are the first\n+block under the `#include`s in `revision.h`. The most likely ones to be set in\n+the `rev_cmdline_info` are `UNINTERESTING` and `BOTTOM`, but these same flags\n+can be used during the walk, as well.\n+\n+=== `struct rev_info`\n+\n+This one is quite a bit longer, and many fields are only used during the walk\n+by `revision.c` - not configuration options. Most of the configurable flags in\n+`struct rev_info` have a mirror in `Documentation/rev-list-options.txt`. It's a\n+good idea to take some time and read through that document.\n+\n+== Basic Commit Walk\n+\n+First, let's see if we can replicate the output of `git log --oneline`. We'll\n+refer back to the implementation frequently to discover norms when performing\n+a revision walk of our own.\n+\n+We'll need all the commits, in order, which preceded our current commit. We will\n+also need to know the name and subject.\n+\n+Ideally, we will also be able to find out which ones are currently at the tip of\n+various branches.\n+\n+=== Setting Up\n+\n+Preparing for your revision walk has some distinct stages.\n+\n+1. Perform default setup for this mode, and others which may be invoked.\n+2. Check configuration files for relevant settings.\n+3. Set up the rev_info struct.\n+4. Tweak the initialized rev_info to suit the current walk.\n+5. Prepare the rev_info for the walk.\n+6. Iterate over the objects, processing each one.\n+\n+==== Default Setups\n+\n+Before you begin to examine user configuration for your revision walk, it's\n+common practice for you to initialize to default any switches that your command\n+may have, as well as ask any other components you may invoke to initialize as\n+well. `git log` does this in `init_log_defaults()`; in that case, one global\n+`decoration_style` is initialized, as well as the grep and diff-UI components.\n+\n+For our purposes, within `git walken`, for the first example we do we don't\n+intend to invoke anything, and we don't have any configuration to do. However,\n+we may want to add some later, so for now, we can add an empty placeholder.\n+Create a new function in `builtin/walken.c`:\n+\n+----\n+static void init_walken_defaults(void)\n+{\n+\t/* We don't actually need the same components `git log` does; leave this\n+\t * empty for now.\n+\t */\n+}\n+----\n+\n+Make sure to add a line invoking it inside of `cmd_walken()`.\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\tinit_walken_defaults();\n+}\n+----\n+\n+==== Configuring From `.gitconfig`\n+\n+Next, we should have a look at any relevant configuration settings (i.e.,\n+settings readable and settable from `git config`). This is done by providing a\n+callback to `git_config()`; within that callback, you can also invoke methods\n+from other components you may need that need to intercept these options. Your\n+callback will be invoked once per each configuration value which Git knows about\n+(global, local, worktree, etc.).\n+\n+Similarly to the default values, we don't have anything to do here yet\n+ourselves; however, we should call `git_default_config()` if we aren't calling\n+any other existing config callbacks.\n+\n+TODO: Use the \"modern\" configset API\n+\n+Add a new function to `builtin/walken.c`:\n+\n+----\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\t/* For now, let's not bother with anything. */\n+\treturn git_default_config(var, value, cb);\n+}\n+----\n+\n+Make sure to invoke `git_config()` with it in your `cmd_walken()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t...\n+\n+\tgit_config(git_walken_config, NULL);\n+}\n+----\n+\n+// TODO: Checking CLI options\n+\n+==== Setting Up `rev_info`\n+\n+Now that we've gathered external configuration and options, it's time to\n+initialize the `rev_info` object which we will use to perform the walk. This is\n+typically done by calling `repo_init_revisions()` with the repository you intend\n+to target, as well as the prefix and your `rev_info` struct.\n+\n+Add the `struct rev_info` and the `repo_init_revisions()` call:\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t/* This can go wherever you like in your declarations.*/\n+\tstruct rev_info rev;\n+\t...\n+\n+\t/* This should go after the git_config() call. */\n+\trepo_init_revisions(the_repository, &rev, prefix);\n+}\n+----\n+\n+==== Tweaking `rev_info` For the Walk\n+\n+We're getting close, but we're still not quite ready to go. Now that `rev` is\n+initialized, we can modify it to fit our needs. This is usually done within a\n+helper for clarity, so let's add one:\n+\n+----\n+static void final_rev_info_setup(struct rev_info *rev)\n+{\n+\t/* We want to mimick the appearance of `git log --oneline`, so let's\n+\t * force oneline format. */\n+\tget_commit_format(\"oneline\", rev);\n+\n+\t/* Start our revision walk at HEAD. */\n+\tadd_head_to_pending(rev);\n+}\n+----\n+\n+[NOTE]\n+====\n+Instead of using the shorthand `add_head_to_pending()`, you could do\n+something like this:\n+----\n+\tstruct setup_revision_opt opt;\n+\n+\tmemset(&opt, 0, sizeof(opt));                                            \n+\topt.def = \"HEAD\";                                                        \n+\topt.revarg_opt = REVARG_COMMITTISH;                                      \n+\tsetup_revisions(argc, argv, rev, &opt);\n+----\n+Using a `setup_revision_opt` gives you finer control over your walk's starting\n+point.\n+====\n+\n+Then let's invoke `final_rev_info_setup()` after the call to\n+`repo_init_revisions()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t...\n+\n+\tfinal_rev_info_setup(&rev);\n+} \n+----\n+\n+Later, we may wish to add more arguments to `final_rev_info_setup()`. But for\n+now, this is all we need.\n+\n+==== Preparing `rev_info` For the Walk\n+\n+Now that `rev` is all initialized and configured, we've got one more setup step\n+before we get rolling. We can do this in a helper, which will both prepare the\n+`rev_info` for the walk, and perform the walk itself. Let's start the helper\n+with the call to `prepare_revision_walk()`.\n+\n+----\n+static int walken_commit_walk(struct rev_info *rev)\n+{\n+\t/* prepare_revision_walk() gets the final steps ready for a revision\n+\t * walk. We check the return value for errors. */\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+}\n+----\n+\n+==== Performing the Walk!\n+\n+Finally! We are ready to begin the walk itself. Now we can see that `rev_info`\n+can also be used as an iterator; we move to the next item in the walk by using\n+`get_revision()` repeatedly. Add the listed variable declarations at the top and\n+the walk loop below the `prepare_revision_walk()` call within your\n+`walken_commit_walk()`:\n+\n+----\n+static int walken_commit_walk(struct rev_info *rev)\n+{\n+\tstruct commit *commit;\n+\tstruct strbuf prettybuf;\n+\tstrbuf_init(&prettybuf, 0);\n+\n+\t...\n+\n+\twhile ((commit = get_revision(rev)) != NULL) {\n+\t\tif (commit == NULL)\n+\t\t\tcontinue;\n+\n+\t\tstrbuf_reset(&prettybuf);\n+\t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);\n+\t\tprintf(_(\"%s\\n\"), prettybuf.buf);\n+\t}\n+\n+\treturn 0;\n+}\n+----\n+\n+Give it a shot.\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken\n+----\n+\n+You should see all of the subject lines of all the commits in\n+your tree's history, in order, ending with the initial commit, \"Initial revision\n+of \"git\", the information manager from hell\". Congratulations! You've written\n+your first revision walk. You can play with printing some additional fields\n+from each commit if you're curious; have a look at the functions available in\n+`commit.h`.\n+\n+=== Adding a Filter\n+\n+Next, let's try to filter the commits we see based on their author. This is\n+equivalent to running `git log --author=<pattern>`. We can add a filter by\n+modifying `rev_info.grep_filter`, which is a `struct grep_opt`. \n+\n+First some setup. Add `init_grep_defaults()` to `init_walken_defaults()` and add\n+`grep_config()` to `git_walken_config()`:\n+\n+----\n+static void init_walken_defaults(void)\n+{\n+\tinit_grep_defaults(the_repository);\n+}\n+\n+...\n+\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\tgrep_config(var, value, cb);\n+\treturn git_default_config(var, value, cb);\n+}\n+----\n+\n+Next, we can modify the `grep_filter`. This is done with convenience functions\n+found in `grep.h`. For fun, we're filtering to only commits from folks using a\n+gmail.com email address - a not-very-precise guess at who may be working on Git\n+as a hobby. Since we're checking the author, which is a specific line in the\n+header, we'll use the `append_header_grep_pattern()` helper. We can use\n+the `enum grep_header_field` to indicate which part of the commit header we want\n+to search.\n+\n+In `final_rev_info_setup()`, add your filter line:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\tappend_header_grep_pattern(&rev->grep_filter, GREP_HEADER_AUTHOR,\n+\t\t\"gmail\");\n+\tcompile_grep_patterns(&rev->grep_filter);\n+\n+\t...\n+}\n+----\n+\n+`append_header_grep_pattern()` adds your new \"gmail\" pattern to `rev_info`, but\n+it won't work unless we compile it with `compile_grep_patterns()`.\n+\n+NOTE: If you are using `setup_revisions()` (for example, if you are passing a\n+`setup_revision_opt` instead of using `add_head_to_pending()`), you don't need\n+to call `compile_grep_patterns()` because `setup_revisions()` calls it for you.\n+\n+NOTE: We could add the same filter via the `append_grep_pattern()` helper if we\n+wanted to, but `append_header_grep_pattern()` adds the `enum grep_context` and\n+`enum grep_pat_token` for us.\n+\n+=== Changing the Order\n+\n+There are a few ways that we can change the order of the commits during a\n+revision walk. Firstly, we can use the `enum rev_sort_order` to choose from some\n+sane orderings.\n+\n+Let's see what happens when we run with `REV_SORT_BY_COMMIT_DATE` as opposed to\n+`REV_SORT_BY_AUTHOR_DATE`. Add the following:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,                    \n+                const char *prefix, struct rev_info *rev)                        \n+{\n+\t...\n+\n+\trev->topo_order = 1;                                                     \n+\trev->sort_order = REV_SORT_BY_COMMIT_DATE;  \n+\n+\t...\n+}\n+----\n+\n+Let's output this into a file so we can easily diff it with the walk sorted by\n+author date.\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken > commit-date.txt\n+----\n+\n+Then, let's sort by author date and run it again.\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,                    \n+                const char *prefix, struct rev_info *rev)                        \n+{\n+\t...\n+\n+\trev->topo_order = 1;                                                     \n+\trev->sort_order = REV_SORT_BY_AUTHOR_DATE;  \n+\n+\t...\n+}\n+----\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken > author-date.txt\n+----\n+\n+Finally, compare the two. This is a little less helpful without object names or\n+dates, but hopefully we get the idea.\n+\n+----\n+$ diff -u commit-date.txt author-date.txt\n+----\n+\n+This display is an indicator for the latency between publishing a commit for\n+review the first time, and getting it actually merged into master.\n+\n+Let's try one more reordering of commits. `rev_info` exposes a `reverse` flag.\n+However, it needs to be applied after `add_head_to_pending()` is called. Find\n+the line where you call `add_head_to_pending()` and set the `reverse` flag right\n+after:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv, const char *prefix,\n+                struct rev_info *rev) \n+{\n+\t...\n+\n+        add_head_to_pending(rev);                                                \n+        rev->reverse = 1; \n+\n+\t...\n+}\n+----\n+\n+Run your walk again and note the difference in order. (If you remove the grep\n+pattern, you should see the last commit this call gives you as your current\n+HEAD.)\n+\n+== Basic Object Walk\n+\n+So far we've been walking only commits. But Git has more types of objects than\n+that! Let's see if we can walk _all_ objects, and find out some information\n+about each one.\n+\n+We can base our work on an example. `git pack-objects` prepares all kinds of\n+objects for packing into a bitmap or packfile. The work we are interested in\n+resides in `builtins/pack-objects.c:get_object_list()`; examination of that\n+function shows that the all-object walk is being performed by\n+`traverse_commit_list()` or `traverse_commit_list_filtered()`. Those two\n+functions reside in `list-objects.c`; examining the source shows that, despite\n+the name, these functions traverse all kinds of objects. Let's have a look at\n+the arguments to `traverse_commit_list_filtered()`, which are a superset of the\n+arguments to the unfiltered version.\n+\n+- `struct list_objects_filter_options *filter_options`: This is a struct which\n+  stores a filter-spec as outlined in `Documentation/rev-list-options.txt`.\n+- `struct rev_info *revs`: This is the `rev_info` used for the walk.\n+- `show_commit_fn show_commit`: A callback which will be used to handle each\n+  individual commit object.\n+- `show_object_fn show_object`: A callback which will be used to handle each\n+  non-commit object (so each blob, tree, or tag).\n+- `void show_data*`: A context buffer which is passed in turn to `show_commit`\n+  and `show_object`.\n+- `struct oidset *omitted`: A linked-list of object IDs which the provided\n+  filter caused to be omitted.\n+\n+It looks like this `traverse_commit_list_filtered()` uses callbacks we provide\n+instead of needing us to call it repeatedly ourselves. Cool! Let's add the\n+callbacks first.\n+\n+For the sake of this tutorial, we'll simply keep track of how many of each kind\n+of object we find. At file scope in `builtin/walken.c` add the following\n+tracking variables:\n+\n+----\n+static int commit_count;\n+static int tag_count;\n+static int blob_count;\n+static int tree_count;\n+----\n+\n+Commits are handled by a different callback than other objects; let's do that\n+one first:\n+\n+----\n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+        commit_count++;\n+}\n+----\n+\n+Since we have the `struct commit` object, we can look at all the same parts that\n+we looked at in our earlier commit-only walk. For the sake of this tutorial,\n+though, we'll just increment the commit counter and move on.\n+\n+The callback for non-commits is a little different, as we'll need to check\n+which kind of object we're dealing with:\n+\n+----\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+        switch (obj->type) {\n+        case OBJ_TREE:\n+                tree_count++;\n+                break;\n+        case OBJ_BLOB:\n+                blob_count++;\n+                break;\n+        case OBJ_TAG:\n+                tag_count++;\n+                break;\n+        case OBJ_COMMIT:\n+                printf(_(\"Unexpectedly encountered a commit in \"\n+                         \"walken_show_object!\\n\"));\n+                commit_count++;\n+                break;\n+        default:\n+                printf(_(\"Unexpected object type %s!\\n\"),\n+                       type_name(obj->type));\n+                break;\n+        }\n+}\n+----\n+\n+To help assure us that we aren't double-counting commits, we'll include some\n+complaining if a commit object is routed through our non-commit callback; we'll\n+also complain if we see an invalid object type.\n+\n+Our main object walk implementation is substantially different from our commit\n+walk implementation, so let's make a new function to perform the object walk. We\n+can perform setup which is applicable to all objects here, too, to keep separate\n+from setup which is applicable to commit-only walks.\n+\n+----\n+static int walken_object_walk(struct rev_info *rev)\n+{\n+}\n+----\n+\n+We'll start by enabling all types of objects in the `struct rev_info`, and\n+asking to have our trees and blobs shown in commit order. We'll also exclude\n+promisors as the walk becomes more complicated with those types of objects. When\n+our settings are ready, we'll perform the normal revision walk setup and\n+initialize our tracking variables.\n+\n+----\n+static int walken_object_walk(struct rev_info *rev)\n+{\n+        rev->tree_objects = 1;\n+        rev->blob_objects = 1;\n+        rev->tag_objects = 1;\n+        rev->tree_blobs_in_commit_order = 1;\n+        rev->exclude_promisor_objects = 1;\n+\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tcommit_count = 0;\n+\ttag_count = 0;\n+\tblob_count = 0;\n+\ttree_count = 0;\n+----\n+\n+Unless you cloned or fetched your repository earlier with a filter,\n+`exclude_promisor_objects` is unlikely to make a difference, but we'll turn it\n+on just to make sure our lives are simple.  We'll also turn on\n+`tree_blobs_in_commit_order`, which means that we will walk a commit's tree and\n+everything it points to immediately after we find each commit, as opposed to\n+waiting for the end and walking through all trees after the commit history has\n+been discovered.\n+\n+Let's start by calling just the unfiltered walk and reporting our counts.\n+Complete your implementation of `walken_object_walk()`:\n+\n+----\n+\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n+\n+\tprintf(_(\"Object walk completed. Found %d commits, %d blobs, %d tags, \"\n+\t\t \"and %d trees.\\n\"), commit_count, blob_count, tag_count,\n+\t       tree_count);\n+\n+\treturn 0;\n+}\n+----\n+\n+Finally, we'll ask `cmd_walken()` to use the object walk instead. Discussing\n+command line options is out of scope for this tutorial, so we'll just hardcode\n+a branch we can change at compile time. Where you call `final_rev_info_setup()`\n+and `walken_commit_walk()`, instead branch like so:\n+\n+----\n+\tif (1) {\n+\t\tadd_head_to_pending(&rev);\n+\t\twalken_object_walk(&rev);\n+\t} else {\n+\t\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n+\t\twalken_commit_walk(&rev);\n+\t}\n+----\n+\n+NOTE: For simplicity, we've avoided all the filters and sorts we applied in\n+`final_rev_info_setup()` and simply added `HEAD` to our pending queue. If you\n+want, you can certainly use the filters we added before by moving\n+`final_rev_info_setup()` out of the conditional and removing the call to\n+`add_head_to_pending()`.\n+\n+Now we can try to run our command! It should take noticeably longer than the\n+commit walk, but an examination of the output will give you an idea why - for\n+example:\n+\n+----\n+Object walk completed. Found 55733 commits, 100274 blobs, 0 tags, and 104210 trees.\n+----\n+\n+This makes sense. We have more trees than commits because the Git project has\n+lots of subdirectories which can change, plus at least one tree per commit. We\n+have no tags because we started on a commit (`HEAD`) and while tags can point to\n+commits, commits can't point to tags.\n+\n+NOTE: You will have different counts when you run this yourself! The number of\n+objects grows along with the Git project.\n+\n+=== Adding a Filter\n+\n+There are a handful of filters that we can apply to the object walk laid out in\n+`Documentation/rev-list-options.txt`. These filters are typically useful for\n+operations such as creating packfiles or performing a partial or shallow clone.\n+They are defined in `list-objects-filter-options.h`. For the purposes of this\n+tutorial we will use the \"tree:1\" filter, which causes the walk to omit all\n+trees and blobs which are not directly referenced by commits reachable from the\n+commit in `pending` when the walk begins. (In our case, that means we omit trees\n+and blobs not directly referenced by HEAD or HEAD's history.)\n+\n+First, we'll need to `#include \"list-objects-filter-options.h`\". Then, we can\n+set up the `struct list_objects_filter_options` and `struct oidset` at the top\n+of `walken_object_walk()`:\n+\n+----\n+static int walken_object_walk(struct rev_info *rev)                              \n+{                                                                                \n+        struct list_objects_filter_options filter_options = {};                  \n+        struct oidset omitted;                                                   \n+        oidset_init(&omitted, 0);                                                \n+\t...\n+----\n+\n+Then, for the sake of simplicity, we'll add a simple build-time branch to use\n+our filter or not. Replace the line calling `traverse_commit_list()` with the\n+following, which will remind us which kind of walk we've just performed:\n+\n+----\n+        if (1) {                                                                 \n+                /* Unfiltered: */                                                \n+                printf(_(\"Unfiltered object walk.\\n\"));                          \n+                traverse_commit_list(rev, walken_show_commit,                    \n+                                walken_show_object, NULL);                       \n+        } else {                                                                 \n+                printf(_(\"Filtered object walk with filterspec 'tree:1'.\\n\"));   \n+                /*                                                               \n+                 * We can parse a tree depth of 1 to demonstrate the kind of     \n+                 * filtering that could occur eg during shallow cloning.         \n+                 */                                                              \n+                parse_list_objects_filter(&filter_options, \"tree:1\");            \n+                                                                                 \n+                traverse_commit_list_filtered(&filter_options, rev,              \n+                        walken_show_commit, walken_show_object, NULL, &omitted); \n+        } \n+----\n+\n+`struct list_objects_filter_options` is usually built directly from a command\n+line argument, so the module provides an easy way to build one from a string.\n+Even though we aren't taking user input right now, we can still build one with\n+a hardcoded string using `parse_list_objects_filter()`.\n+\n+After we run `traverse_commit_list_filtered()` we would also be able to examine\n+`omitted`, which is a linked-list of all objects we did not include in our walk.\n+Since all omitted objects are included, the performance of\n+`traverse_commit_list_filtered()` with a non-null `omitted` arument is equitable\n+with the performance of `traverse_commit_list()`; so for our purposes, we leave\n+it null. It's easy to provide one and iterate over it, though - check `oidset.h`\n+for the declaration of the accessor methods for `oidset`.\n+\n+With the filter spec \"tree:1\", we are expecting to see _only_ the root tree for\n+each commit; therefore, the tree object count should be less than or equal to\n+the number of commits. (For an example of why that's true: `git commit --revert`\n+points to the same tree object as its grandparent.)\n+\n+=== Changing the Order\n+\n+Finally, let's demonstrate that you can also reorder walks of all objects, not\n+just walks of commits. First, we'll make our handlers chattier - modify\n+`walken_show_commit()` and `walken_show_object` to print the object as they go:\n+\n+----\n+static void walken_show_commit(struct commit *cmt, void *buf)                    \n+{                                                                                \n+        printf(_(\"commit: %s\\n\"), oid_to_hex(&cmt->object.oid));                 \n+        commit_count++;                                                          \n+}                                                                                \n+                                                                                 \n+static void walken_show_object(struct object *obj, const char *str, void *buf)   \n+{                                                                                \n+        printf(_(\"%s: %s\\n\"), type_name(obj->type), oid_to_hex(&obj->oid));      \n+\t...\n+}\n+----\n+\n+(Try to leave the counter increment logic in place in `walken_show_object()`.)\n+\n+With only that change, run again (but save yourself some scrollback):\n+\n+----\n+$ ./bin-wrappers/git walken | head -n 10\n+----\n+\n+Take a look at the top commit with `git show` and the OID you printed; it should\n+be the same as the output of `git show HEAD`.\n+\n+Next, let's change a setting on our `struct rev_info` within\n+`walken_object_walk()`. Find where you're changing the other settings on `rev`,\n+such as `rev->tree_objects` and `rev->tree_blobs_in_commit_order`, and add\n+another setting at the bottom:\n+\n+----\n+\t...\n+\n+        rev->tree_objects = 1;                                                   \n+        rev->blob_objects = 1;                                                   \n+        rev->tag_objects = 1;                                                    \n+        rev->tree_blobs_in_commit_order = 1;                                     \n+        rev->exclude_promisor_objects = 1;                                       \n+        rev->reverse = 1;\n+\n+\t...\n+----\n+\n+Now, run again, but this time, let's grab the last handful of objects instead\n+of the first handful:\n+\n+----\n+$ make\n+$ ./bin-wrappers git walken | tail -n 10\n+----\n+\n+The last commit object given should have the same OID as the one we saw at the\n+top before, and running `git show <oid>` with that OID should give you again\n+the same results as `git show HEAD`. Furthermore, if you run and examine the\n+first ten lines again (with `head` instead of `tail` like we did before applying\n+the `reverse` setting), you should see that now the first commit printed is the\n+initial commit, `e83c5163`.\n+\n+== Wrapping Up\n+\n+Let's review. In this tutorial, we:\n+\n+- Built a commit walk from the ground up\n+- Enabled a grep filter for that commit walk\n+- Changed the sort order of that filtered commit walk\n+- Built an object walk (tags, commits, trees, and blobs) from the ground up\n+- Learned how to add a filter-spec to an object walk\n+- Changed the display order of the filtered object walk\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376793","messageId":"20190607010811.52944-1-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010708.46654-1-emilyshaffer@google.com","subject":"[RFC PATCH 00/13] example implementation of revwalk tutorial","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:07:58Z","receivedAt":"2019-06-07T01:08:38Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"This patchset is NOT intended to be merged to the Git project!\n\nThis patchset should indicate what a contributor would generate by following the\nMyFirstRevWalk tutorial.\n\nI intend to push a feature branch with these patches to my own mirror of\nGit on Github (github.com/nasamuffin/git/tree/revwalk). I'm sending them\nfor review by the list to check for consistency with the Git codebase,\nso they aren't a bad example for new contributors.\n\nThanks for any reviews, all!\n - Emily\n\nEmily Shaffer (13):\n  walken: add infrastructure for revwalk demo\n  walken: add usage to enable -h\n  walken: add placeholder to initialize defaults\n  walken: add handler to git_config\n  walken: configure rev_info and prepare for walk\n  walken: perform our basic revision walk\n  walken: filter for authors from gmail address\n  walken: demonstrate various topographical sorts\n  walken: demonstrate reversing a revision walk list\n  walken: add unfiltered object walk from HEAD\n  walken: add filtered object walk\n  walken: count omitted objects\n  walken: reverse the object walk order\n\n Makefile         |   1 +\n builtin.h        |   1 +\n builtin/walken.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++\n git.c            |   1 +\n 4 files changed, 266 insertions(+)\n create mode 100644 builtin/walken.c\n\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376794","messageId":"20190607010811.52944-2-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010811.52944-1-emilyshaffer@google.com","subject":"[RFC PATCH 01/13] walken: add infrastructure for revwalk demo","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:07:59Z","receivedAt":"2019-06-07T01:08:58Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Begin to add scaffolding for `git walken`, a toy command which we will\nteach to perform a number of revision walks, in order to demonstrate the\nmechanics of revision walking for developers new to the Git project.\n\nThis commit is the beginning of an educational series which correspond\nto the tutorial in Documentation/MyFirstRevWalk.txt.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n Makefile         |  1 +\n builtin.h        |  1 +\n builtin/walken.c | 14 ++++++++++++++\n 3 files changed, 16 insertions(+)\n create mode 100644 builtin/walken.c\n\ndiff --git a/Makefile b/Makefile\nindex 8a7e235352..a25d46c7a3 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1143,6 +1143,7 @@ BUILTIN_OBJS += builtin/var.o\n BUILTIN_OBJS += builtin/verify-commit.o\n BUILTIN_OBJS += builtin/verify-pack.o\n BUILTIN_OBJS += builtin/verify-tag.o\n+BUILTIN_OBJS += builtin/walken.o\n BUILTIN_OBJS += builtin/worktree.o\n BUILTIN_OBJS += builtin/write-tree.o\n \ndiff --git a/builtin.h b/builtin.h\nindex ec7e0954c4..c919736c36 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -242,6 +242,7 @@ int cmd_var(int argc, const char **argv, const char *prefix);\n int cmd_verify_commit(int argc, const char **argv, const char *prefix);\n int cmd_verify_tag(int argc, const char **argv, const char *prefix);\n int cmd_version(int argc, const char **argv, const char *prefix);\n+int cmd_walken(int argc, const char **argv, const char *prefix);\n int cmd_whatchanged(int argc, const char **argv, const char *prefix);\n int cmd_worktree(int argc, const char **argv, const char *prefix);\n int cmd_write_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/walken.c b/builtin/walken.c\nnew file mode 100644\nindex 0000000000..bfeaa5188d\n--- /dev/null\n+++ b/builtin/walken.c\n@@ -0,0 +1,14 @@\n+/*\n+ * \"git walken\"\n+ *\n+ * Part of the \"My First Revision Walk\" tutorial.\n+ */\n+\n+#include <stdio.h>\n+#include \"builtin.h\"\n+\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\tprintf(_(\"cmd_walken incoming...\\n\"));\n+\treturn 0;\n+}\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376795","messageId":"20190607010811.52944-3-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010811.52944-1-emilyshaffer@google.com","subject":"[RFC PATCH 02/13] walken: add usage to enable -h","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:08:00Z","receivedAt":"2019-06-07T01:09:00Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"One requirement of the Git test suite is that all commands support '-h',\nwhich is captured by parse_options(). In order to support this flag, add\na short usage text to walken.c and invoke parse_options().\n\nWith this change, we can now add cmd_walken to the builtins set and\nexpect tests to pass, so we'll do so - cmd_walken is now open for\nbusiness.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 12 ++++++++++++\n git.c            |  1 +\n 2 files changed, 13 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex bfeaa5188d..5ae7c7d93f 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -6,9 +6,21 @@\n \n #include <stdio.h>\n #include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n+static const char * const walken_usage[] = {\n+\tN_(\"git walken\"),\n+\tNULL,\n+};\n \n int cmd_walken(int argc, const char **argv, const char *prefix)\n {\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n+\n \tprintf(_(\"cmd_walken incoming...\\n\"));\n \treturn 0;\n }\ndiff --git a/git.c b/git.c\nindex 1bf9c94550..209c42836f 100644\n--- a/git.c\n+++ b/git.c\n@@ -600,6 +600,7 @@ static struct cmd_struct commands[] = {\n \t{ \"verify-pack\", cmd_verify_pack },\n \t{ \"verify-tag\", cmd_verify_tag, RUN_SETUP },\n \t{ \"version\", cmd_version },\n+\t{ \"walken\", cmd_walken, RUN_SETUP },\n \t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP },\n \t{ \"worktree\", cmd_worktree, RUN_SETUP | NO_PARSEOPT },\n \t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376796","messageId":"20190607010811.52944-4-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010811.52944-1-emilyshaffer@google.com","subject":"[RFC PATCH 03/13] walken: add placeholder to initialize defaults","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:08:01Z","receivedAt":"2019-06-07T01:09:02Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Eventually, we will want a good place to initialize default variables\nfor use during our revision walk(s) in `git walken`. For now, there's\nnothing to do here, but let's add the scaffolding so that it's easy to\ntell where to put the setup later on.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 13 +++++++++++++\n 1 file changed, 13 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 5ae7c7d93f..dcee906556 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -13,6 +13,18 @@ static const char * const walken_usage[] = {\n \tNULL,\n };\n \n+/*\n+ * Within init_walken_defaults() we can call into other useful defaults to set\n+ * in the global scope or on the_repository. It's okay to borrow from other\n+ * functions which are doing something relatively similar to yours.\n+ */\n+static void init_walken_defaults(void)\n+{\n+\t/* We don't actually need the same components `git log` does; leave this\n+\t * empty for now.\n+\t */\n+}\n+\n int cmd_walken(int argc, const char **argv, const char *prefix)\n {\n \tstruct option options[] = {\n@@ -21,6 +33,7 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n \n+\tinit_walken_defaults();\n \tprintf(_(\"cmd_walken incoming...\\n\"));\n \treturn 0;\n }\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376797","messageId":"20190607010811.52944-5-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010811.52944-1-emilyshaffer@google.com","subject":"[RFC PATCH 04/13] walken: add handler to git_config","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:08:02Z","receivedAt":"2019-06-07T01:09:04Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"For now, we have no configuration options we want to set up for\nourselves, but in the future we may need to. At the very least, we\nshould invoke git_default_config() for each config option; we will do so\ninside of a skeleton config callback so that we know where to add\nconfiguration handling later on when we need it.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 26 ++++++++++++++++++++++++++\n 1 file changed, 26 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex dcee906556..5d1666a5da 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -6,6 +6,7 @@\n \n #include <stdio.h>\n #include \"builtin.h\"\n+#include \"config.h\"\n #include \"parse-options.h\"\n \n static const char * const walken_usage[] = {\n@@ -25,6 +26,28 @@ static void init_walken_defaults(void)\n \t */\n }\n \n+/*\n+ * This method will be called back by git_config(). It is used to gather values\n+ * from the configuration files available to Git.\n+ *\n+ * Each time git_config() finds a configuration file entry, it calls this\n+ * callback. Then, this function should compare it to entries which concern us,\n+ * and make settings changes as necessary.\n+ *\n+ * If we are called with a config setting we care about, we should use one of\n+ * the helpers which exist in config.h to pull out the value for ourselves, i.e.\n+ * git_config_string(...) or git_config_bool(...).\n+ *\n+ * If we don't match anything, we should pass it along to another stakeholder\n+ * who may otherwise care - in log's case, grep, gpg, and diff-ui. For our case,\n+ * we'll ignore everybody else.\n+ */\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\t/* For now, let's not bother with anything. */\n+\treturn git_default_config(var, value, cb);\n+}\n+\n int cmd_walken(int argc, const char **argv, const char *prefix)\n {\n \tstruct option options[] = {\n@@ -34,6 +57,9 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n \n \tinit_walken_defaults();\n+\n+\tgit_config(git_walken_config, NULL);\n+\n \tprintf(_(\"cmd_walken incoming...\\n\"));\n \treturn 0;\n }\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376798","messageId":"20190607010811.52944-6-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010811.52944-1-emilyshaffer@google.com","subject":"[RFC PATCH 05/13] walken: configure rev_info and prepare for walk","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:08:03Z","receivedAt":"2019-06-07T01:09:08Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"`struct rev_info` is what's used by the struct itself.\n`repo_init_revisions()` initializes the struct; then we need to set it\nup for the walk we want to perform, which is done in\n`final_rev_info_setup()`.\n\nThe most important step here is adding the first object we want to walk\nto the pending array. Here, we take the easy road and use\n`add_head_to_pending()`; there is also a way to do it with\n`setup_revision_opt()` and `setup_revisions()` which we demonstrate but\ndo not use. If we were to forget this step, the walk would do nothing -\nthe pending queue would be checked, determined to be empty, and the walk\nwould terminate immediately.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 42 ++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 42 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 5d1666a5da..c101db38c7 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -6,6 +6,7 @@\n \n #include <stdio.h>\n #include \"builtin.h\"\n+#include \"revision.h\"\n #include \"config.h\"\n #include \"parse-options.h\"\n \n@@ -26,6 +27,35 @@ static void init_walken_defaults(void)\n \t */\n }\n \n+/*\n+ * cmd_log calls a second set of init after the repo_init_revisions call. We'll\n+ * mirror those settings in post_repo_init_init.\n+ */\n+static void final_rev_info_setup(int argc, const char **argv, const char *prefix,\n+\t\tstruct rev_info *rev)\n+{\n+\tstruct setup_revision_opt opt;\n+\n+\t/* setup_revision_opt is used to pass options to the setup_revisions()\n+\t * call. It's got some special items for submodules and other types of\n+\t * optimizations, but for now, we'll just point it to HEAD and call it\n+\t * good. First we should make sure to reset it. TODO: This is useful for\n+\t * more complicated stuff revisions, but a decent shortcut for the first\n+\t * pass is add_head_to_pending().\n+\t */\n+\tmemset(&opt, 0, sizeof(opt));\n+\topt.def = \"HEAD\";\n+\topt.revarg_opt = REVARG_COMMITTISH;\n+\t//setup_revisions(argc, argv, rev, &opt);\n+\n+\t/* Let's force oneline format. */\n+\tget_commit_format(\"oneline\", rev);\n+\trev->verbose_header = 1;\n+\t\n+\t/* add the HEAD to pending so we can start */\n+\tadd_head_to_pending(rev);\n+}\n+\n /*\n  * This method will be called back by git_config(). It is used to gather values\n  * from the configuration files available to Git.\n@@ -54,12 +84,24 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\tstruct rev_info rev;\n+\n \targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n \n \tinit_walken_defaults();\n \n \tgit_config(git_walken_config, NULL);\n \n+\t/* Time to set up the walk. repo_init_revisions sets up rev_info with\n+\t * the defaults, but then you need to make some configuration settings\n+\t * to make it do what's special about your walk.\n+\t */\n+\trepo_init_revisions(the_repository, &rev, prefix);\n+\n+\t/* Before we do the walk, we need to set a starting point. It's not\n+\t * coming from opt. */\n+\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n+\n \tprintf(_(\"cmd_walken incoming...\\n\"));\n \treturn 0;\n }\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376799","messageId":"20190607010811.52944-7-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010811.52944-1-emilyshaffer@google.com","subject":"[RFC PATCH 06/13] walken: perform our basic revision walk","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:08:04Z","receivedAt":"2019-06-07T01:09:11Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Add the final steps needed and implement the walk loop itself. We add a\nmethod walken_commit_walk() which performs the final setup to revision.c\nand then iterates over commits from get_revision().\n\nThis basic walk only prints the subject line of each commit in the\nhistory. It is nearly equivalent to `git log --oneline`.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 41 +++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 41 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex c101db38c7..9cf19a24ab 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -7,8 +7,11 @@\n #include <stdio.h>\n #include \"builtin.h\"\n #include \"revision.h\"\n+#include \"commit.h\"\n #include \"config.h\"\n #include \"parse-options.h\"\n+#include \"pretty.h\"\n+#include \"line-log.h\"\n \n static const char * const walken_usage[] = {\n \tN_(\"git walken\"),\n@@ -78,6 +81,39 @@ static int git_walken_config(const char *var, const char *value, void *cb)\n \treturn git_default_config(var, value, cb);\n }\n \n+/*\n+ * walken_commit_walk() is invoked by cmd_walken() after initialization. It\n+ * does the commit walk only.\n+ */\n+static int walken_commit_walk(struct rev_info *rev)\n+{\n+\tstruct commit *commit;\n+\tstruct strbuf prettybuf;\n+\n+\tstrbuf_init(&prettybuf, 0);\n+\n+\n+\t/* prepare_revision_walk() gets the final steps ready for a revision\n+\t * walk. We check the return value for errors. */\n+\tif (prepare_revision_walk(rev)) {\n+\t\tdie(_(\"revision walk setup failed\"));\n+\t}\n+\n+\t/* Now we can start the real commit walk. get_revision grabs the next\n+\t * revision based on the contents of rev.\n+\t */\n+\trev->diffopt.close_file = 0;\n+\twhile ((commit = get_revision(rev)) != NULL) {\n+\t\tif (commit == NULL)\n+\t\t\tcontinue;\n+\t\tstrbuf_reset(&prettybuf);\n+\t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);\n+\t\tprintf(_(\"%s\\n\"), prettybuf.buf);\n+\n+\t}\n+\treturn 0;\n+}\n+\n int cmd_walken(int argc, const char **argv, const char *prefix)\n {\n \tstruct option options[] = {\n@@ -98,10 +134,15 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \t */\n \trepo_init_revisions(the_repository, &rev, prefix);\n \n+\t/* We can set our traversal flags here. */\n+\trev.always_show_header = 1;\n+\n \t/* Before we do the walk, we need to set a starting point. It's not\n \t * coming from opt. */\n \tfinal_rev_info_setup(argc, argv, prefix, &rev);\n \n+\twalken_commit_walk(&rev);\n+\n \tprintf(_(\"cmd_walken incoming...\\n\"));\n \treturn 0;\n }\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376800","messageId":"20190607010811.52944-8-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010811.52944-1-emilyshaffer@google.com","subject":"[RFC PATCH 07/13] walken: filter for authors from gmail address","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:08:05Z","receivedAt":"2019-06-07T01:09:13Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"In order to demonstrate how to create grep filters for revision walks,\nfilter the walk performed by cmd_walken() to print only commits which\nare authored by someone with a gmail address.\n\nThis commit demonstrates how to append a grep pattern to a\nrev_info.grep_filter, to teach new contributors how to create their own\nmore generalized grep filters during revision walks.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 12 ++++++++----\n 1 file changed, 8 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 9cf19a24ab..6c0f4e7b7a 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -12,6 +12,7 @@\n #include \"parse-options.h\"\n #include \"pretty.h\"\n #include \"line-log.h\"\n+#include \"grep.h\"\n \n static const char * const walken_usage[] = {\n \tN_(\"git walken\"),\n@@ -25,9 +26,8 @@ static const char * const walken_usage[] = {\n  */\n static void init_walken_defaults(void)\n {\n-\t/* We don't actually need the same components `git log` does; leave this\n-\t * empty for now.\n-\t */\n+\t/* Needed by our grep filter. */\n+\tinit_grep_defaults(the_repository);\n }\n \n /*\n@@ -51,6 +51,10 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n \topt.revarg_opt = REVARG_COMMITTISH;\n \t//setup_revisions(argc, argv, rev, &opt);\n \n+\t/* Add a grep pattern to the author line in the header. */\n+\tappend_header_grep_pattern(&rev->grep_filter, GREP_HEADER_AUTHOR, \"gmail\");\n+\tcompile_grep_patterns(&rev->grep_filter);\n+\n \t/* Let's force oneline format. */\n \tget_commit_format(\"oneline\", rev);\n \trev->verbose_header = 1;\n@@ -77,7 +81,7 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n  */\n static int git_walken_config(const char *var, const char *value, void *cb)\n {\n-\t/* For now, let's not bother with anything. */\n+\tgrep_config(var, value, cb);\n \treturn git_default_config(var, value, cb);\n }\n \n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376801","messageId":"20190607010811.52944-9-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010811.52944-1-emilyshaffer@google.com","subject":"[RFC PATCH 08/13] walken: demonstrate various topographical sorts","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:08:06Z","receivedAt":"2019-06-07T01:09:16Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Order the revision walk by author or commit dates, to demonstrate how to\napply topo_sort to a revision walk.\n\nWhile following the tutorial, new contributors are guided to run a walk\nwith each sort and compare the results.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 5 +++++\n 1 file changed, 5 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 6c0f4e7b7a..716d31f04e 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -61,6 +61,11 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n \t\n \t/* add the HEAD to pending so we can start */\n \tadd_head_to_pending(rev);\n+\t\n+\t/* Let's play with the sort order. */\n+\trev->topo_order = 1;\n+\trev->sort_order = REV_SORT_BY_COMMIT_DATE;\n+\t/* rev->sort_order = REV_SORT_BY_AUTHOR_DATE; */\n }\n \n /*\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376802","messageId":"20190607010811.52944-10-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010811.52944-1-emilyshaffer@google.com","subject":"[RFC PATCH 09/13] walken: demonstrate reversing a revision walk list","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:08:07Z","receivedAt":"2019-06-07T01:09:18Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"The final installment in the tutorial about sorting revision walk\noutputs. This commit reverses the commit list, so that we see newer\ncommits last (handy since we aren't using a pager).\n\nIt's important to note that rev->reverse needs to be set after\nadd_head_to_pending() or before setup_revisions(). (This is mentioned in\nthe accompanying tutorial.)\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 3 +++\n 1 file changed, 3 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 716d31f04e..86c8d29c48 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -61,6 +61,9 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n \t\n \t/* add the HEAD to pending so we can start */\n \tadd_head_to_pending(rev);\n+\n+\t/* Reverse the order */\n+\trev->reverse = 1;\n \t\n \t/* Let's play with the sort order. */\n \trev->topo_order = 1;\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376803","messageId":"20190607010811.52944-11-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010811.52944-1-emilyshaffer@google.com","subject":"[RFC PATCH 10/13] walken: add unfiltered object walk from HEAD","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:08:08Z","receivedAt":"2019-06-07T01:09:21Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Provide a demonstration of a revision walk which traverses all types of\nobject, not just commits. This type of revision walk is used for\noperations such as creating packfiles and performing fetches or clones,\nso it's useful to teach new developers how it works. For starters, only\ndemonstrate the unfiltered version, as this will make the tutorial\neasier to follow.\n\nThis commit is part of a tutorial on revision walking.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++--\n 1 file changed, 77 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 86c8d29c48..408af6c841 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -12,6 +12,7 @@\n #include \"parse-options.h\"\n #include \"pretty.h\"\n #include \"line-log.h\"\n+#include \"list-objects.h\"\n #include \"grep.h\"\n \n static const char * const walken_usage[] = {\n@@ -19,6 +20,11 @@ static const char * const walken_usage[] = {\n \tNULL,\n };\n \n+static int commit_count;\n+static int tag_count;\n+static int blob_count;\n+static int tree_count;\n+\n /*\n  * Within init_walken_defaults() we can call into other useful defaults to set\n  * in the global scope or on the_repository. It's okay to borrow from other\n@@ -93,6 +99,70 @@ static int git_walken_config(const char *var, const char *value, void *cb)\n \treturn git_default_config(var, value, cb);\n }\n \n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+\tcommit_count++;\n+}\n+\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+\tswitch (obj->type) {\n+\tcase OBJ_TREE:\n+\t\ttree_count++;\n+\t\tbreak;\n+\tcase OBJ_BLOB:\n+\t\tblob_count++;\n+\t\tbreak;\n+\tcase OBJ_TAG:\n+\t\ttag_count++;\n+\t\tbreak;\n+\tcase OBJ_COMMIT:\n+\t\tprintf(_(\"Unexpectedly encountered a commit in \"\n+\t\t\t \"walken_show_object!\\n\"));\n+\t\tcommit_count++;\n+\t\tbreak;\n+\tdefault:\n+\t\tprintf(_(\"Unexpected object type %s!\\n\"),\n+\t\t       type_name(obj->type));\n+\t\tbreak;\n+\t}\n+}\n+\n+/*\n+ * walken_object_walk() is invoked by cmd_walken() after initialization. It does\n+ * a walk of all object types.\n+ */\n+static int walken_object_walk(struct rev_info *rev)\n+{\n+\tstruct list_objects_filter_options filter_options = {};\n+\tstruct oidset omitted;\n+\toidset_init(&omitted, 0);\n+\n+\tprintf(\"walken_object_walk beginning...\\n\");\n+\n+\trev->tree_objects = 1;\n+\trev->blob_objects = 1;\n+\trev->tag_objects = 1;\n+\trev->tree_blobs_in_commit_order = 1;\n+\trev->exclude_promisor_objects = 1;\n+\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tcommit_count = 0;\n+\ttag_count = 0;\n+\tblob_count = 0;\n+\ttree_count = 0;\n+\n+\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n+\n+\tprintf(_(\"Object walk completed. Found %d commits, %d blobs, %d tags, \"\n+\t       \"and %d trees.\\n\"), commit_count, blob_count, tag_count,\n+\t       tree_count);\n+\n+\treturn 0;\n+}\n+\n /*\n  * walken_commit_walk() is invoked by cmd_walken() after initialization. It\n  * does the commit walk only.\n@@ -151,9 +221,14 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \n \t/* Before we do the walk, we need to set a starting point. It's not\n \t * coming from opt. */\n-\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n \n-\twalken_commit_walk(&rev);\n+\tif (1) {\n+\t\tadd_head_to_pending(&rev);\n+\t\twalken_object_walk(&rev);\n+\t} else {\n+\t\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n+\t\twalken_commit_walk(&rev);\n+\t}\n \n \tprintf(_(\"cmd_walken incoming...\\n\"));\n \treturn 0;\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376804","messageId":"20190607010811.52944-12-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010811.52944-1-emilyshaffer@google.com","subject":"[RFC PATCH 11/13] walken: add filtered object walk","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:08:09Z","receivedAt":"2019-06-07T01:09:24Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Demonstrate how filter specs can be used when performing a revision walk\nof all object types. In this case, tree depth is used. Contributors who\nare following the revision walking tutorial will be encouraged to run\nthe revision walk with and without the filter in order to compare the\nnumber of objects seen in each case.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 18 +++++++++++++++++-\n 1 file changed, 17 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 408af6c841..f2c98bcd6b 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -13,6 +13,7 @@\n #include \"pretty.h\"\n #include \"line-log.h\"\n #include \"list-objects.h\"\n+#include \"list-objects-filter-options.h\"\n #include \"grep.h\"\n \n static const char * const walken_usage[] = {\n@@ -154,7 +155,22 @@ static int walken_object_walk(struct rev_info *rev)\n \tblob_count = 0;\n \ttree_count = 0;\n \n-\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n+\tif (1) {\n+\t\t/* Unfiltered: */\n+\t\tprintf(_(\"Unfiltered object walk.\\n\"));\n+\t\ttraverse_commit_list(rev, walken_show_commit,\n+\t\t\t\twalken_show_object, NULL);\n+\t} else {\n+\t\tprintf(_(\"Filtered object walk with filterspec 'tree:1'.\\n\"));\n+\t\t/*\n+\t\t * We can parse a tree depth of 1 to demonstrate the kind of\n+\t\t * filtering that could occur eg during shallow cloning.\n+\t\t */\n+\t\tparse_list_objects_filter(&filter_options, \"tree:1\");\n+\n+\t\ttraverse_commit_list_filtered(&filter_options, rev,\n+\t\t\twalken_show_commit, walken_show_object, NULL, &omitted);\n+\t}\n \n \tprintf(_(\"Object walk completed. Found %d commits, %d blobs, %d tags, \"\n \t       \"and %d trees.\\n\"), commit_count, blob_count, tag_count,\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376805","messageId":"20190607010811.52944-13-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010811.52944-1-emilyshaffer@google.com","subject":"[RFC PATCH 12/13] walken: count omitted objects","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:08:10Z","receivedAt":"2019-06-07T01:09:27Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"It may be illuminating to see which objects were not included within a\ngiven filter. This also demonstrates, since filter-spec \"tree:1\" is\nused, that the 'omitted' list contains all objects which are omitted,\nnot just the first objects which were omitted - that is, it continues to\ndereference omitted trees and commits.\n\nThis is part of a tutorial on performing revision walks.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 13 +++++++++++--\n 1 file changed, 11 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex f2c98bcd6b..d93725ee88 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -137,6 +137,9 @@ static int walken_object_walk(struct rev_info *rev)\n {\n \tstruct list_objects_filter_options filter_options = {};\n \tstruct oidset omitted;\n+\tstruct oidset_iter oit;\n+\tstruct object_id *oid = NULL;\n+\tint omitted_count = 0;\n \toidset_init(&omitted, 0);\n \n \tprintf(\"walken_object_walk beginning...\\n\");\n@@ -172,9 +175,15 @@ static int walken_object_walk(struct rev_info *rev)\n \t\t\twalken_show_commit, walken_show_object, NULL, &omitted);\n \t}\n \n+\t/* Count the omitted objects. */\n+\toidset_iter_init(&omitted, &oit);\n+\n+\twhile ((oid = oidset_iter_next(&oit)))\n+\t\tomitted_count++;\n+\n \tprintf(_(\"Object walk completed. Found %d commits, %d blobs, %d tags, \"\n-\t       \"and %d trees.\\n\"), commit_count, blob_count, tag_count,\n-\t       tree_count);\n+\t       \"and %d trees; %d omitted objects.\\n\"), commit_count,\n+\t       blob_count, tag_count, tree_count, omitted_count);\n \n \treturn 0;\n }\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376806","messageId":"20190607010811.52944-14-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010811.52944-1-emilyshaffer@google.com","subject":"[RFC PATCH 13/13] walken: reverse the object walk order","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-07T01:08:11Z","receivedAt":"2019-06-07T01:09:29Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Demonstrate that just like commit walks, object walks can have their\norder reversed. Additionally, add verbose logging of objects encountered\nin order to let contributors prove to themselves that the walk has\nactually been reversed. With this commit, `git walken` becomes extremely\nchatty - it's recommended to pipe the output through `head` or `tail` or\nto redirect it into a file.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 3 +++\n 1 file changed, 3 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex d93725ee88..4bfee3a2d7 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -102,11 +102,13 @@ static int git_walken_config(const char *var, const char *value, void *cb)\n \n static void walken_show_commit(struct commit *cmt, void *buf)\n {\n+\tprintf(_(\"commit: %s\\n\"), oid_to_hex(&cmt->object.oid));\n \tcommit_count++;\n }\n \n static void walken_show_object(struct object *obj, const char *str, void *buf)\n {\n+\tprintf(_(\"%s: %s\\n\"), type_name(obj->type), oid_to_hex(&obj->oid));\n \tswitch (obj->type) {\n \tcase OBJ_TREE:\n \t\ttree_count++;\n@@ -149,6 +151,7 @@ static int walken_object_walk(struct rev_info *rev)\n \trev->tag_objects = 1;\n \trev->tree_blobs_in_commit_order = 1;\n \trev->exclude_promisor_objects = 1;\n+\trev->reverse = 1;\n \n \tif (prepare_revision_walk(rev))\n \t\tdie(_(\"revision walk setup failed\"));\n-- \n2.22.0.rc1.311.g5d7573a151-goog\n\n"},{"id":"376811","messageId":"CAPig+cTZFL=GzM_-S2JMWWxRU7poJ87f3a9ZcFjjUe1T131eEQ@mail.gmail.com","threadId":"51257","inReplyTo":"20190607010708.46654-1-emilyshaffer@google.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-07T06:21:07Z","receivedAt":"2019-06-07T06:21:23Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Jun 6, 2019 at 9:08 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> [...]\n> The tutorial covers a basic overview of the structs involved during\n> revision walk, setting up a basic commit walk, setting up a basic\n> all-object walk, and adding some configuration changes to both walk\n> types. It intentionally does not cover how to create new commands or\n> search for options from the command line or gitconfigs.\n> [...]\n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> ---\n> diff --git a/Documentation/.gitignore b/Documentation/.gitignore\n> @@ -12,6 +12,7 @@ cmds-*.txt\n>  SubmittingPatches.txt\n> +MyFirstRevWalk.txt\n\nThe new file itself is named Documentation/MyFirstRevWalk.txt, so why\nadd it to .gitignore?\n\n> diff --git a/Documentation/MyFirstRevWalk.txt b/Documentation/MyFirstRevWalk.txt\n> @@ -0,0 +1,826 @@\n> +== What's a Revision Walk?\n> +\n> +The revision walk is a key concept in Git - this is the process that underpins\n> +operations like `git log`, `git blame`, and `git reflog`. Beginning at HEAD, the\n> +list of objects is found by walking parent relationships between objects. The\n> +revision walk can also be usedto determine whether or not a given object is\n\ns/usedto/used to/\n\n> +reachable from the current HEAD pointer.\n> +\n> +We'll put our fiddling into a new command. For fun, let's name it `git walken`.\n> +Open up a new file `builtin/walken.c` and set up the command handler:\n> +\n> +----\n> +/*\n> + * \"git walken\"\n> + *\n> + * Part of the \"My First Revision Walk\" tutorial.\n> + */\n> +\n> +#include <stdio.h>\n> +#include \"builtin.h\"\n\nGit source files must always include cache.h or git-compat-util.h (or,\nfor builtins, builtin.h) as the very first header since those headers\ntake care of differences which might crop up as problems with system\nheaders on various platforms. System headers are included after Git\nheaders. So, stdio.h should be included after builtin.h. In this case,\nhowever, stdio.h will get pulled in by git-compat-util.h anyhow, so\nyou need not include it here.\n\n> +Add usage text and `-h` handling, in order to pass the test suite:\n> +\n> +----\n> +static const char * const walken_usage[] = {\n> +       N_(\"git walken\"),\n> +       NULL,\n> +}\n\nUnless you plan on referencing this from functions other than\ncmd_walken(), it need not be global.\n\n> +int cmd_walken(int argc, const char **argv, const char *prefix)\n> +{\n> +       struct option options[] = {\n> +               OPT_END()\n> +       };\n> +\n> +       argc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n> +\n> +       ...\n\nPerhaps comment out the \"...\" or remove it altogether to avoid having\nthe compiler barf when the below instructions tell the reader to build\nthe command.\n\n> +}\n> +\n> +Also add the relevant line in builtin.h near `cmd_whatchanged()`:\n\ns/builtin.h/`&`/\n\n> +Build and test out your command, without forgetting to ensure the `DEVELOPER`\n> +flag is set:\n> +\n> +----\n> +echo DEVELOPER=1 >config.mak\n\nThis will blast existing content of 'config.mak' which could be\ndangerous. It might be better to suggest >> instead.\n\n> +`name` is the SHA-1 of the object - a 40-digit hex string you may be familiar\n> +with from using Git to organize your source in the past. Check the tutorial\n> +mentioned above towards the top for a discussion of where the SHA-1 can come\n> +from.\n\nWith all the recent work to move away from SHA-1 and to support other\nhash functions, perhaps just call this \"object ID\" rather than SHA-1,\nand drop mention of it being exactly 40 digits. Instead, perhaps say\nsomething like \"...is the hexadecimal representation of the object\nID...\".\n\n> +== Basic Commit Walk\n> +\n> +First, let's see if we can replicate the output of `git log --oneline`. We'll\n> +refer back to the implementation frequently to discover norms when performing\n> +a revision walk of our own.\n> +\n> +We'll need all the commits, in order, which preceded our current commit. We will\n> +also need to know the name and subject.\n\nThis paragraph confused me. I read it as these being prerequisites I\nwould somehow have to provide in order to write the code. Perhaps it\ncan be rephrased to state that this is what the code will be doing.\nMaybe: \"To do this, we will find all the commits, in order, which\nprecede the current commit, and extract from them the name and subject\n[of the commit message]\" or something.\n\n> +=== Setting Up\n> +\n> +Preparing for your revision walk has some distinct stages.\n> +\n> +1. Perform default setup for this mode, and others which may be invoked.\n> +2. Check configuration files for relevant settings.\n> +3. Set up the rev_info struct.\n> +4. Tweak the initialized rev_info to suit the current walk.\n> +5. Prepare the rev_info for the walk.\n\ns/rev_info/`&`/ in the above three lines.\n\n> +==== Default Setups\n> +\n> +Before you begin to examine user configuration for your revision walk, it's\n> +common practice for you to initialize to default any switches that your command\n> +may have, as well as ask any other components you may invoke to initialize as\n> +well. `git log` does this in `init_log_defaults()`; in that case, one global\n> +`decoration_style` is initialized, as well as the grep and diff-UI components.\n> +\n> +For our purposes, within `git walken`, for the first example we do we don't\n\n\"we do we don't\"?\n\n> +intend to invoke anything, and we don't have any configuration to do. However,\n\n\"invoke anything\" is pretty nebulous, as is the earlier \"components\nyou may invoke\". A newcomer is unlikely to know what this means, so\nperhaps it needs an example (even if just a short parenthetical\ncomment).\n\n> +we may want to add some later, so for now, we can add an empty placeholder.\n> +Create a new function in `builtin/walken.c`:\n> +\n> +----\n> +static void init_walken_defaults(void)\n> +{\n> +       /* We don't actually need the same components `git log` does; leave this\n> +        * empty for now.\n> +        */\n> +}\n\n/*\n * Git multi-line comments\n * are formatted like this.\n */\n\n> +Add a new function to `builtin/walken.c`:\n> +\n> +----\n> +static int git_walken_config(const char *var, const char *value, void *cb)\n> +{\n> +       /* For now, let's not bother with anything. */\n> +       return git_default_config(var, value, cb);\n> +}\n\nComment is somewhat confusing. Perhaps say instead \"We don't currently\nhave custom configuration, so fall back to git_default_config()\" or\nsomething.\n\n> +==== Setting Up `rev_info`\n> +\n> +Now that we've gathered external configuration and options, it's time to\n> +initialize the `rev_info` object which we will use to perform the walk. This is\n> +typically done by calling `repo_init_revisions()` with the repository you intend\n> +to target, as well as the prefix and your `rev_info` struct.\n\nMaybe: s/the prefix/the `&` argument of `cmd_walken`/\n\n> +Add the `struct rev_info` and the `repo_init_revisions()` call:\n> +----\n> +int cmd_walken(int argc, const char **argv, const char *prefix)\n> +{\n> +       /* This can go wherever you like in your declarations.*/\n> +       struct rev_info rev;\n> +       ...\n\nA less verbose way to indicate the same without using a /* comment */:\n\n    ...\n    struct rev_info rev;\n    ...\n\n> +       /* This should go after the git_config() call. */\n> +       repo_init_revisions(the_repository, &rev, prefix);\n> +}\n> +----\n> +static void final_rev_info_setup(struct rev_info *rev)\n> +{\n> +       /* We want to mimick the appearance of `git log --oneline`, so let's\n> +        * force oneline format. */\n\ns/mimick/mimic/\n\n/*\n * Multi-line\n * comment.\n */\n\n> +==== Preparing `rev_info` For the Walk\n> +\n> +Now that `rev` is all initialized and configured, we've got one more setup step\n> +before we get rolling. We can do this in a helper, which will both prepare the\n> +`rev_info` for the walk, and perform the walk itself. Let's start the helper\n> +with the call to `prepare_revision_walk()`.\n> +\n> +----\n> +static int walken_commit_walk(struct rev_info *rev)\n> +{\n> +       /* prepare_revision_walk() gets the final steps ready for a revision\n> +        * walk. We check the return value for errors. */\n\nNot at all sure what this comment is trying to say. Also, the second\nsentence adds no value to what the code itself already says clearly by\nactually checking the return value.\n\n> +       if (prepare_revision_walk(rev))\n> +               die(_(\"revision walk setup failed\"));\n> +}\n> +==== Performing the Walk!\n> +\n> +Finally! We are ready to begin the walk itself. Now we can see that `rev_info`\n> +can also be used as an iterator; we move to the next item in the walk by using\n> +`get_revision()` repeatedly. Add the listed variable declarations at the top and\n> +the walk loop below the `prepare_revision_walk()` call within your\n> +`walken_commit_walk()`:\n> +\n> +----\n> +static int walken_commit_walk(struct rev_info *rev)\n> +{\n> +       struct commit *commit;\n> +       struct strbuf prettybuf;\n> +       strbuf_init(&prettybuf, 0);\n\nMore idiomatic:\n\n    struct strbuf prettybuf = STRBUF_INIT;\n\n> +       while ((commit = get_revision(rev)) != NULL) {\n> +               if (commit == NULL)\n> +                       continue;\n\nIdiomatic Git code doesn't mention NULL explicitly in conditionals, so:\n\n    while ((commit = get_revision(rev))) {\n        if (!commit)\n            continue;\n\n> +               strbuf_reset(&prettybuf);\n> +               pp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);\n\nEarlier, you talked about calling get_commit_format(\"oneline\",...) to\nget \"oneline\" output, so what is the purpose of CMIT_FMT_ONELINE here?\nThe text should explain more clearly what these two different\n\"online\"-related bits mean.\n\n> +               printf(_(\"%s\\n\"), prettybuf.buf);\n\nThere is nothing here to localize, so drop _(...):\n\n    printf(\"%s\\n\", prettybuf.buf);\n\nor perhaps just:\n\n    puts(prettybuf.buf);\n\n> +       }\n> +\n> +       return 0;\n> +}\n\nWhat does the return value signify?\n\n> +=== Adding a Filter\n> +\n> +Next, we can modify the `grep_filter`. This is done with convenience functions\n> +found in `grep.h`. For fun, we're filtering to only commits from folks using a\n> +gmail.com email address - a not-very-precise guess at who may be working on Git\n\nPerhaps? s/gmail.com/`&`/\n\n> +=== Changing the Order\n> +\n> +Let's see what happens when we run with `REV_SORT_BY_COMMIT_DATE` as opposed to\n> +`REV_SORT_BY_AUTHOR_DATE`. Add the following:\n> +\n> +static void final_rev_info_setup(int argc, const char **argv,\n> +                const char *prefix, struct rev_info *rev)\n> +{\n> +       ...\n> +\n> +       rev->topo_order = 1;\n> +       rev->sort_order = REV_SORT_BY_COMMIT_DATE;\n\nThe assignment to rev->sort_order is obvious enough, but the\nrev->topo_order assignment is quite mysterious to someone coming to\nthis tutorial to learn about revision walking, thus some commentary\nexplaining 'topo_order' would be a good idea.\n\n> +Finally, compare the two. This is a little less helpful without object names or\n> +dates, but hopefully we get the idea.\n> +\n> +----\n> +$ diff -u commit-date.txt author-date.txt\n> +----\n> +\n> +This display is an indicator for the latency between publishing a commit for\n> +review the first time, and getting it actually merged into master.\n\nPerhaps: s/master/`&`/\n\nEven as a long-time contributor to the project, I had to pause over\nthis statement for several seconds before figuring out what it was\ntalking about. Without a long-winded explanation of how topics\nprogress from submission through 'pu' through 'next' through 'master'\nand finally into a release, the above statement is likely to be\nmystifying to a newcomer. Perhaps it should be dropped.\n\n> +Let's try one more reordering of commits. `rev_info` exposes a `reverse` flag.\n> +However, it needs to be applied after `add_head_to_pending()` is called. Find\n\nThis leaves the reader hanging, wondering why 'reverse' needs to be\nassigned after add_head_to_pending().\n\n> +== Basic Object Walk\n> +\n> +static void walken_show_commit(struct commit *cmt, void *buf)\n> +{\n> +        commit_count++;\n> +}\n> +----\n> +\n> +Since we have the `struct commit` object, we can look at all the same parts that\n> +we looked at in our earlier commit-only walk. For the sake of this tutorial,\n> +though, we'll just increment the commit counter and move on.\n\nThis leaves the reader wondering what 'buf' is and what it's used for.\nPresumably this is the 'show_data' context mentioned earlier? If so,\nperhaps name this 'ctxt' or 'context' or something and, because this\nis a tutorial trying to teach revision walking, say a quick word about\nhow it might be used.\n\n> +static void walken_show_object(struct object *obj, const char *str, void *buf)\n> +{\n> +        switch (obj->type) {\n> +        [...]\n> +        case OBJ_COMMIT:\n> +                printf(_(\"Unexpectedly encountered a commit in \"\n> +                         \"walken_show_object!\\n\"));\n> +                commit_count++;\n> +                break;\n> +        default:\n> +                printf(_(\"Unexpected object type %s!\\n\"),\n> +                       type_name(obj->type));\n> +                break;\n> +        }\n> +}\n\nModern practice in this project is to start error messages with\nlowercase and to not punctuate the end (no need for \"!\").\n\nAlso, same complaint about the mysterious 'str' argument to the\ncallback as for 'buf' mentioned above.\n\n> +To help assure us that we aren't double-counting commits, we'll include some\n> +complaining if a commit object is routed through our non-commit callback; we'll\n> +also complain if we see an invalid object type.\n\n Are these two error cases \"impossible\" conditions or can they\nactually arise in practice? If the former, use die() instead and drop\nuse of _(...) so as to avoid confusing the reader into thinking that\nthe behavior is indeterminate.\n\n> +Our main object walk implementation is substantially different from our commit\n> +walk implementation, so let's make a new function to perform the object walk. We\n> +can perform setup which is applicable to all objects here, too, to keep separate\n> +from setup which is applicable to commit-only walks.\n> +\n> +----\n> +static int walken_object_walk(struct rev_info *rev)\n> +{\n> +}\n> +----\n\nThis skeleton function definition is populated immediately below, so\nit's not clear why it needs to be shown here.\n\n> +We'll start by enabling all types of objects in the `struct rev_info`, and\n> +asking to have our trees and blobs shown in commit order. We'll also exclude\n> +promisors as the walk becomes more complicated with those types of objects. When\n> +our settings are ready, we'll perform the normal revision walk setup and\n> +initialize our tracking variables.\n> +\n> +----\n> +static int walken_object_walk(struct rev_info *rev)\n> +{\n> +        rev->tree_objects = 1;\n> +        rev->blob_objects = 1;\n> +        rev->tag_objects = 1;\n> +        rev->tree_blobs_in_commit_order = 1;\n> +        rev->exclude_promisor_objects = 1;\n> +        [...]\n> +----\n> +\n> +Unless you cloned or fetched your repository earlier with a filter,\n> +`exclude_promisor_objects` is unlikely to make a difference, but we'll turn it\n> +on just to make sure our lives are simple.  We'll also turn on\n> +`tree_blobs_in_commit_order`, which means that we will walk a commit's tree and\n> +everything it points to immediately after we find each commit, as opposed to\n> +waiting for the end and walking through all trees after the commit history has\n> +been discovered.\n\nThis paragraph is repeating much of the information in the paragraph\njust above the code snippet. One or the other should be dropped or\nthinned to avoid the duplication.\n\n> +Let's start by calling just the unfiltered walk and reporting our counts.\n> +Complete your implementation of `walken_object_walk()`:\n> +\n> +----\n> +       traverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n> +\n> +       printf(_(\"Object walk completed. Found %d commits, %d blobs, %d tags, \"\n> +                \"and %d trees.\\n\"), commit_count, blob_count, tag_count,\n> +              tree_count);\n\nOr make the output more useful by having it be machine-parseable (and\nnot localized):\n\n    printf(\"commits %d\\nblobs %d\\ntags %d\\ntrees %d\\n\",\n        commit_count, blob_count, tag_cont, tree_count);\n\n> +       return 0;\n> +}\n\nWhat does the return value signify?\n\n> +Now we can try to run our command! It should take noticeably longer than the\n> +commit walk, but an examination of the output will give you an idea why - for\n> +example:\n> +\n> +----\n> +Object walk completed. Found 55733 commits, 100274 blobs, 0 tags, and 104210 trees.\n> +----\n> +\n> +This makes sense. We have more trees than commits because the Git project has\n> +lots of subdirectories which can change, plus at least one tree per commit. We\n> +have no tags because we started on a commit (`HEAD`) and while tags can point to\n> +commits, commits can't point to tags.\n> +\n> +NOTE: You will have different counts when you run this yourself! The number of\n> +objects grows along with the Git project.\n\nNot sure if this NOTE is useful; after all, you introduced the output\nby saying \"for example\".\n\n> +=== Adding a Filter\n> +\n> +There are a handful of filters that we can apply to the object walk laid out in\n> +`Documentation/rev-list-options.txt`. These filters are typically useful for\n> +operations such as creating packfiles or performing a partial or shallow clone.\n> +They are defined in `list-objects-filter-options.h`. For the purposes of this\n> +tutorial we will use the \"tree:1\" filter, which causes the walk to omit all\n> +trees and blobs which are not directly referenced by commits reachable from the\n> +commit in `pending` when the walk begins. (In our case, that means we omit trees\n> +and blobs not directly referenced by HEAD or HEAD's history.)\n\nNeed some explanation of what 'pending' is, as it's just mysterious as written.\n\n> +First, we'll need to `#include \"list-objects-filter-options.h`\". Then, we can\n> +set up the `struct list_objects_filter_options` and `struct oidset` at the top\n> +of `walken_object_walk()`:\n> +\n> +----\n> +static int walken_object_walk(struct rev_info *rev)\n> +{\n> +        struct list_objects_filter_options filter_options = {};\n> +        struct oidset omitted;\n> +        oidset_init(&omitted, 0);\n> +       ...\n\nThis 'omitted' is so far removed from the description of the 'omitted'\nargument to traverse_commit_list_filtered() way earlier in the\ntutorial that a reader is likely to have forgotten what it's about\n(indeed, I did). Some explanation, even if superficial, is likely\nwarranted here or at least mention that it is explained in more detail\nbelow (as I discovered).\n\n> +After we run `traverse_commit_list_filtered()` we would also be able to examine\n> +`omitted`, which is a linked-list of all objects we did not include in our walk.\n> +Since all omitted objects are included, the performance of\n> +`traverse_commit_list_filtered()` with a non-null `omitted` arument is equitable\n\ns/arument/argument/\n\n> +with the performance of `traverse_commit_list()`; so for our purposes, we leave\n> +it null. It's easy to provide one and iterate over it, though - check `oidset.h`\n> +for the declaration of the accessor methods for `oidset`.\n\nI'm confused. What are we leaving NULL here?\n\n> +=== Changing the Order\n> +\n> +Finally, let's demonstrate that you can also reorder walks of all objects, not\n> +just walks of commits. First, we'll make our handlers chattier - modify\n> +`walken_show_commit()` and `walken_show_object` to print the object as they go:\n\ns/walken_show_object/&()/\n\n> +static void walken_show_commit(struct commit *cmt, void *buf)\n> +{\n> +        printf(_(\"commit: %s\\n\"), oid_to_hex(&cmt->object.oid));\n> +        commit_count++;\n> +}\n\nIs there a bunch of trailing whitespace on these lines of the code\nsample (and in some lines below)?\n\n> +static void walken_show_object(struct object *obj, const char *str, void *buf)\n> +{\n> +        printf(_(\"%s: %s\\n\"), type_name(obj->type), oid_to_hex(&obj->oid));\n\nLocalizing \"%s: %s\\n\" via _(...) probably doesn't add value, which\nimplies that you might not want to be localizing \"commit\" above\neither.\n\n> +(Try to leave the counter increment logic in place in `walken_show_object()`.)\n> +\n> +With only that change, run again (but save yourself some scrollback):\n> +\n> +----\n> +$ ./bin-wrappers/git walken | head -n 10\n> +----\n> +\n> +Take a look at the top commit with `git show` and the OID you printed; it should\n> +be the same as the output of `git show HEAD`.\n\nI think this is the first use of \"OID\", which might be mysterious and\nconfusing to a newcomer. Earlier, you used SHA-1 and I suggested\n\"object ID\" instead. Perhaps use the same here, or define OID earlier\nin the document in place of SHA-1.\n\n> +Next, let's change a setting on our `struct rev_info` within\n> +`walken_object_walk()`. Find where you're changing the other settings on `rev`,\n> +such as `rev->tree_objects` and `rev->tree_blobs_in_commit_order`, and add\n> +another setting at the bottom:\n\nInstead of nebulous \"another setting\", mentioning 'reverse' explicitly\nwould make this clearer.\n\n> +        rev->tree_objects = 1;\n> +        rev->blob_objects = 1;\n> +        rev->tag_objects = 1;\n> +        rev->tree_blobs_in_commit_order = 1;\n> +        rev->exclude_promisor_objects = 1;\n> +        rev->reverse = 1;\n"},{"id":"376848","messageId":"712ced21-cc7a-0a4a-9cf2-64b1e7c5127a@jeffhostetler.com","threadId":"51257","inReplyTo":"20190607010811.52944-12-emilyshaffer@google.com","subject":"Re: [RFC PATCH 11/13] walken: add filtered object walk","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2019-06-07T19:15:53Z","receivedAt":"2019-06-07T19:15:56Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 6/6/2019 9:08 PM, Emily Shaffer wrote:\n> Demonstrate how filter specs can be used when performing a revision walk\n> of all object types. In this case, tree depth is used. Contributors who\n> are following the revision walking tutorial will be encouraged to run\n> the revision walk with and without the filter in order to compare the\n> number of objects seen in each case.\n> \n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> ---\n>   builtin/walken.c | 18 +++++++++++++++++-\n>   1 file changed, 17 insertions(+), 1 deletion(-)\n> \n> diff --git a/builtin/walken.c b/builtin/walken.c\n> index 408af6c841..f2c98bcd6b 100644\n> --- a/builtin/walken.c\n> +++ b/builtin/walken.c\n> @@ -13,6 +13,7 @@\n>   #include \"pretty.h\"\n>   #include \"line-log.h\"\n>   #include \"list-objects.h\"\n> +#include \"list-objects-filter-options.h\"\n>   #include \"grep.h\"\n>   \n>   static const char * const walken_usage[] = {\n> @@ -154,7 +155,22 @@ static int walken_object_walk(struct rev_info *rev)\n>   \tblob_count = 0;\n>   \ttree_count = 0;\n>   \n> -\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n> +\tif (1) {\n> +\t\t/* Unfiltered: */\n> +\t\tprintf(_(\"Unfiltered object walk.\\n\"));\n> +\t\ttraverse_commit_list(rev, walken_show_commit,\n> +\t\t\t\twalken_show_object, NULL);\n> +\t} else {\n> +\t\tprintf(_(\"Filtered object walk with filterspec 'tree:1'.\\n\"));\n> +\t\t/*\n> +\t\t * We can parse a tree depth of 1 to demonstrate the kind of\n> +\t\t * filtering that could occur eg during shallow cloning.\n> +\t\t */\n\nI think I'd avoid the term \"shallow clone\" here.  Shallow clone\nrefers to getting a limited commit history.  That's orthogonal from\npartial clone and the filtered tree walk that operates *within* a commit\nor a series of commits.\n\nGranted, a user might want to do both a shallow and partial clone (and\nthen later partial fetches), but I wouldn't mix the concepts here.\n\n\n> +\t\tparse_list_objects_filter(&filter_options, \"tree:1\");\n> +\n> +\t\ttraverse_commit_list_filtered(&filter_options, rev,\n> +\t\t\twalken_show_commit, walken_show_object, NULL, &omitted);\n> +\t}\n>   \n>   \tprintf(_(\"Object walk completed. Found %d commits, %d blobs, %d tags, \"\n>   \t       \"and %d trees.\\n\"), commit_count, blob_count, tag_count,\n> \n"},{"id":"376968","messageId":"xmqqef41mbmd.fsf@gitster-ct.c.googlers.com","threadId":"51257","inReplyTo":"20190607010708.46654-1-emilyshaffer@google.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-06-10T20:25:14Z","receivedAt":"2019-06-10T20:25:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Emily Shaffer <emilyshaffer@google.com> writes:\n\n> I'll also be mailing an RFC patchset In-Reply-To this message; the RFC\n> patchset should not be merged to Git, as I intend to host it in my own\n> mirror as an example. I hosted a similar example for the\n> MyFirstContribution tutorial; it's visible at\n> https://github.com/nasamuffin/git/tree/psuh. There might be a better\n> place to host these so I don't \"own\" them but I'm not sure what it is;\n> keeping them as a live branch somewhere struck me as an okay way to keep\n> them from getting stale.\n\nYes, writing the initial version is one thing, but keeping it alive\nis more work and more important.  As the underlying API changes over\ntime, it will become necessary to update the sample implementation,\nbut for a newbie who wants to learn by building \"walken\" on top of\nthe then-current codebase and API, it would not be so helpful to\nshow \"these 7 patches were for older codebase, and the tip 2 are\nincremental updates to adjust to the newer API\", so the maintenance\nof these sample patches may need different paradigm than the norm\nfor our main codebase that values incremental polishing.\n\n"},{"id":"376969","messageId":"xmqq7e9tmahm.fsf@gitster-ct.c.googlers.com","threadId":"51257","inReplyTo":"20190607010708.46654-1-emilyshaffer@google.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-06-10T20:49:41Z","receivedAt":"2019-06-10T20:49:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Emily Shaffer <emilyshaffer@google.com> writes:\n\n> +My First Revision Walk\n> +======================\n> +\n> +== What's a Revision Walk?\n> +\n> +The revision walk is a key concept in Git - this is the process that underpins\n> +operations like `git log`, `git blame`, and `git reflog`. Beginning at HEAD, the\n> +list of objects is found by walking parent relationships between objects. The\n> +revision walk can also be usedto determine whether or not a given object is\n> +reachable from the current HEAD pointer.\n\ns/usedto/used to/;\n\n> +We'll put our fiddling into a new command. For fun, let's name it `git walken`.\n> +Open up a new file `builtin/walken.c` and set up the command handler:\n> +\n> +----\n> +/*\n> + * \"git walken\"\n> + *\n> + * Part of the \"My First Revision Walk\" tutorial.\n> + */\n> +\n> +#include <stdio.h>\n\nBad idea.  In the generic part of the codebase, system headers are\nsupposed to be supplied by including git-compat-util.h (or cache.h\nor builtin.h, that are common header files that begin by including\nit and are allowed by CodingGuidelines to be used as such).\n\n> +#include \"builtin.h\"\n> +\n> +int cmd_walken(int argc, const char **argv, const char *prefix)\n> +{\n> +        printf(_(\"cmd_walken incoming...\\n\"));\n> +        return 0;\n> +}\n> +----\n\nI wonder if it makes sense to use trace instead of printf, as our\nreader has already seen the psuh example for doing the above.\n\n> +Add usage text and `-h` handling, in order to pass the test suite:\n\nIt is not wrong per-se, and it indeed is a very good practice to\nmake sure that our subcommands consistently gives usage text and\nshort usage.  Encouraging them early is a good idea.\n\nBut \"in order to pass the test suite\" invites \"eh, the test suite\ndoes not pass without usage and -h?  why?\".\n\nEither drop the mention of \"the test suite\", or perhaps say\nsomething like\n\n\tAdd usage text and `-h` handling, like all the subcommands\n\tshould consistently do (our test suite will notice and\n\tcomplain if you fail to do so).\n\ni.e. the real purpose is consistency and usability; test suite is\nmerely an enforcement mechanism.\n\n> +----\n> +{ \"walken\", cmd_walken, RUN_SETUP },\n> +----\n> +\n> +Add it to the `Makefile` near the line for `builtin\\worktree.o`:\n\nBackslash intended?\n"},{"id":"376970","messageId":"xmqq36khm8rj.fsf@gitster-ct.c.googlers.com","threadId":"51257","inReplyTo":"CAPig+cTZFL=GzM_-S2JMWWxRU7poJ87f3a9ZcFjjUe1T131eEQ@mail.gmail.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-06-10T21:26:56Z","receivedAt":"2019-06-10T21:27:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n>> +/*\n>> + * \"git walken\"\n>> + *\n>> + * Part of the \"My First Revision Walk\" tutorial.\n>> + */\n>> +\n>> +#include <stdio.h>\n>> +#include \"builtin.h\"\n>\n> Git source files must always include cache.h or git-compat-util.h (or,\n> for builtins, builtin.h) as the very first header since those headers\n> take care of differences which might crop up as problems with system\n> headers on various platforms. System headers are included after Git\n> headers. So, stdio.h should be included after builtin.h. In this case,\n\nActually the idea is that platform agnostic part of the codebase\nshould not have to include _any_ system header themselves; instead,\nincluding git-compat-util.h should take care of the system header\nfiles *including* the funky ordering requirements some platforms may\nhave.  So, we'd want to go stronger than \"should be included after\";\nit shouldn't have to be included or our git-compat-util.h is wrong.\n\nI've started reading the patch myself, but it seems that you've\nalready done a lot more thorough read-thru than I would have done,\nso thank you very much for that.\n\n"},{"id":"376972","messageId":"CAPig+cQiDjMd5gG+JPE-7gxWxVYpUGAHGm39Z1kXh4WsEiMZkQ@mail.gmail.com","threadId":"51257","inReplyTo":"xmqq36khm8rj.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-10T21:38:08Z","receivedAt":"2019-06-10T21:38:22Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Jun 10, 2019 at 5:27 PM Junio C Hamano <gitster@pobox.com> wrote:\n> Eric Sunshine <sunshine@sunshineco.com> writes:\n> >> +#include <stdio.h>\n> >> +#include \"builtin.h\"\n> >\n> > Git source files must always include cache.h or git-compat-util.h (or,\n> > for builtins, builtin.h) as the very first header since those headers\n> > take care of differences which might crop up as problems with system\n> > headers on various platforms. System headers are included after Git\n> > headers. So, stdio.h should be included after builtin.h. In this case,\n>\n> Actually the idea is that platform agnostic part of the codebase\n> should not have to include _any_ system header themselves; instead,\n> including git-compat-util.h should take care of the system header\n> files *including* the funky ordering requirements some platforms may\n> have.  So, we'd want to go stronger than \"should be included after\";\n> it shouldn't have to be included or our git-compat-util.h is wrong.\n\nThanks for clarifying that.\n"},{"id":"377372","messageId":"20190617203017.GA100487@google.com","threadId":"51257","inReplyTo":"712ced21-cc7a-0a4a-9cf2-64b1e7c5127a@jeffhostetler.com","subject":"Re: [RFC PATCH 11/13] walken: add filtered object walk","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-17T20:30:17Z","receivedAt":"2019-06-17T20:30:24Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Fri, Jun 07, 2019 at 03:15:53PM -0400, Jeff Hostetler wrote:\n> \n> \n> On 6/6/2019 9:08 PM, Emily Shaffer wrote:\n> > Demonstrate how filter specs can be used when performing a revision walk\n> > of all object types. In this case, tree depth is used. Contributors who\n> > are following the revision walking tutorial will be encouraged to run\n> > the revision walk with and without the filter in order to compare the\n> > number of objects seen in each case.\n> > \n> > Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> > ---\n> >   builtin/walken.c | 18 +++++++++++++++++-\n> >   1 file changed, 17 insertions(+), 1 deletion(-)\n> > \n> > diff --git a/builtin/walken.c b/builtin/walken.c\n> > index 408af6c841..f2c98bcd6b 100644\n> > --- a/builtin/walken.c\n> > +++ b/builtin/walken.c\n> > @@ -13,6 +13,7 @@\n> >   #include \"pretty.h\"\n> >   #include \"line-log.h\"\n> >   #include \"list-objects.h\"\n> > +#include \"list-objects-filter-options.h\"\n> >   #include \"grep.h\"\n> >   static const char * const walken_usage[] = {\n> > @@ -154,7 +155,22 @@ static int walken_object_walk(struct rev_info *rev)\n> >   \tblob_count = 0;\n> >   \ttree_count = 0;\n> > -\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n> > +\tif (1) {\n> > +\t\t/* Unfiltered: */\n> > +\t\tprintf(_(\"Unfiltered object walk.\\n\"));\n> > +\t\ttraverse_commit_list(rev, walken_show_commit,\n> > +\t\t\t\twalken_show_object, NULL);\n> > +\t} else {\n> > +\t\tprintf(_(\"Filtered object walk with filterspec 'tree:1'.\\n\"));\n> > +\t\t/*\n> > +\t\t * We can parse a tree depth of 1 to demonstrate the kind of\n> > +\t\t * filtering that could occur eg during shallow cloning.\n> > +\t\t */\n> \n> I think I'd avoid the term \"shallow clone\" here.  Shallow clone\n> refers to getting a limited commit history.  That's orthogonal from\n> partial clone and the filtered tree walk that operates *within* a commit\n> or a series of commits.\n> \n> Granted, a user might want to do both a shallow and partial clone (and\n> then later partial fetches), but I wouldn't mix the concepts here.\n\nIt's a valid complaint. I removed the mention of shallow cloning and\nreplaced it with a reference to the documentation for --filter in\nrev-list. Thanks.\n\n - Emily\n"},{"id":"377377","messageId":"20190617231959.GB100487@google.com","threadId":"51257","inReplyTo":"CAPig+cTZFL=GzM_-S2JMWWxRU7poJ87f3a9ZcFjjUe1T131eEQ@mail.gmail.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-17T23:19:59Z","receivedAt":"2019-06-17T23:20:10Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Fri, Jun 07, 2019 at 02:21:07AM -0400, Eric Sunshine wrote:\n> On Thu, Jun 6, 2019 at 9:08 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > [...]\n> > The tutorial covers a basic overview of the structs involved during\n> > revision walk, setting up a basic commit walk, setting up a basic\n> > all-object walk, and adding some configuration changes to both walk\n> > types. It intentionally does not cover how to create new commands or\n> > search for options from the command line or gitconfigs.\n> > [...]\n> > Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> > ---\n> > diff --git a/Documentation/.gitignore b/Documentation/.gitignore\n> > @@ -12,6 +12,7 @@ cmds-*.txt\n> >  SubmittingPatches.txt\n> > +MyFirstRevWalk.txt\n> \n> The new file itself is named Documentation/MyFirstRevWalk.txt, so why\n> add it to .gitignore?\n\nYep, fixed. Holdover from an initial attempt which named the file\nMyFirstRevWalk (no extension), which was then corrected for the earlier\ntutorial I sent. Thanks.\n\n> \n> > diff --git a/Documentation/MyFirstRevWalk.txt b/Documentation/MyFirstRevWalk.txt\n> > @@ -0,0 +1,826 @@\n> > +== What's a Revision Walk?\n> > +\n> > +The revision walk is a key concept in Git - this is the process that underpins\n> > +operations like `git log`, `git blame`, and `git reflog`. Beginning at HEAD, the\n> > +list of objects is found by walking parent relationships between objects. The\n> > +revision walk can also be usedto determine whether or not a given object is\n> \n> s/usedto/used to/\n\nDone.\n\n> \n> > +reachable from the current HEAD pointer.\n> > +\n> > +We'll put our fiddling into a new command. For fun, let's name it `git walken`.\n> > +Open up a new file `builtin/walken.c` and set up the command handler:\n> > +\n> > +----\n> > +/*\n> > + * \"git walken\"\n> > + *\n> > + * Part of the \"My First Revision Walk\" tutorial.\n> > + */\n> > +\n> > +#include <stdio.h>\n> > +#include \"builtin.h\"\n> \n> Git source files must always include cache.h or git-compat-util.h (or,\n> for builtins, builtin.h) as the very first header since those headers\n> take care of differences which might crop up as problems with system\n> headers on various platforms. System headers are included after Git\n> headers. So, stdio.h should be included after builtin.h. In this case,\n> however, stdio.h will get pulled in by git-compat-util.h anyhow, so\n> you need not include it here.\n\nDone.\n\n> \n> > +Add usage text and `-h` handling, in order to pass the test suite:\n> > +\n> > +----\n> > +static const char * const walken_usage[] = {\n> > +       N_(\"git walken\"),\n> > +       NULL,\n> > +}\n> \n> Unless you plan on referencing this from functions other than\n> cmd_walken(), it need not be global.\n\nDone; bad C++ habits sneaking in. :)\n\n> \n> > +int cmd_walken(int argc, const char **argv, const char *prefix)\n> > +{\n> > +       struct option options[] = {\n> > +               OPT_END()\n> > +       };\n> > +\n> > +       argc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n> > +\n> > +       ...\n> \n> Perhaps comment out the \"...\" or remove it altogether to avoid having\n> the compiler barf when the below instructions tell the reader to build\n> the command.\n\nHmm. That part I'm not so sure about. I like to use the \"...\" to\nindicate where the code in the snippet should be added around the other\ncode already in the file - which I suppose it does just as clearly if\nit's commented - but I also hope folks are not simply copy-pasting\nblindly from the tutorial.\n\nIt seems like including uncommented \"...\" in code tutorials is pretty\ncommon.\n\nI don't think I have a good reason to push back on this except that I\nthink \"/* ... */\" is ugly :)\n\nI'll go through and replace \"...\" with some actual hints about what's\nsupposed to go there; for example, here I'll replace with \"/* print and\nreturn */\".\n\n> \n> > +}\n> > +\n> > +Also add the relevant line in builtin.h near `cmd_whatchanged()`:\n> \n> s/builtin.h/`&`/\n\nDone.\n\n> \n> > +Build and test out your command, without forgetting to ensure the `DEVELOPER`\n> > +flag is set:\n> > +\n> > +----\n> > +echo DEVELOPER=1 >config.mak\n> \n> This will blast existing content of 'config.mak' which could be\n> dangerous. It might be better to suggest >> instead.\n\nDone.\n\n> \n> > +`name` is the SHA-1 of the object - a 40-digit hex string you may be familiar\n> > +with from using Git to organize your source in the past. Check the tutorial\n> > +mentioned above towards the top for a discussion of where the SHA-1 can come\n> > +from.\n> \n> With all the recent work to move away from SHA-1 and to support other\n> hash functions, perhaps just call this \"object ID\" rather than SHA-1,\n> and drop mention of it being exactly 40 digits. Instead, perhaps say\n> something like \"...is the hexadecimal representation of the object\n> ID...\".\n\nGood point. Will do.\n\n> \n> > +== Basic Commit Walk\n> > +\n> > +First, let's see if we can replicate the output of `git log --oneline`. We'll\n> > +refer back to the implementation frequently to discover norms when performing\n> > +a revision walk of our own.\n> > +\n> > +We'll need all the commits, in order, which preceded our current commit. We will\n> > +also need to know the name and subject.\n> \n> This paragraph confused me. I read it as these being prerequisites I\n> would somehow have to provide in order to write the code. Perhaps it\n> can be rephrased to state that this is what the code will be doing.\n> Maybe: \"To do this, we will find all the commits, in order, which\n> precede the current commit, and extract from them the name and subject\n> [of the commit message]\" or something.\n\nYeah, good point. Thanks - this is the kind of thing that sounds logical\nwhen you write it but not when you read it later :)\n\n> \n> > +=== Setting Up\n> > +\n> > +Preparing for your revision walk has some distinct stages.\n> > +\n> > +1. Perform default setup for this mode, and others which may be invoked.\n> > +2. Check configuration files for relevant settings.\n> > +3. Set up the rev_info struct.\n> > +4. Tweak the initialized rev_info to suit the current walk.\n> > +5. Prepare the rev_info for the walk.\n> \n> s/rev_info/`&`/ in the above three lines.\n\nDone.\n\n> \n> > +==== Default Setups\n> > +\n> > +Before you begin to examine user configuration for your revision walk, it's\n> > +common practice for you to initialize to default any switches that your command\n> > +may have, as well as ask any other components you may invoke to initialize as\n> > +well. `git log` does this in `init_log_defaults()`; in that case, one global\n> > +`decoration_style` is initialized, as well as the grep and diff-UI components.\n> > +\n> > +For our purposes, within `git walken`, for the first example we do we don't\n> \n> \"we do we don't\"?\n> \n> > +intend to invoke anything, and we don't have any configuration to do. However,\n> \n> \"invoke anything\" is pretty nebulous, as is the earlier \"components\n> you may invoke\". A newcomer is unlikely to know what this means, so\n> perhaps it needs an example (even if just a short parenthetical\n> comment).\n\nI have tried to reword this; I hope this is a little clearer.\n\n  Before you begin to examine user configuration for your revision walk, it's      \n  common practice for you to initialize to default any switches that your command  \n  may have, as well as ask any other components you may invoke to initialize as    \n  well (for example, how `git log` also uses the `grep` and `diff` components).    \n  `git log` does this in `init_log_defaults()`; in that case, one global           \n  `decoration_style` is initialized, as well as the grep and diff-UI components.   \n                                                                                   \n  For our purposes, within `git walken`, for the first example we don't intend to  \n  use any other components within Git, and we don't have any configuration to do.  \n  However, we may want to add some later, so for now, we can add an empty          \n  placeholder. Create a new function in `builtin/walken.c`: \n\n> \n> > +we may want to add some later, so for now, we can add an empty placeholder.\n> > +Create a new function in `builtin/walken.c`:\n> > +\n> > +----\n> > +static void init_walken_defaults(void)\n> > +{\n> > +       /* We don't actually need the same components `git log` does; leave this\n> > +        * empty for now.\n> > +        */\n> > +}\n> \n> /*\n>  * Git multi-line comments\n>  * are formatted like this.\n>  */\n\nDone; I'll look through the rest of the samples for it too.\n> \n> > +Add a new function to `builtin/walken.c`:\n> > +\n> > +----\n> > +static int git_walken_config(const char *var, const char *value, void *cb)\n> > +{\n> > +       /* For now, let's not bother with anything. */\n> > +       return git_default_config(var, value, cb);\n> > +}\n> \n> Comment is somewhat confusing. Perhaps say instead \"We don't currently\n> have custom configuration, so fall back to git_default_config()\" or\n> something.\n\nDone.\n\n> \n> > +==== Setting Up `rev_info`\n> > +\n> > +Now that we've gathered external configuration and options, it's time to\n> > +initialize the `rev_info` object which we will use to perform the walk. This is\n> > +typically done by calling `repo_init_revisions()` with the repository you intend\n> > +to target, as well as the prefix and your `rev_info` struct.\n> \n> Maybe: s/the prefix/the `&` argument of `cmd_walken`/\n\nDone.\n\n> \n> > +Add the `struct rev_info` and the `repo_init_revisions()` call:\n> > +----\n> > +int cmd_walken(int argc, const char **argv, const char *prefix)\n> > +{\n> > +       /* This can go wherever you like in your declarations.*/\n> > +       struct rev_info rev;\n> > +       ...\n> \n> A less verbose way to indicate the same without using a /* comment */:\n> \n>     ...\n>     struct rev_info rev;\n>     ...\n\nPer the earlier comment about losing \"...\" I'm not going to take this\ncomment; I'll also be replacing the \"...\" after.\n\n> \n> > +       /* This should go after the git_config() call. */\n> > +       repo_init_revisions(the_repository, &rev, prefix);\n> > +}\n> > +----\n> > +static void final_rev_info_setup(struct rev_info *rev)\n> > +{\n> > +       /* We want to mimick the appearance of `git log --oneline`, so let's\n> > +        * force oneline format. */\n> \n> s/mimick/mimic/\n> \n> /*\n>  * Multi-line\n>  * comment.\n>  */\n\nDone.\n\n> \n> > +==== Preparing `rev_info` For the Walk\n> > +\n> > +Now that `rev` is all initialized and configured, we've got one more setup step\n> > +before we get rolling. We can do this in a helper, which will both prepare the\n> > +`rev_info` for the walk, and perform the walk itself. Let's start the helper\n> > +with the call to `prepare_revision_walk()`.\n> > +\n> > +----\n> > +static int walken_commit_walk(struct rev_info *rev)\n> > +{\n> > +       /* prepare_revision_walk() gets the final steps ready for a revision\n> > +        * walk. We check the return value for errors. */\n> \n> Not at all sure what this comment is trying to say. Also, the second\n> sentence adds no value to what the code itself already says clearly by\n> actually checking the return value.\n\nAttempted to rephrase. I ended up with:\n\n  /*                                                                       \n   * prepare_revision_walk() does the final setup needed by revision.h     \n   * before a walk. It may return an error if there is a problem.          \n   */ \n\nMaybe the second sentence still doesn't serve a purpose, but I was\ntrying to express that prepare_revision_walk() won't die() on its own.\n\n> \n> > +       if (prepare_revision_walk(rev))\n> > +               die(_(\"revision walk setup failed\"));\n> > +}\n> > +==== Performing the Walk!\n> > +\n> > +Finally! We are ready to begin the walk itself. Now we can see that `rev_info`\n> > +can also be used as an iterator; we move to the next item in the walk by using\n> > +`get_revision()` repeatedly. Add the listed variable declarations at the top and\n> > +the walk loop below the `prepare_revision_walk()` call within your\n> > +`walken_commit_walk()`:\n> > +\n> > +----\n> > +static int walken_commit_walk(struct rev_info *rev)\n> > +{\n> > +       struct commit *commit;\n> > +       struct strbuf prettybuf;\n> > +       strbuf_init(&prettybuf, 0);\n> \n> More idiomatic:\n> \n>     struct strbuf prettybuf = STRBUF_INIT;\n\nOk, I'll change it. I wasn't sure which one was preferred, so this is\nsuper helpful. Thanks.\n\n> \n> > +       while ((commit = get_revision(rev)) != NULL) {\n> > +               if (commit == NULL)\n> > +                       continue;\n> \n> Idiomatic Git code doesn't mention NULL explicitly in conditionals, so:\n> \n>     while ((commit = get_revision(rev))) {\n>         if (!commit)\n>             continue;\n\nDone, thanks.\n\n> \n> > +               strbuf_reset(&prettybuf);\n> > +               pp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);\n> \n> Earlier, you talked about calling get_commit_format(\"oneline\",...) to\n> get \"oneline\" output, so what is the purpose of CMIT_FMT_ONELINE here?\n> The text should explain more clearly what these two different\n> \"online\"-related bits mean.\n\nThanks. I've got to research a little on this one. I'll clarify it\nbefore the next reroll.\n\n> \n> > +               printf(_(\"%s\\n\"), prettybuf.buf);\n> \n> There is nothing here to localize, so drop _(...):\n> \n>     printf(\"%s\\n\", prettybuf.buf);\n> \n> or perhaps just:\n> \n>     puts(prettybuf.buf);\n\nSure, I'll use this one.\n\n> \n> > +       }\n> > +\n> > +       return 0;\n> > +}\n> \n> What does the return value signify?\n\nWill double check that I don't use it for anything; I can probalby drop\nit and make this a void function instead.\n\n> \n> > +=== Adding a Filter\n> > +\n> > +Next, we can modify the `grep_filter`. This is done with convenience functions\n> > +found in `grep.h`. For fun, we're filtering to only commits from folks using a\n> > +gmail.com email address - a not-very-precise guess at who may be working on Git\n> \n> Perhaps? s/gmail.com/`&`/\n\nDone.\n\n> \n> > +=== Changing the Order\n> > +\n> > +Let's see what happens when we run with `REV_SORT_BY_COMMIT_DATE` as opposed to\n> > +`REV_SORT_BY_AUTHOR_DATE`. Add the following:\n> > +\n> > +static void final_rev_info_setup(int argc, const char **argv,\n> > +                const char *prefix, struct rev_info *rev)\n> > +{\n> > +       ...\n> > +\n> > +       rev->topo_order = 1;\n> > +       rev->sort_order = REV_SORT_BY_COMMIT_DATE;\n> \n> The assignment to rev->sort_order is obvious enough, but the\n> rev->topo_order assignment is quite mysterious to someone coming to\n> this tutorial to learn about revision walking, thus some commentary\n> explaining 'topo_order' would be a good idea.\n\nWill do.\n\n> \n> > +Finally, compare the two. This is a little less helpful without object names or\n> > +dates, but hopefully we get the idea.\n> > +\n> > +----\n> > +$ diff -u commit-date.txt author-date.txt\n> > +----\n> > +\n> > +This display is an indicator for the latency between publishing a commit for\n> > +review the first time, and getting it actually merged into master.\n> \n> Perhaps: s/master/`&`/\n> \n> Even as a long-time contributor to the project, I had to pause over\n> this statement for several seconds before figuring out what it was\n> talking about. Without a long-winded explanation of how topics\n> progress from submission through 'pu' through 'next' through 'master'\n> and finally into a release, the above statement is likely to be\n> mystifying to a newcomer. Perhaps it should be dropped.\n\nSuch an explanation exists in MyFirstContribution.txt. I will include a\nshameless plug to that document here. :)\n\n> \n> > +Let's try one more reordering of commits. `rev_info` exposes a `reverse` flag.\n> > +However, it needs to be applied after `add_head_to_pending()` is called. Find\n> \n> This leaves the reader hanging, wondering why 'reverse' needs to be\n> assigned after add_head_to_pending().\n\nWill address.\n\n> \n> > +== Basic Object Walk\n> > +\n> > +static void walken_show_commit(struct commit *cmt, void *buf)\n> > +{\n> > +        commit_count++;\n> > +}\n> > +----\n> > +\n> > +Since we have the `struct commit` object, we can look at all the same parts that\n> > +we looked at in our earlier commit-only walk. For the sake of this tutorial,\n> > +though, we'll just increment the commit counter and move on.\n> \n> This leaves the reader wondering what 'buf' is and what it's used for.\n> Presumably this is the 'show_data' context mentioned earlier? If so,\n> perhaps name this 'ctxt' or 'context' or something and, because this\n> is a tutorial trying to teach revision walking, say a quick word about\n> how it might be used.\n> \n> > +static void walken_show_object(struct object *obj, const char *str, void *buf)\n> > +{\n> > +        switch (obj->type) {\n> > +        [...]\n> > +        case OBJ_COMMIT:\n> > +                printf(_(\"Unexpectedly encountered a commit in \"\n> > +                         \"walken_show_object!\\n\"));\n> > +                commit_count++;\n> > +                break;\n> > +        default:\n> > +                printf(_(\"Unexpected object type %s!\\n\"),\n> > +                       type_name(obj->type));\n> > +                break;\n> > +        }\n> > +}\n> \n> Modern practice in this project is to start error messages with\n> lowercase and to not punctuate the end (no need for \"!\").\n\nDone.\n \n> Also, same complaint about the mysterious 'str' argument to the\n> callback as for 'buf' mentioned above.\n\nWill do.\n\n> \n> > +To help assure us that we aren't double-counting commits, we'll include some\n> > +complaining if a commit object is routed through our non-commit callback; we'll\n> > +also complain if we see an invalid object type.\n> \n>  Are these two error cases \"impossible\" conditions or can they\n> actually arise in practice? If the former, use die() instead and drop\n> use of _(...) so as to avoid confusing the reader into thinking that\n> the behavior is indeterminate.\n\nAh, these should be impossible. I'll turn them into die().\n\n> \n> > +Our main object walk implementation is substantially different from our commit\n> > +walk implementation, so let's make a new function to perform the object walk. We\n> > +can perform setup which is applicable to all objects here, too, to keep separate\n> > +from setup which is applicable to commit-only walks.\n> > +\n> > +----\n> > +static int walken_object_walk(struct rev_info *rev)\n> > +{\n> > +}\n> > +----\n> \n> This skeleton function definition is populated immediately below, so\n> it's not clear why it needs to be shown here.\n\nYeah, you're right. Removed the skeleton snippet.\n\n> \n> > +We'll start by enabling all types of objects in the `struct rev_info`, and\n> > +asking to have our trees and blobs shown in commit order. We'll also exclude\n> > +promisors as the walk becomes more complicated with those types of objects. When\n> > +our settings are ready, we'll perform the normal revision walk setup and\n> > +initialize our tracking variables.\n> > +\n> > +----\n> > +static int walken_object_walk(struct rev_info *rev)\n> > +{\n> > +        rev->tree_objects = 1;\n> > +        rev->blob_objects = 1;\n> > +        rev->tag_objects = 1;\n> > +        rev->tree_blobs_in_commit_order = 1;\n> > +        rev->exclude_promisor_objects = 1;\n> > +        [...]\n> > +----\n> > +\n> > +Unless you cloned or fetched your repository earlier with a filter,\n> > +`exclude_promisor_objects` is unlikely to make a difference, but we'll turn it\n> > +on just to make sure our lives are simple.  We'll also turn on\n> > +`tree_blobs_in_commit_order`, which means that we will walk a commit's tree and\n> > +everything it points to immediately after we find each commit, as opposed to\n> > +waiting for the end and walking through all trees after the commit history has\n> > +been discovered.\n> \n> This paragraph is repeating much of the information in the paragraph\n> just above the code snippet. One or the other should be dropped or\n> thinned to avoid the duplication.\n\n  We'll start by enabling all types of objects in the `struct rev_info`. Unless    \n  you cloned or fetched your repository earlier with a filter,                     \n  `exclude_promisor_objects` is unlikely to make a difference, but we'll turn it   \n  on just to make sure our lives are simple. We'll also turn on                    \n  `tree_blobs_in_commit_order`, which means that we will walk a commit's tree and  \n  everything it points to immediately after we find each commit, as opposed to     \n  waiting for the end and walking through all trees after the commit history has   \n  been discovered. With the appropriate settings configured, we are ready to call  \n  `prepare_revision_walk()`.\n\n> \n> > +Let's start by calling just the unfiltered walk and reporting our counts.\n> > +Complete your implementation of `walken_object_walk()`:\n> > +\n> > +----\n> > +       traverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n> > +\n> > +       printf(_(\"Object walk completed. Found %d commits, %d blobs, %d tags, \"\n> > +                \"and %d trees.\\n\"), commit_count, blob_count, tag_count,\n> > +              tree_count);\n> \n> Or make the output more useful by having it be machine-parseable (and\n> not localized):\n> \n>     printf(\"commits %d\\nblobs %d\\ntags %d\\ntrees %d\\n\",\n>         commit_count, blob_count, tag_cont, tree_count);\n\nI'm not sure whether I agree, since it's a useless toy command only for human\nparsing.\n\n> \n> > +       return 0;\n> > +}\n> \n> What does the return value signify?\n\nYeah, again I think I can get rid of this; I'll take a look at the final\nsample code and make sure it can go.\n\n> \n> > +Now we can try to run our command! It should take noticeably longer than the\n> > +commit walk, but an examination of the output will give you an idea why - for\n> > +example:\n> > +\n> > +----\n> > +Object walk completed. Found 55733 commits, 100274 blobs, 0 tags, and 104210 trees.\n> > +----\n> > +\n> > +This makes sense. We have more trees than commits because the Git project has\n> > +lots of subdirectories which can change, plus at least one tree per commit. We\n> > +have no tags because we started on a commit (`HEAD`) and while tags can point to\n> > +commits, commits can't point to tags.\n> > +\n> > +NOTE: You will have different counts when you run this yourself! The number of\n> > +objects grows along with the Git project.\n> \n> Not sure if this NOTE is useful; after all, you introduced the output\n> by saying \"for example\".\n\nI think you're probably right, but I'll try to fix this by slightly\nfleshing out the \"for example\" phrasing.\n\n> \n> > +=== Adding a Filter\n> > +\n> > +There are a handful of filters that we can apply to the object walk laid out in\n> > +`Documentation/rev-list-options.txt`. These filters are typically useful for\n> > +operations such as creating packfiles or performing a partial or shallow clone.\n> > +They are defined in `list-objects-filter-options.h`. For the purposes of this\n> > +tutorial we will use the \"tree:1\" filter, which causes the walk to omit all\n> > +trees and blobs which are not directly referenced by commits reachable from the\n> > +commit in `pending` when the walk begins. (In our case, that means we omit trees\n> > +and blobs not directly referenced by HEAD or HEAD's history.)\n> \n> Need some explanation of what 'pending' is, as it's just mysterious as written.\n\nDone. I've tried to explain it by drawing a parallel to BFS tree\ntraversal, although that might be even more confusing as the DAG isn't\nquite the same.\n\n> \n> > +First, we'll need to `#include \"list-objects-filter-options.h`\". Then, we can\n> > +set up the `struct list_objects_filter_options` and `struct oidset` at the top\n> > +of `walken_object_walk()`:\n> > +\n> > +----\n> > +static int walken_object_walk(struct rev_info *rev)\n> > +{\n> > +        struct list_objects_filter_options filter_options = {};\n> > +        struct oidset omitted;\n> > +        oidset_init(&omitted, 0);\n> > +       ...\n> \n> This 'omitted' is so far removed from the description of the 'omitted'\n> argument to traverse_commit_list_filtered() way earlier in the\n> tutorial that a reader is likely to have forgotten what it's about\n> (indeed, I did). Some explanation, even if superficial, is likely\n> warranted here or at least mention that it is explained in more detail\n> below (as I discovered).\n> \n> > +After we run `traverse_commit_list_filtered()` we would also be able to examine\n> > +`omitted`, which is a linked-list of all objects we did not include in our walk.\n> > +Since all omitted objects are included, the performance of\n> > +`traverse_commit_list_filtered()` with a non-null `omitted` arument is equitable\n> \n> s/arument/argument/\n> \n> > +with the performance of `traverse_commit_list()`; so for our purposes, we leave\n> > +it null. It's easy to provide one and iterate over it, though - check `oidset.h`\n> > +for the declaration of the accessor methods for `oidset`.\n> \n> I'm confused. What are we leaving NULL here?\n\nYeah, this isn't very well written. I'll try to rephrase it; I think I\nmeant to leave `omitted` out of the arglist to\n`traverse_comit_list_filtered()` but looks like I didn't manage to do so\nin the actual impl.  I think I'll break out an additional section to show\nhow `--filter-print-omitted` works, instead of just leaving this with an\nRTFM at the end. (This will also end up with a reroll of the example\npatchset, too.)\n> \n> > +=== Changing the Order\n> > +\n> > +Finally, let's demonstrate that you can also reorder walks of all objects, not\n> > +just walks of commits. First, we'll make our handlers chattier - modify\n> > +`walken_show_commit()` and `walken_show_object` to print the object as they go:\n> \n> s/walken_show_object/&()/\n\nDone.\n\n> \n> > +static void walken_show_commit(struct commit *cmt, void *buf)\n> > +{\n> > +        printf(_(\"commit: %s\\n\"), oid_to_hex(&cmt->object.oid));\n> > +        commit_count++;\n> > +}\n> \n> Is there a bunch of trailing whitespace on these lines of the code\n> sample (and in some lines below)?\n\nOh no, there might be. Bad on me for my copy/paste between vim windows\nworkflow; I thought I had trimmed from all of them but guess not. I'll\ncheck over the whole doc and fix it up.\n> \n> > +static void walken_show_object(struct object *obj, const char *str, void *buf)\n> > +{\n> > +        printf(_(\"%s: %s\\n\"), type_name(obj->type), oid_to_hex(&obj->oid));\n> \n> Localizing \"%s: %s\\n\" via _(...) probably doesn't add value, which\n> implies that you might not want to be localizing \"commit\" above\n> either.\n\nThis is closer to machine-readable, so I'll remove the locale.\n\n> \n> > +(Try to leave the counter increment logic in place in `walken_show_object()`.)\n> > +\n> > +With only that change, run again (but save yourself some scrollback):\n> > +\n> > +----\n> > +$ ./bin-wrappers/git walken | head -n 10\n> > +----\n> > +\n> > +Take a look at the top commit with `git show` and the OID you printed; it should\n> > +be the same as the output of `git show HEAD`.\n> \n> I think this is the first use of \"OID\", which might be mysterious and\n> confusing to a newcomer. Earlier, you used SHA-1 and I suggested\n> \"object ID\" instead. Perhaps use the same here, or define OID earlier\n> in the document in place of SHA-1.\n\nYeah, I ended up replacing it above with \"object ID (OID)\" but this is\nfar enough along that I think I'll replace it with \"object ID\" here too.\n\n> \n> > +Next, let's change a setting on our `struct rev_info` within\n> > +`walken_object_walk()`. Find where you're changing the other settings on `rev`,\n> > +such as `rev->tree_objects` and `rev->tree_blobs_in_commit_order`, and add\n> > +another setting at the bottom:\n> \n> Instead of nebulous \"another setting\", mentioning 'reverse' explicitly\n> would make this clearer.\n\nDone.\n\n> \n> > +        rev->tree_objects = 1;\n> > +        rev->blob_objects = 1;\n> > +        rev->tag_objects = 1;\n> > +        rev->tree_blobs_in_commit_order = 1;\n> > +        rev->exclude_promisor_objects = 1;\n> > +        rev->reverse = 1;\n\nThank you so much for taking the time to do a detailed review of this.\nThis is great feedback.\n\n - Emily\n"},{"id":"377378","messageId":"20190617233341.GC100487@google.com","threadId":"51257","inReplyTo":"xmqq7e9tmahm.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-17T23:33:41Z","receivedAt":"2019-06-17T23:33:49Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Mon, Jun 10, 2019 at 01:49:41PM -0700, Junio C Hamano wrote:\n> Emily Shaffer <emilyshaffer@google.com> writes:\n> \n> > +My First Revision Walk\n> > +======================\n> > +\n> > +== What's a Revision Walk?\n> > +\n> > +The revision walk is a key concept in Git - this is the process that underpins\n> > +operations like `git log`, `git blame`, and `git reflog`. Beginning at HEAD, the\n> > +list of objects is found by walking parent relationships between objects. The\n> > +revision walk can also be usedto determine whether or not a given object is\n> > +reachable from the current HEAD pointer.\n> \n> s/usedto/used to/;\nDone.\n> \n> > +We'll put our fiddling into a new command. For fun, let's name it `git walken`.\n> > +Open up a new file `builtin/walken.c` and set up the command handler:\n> > +\n> > +----\n> > +/*\n> > + * \"git walken\"\n> > + *\n> > + * Part of the \"My First Revision Walk\" tutorial.\n> > + */\n> > +\n> > +#include <stdio.h>\n> \n> Bad idea.  In the generic part of the codebase, system headers are\n> supposed to be supplied by including git-compat-util.h (or cache.h\n> or builtin.h, that are common header files that begin by including\n> it and are allowed by CodingGuidelines to be used as such).\nDone.\n> \n> > +#include \"builtin.h\"\n> > +\n> > +int cmd_walken(int argc, const char **argv, const char *prefix)\n> > +{\n> > +        printf(_(\"cmd_walken incoming...\\n\"));\n> > +        return 0;\n> > +}\n> > +----\n> \n> I wonder if it makes sense to use trace instead of printf, as our\n> reader has already seen the psuh example for doing the above.\n\nHmmm. I will think about it and look into the intended use of each. I\nhadn't considered using a different logging method.\n\n> \n> > +Add usage text and `-h` handling, in order to pass the test suite:\n> \n> It is not wrong per-se, and it indeed is a very good practice to\n> make sure that our subcommands consistently gives usage text and\n> short usage.  Encouraging them early is a good idea.\n> \n> But \"in order to pass the test suite\" invites \"eh, the test suite\n> does not pass without usage and -h?  why?\".\n> \n> Either drop the mention of \"the test suite\", or perhaps say\n> something like\n> \n> \tAdd usage text and `-h` handling, like all the subcommands\n> \tshould consistently do (our test suite will notice and\n> \tcomplain if you fail to do so).\n> \n> i.e. the real purpose is consistency and usability; test suite is\n> merely an enforcement mechanism.\n\nYeah, you're right. I'll reword this.\n\n> \n> > +----\n> > +{ \"walken\", cmd_walken, RUN_SETUP },\n> > +----\n> > +\n> > +Add it to the `Makefile` near the line for `builtin\\worktree.o`:\n> \n> Backslash intended?\n\nNope, typo.\n\n\nThanks for the comments, Junio.\n\n - Emily\n"},{"id":"377379","messageId":"20190617235053.GD100487@google.com","threadId":"51257","inReplyTo":"xmqqef41mbmd.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-17T23:50:53Z","receivedAt":"2019-06-17T23:51:01Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Mon, Jun 10, 2019 at 01:25:14PM -0700, Junio C Hamano wrote:\n> Emily Shaffer <emilyshaffer@google.com> writes:\n> \n> > I'll also be mailing an RFC patchset In-Reply-To this message; the RFC\n> > patchset should not be merged to Git, as I intend to host it in my own\n> > mirror as an example. I hosted a similar example for the\n> > MyFirstContribution tutorial; it's visible at\n> > https://github.com/nasamuffin/git/tree/psuh. There might be a better\n> > place to host these so I don't \"own\" them but I'm not sure what it is;\n> > keeping them as a live branch somewhere struck me as an okay way to keep\n> > them from getting stale.\n> \n> Yes, writing the initial version is one thing, but keeping it alive\n> is more work and more important.  As the underlying API changes over\n> time, it will become necessary to update the sample implementation,\n> but for a newbie who wants to learn by building \"walken\" on top of\n> the then-current codebase and API, it would not be so helpful to\n> show \"these 7 patches were for older codebase, and the tip 2 are\n> incremental updates to adjust to the newer API\", so the maintenance\n> of these sample patches may need different paradigm than the norm\n> for our main codebase that values incremental polishing.\n>\nI'm trying to think of how it would end up working if I tried to use a\nGithub workflow. I think it wouldn't - someone would open a PR, and then\nI'd have to rewrite that change into the appropriate commit in the live\nbranch and push the entire branch anew. Considering that workflow leaves\nme doubly convinced that leaving it in my personal fork indefinitely\nmight not be wise (what if I become unable to continue maintaining it)?\n\nI wonder if this is something that might fit well in\none of the more closely-associated mirrors, like gitster/git or\ngitgitgadget/git - although I wonder if those count as \"owned\" by Junio\nand Johannes, respectively. Hmmmm.\n\nMaybe there's a case for storing them as a set of patch files that are\nrevision-controlled somewhere within Documentation/? There was some\ndiscussion on the IRC a few weeks ago about trying to organize these\ntutorials into their own directory to form a sort of \"Git Contribution\n101\" course, maybe it makes sense to store there?\n\n  Documentation/contributing/myfirstcontrib/MyFirstContrib.txt\n  Documentation/contributing/myfirstcontrib/sample/*.patch\n  Documentation/contributing/myfirstrevwalk/MyFirstRevWalk.txt\n  Documentation/contributing/myfirstrevwalk/sample/*.patch\n\nI don't love the idea of maintaining text patches with the expectation\nthat they should cleanly apply always, but it might make the idea that\nthey shouldn't contain 2 patches on the tip for API adjustment more\nclear. And it would be probably pretty easy to inflate and build them\nwith a build target or something. Hmmmmmmmmm.\n\n - Emily\n"},{"id":"377488","messageId":"CAPig+cSyEHUvx39stA0kx1c6kKYO7jk7Sk_Q=etEro_h=3ucOw@mail.gmail.com","threadId":"51257","inReplyTo":"20190617231959.GB100487@google.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-19T08:13:35Z","receivedAt":"2019-06-19T08:13:51Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Jun 17, 2019 at 7:20 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> On Fri, Jun 07, 2019 at 02:21:07AM -0400, Eric Sunshine wrote:\n> > On Thu, Jun 6, 2019 at 9:08 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > > +int cmd_walken(int argc, const char **argv, const char *prefix)\n> > > +{\n> > > +       struct option options[] = {\n> > > +               OPT_END()\n> > > +       };\n> > > +\n> > > +       argc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n> > > +\n> > > +       ...\n> >\n> > Perhaps comment out the \"...\" or remove it altogether to avoid having\n> > the compiler barf when the below instructions tell the reader to build\n> > the command.\n>\n> Hmm. That part I'm not so sure about. I like to use the \"...\" to\n> indicate where the code in the snippet should be added around the other\n> code already in the file - which I suppose it does just as clearly if\n> it's commented - but I also hope folks are not simply copy-pasting\n> blindly from the tutorial.\n>\n> It seems like including uncommented \"...\" in code tutorials is pretty\n> common.\n\nYou're right, and that's not what I was \"complaining\" about. Looking\nback at your original email, I see that I somehow got confused and\ndidn't realize or (quickly) forgot that you had already presented a\n_complete_ cmd_walken() snippet just above that spot, and that the\ncmd_walken() snippet upon which I was commenting was _incomplete_,\nthus the \"...\" was perfectly justified. Not realizing that the\nincomplete cmd_walken() example was just that (incomplete), I\n\"complained\" that the following \"compile the project\" instructions\nwould barf on \"...\".\n\nMaybe I got confused because the tiny cmd_walken() snippets followed\none another so closely (or because I got interrupted several times\nduring the review), but one way to avoid that would be to present a\nsingle _complete_ snippet from the start, followed by a bit of\nexplanation. That is, something like this:\n\n    Open up a new file `builtin/walken.c` and set up the command handler:\n\n    ----\n    /* \"git walken\" -- Part of the \"My First Revision Walk\" tutorial. */\n    #include \"builtin.h\"\n\n    int cmd_walken(int argc, const char **argv, const char *prefix)\n    {\n        const char * const usage[] = {\n            N_(\"git walken\"),\n            NULL,\n        }\n        struct option options[] = {\n            OPT_END()\n        };\n\n        argc = parse_options(argc, argv, prefix, options, usage, 0);\n\n        printf(_(\"cmd_walken incoming...\\n\"));\n        return 0;\n    }\n    ----\n\n    `usage` is the usage message presented by `git -h walken`, and\n    `options` will eventually specify command-line options.\n\n> I don't think I have a good reason to push back on this except that I\n> think \"/* ... */\" is ugly :)\n>\n> I'll go through and replace \"...\" with some actual hints about what's\n> supposed to go there; for example, here I'll replace with \"/* print and\n> return */\".\n\nSeeing as my initial review comment was in error, I'm not sure that\nyou ought to replace \"...\" with anything else.\n\n> > \"invoke anything\" is pretty nebulous, as is the earlier \"components\n> > you may invoke\". A newcomer is unlikely to know what this means, so\n> > perhaps it needs an example (even if just a short parenthetical\n> > comment).\n>\n> I have tried to reword this; I hope this is a little clearer.\n>\n>   Before you begin to examine user configuration for your revision walk, it's\n>   common practice for you to initialize to default any switches that your command\n>   may have, as well as ask any other components you may invoke to initialize as\n>   well (for example, how `git log` also uses the `grep` and `diff` components).\n>   `git log` does this in `init_log_defaults()`; in that case, one global\n>   `decoration_style` is initialized, as well as the grep and diff-UI components.\n\nBy trying to express too many things at once, it's still difficult to\nfollow. Perhaps use shorter, more easily digestible sentences, like\nthis:\n\n    Before examining configuration files which may modify command\n    behavior, set up default state for switches or options your\n    command may have. If your command utilizes other Git components,\n    ask them to set up their default states, as well. For instance,\n    `git log` takes advantage of `grep` and `diff` functionality; its\n    init_log_defaults() sets its own state (`decoration_style`) and\n    asks `grep` and `diff` to initialize themselves by calling their\n    initialization functions.\n\n> > > +static int walken_commit_walk(struct rev_info *rev)\n> > > +{\n> > > +       /* prepare_revision_walk() gets the final steps ready for a revision\n> > > +        * walk. We check the return value for errors. */\n> >\n> > Not at all sure what this comment is trying to say. Also, the second\n> > sentence adds no value to what the code itself already says clearly by\n> > actually checking the return value.\n>\n> Attempted to rephrase. I ended up with:\n>\n>   /*\n>    * prepare_revision_walk() does the final setup needed by revision.h\n>    * before a walk. It may return an error if there is a problem.\n>    */\n>\n> Maybe the second sentence still doesn't serve a purpose, but I was\n> trying to express that prepare_revision_walk() won't die() on its own.\n>\n> >\n> > > +       if (prepare_revision_walk(rev))\n> > > +               die(_(\"revision walk setup failed\"));\n\nAs this is just a toy example, I don't care too strongly about the\nunnecessary second sentence. On the other hand, the tutorial is trying\nto teach people how to contribute to this project, and on this\nproject, that sort of pointless comment is likely to be called out in\nreview. In fact, given that view, the entire comment block is\nunnecessary (it doesn't add any value for anyone reviewing or reading\nthe code), so it might make more sense to drop the comment from the\ncode entirely, and just do a better job explaining in prose above the\nsnippet why you are calling that function. For instance:\n\n    ... Let's start the helper with the call to `prepare_revision_walk()`,\n    which does the final setup of the `rev_info` structure before it can\n    be used.\n\nThe above observation may be more widely applicable than to just this\none instance. Don't use in-code comments for what should be explained\nin prose if the in-code comment adds no value to the code itself (to\nwit, if a reviewer would say \"don't repeat in a comment what the code\nalready says clearly\" or \"don't use a comment to state the obvious\").\n\n> > > +This display is an indicator for the latency between publishing a commit for\n> > > +review the first time, and getting it actually merged into master.\n> >\n> > Perhaps: s/master/`&`/\n> >\n> > Even as a long-time contributor to the project, I had to pause over\n> > this statement for several seconds before figuring out what it was\n> > talking about. Without a long-winded explanation of how topics\n> > progress from submission through 'pu' through 'next' through 'master'\n> > and finally into a release, the above statement is likely to be\n> > mystifying to a newcomer. Perhaps it should be dropped.\n>\n> Such an explanation exists in MyFirstContribution.txt. I will include a\n> shameless plug to that document here. :)\n\nI found that this sort of tangential reference disturbed the flow of\nthe tutorial, leading the mind astray from the otherwise natural\nprogression of the presentation. So, I'm not convinced that talking\nabout the migration of a topic in the Git project itself adds value to\nthis tutorial. The same effect could be seen when commits have been\nre-ordered via git-rebase, too, right? Perhaps mention that instead?\n\n> > > +       printf(_(\"Object walk completed. Found %d commits, %d blobs, %d tags, \"\n> > > +                \"and %d trees.\\n\"), commit_count, blob_count, tag_count,\n> > > +              tree_count);\n> >\n> > Or make the output more useful by having it be machine-parseable (and\n> > not localized):\n> >\n> >     printf(\"commits %d\\nblobs %d\\ntags %d\\ntrees %d\\n\",\n> >         commit_count, blob_count, tag_cont, tree_count);\n>\n> I'm not sure whether I agree, since it's a useless toy command only for human\n> parsing.\n\nTrue, it's not a big deal, and I don't insist upon it. But, if you\nmention in prose that this output is easily machine-parseable, then\nperhaps that nudges the reader a bit in the direction of thinking\nabout porcelain vs. plumbing, which is something a contributor to this\nproject eventually has to be concerned with (the sooner, the better).\n"},{"id":"377534","messageId":"xmqq4l4lr4dy.fsf@gitster-ct.c.googlers.com","threadId":"51257","inReplyTo":"20190617235053.GD100487@google.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-06-19T15:17:29Z","receivedAt":"2019-06-19T15:17:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Emily Shaffer <emilyshaffer@google.com> writes:\n\n> Maybe there's a case for storing them as a set of patch files that are\n> revision-controlled somewhere within Documentation/? There was some\n> discussion on the IRC a few weeks ago about trying to organize these\n> tutorials into their own directory to form a sort of \"Git Contribution\n> 101\" course, maybe it makes sense to store there?\n>\n>   Documentation/contributing/myfirstcontrib/MyFirstContrib.txt\n>   Documentation/contributing/myfirstcontrib/sample/*.patch\n>   Documentation/contributing/myfirstrevwalk/MyFirstRevWalk.txt\n>   Documentation/contributing/myfirstrevwalk/sample/*.patch\n>\n> I don't love the idea of maintaining text patches with the expectation\n> that they should cleanly apply always,...\n\nWell, I actually think the above organization does match the intent\nof the \"My first contribution codelab\" perfectly.  When the codebase,\nthe workflow used by the project, and/or the coding or documentation\nguideline gets updated, the text that documents how to contribute to\nthe project as well as the sample patches must be updated to match\nthe updated reality.\n\nI agree with you that maintaining the *.patch files to always\ncleanly apply is less than ideal.  A topic to update the sample\npatches and tutorial text may be competing with another topic that\nupdates the very API the tutorials are teaching, and the sample\npatches may not apply cleanly when two topics are merged together,\neven if the \"update sample patches and tutorial text\" topic does\nupdate them to match the API at the tip of the topic branch itself.\nOne thing we _could_ do is to pin the target version of the codebase\nfor the sake of tutorial.  IOW, the sample/*.patch may not apply\ncleanly to the version of the tree these patches were taken from,\nbut would always apply cleanly to the most recent released version\nbefore the last update to the tutorial, or something like that.\n\nAlso having to review the patch to sample/*.patch files will be\nunpleasant.\n"},{"id":"377601","messageId":"20190619233556.GH100487@google.com","threadId":"51257","inReplyTo":"CAPig+cSyEHUvx39stA0kx1c6kKYO7jk7Sk_Q=etEro_h=3ucOw@mail.gmail.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-19T23:35:56Z","receivedAt":"2019-06-19T23:36:05Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Wed, Jun 19, 2019 at 04:13:35AM -0400, Eric Sunshine wrote:\n> On Mon, Jun 17, 2019 at 7:20 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > On Fri, Jun 07, 2019 at 02:21:07AM -0400, Eric Sunshine wrote:\n> > > On Thu, Jun 6, 2019 at 9:08 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > > > +int cmd_walken(int argc, const char **argv, const char *prefix)\n> > > > +{\n> > > > +       struct option options[] = {\n> > > > +               OPT_END()\n> > > > +       };\n> > > > +\n> > > > +       argc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n> > > > +\n> > > > +       ...\n> > >\n> > > Perhaps comment out the \"...\" or remove it altogether to avoid having\n> > > the compiler barf when the below instructions tell the reader to build\n> > > the command.\n> >\n> > Hmm. That part I'm not so sure about. I like to use the \"...\" to\n> > indicate where the code in the snippet should be added around the other\n> > code already in the file - which I suppose it does just as clearly if\n> > it's commented - but I also hope folks are not simply copy-pasting\n> > blindly from the tutorial.\n> >\n> > It seems like including uncommented \"...\" in code tutorials is pretty\n> > common.\n> \n> You're right, and that's not what I was \"complaining\" about. Looking\n> back at your original email, I see that I somehow got confused and\n> didn't realize or (quickly) forgot that you had already presented a\n> _complete_ cmd_walken() snippet just above that spot, and that the\n> cmd_walken() snippet upon which I was commenting was _incomplete_,\n> thus the \"...\" was perfectly justified. Not realizing that the\n> incomplete cmd_walken() example was just that (incomplete), I\n> \"complained\" that the following \"compile the project\" instructions\n> would barf on \"...\".\n> \n> Maybe I got confused because the tiny cmd_walken() snippets followed\n> one another so closely (or because I got interrupted several times\n> during the review), but one way to avoid that would be to present a\n> single _complete_ snippet from the start, followed by a bit of\n> explanation. That is, something like this:\n> \n>     Open up a new file `builtin/walken.c` and set up the command handler:\n> \n>     ----\n>     /* \"git walken\" -- Part of the \"My First Revision Walk\" tutorial. */\n>     #include \"builtin.h\"\n> \n>     int cmd_walken(int argc, const char **argv, const char *prefix)\n>     {\n>         const char * const usage[] = {\n>             N_(\"git walken\"),\n>             NULL,\n>         }\n>         struct option options[] = {\n>             OPT_END()\n>         };\n> \n>         argc = parse_options(argc, argv, prefix, options, usage, 0);\n> \n>         printf(_(\"cmd_walken incoming...\\n\"));\n>         return 0;\n>     }\n>     ----\n> \n>     `usage` is the usage message presented by `git -h walken`, and\n>     `options` will eventually specify command-line options.\n\nHmm. I can say that I personally would find that much more difficult to\nfollow interactively, and I'd be tempted to copy-and-paste and skim\nthrough the wall of text if I was presented with such a snippet.\nHowever, I could also imagine the reverse - someone becoming tired of\nhaving their hand held through a fairly straightforward implementation,\nwhen they're perfectly capable of reading a long description and would\njust like to get on with it.\n\nI'm really curious about what others think in this scenario, since I\nimagine it boils down to individual learning styles.\n\n(Maybe we can split the difference and present a complete patch or new\nfunction, followed by a breakdown? That would end up even more verbose\nthan the current approach, though.)\n\n... Now that I'm thinking more about this, and reading some of your\nlater comments on this mail, I think it might be valuable to lean on the\nsample patchset for complete code samples, especially if we figure a\ngood way to distribute the patchset near the tutorial (as Junio and I\nare discussing in another branch of this thread). Then we can keep the\ntutorial concise, but have the complete code available for those who\nprefer to look there.\n\n> \n> > I don't think I have a good reason to push back on this except that I\n> > think \"/* ... */\" is ugly :)\n> >\n> > I'll go through and replace \"...\" with some actual hints about what's\n> > supposed to go there; for example, here I'll replace with \"/* print and\n> > return */\".\n> \n> Seeing as my initial review comment was in error, I'm not sure that\n> you ought to replace \"...\" with anything else.\n> \n> > > \"invoke anything\" is pretty nebulous, as is the earlier \"components\n> > > you may invoke\". A newcomer is unlikely to know what this means, so\n> > > perhaps it needs an example (even if just a short parenthetical\n> > > comment).\n> >\n> > I have tried to reword this; I hope this is a little clearer.\n> >\n> >   Before you begin to examine user configuration for your revision walk, it's\n> >   common practice for you to initialize to default any switches that your command\n> >   may have, as well as ask any other components you may invoke to initialize as\n> >   well (for example, how `git log` also uses the `grep` and `diff` components).\n> >   `git log` does this in `init_log_defaults()`; in that case, one global\n> >   `decoration_style` is initialized, as well as the grep and diff-UI components.\n> \n> By trying to express too many things at once, it's still difficult to\n> follow. Perhaps use shorter, more easily digestible sentences, like\n> this:\n> \n>     Before examining configuration files which may modify command\n>     behavior, set up default state for switches or options your\n>     command may have. If your command utilizes other Git components,\n>     ask them to set up their default states, as well. For instance,\n>     `git log` takes advantage of `grep` and `diff` functionality; its\n>     init_log_defaults() sets its own state (`decoration_style`) and\n>     asks `grep` and `diff` to initialize themselves by calling their\n>     initialization functions.\n\nYeah, I like this a lot. Thanks! I took it word for word; will be adding\nyou to the Helped-by line of the commit.\n\n> As this is just a toy example, I don't care too strongly about the\n> unnecessary second sentence. On the other hand, the tutorial is trying\n> to teach people how to contribute to this project, and on this\n> project, that sort of pointless comment is likely to be called out in\n> review. In fact, given that view, the entire comment block is\n> unnecessary (it doesn't add any value for anyone reviewing or reading\n> the code), so it might make more sense to drop the comment from the\n> code entirely, and just do a better job explaining in prose above the\n> snippet why you are calling that function. For instance:\n> \n>     ... Let's start the helper with the call to `prepare_revision_walk()`,\n>     which does the final setup of the `rev_info` structure before it can\n>     be used.\n> \n> The above observation may be more widely applicable than to just this\n> one instance. Don't use in-code comments for what should be explained\n> in prose if the in-code comment adds no value to the code itself (to\n> wit, if a reviewer would say \"don't repeat in a comment what the code\n> already says clearly\" or \"don't use a comment to state the obvious\").\n\nI'm of two minds about this. On the one hand, I'm somewhat in favor of\nleaving contextual, informational comments in the sample code, so the\nsample code can teach on its own without the tutorial (specifically, I\nmean the patchset that was sent alongside this one as RFC). On the other\nhand, you're right that adding these informational comments doesn't\nmodel best practices for real commits.\n\nI don't have a strong opposition to removing those comments from the\nin-place samples in the tutorial itself. But I do think it's useful to\ninclude them in the sample patchset, which is intended as an additional\nlearning tool, rather than as a pristine code example - especially if we\nmake it clear in the commit messages there.\n\n> \n> > > > +This display is an indicator for the latency between publishing a commit for\n> > > > +review the first time, and getting it actually merged into master.\n> > >\n> > > Perhaps: s/master/`&`/\n> > >\n> > > Even as a long-time contributor to the project, I had to pause over\n> > > this statement for several seconds before figuring out what it was\n> > > talking about. Without a long-winded explanation of how topics\n> > > progress from submission through 'pu' through 'next' through 'master'\n> > > and finally into a release, the above statement is likely to be\n> > > mystifying to a newcomer. Perhaps it should be dropped.\n> >\n> > Such an explanation exists in MyFirstContribution.txt. I will include a\n> > shameless plug to that document here. :)\n> \n> I found that this sort of tangential reference disturbed the flow of\n> the tutorial, leading the mind astray from the otherwise natural\n> progression of the presentation. So, I'm not convinced that talking\n> about the migration of a topic in the Git project itself adds value to\n> this tutorial. The same effect could be seen when commits have been\n> re-ordered via git-rebase, too, right? Perhaps mention that instead?\n\nYeah, that's a good point. I'll try to mention it in a more\nuniversally-applicable way, like you suggested.\n\n> \n> > > > +       printf(_(\"Object walk completed. Found %d commits, %d blobs, %d tags, \"\n> > > > +                \"and %d trees.\\n\"), commit_count, blob_count, tag_count,\n> > > > +              tree_count);\n> > >\n> > > Or make the output more useful by having it be machine-parseable (and\n> > > not localized):\n> > >\n> > >     printf(\"commits %d\\nblobs %d\\ntags %d\\ntrees %d\\n\",\n> > >         commit_count, blob_count, tag_cont, tree_count);\n> >\n> > I'm not sure whether I agree, since it's a useless toy command only for human\n> > parsing.\n> \n> True, it's not a big deal, and I don't insist upon it. But, if you\n> mention in prose that this output is easily machine-parseable, then\n> perhaps that nudges the reader a bit in the direction of thinking\n> about porcelain vs. plumbing, which is something a contributor to this\n> project eventually has to be concerned with (the sooner, the better).\n\nOh, that's a very good point. I'll frame it that way - that's a handy\nplace to slip in some bonus context about Git. Thanks.\n\n  NOTE: We aren't localizing the printf here because we have purposefully\n  formatted it in a machine-parseable way. Commands in Git are divided into\n  \"plumbing\" and \"porcelain\"; the \"plumbing\" commands are machine-parseable and\n  intended for use in scripts, while the \"porcelain\" commands are intended for\n  human interaction. Output intended for script usage doesn't need to be\n  localized; output intended for humans does.\n\n\nThanks again for the review effort.\n\n - Emily\n"},{"id":"377702","messageId":"20190620210649.GA87385@google.com","threadId":"51257","inReplyTo":"xmqq4l4lr4dy.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-20T21:06:49Z","receivedAt":"2019-06-20T21:06:59Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Wed, Jun 19, 2019 at 08:17:29AM -0700, Junio C Hamano wrote:\n> Emily Shaffer <emilyshaffer@google.com> writes:\n> \n> > Maybe there's a case for storing them as a set of patch files that are\n> > revision-controlled somewhere within Documentation/? There was some\n> > discussion on the IRC a few weeks ago about trying to organize these\n> > tutorials into their own directory to form a sort of \"Git Contribution\n> > 101\" course, maybe it makes sense to store there?\n> >\n> >   Documentation/contributing/myfirstcontrib/MyFirstContrib.txt\n> >   Documentation/contributing/myfirstcontrib/sample/*.patch\n> >   Documentation/contributing/myfirstrevwalk/MyFirstRevWalk.txt\n> >   Documentation/contributing/myfirstrevwalk/sample/*.patch\n> >\n> > I don't love the idea of maintaining text patches with the expectation\n> > that they should cleanly apply always,...\n> \n> Well, I actually think the above organization does match the intent\n> of the \"My first contribution codelab\" perfectly.  When the codebase,\n> the workflow used by the project, and/or the coding or documentation\n> guideline gets updated, the text that documents how to contribute to\n> the project as well as the sample patches must be updated to match\n> the updated reality.\n> \n> I agree with you that maintaining the *.patch files to always\n> cleanly apply is less than ideal.  A topic to update the sample\n> patches and tutorial text may be competing with another topic that\n> updates the very API the tutorials are teaching, and the sample\n> patches may not apply cleanly when two topics are merged together,\n> even if the \"update sample patches and tutorial text\" topic does\n> update them to match the API at the tip of the topic branch itself.\n> One thing we _could_ do is to pin the target version of the codebase\n> for the sake of tutorial.  IOW, the sample/*.patch may not apply\n> cleanly to the version of the tree these patches were taken from,\n> but would always apply cleanly to the most recent released version\n> before the last update to the tutorial, or something like that.\n> \n> Also having to review the patch to sample/*.patch files will be\n> unpleasant.\n\nI wonder if we can ease some pain for both of the above issues by\nincluding some scripts to \"inflate\" the patch files into a topic branch,\nor figure out some more easily-reviewed (but more complicated, I\nsuppose) method for sending updates to the sample/*.patch files.\n\nImagining workflows like this:\n\nDoing the tutorial:\n - In worktree a/.\n - Run a magic script which creates a worktree with the sample code, b/.\n - Read through a/Documentation/MyFirstContribution.txt and generate\n   a/builtins/psuh.c, referring to b/builtins/psuh.c if confused.\n\nRebasing the tutorial patches:\n - In worktree a/.\n - Run a magic script which checks out a new branch at the last known\n   good base for the patchset, then applies all the patches.\n - Now faced with, likely, a topic branch based on v<n-1> (where n is\n   latest release).\n - `git rebase v<n> -x (make && ./bin-wrappers/git psuh)`\n - Interactively fix conflicts\n - Run a script to generate a magic interdiff from the old version of\n   patches\n - Mail out magic interdiff to list and get approval\n - (Maybe maintainer does this when interdiff is happy? Maybe updater\n   does this when review looks good?) Run a magic script to regenerate\n   patches from rebased branch, and note somewhere they are based on\n   v<n>\n - Mail sample/*.patch (based on v<n>) to list (if maintainer rolled the\n   patches after interdiff approval, this step can be skipped)\n\n(This seems to still be a lot of steps, even with the magic script..)\n\nAlternatively, for the same process:\n Updater: Run a magic script to create topic branch based on v<n-1>\n   (like before)\n U: `git rebase v<n> -x (make && ./bin-wrappers/git psuh)`\n U: Interactively fix conflicts\n U: Run a script to turn topic branch back into sample/*.patch\n U: Send email with changes to sample/*.patch (this will be ugly and\n    unreadable) - message ID <M1>\n Reviewer: Run a magic script, providing <M1> argument, which grabs the\n    diff-of-.patch and generates an interdiff, or a topic branch based\n    on v<n>\n R: Send comments explaining where issue is (tricky to find where to\n    inline in the diff-of-.patch)\n U: Reroll diff-of-.patch email\n R: Accepts\n Maintainer: Applies diff-of-.patch email normally\n\n I suppose for the first suggestion, there ends up being quite a lot of\n onus on the maintainer, and a lot of trust that there is no difference\n between the RFC easy-to-read interdiff patchset. For the second\n suggestion, there ends up being onus on the reviewers to run some\n magical script. Maybe we can split the difference by expecting Updater\n to provide the interdiff below the --- line? Maybe in practice the\n diff-of-.patch isn't so unreadable, if it's only minor changes needed\n to bring the tutorial up to latest?\n\n I'm not sure there's a way to make this totally painless using email\n tools.\n\n  - Emily\n"},{"id":"377831","messageId":"CAPig+cTCfm3wi2G6q1ZW9wVwQB9zp0TQ96BXKKt8E+qxfR-UWA@mail.gmail.com","threadId":"51257","inReplyTo":"20190619233556.GH100487@google.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-23T18:54:59Z","receivedAt":"2019-06-23T18:55:15Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 19, 2019 at 7:36 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> On Wed, Jun 19, 2019 at 04:13:35AM -0400, Eric Sunshine wrote:\n> > Maybe I got confused because the tiny cmd_walken() snippets followed\n> > one another so closely (or because I got interrupted several times\n> > during the review), but one way to avoid that would be to present a\n> > single _complete_ snippet from the start, followed by a bit of\n> > explanation. [...]\n>\n> Hmm. I can say that I personally would find that much more difficult to\n> follow interactively, and I'd be tempted to copy-and-paste and skim\n> through the wall of text if I was presented with such a snippet.\n> However, I could also imagine the reverse - someone becoming tired of\n> having their hand held through a fairly straightforward implementation,\n> when they're perfectly capable of reading a long description and would\n> just like to get on with it.\n>\n> (Maybe we can split the difference and present a complete patch or new\n> function, followed by a breakdown? That would end up even more verbose\n> than the current approach, though.)\n\nIt might not be that important and may not need fixing considering\nthat I read it correctly the second time, and don't know how I managed\nto get confused on the first read.\n\n> > As this is just a toy example, I don't care too strongly about the\n> > unnecessary second sentence. On the other hand, the tutorial is trying\n> > to teach people how to contribute to this project, and on this\n> > project, that sort of pointless comment is likely to be called out in\n> > review. In fact, given that view, the entire comment block is\n> > unnecessary (it doesn't add any value for anyone reviewing or reading\n> > the code), so it might make more sense to drop the comment from the\n> > code entirely, and just do a better job explaining in prose above the\n> > snippet why you are calling that function. For instance:\n> >\n> >     ... Let's start the helper with the call to `prepare_revision_walk()`,\n> >     which does the final setup of the `rev_info` structure before it can\n> >     be used.\n> >\n> > The above observation may be more widely applicable than to just this\n> > one instance. Don't use in-code comments for what should be explained\n> > in prose if the in-code comment adds no value to the code itself (to\n> > wit, if a reviewer would say \"don't repeat in a comment what the code\n> > already says clearly\" or \"don't use a comment to state the obvious\").\n>\n> I'm of two minds about this. On the one hand, I'm somewhat in favor of\n> leaving contextual, informational comments in the sample code, so the\n> sample code can teach on its own without the tutorial (specifically, I\n> mean the patchset that was sent alongside this one as RFC). On the other\n> hand, you're right that adding these informational comments doesn't\n> model best practices for real commits.\n>\n> I don't have a strong opposition to removing those comments from the\n> in-place samples in the tutorial itself. But I do think it's useful to\n> include them in the sample patchset, which is intended as an additional\n> learning tool, rather than as a pristine code example - especially if we\n> make it clear in the commit messages there.\n\nIndeed, having the comments in the sample patch-set makes sense for\npeople who learn better that way (by seeing a complete piece of code).\n\n> > > > Or make the output more useful by having it be machine-parseable (and\n> > > > not localized):\n> > > >\n> > > >     printf(\"commits %d\\nblobs %d\\ntags %d\\ntrees %d\\n\",\n> > > >         commit_count, blob_count, tag_cont, tree_count);\n> > >\n> > > I'm not sure whether I agree, since it's a useless toy command only for human\n> > > parsing.\n> >\n> > True, it's not a big deal, and I don't insist upon it. But, if you\n> > mention in prose that this output is easily machine-parseable, then\n> > perhaps that nudges the reader a bit in the direction of thinking\n> > about porcelain vs. plumbing, which is something a contributor to this\n> > project eventually has to be concerned with (the sooner, the better).\n>\n> Oh, that's a very good point. I'll frame it that way - that's a handy\n> place to slip in some bonus context about Git. Thanks.\n>\n>   NOTE: We aren't localizing the printf here because we have purposefully\n>   formatted it in a machine-parseable way. Commands in Git are divided into\n>   \"plumbing\" and \"porcelain\"; the \"plumbing\" commands are machine-parseable and\n>   intended for use in scripts, while the \"porcelain\" commands are intended for\n>   human interaction. Output intended for script usage doesn't need to be\n>   localized; output intended for humans does.\n\nI'd go with stronger language than \"doesn't need to be localized\" and\nsay instead that plumbing output \"must not be localized\" since scripts\ndepend upon stable output (and stable API).\n"},{"id":"378090","messageId":"20190626234915.171658-1-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190607010708.46654-1-emilyshaffer@google.com","subject":"[PATCH v2] documentation: add tutorial for revision walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:49:15Z","receivedAt":"2019-06-26T23:49:30Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Existing documentation on revision walks seems to be primarily intended\nas a reference for those already familiar with the procedure. This\ntutorial attempts to give an entry-level guide to a couple of bare-bones\nrevision walks so that new Git contributors can learn the concepts\nwithout having to wade through options parsing or special casing.\n\nThe target audience is a Git contributor who is just getting started\nwith the concept of revision walking. The goal is to prepare this\ncontributor to be able to understand and modify existing commands which\nperform revision walks more easily, although it will also prepare\ncontributors to create new commands which perform walks.\n\nThe tutorial covers a basic overview of the structs involved during\nrevision walk, setting up a basic commit walk, setting up a basic\nall-object walk, and adding some configuration changes to both walk\ntypes. It intentionally does not cover how to create new commands or\nsearch for options from the command line or gitconfigs.\n\nThere is an associated patchset at\nhttps://github.com/nasamuffin/git/tree/revwalk that contains a reference\nimplementation of the code generated by this tutorial.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\n---\nSignificant changes since r1 related to Eric and Junio's comments:\n - Formatting fixes (multiline comments, whitespaces, etc.)\n - Clarifications:\n   - oneline options\n   - `buf` arg in object walk callback\n   - topo_order\n   - placement of reverse setting\n   - '-h' handling\n - Converted patchset to plumbing tool, using trace where appropriate\n - Removed unnecessary return values in walken_commit_walk and\n   walken_object_walk\n - Added an entire section devoted to the `omitted` list\n - Tried to remove unnecessary explanatory comments in tutorial (and\n   added more explanatory comments in sample code)\n\nThere's still the question of how the samples should be\nsource-controlled and kept fresh. I think we left it at \"Maybe checking\nin a stack of *.patch files would be tolerable\", but it didn't sound\nlike anybody was quite happy with that solution.\n\n - Emily\n\n Documentation/Makefile           |   1 +\n Documentation/MyFirstRevWalk.txt | 910 +++++++++++++++++++++++++++++++\n 2 files changed, 911 insertions(+)\n create mode 100644 Documentation/MyFirstRevWalk.txt\n\ndiff --git a/Documentation/Makefile b/Documentation/Makefile\nindex 76f2ecfc1b..91e5da67c4 100644\n--- a/Documentation/Makefile\n+++ b/Documentation/Makefile\n@@ -78,6 +78,7 @@ SP_ARTICLES += $(API_DOCS)\n \n TECH_DOCS += MyFirstContribution\n TECH_DOCS += SubmittingPatches\n+TECH_DOCS += MyFirstRevWalk\n TECH_DOCS += technical/hash-function-transition\n TECH_DOCS += technical/http-protocol\n TECH_DOCS += technical/index-format\ndiff --git a/Documentation/MyFirstRevWalk.txt b/Documentation/MyFirstRevWalk.txt\nnew file mode 100644\nindex 0000000000..ea2f16c4b1\n--- /dev/null\n+++ b/Documentation/MyFirstRevWalk.txt\n@@ -0,0 +1,910 @@\n+My First Revision Walk\n+======================\n+\n+== What's a Revision Walk?\n+\n+The revision walk is a key concept in Git - this is the process that underpins\n+operations like `git log`, `git blame`, and `git reflog`. Beginning at HEAD, the\n+list of objects is found by walking parent relationships between objects. The\n+revision walk can also be used to determine whether or not a given object is\n+reachable from the current HEAD pointer.\n+\n+=== Related Reading\n+\n+- `Documentation/user-manual.txt` under \"Hacking Git\" contains some coverage of\n+  the revision walker in its various incarnations.\n+- `Documentation/technical/api-revision-walking.txt`\n+- https://eagain.net/articles/git-for-computer-scientists/[Git for Computer Scientists]\n+  gives a good overview of the types of objects in Git and what your revision\n+  walk is really describing.\n+\n+== Setting Up\n+\n+Create a new branch from `master`.\n+\n+----\n+git checkout -b revwalk origin/master\n+----\n+\n+We'll put our fiddling into a new command. For fun, let's name it `git walken`.\n+Open up a new file `builtin/walken.c` and set up the command handler:\n+\n+----\n+/*\n+ * \"git walken\"\n+ *\n+ * Part of the \"My First Revision Walk\" tutorial.\n+ */\n+\n+#include \"builtin.h\"\n+\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\ttrace_printf(_(\"cmd_walken incoming...\\n\"));\n+\treturn 0;\n+}\n+----\n+\n+NOTE: `trace_printf()` differs from `printf()` in that it can be turned on or\n+off at runtime. For the purposes of this tutorial, we will write `walken` as\n+though it is intended for use as a \"plumbing\" command: that is, a command which\n+is used primarily in scripts, rather than interactively by humans (a \"porcelain\"\n+command). So we will send our debug output to `trace_printf()` instead. When\n+running, enable trace output by setting the environment variable `GIT_TRACE`.\n+\n+Add usage text and `-h` handling, like all subcommands should consistently do\n+(our test suite will notice and complain if you fail to do so).\n+\n+----\n+const char * const walken_usage[] = {\n+\tN_(\"git walken\"),\n+\tNULL,\n+}\n+\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n+\n+\t...\n+}\n+----\n+\n+Also add the relevant line in `builtin.h` near `cmd_whatchanged()`:\n+\n+----\n+extern int cmd_walken(int argc, const char **argv, const char *prefix);\n+----\n+\n+Include the command in `git.c` in `commands[]` near the entry for `whatchanged`,\n+maintaining alphabetical ordering:\n+\n+----\n+{ \"walken\", cmd_walken, RUN_SETUP },\n+----\n+\n+Add it to the `Makefile` near the line for `builtin\\worktree.o`:\n+\n+----\n+BUILTIN_OBJS += builtin/walken.o\n+----\n+\n+Build and test out your command, without forgetting to ensure the `DEVELOPER`\n+flag is set, and with `GIT_TRACE` enabled so the debug output can be seen:\n+\n+----\n+$ echo DEVELOPER=1 >>config.mak\n+$ make\n+$ GIT_TRACE=1 ./bin-wrappers/git walken\n+----\n+\n+NOTE: For a more exhaustive overview of the new command process, take a look at\n+`Documentation/MyFirstContribution.txt`.\n+\n+NOTE: A reference implementation can be found at TODO LINK.\n+\n+=== `struct rev_cmdline_info`\n+\n+The definition of `struct rev_cmdline_info` can be found in `revision.h`.\n+\n+This struct is contained within the `rev_info` struct and is used to reflect\n+parameters provided by the user over the CLI.\n+\n+`nr` represents the number of `rev_cmdline_entry` present in the array.\n+\n+`alloc` is used by the `ALLOC_GROW` macro. Check\n+`Documentation/technical/api-allocation-growing.txt` - this variable is used to\n+track the allocated size of the list.\n+\n+Per entry, we find:\n+\n+`item` is the object provided upon which to base the revision walk. Items in Git\n+can be blobs, trees, commits, or tags. (See `Documentation/gittutorial-2.txt`.)\n+\n+`name` is the object ID (OID) of the object - a hex string you may be familiar\n+with from using Git to organize your source in the past. Check the tutorial\n+mentioned above towards the top for a discussion of where the OID can come\n+from.\n+\n+`whence` indicates some information about what to do with the parents of the\n+specified object. We'll explore this flag more later on; take a look at\n+`Documentation/revisions.txt` to get an idea of what could set the `whence`\n+value.\n+\n+`flags` are used to hint the beginning of the revision walk and are the first\n+block under the `#include`s in `revision.h`. The most likely ones to be set in\n+the `rev_cmdline_info` are `UNINTERESTING` and `BOTTOM`, but these same flags\n+can be used during the walk, as well.\n+\n+=== `struct rev_info`\n+\n+This one is quite a bit longer, and many fields are only used during the walk\n+by `revision.c` - not configuration options. Most of the configurable flags in\n+`struct rev_info` have a mirror in `Documentation/rev-list-options.txt`. It's a\n+good idea to take some time and read through that document.\n+\n+== Basic Commit Walk\n+\n+First, let's see if we can replicate the output of `git log --oneline`. We'll\n+refer back to the implementation frequently to discover norms when performing\n+a revision walk of our own.\n+\n+To do so, we'll first find all the commits, in order, which preceded the current\n+commit. We'll extract the name and subject of the commit from each.\n+\n+Ideally, we will also be able to find out which ones are currently at the tip of\n+various branches.\n+\n+=== Setting Up\n+\n+Preparing for your revision walk has some distinct stages.\n+\n+1. Perform default setup for this mode, and others which may be invoked.\n+2. Check configuration files for relevant settings.\n+3. Set up the `rev_info` struct.\n+4. Tweak the initialized `rev_info` to suit the current walk.\n+5. Prepare the `rev_info` for the walk.\n+6. Iterate over the objects, processing each one.\n+\n+==== Default Setups\n+\n+Before examining configuration files which may modify command behavior, set up\n+default state for switches or options your command may have. If your command\n+utilizes other Git components, ask them to set up their default states as well.\n+For instance, `git log` takes advantage of `grep` and `diff` functionality, so\n+its `init_log_defaults()` sets its own state (`decoration_style`) and asks\n+`grep` and `diff` to initialize themselves by calling each of their\n+initialization functions.\n+\n+For our purposes, within `git walken`, for the first example we don't intend to\n+use any other components within Git, and we don't have any configuration to do.\n+However, we may want to add some later, so for now, we can add an empty\n+placeholder. Create a new function in `builtin/walken.c`:\n+\n+----\n+static void init_walken_defaults(void)\n+{\n+\t/*\n+\t * We don't actually need the same components `git log` does; leave this\n+\t * empty for now.\n+\t */\n+}\n+----\n+\n+Make sure to add a line invoking it inside of `cmd_walken()`.\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\tinit_walken_defaults();\n+}\n+----\n+\n+==== Configuring From `.gitconfig`\n+\n+Next, we should have a look at any relevant configuration settings (i.e.,\n+settings readable and settable from `git config`). This is done by providing a\n+callback to `git_config()`; within that callback, you can also invoke methods\n+from other components you may need that need to intercept these options. Your\n+callback will be invoked once per each configuration value which Git knows about\n+(global, local, worktree, etc.).\n+\n+Similarly to the default values, we don't have anything to do here yet\n+ourselves; however, we should call `git_default_config()` if we aren't calling\n+any other existing config callbacks.\n+\n+Add a new function to `builtin/walken.c`:\n+\n+----\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\t/*\n+\t * For now, we don't have any custom configuration, so fall back to\n+\t * the default config.\n+\t */\n+\treturn git_default_config(var, value, cb);\n+}\n+----\n+\n+Make sure to invoke `git_config()` with it in your `cmd_walken()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t...\n+\n+\tgit_config(git_walken_config, NULL);\n+\n+\t...\n+}\n+----\n+\n+// TODO: Checking CLI options\n+\n+==== Setting Up `rev_info`\n+\n+Now that we've gathered external configuration and options, it's time to\n+initialize the `rev_info` object which we will use to perform the walk. This is\n+typically done by calling `repo_init_revisions()` with the repository you intend\n+to target, as well as the `prefix` argument of `cmd_walken` and your `rev_info`\n+struct.\n+\n+Add the `struct rev_info` and the `repo_init_revisions()` call:\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t/* This can go wherever you like in your declarations.*/\n+\tstruct rev_info rev;\n+\t...\n+\n+\t/* This should go after the git_config() call. */\n+\trepo_init_revisions(the_repository, &rev, prefix);\n+\n+\t...\n+}\n+----\n+\n+==== Tweaking `rev_info` For the Walk\n+\n+We're getting close, but we're still not quite ready to go. Now that `rev` is\n+initialized, we can modify it to fit our needs. This is usually done within a\n+helper for clarity, so let's add one:\n+\n+----\n+static void final_rev_info_setup(struct rev_info *rev)\n+{\n+\t/*\n+\t * We want to mimic the appearance of `git log --oneline`, so let's\n+\t * force oneline format.\n+\t */\n+\tget_commit_format(\"oneline\", rev);\n+\n+\t/* Start our revision walk at HEAD. */\n+\tadd_head_to_pending(rev);\n+}\n+----\n+\n+[NOTE]\n+====\n+Instead of using the shorthand `add_head_to_pending()`, you could do\n+something like this:\n+----\n+\tstruct setup_revision_opt opt;\n+\n+\tmemset(&opt, 0, sizeof(opt));\n+\topt.def = \"HEAD\";\n+\topt.revarg_opt = REVARG_COMMITTISH;\n+\tsetup_revisions(argc, argv, rev, &opt);\n+----\n+Using a `setup_revision_opt` gives you finer control over your walk's starting\n+point.\n+====\n+\n+Then let's invoke `final_rev_info_setup()` after the call to\n+`repo_init_revisions()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t...\n+\n+\tfinal_rev_info_setup(&rev);\n+\n+\t...\n+}\n+----\n+\n+Later, we may wish to add more arguments to `final_rev_info_setup()`. But for\n+now, this is all we need.\n+\n+==== Preparing `rev_info` For the Walk\n+\n+Now that `rev` is all initialized and configured, we've got one more setup step\n+before we get rolling. We can do this in a helper, which will both prepare the\n+`rev_info` for the walk, and perform the walk itself. Let's start the helper\n+with the call to `prepare_revision_walk()`, which can return an error without\n+dying on its own:\n+\n+----\n+static void walken_commit_walk(struct rev_info *rev)\n+{\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+}\n+----\n+\n+NOTE: `die()` prints to `stderr` and exits the program. Since it will print to\n+`stderr` it's likely to be seen by a human, so we will localize it.\n+\n+==== Performing the Walk!\n+\n+Finally! We are ready to begin the walk itself. Now we can see that `rev_info`\n+can also be used as an iterator; we move to the next item in the walk by using\n+`get_revision()` repeatedly. Add the listed variable declarations at the top and\n+the walk loop below the `prepare_revision_walk()` call within your\n+`walken_commit_walk()`:\n+\n+----\n+static void walken_commit_walk(struct rev_info *rev)\n+{\n+\tstruct commit *commit;\n+\tstruct strbuf prettybuf = STRBUF_INIT;\n+\n+\t...\n+\n+\twhile ((commit = get_revision(rev))) {\n+\t\tif (!commit)\n+\t\t\tcontinue;\n+\n+\t\tstrbuf_reset(&prettybuf);\n+\t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);\n+\t\tputs(prettybuf.buf);\n+\t}\n+}\n+----\n+\n+NOTE: `puts()` prints a `char*` to `stdout`. Since this is the part of the\n+command we expect to be machine-parsed, we're sending it directly to stdout.\n+\n+Give it a shot.\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken\n+----\n+\n+You should see all of the subject lines of all the commits in\n+your tree's history, in order, ending with the initial commit, \"Initial revision\n+of \"git\", the information manager from hell\". Congratulations! You've written\n+your first revision walk. You can play with printing some additional fields\n+from each commit if you're curious; have a look at the functions available in\n+`commit.h`.\n+\n+=== Adding a Filter\n+\n+Next, let's try to filter the commits we see based on their author. This is\n+equivalent to running `git log --author=<pattern>`. We can add a filter by\n+modifying `rev_info.grep_filter`, which is a `struct grep_opt`.\n+\n+First some setup. Add `init_grep_defaults()` to `init_walken_defaults()` and add\n+`grep_config()` to `git_walken_config()`:\n+\n+----\n+static void init_walken_defaults(void)\n+{\n+\tinit_grep_defaults(the_repository);\n+}\n+\n+...\n+\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\tgrep_config(var, value, cb);\n+\treturn git_default_config(var, value, cb);\n+}\n+----\n+\n+Next, we can modify the `grep_filter`. This is done with convenience functions\n+found in `grep.h`. For fun, we're filtering to only commits from folks using a\n+`gmail.com` email address - a not-very-precise guess at who may be working on\n+Git as a hobby. Since we're checking the author, which is a specific line in the\n+header, we'll use the `append_header_grep_pattern()` helper. We can use\n+the `enum grep_header_field` to indicate which part of the commit header we want\n+to search.\n+\n+In `final_rev_info_setup()`, add your filter line:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\tappend_header_grep_pattern(&rev->grep_filter, GREP_HEADER_AUTHOR,\n+\t\t\"gmail\");\n+\tcompile_grep_patterns(&rev->grep_filter);\n+\n+\t...\n+}\n+----\n+\n+`append_header_grep_pattern()` adds your new \"gmail\" pattern to `rev_info`, but\n+it won't work unless we compile it with `compile_grep_patterns()`.\n+\n+NOTE: If you are using `setup_revisions()` (for example, if you are passing a\n+`setup_revision_opt` instead of using `add_head_to_pending()`), you don't need\n+to call `compile_grep_patterns()` because `setup_revisions()` calls it for you.\n+\n+NOTE: We could add the same filter via the `append_grep_pattern()` helper if we\n+wanted to, but `append_header_grep_pattern()` adds the `enum grep_context` and\n+`enum grep_pat_token` for us.\n+\n+=== Changing the Order\n+\n+There are a few ways that we can change the order of the commits during a\n+revision walk. Firstly, we can use the `enum rev_sort_order` to choose from some\n+sane orderings.\n+\n+`topo_order` is the same as `git log --topo-order`: we avoid showing a parent\n+before all of its children have been shown, and we avoid mixing commits which\n+are in different lines of history. (`git help log`'s section on `--topo-order`\n+has a very nice diagram to illustrate this.)\n+\n+Let's see what happens when we run with `REV_SORT_BY_COMMIT_DATE` as opposed to\n+`REV_SORT_BY_AUTHOR_DATE`. Add the following:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\trev->topo_order = 1;\n+\trev->sort_order = REV_SORT_BY_COMMIT_DATE;\n+\n+\t...\n+}\n+----\n+\n+Let's output this into a file so we can easily diff it with the walk sorted by\n+author date.\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken > commit-date.txt\n+----\n+\n+Then, let's sort by author date and run it again.\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\trev->topo_order = 1;\n+\trev->sort_order = REV_SORT_BY_AUTHOR_DATE;\n+\n+\t...\n+}\n+----\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken > author-date.txt\n+----\n+\n+Finally, compare the two. This is a little less helpful without object names or\n+dates, but hopefully we get the idea.\n+\n+----\n+$ diff -u commit-date.txt author-date.txt\n+----\n+\n+This display indicates that commits can be reordered after they're written, for\n+example with `git rebase`.\n+\n+Let's try one more reordering of commits. `rev_info` exposes a `reverse` flag.\n+Set that flag somewhere inside of `final_rev_info_setup()`:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv, const char *prefix,\n+\t\tstruct rev_info *rev)\n+{\n+\t...\n+\n+\trev->reverse = 1;\n+\n+\t...\n+}\n+----\n+\n+Run your walk again and note the difference in order. (If you remove the grep\n+pattern, you should see the last commit this call gives you as your current\n+HEAD.)\n+\n+== Basic Object Walk\n+\n+So far we've been walking only commits. But Git has more types of objects than\n+that! Let's see if we can walk _all_ objects, and find out some information\n+about each one.\n+\n+We can base our work on an example. `git pack-objects` prepares all kinds of\n+objects for packing into a bitmap or packfile. The work we are interested in\n+resides in `builtins/pack-objects.c:get_object_list()`; examination of that\n+function shows that the all-object walk is being performed by\n+`traverse_commit_list()` or `traverse_commit_list_filtered()`. Those two\n+functions reside in `list-objects.c`; examining the source shows that, despite\n+the name, these functions traverse all kinds of objects. Let's have a look at\n+the arguments to `traverse_commit_list_filtered()`, which are a superset of the\n+arguments to the unfiltered version.\n+\n+- `struct list_objects_filter_options *filter_options`: This is a struct which\n+  stores a filter-spec as outlined in `Documentation/rev-list-options.txt`.\n+- `struct rev_info *revs`: This is the `rev_info` used for the walk.\n+- `show_commit_fn show_commit`: A callback which will be used to handle each\n+  individual commit object.\n+- `show_object_fn show_object`: A callback which will be used to handle each\n+  non-commit object (so each blob, tree, or tag).\n+- `void *show_data`: A context buffer which is passed in turn to `show_commit`\n+  and `show_object`.\n+- `struct oidset *omitted`: A linked-list of object IDs which the provided\n+  filter caused to be omitted.\n+\n+It looks like this `traverse_commit_list_filtered()` uses callbacks we provide\n+instead of needing us to call it repeatedly ourselves. Cool! Let's add the\n+callbacks first.\n+\n+For the sake of this tutorial, we'll simply keep track of how many of each kind\n+of object we find. At file scope in `builtin/walken.c` add the following\n+tracking variables:\n+\n+----\n+static int commit_count;\n+static int tag_count;\n+static int blob_count;\n+static int tree_count;\n+----\n+\n+Commits are handled by a different callback than other objects; let's do that\n+one first:\n+\n+----\n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+\tcommit_count++;\n+}\n+----\n+\n+The `cmt` argument is fairly self-explanatory. But it's worth mentioning that\n+the `buf` argument is actually the context buffer that we can provide to the\n+traversal calls - `show_data`, which we mentioned a moment ago.\n+\n+Since we have the `struct commit` object, we can look at all the same parts that\n+we looked at in our earlier commit-only walk. For the sake of this tutorial,\n+though, we'll just increment the commit counter and move on.\n+\n+The callback for non-commits is a little different, as we'll need to check\n+which kind of object we're dealing with:\n+\n+----\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+\tswitch (obj->type) {\n+\tcase OBJ_TREE:\n+\t\ttree_count++;\n+\t\tbreak;\n+\tcase OBJ_BLOB:\n+\t\tblob_count++;\n+\t\tbreak;\n+\tcase OBJ_TAG:\n+\t\ttag_count++;\n+\t\tbreak;\n+\tcase OBJ_COMMIT:\n+\t\tdie(\"unexpectedly encountered a commit in \"\n+\t\t\t \"walken_show_object\\n\");\n+\t\tcommit_count++;\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"unexpected object type %s\\n\", type_name(obj->type));\n+\t\tbreak;\n+\t}\n+}\n+----\n+\n+Again, `obj` is fairly self-explanatory, and we can guess that `buf` is the same\n+context pointer that `walken_show_commit()` receives: the `show_data` argument\n+to `traverse_commit_list()` and `traverse_commit_list_filtered()`. Finally,\n+`str` contains the name of the object, which ends up being something like\n+`foo.txt` (blob), `bar/baz` (tree), or `v1.2.3` (tag).\n+\n+To help assure us that we aren't double-counting commits, we'll include some\n+complaining if a commit object is routed through our non-commit callback; we'll\n+also complain if we see an invalid object type.\n+\n+Our main object walk implementation is substantially different from our commit\n+walk implementation, so let's make a new function to perform the object walk. We\n+can perform setup which is applicable to all objects here, too, to keep separate\n+from setup which is applicable to commit-only walks.\n+\n+We'll start by enabling all types of objects in the `struct rev_info`. Unless\n+you cloned or fetched your repository earlier with a filter,\n+`exclude_promisor_objects` is unlikely to make a difference, but we'll turn it\n+on just to make sure our lives are simple. We'll also turn on\n+`tree_blobs_in_commit_order`, which means that we will walk a commit's tree and\n+everything it points to immediately after we find each commit, as opposed to\n+waiting for the end and walking through all trees after the commit history has\n+been discovered. With the appropriate settings configured, we are ready to call\n+`prepare_revision_walk()`.\n+\n+----\n+static void walken_object_walk(struct rev_info *rev)\n+{\n+\trev->tree_objects = 1;\n+\trev->blob_objects = 1;\n+\trev->tag_objects = 1;\n+\trev->tree_blobs_in_commit_order = 1;\n+\trev->exclude_promisor_objects = 1;\n+\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tcommit_count = 0;\n+\ttag_count = 0;\n+\tblob_count = 0;\n+\ttree_count = 0;\n+----\n+\n+Let's start by calling just the unfiltered walk and reporting our counts.\n+Complete your implementation of `walken_object_walk()`:\n+\n+----\n+\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n+\n+\tprintf(\"commits %d\\nblobs %d\\ntags %d\\ntrees %d\\n\", commit_count,\n+\t\tblob_count, tag_count, tree_count);\n+}\n+----\n+\n+NOTE: This output is intended to be machine-parsed. Therefore, we are not\n+sending it to `trace_printf()`, and we are not localizing it - we need scripts\n+to be able to count on the formatting to be exactly the way it is shown here.\n+If we were intending this output to be read by humans, we would need to localize\n+it with `_()`.\n+\n+Finally, we'll ask `cmd_walken()` to use the object walk instead. Discussing\n+command line options is out of scope for this tutorial, so we'll just hardcode\n+a branch we can change at compile time. Where you call `final_rev_info_setup()`\n+and `walken_commit_walk()`, instead branch like so:\n+\n+----\n+\tif (1) {\n+\t\tadd_head_to_pending(&rev);\n+\t\twalken_object_walk(&rev);\n+\t} else {\n+\t\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n+\t\twalken_commit_walk(&rev);\n+\t}\n+----\n+\n+NOTE: For simplicity, we've avoided all the filters and sorts we applied in\n+`final_rev_info_setup()` and simply added `HEAD` to our pending queue. If you\n+want, you can certainly use the filters we added before by moving\n+`final_rev_info_setup()` out of the conditional and removing the call to\n+`add_head_to_pending()`.\n+\n+Now we can try to run our command! It should take noticeably longer than the\n+commit walk, but an examination of the output will give you an idea why. Your\n+output should look similar to this example, but with different counts:\n+\n+----\n+Object walk completed. Found 55733 commits, 100274 blobs, 0 tags, and 104210 trees.\n+----\n+\n+This makes sense. We have more trees than commits because the Git project has\n+lots of subdirectories which can change, plus at least one tree per commit. We\n+have no tags because we started on a commit (`HEAD`) and while tags can point to\n+commits, commits can't point to tags.\n+\n+NOTE: You will have different counts when you run this yourself! The number of\n+objects grows along with the Git project.\n+\n+=== Adding a Filter\n+\n+There are a handful of filters that we can apply to the object walk laid out in\n+`Documentation/rev-list-options.txt`. These filters are typically useful for\n+operations such as creating packfiles or performing a partial clone. They are\n+defined in `list-objects-filter-options.h`. For the purposes of this tutorial we\n+will use the \"tree:1\" filter, which causes the walk to omit all trees and blobs\n+which are not directly referenced by commits reachable from the commit in\n+`pending` when the walk begins. (`pending` is the list of objects which need to\n+be traversed during a walk; you can imagine a breadth-first tree traversal to\n+help understand. In our case, that means we omit trees and blobs not directly\n+referenced by `HEAD` or `HEAD`'s history, because we begin the walk with only\n+`HEAD` in the `pending` list.)\n+\n+First, we'll need to `#include \"list-objects-filter-options.h`\" and set up the\n+`struct list_objects_filter_options` at the top of the function.\n+\n+----\n+static void walken_object_walk(struct rev_info *rev)\n+{\n+\tstruct list_objects_filter_options filter_options = {};\n+\n+\t...\n+----\n+\n+For now, we are not going to track the omitted objects, so we'll replace those\n+parameters with `NULL`. For the sake of simplicity, we'll add a simple\n+build-time branch to use our filter or not. Replace the line calling\n+`traverse_commit_list()` with the following, which will remind us which kind of\n+walk we've just performed:\n+\n+----\n+\tif (1) {\n+\t\t/* Unfiltered: */\n+\t\ttrace_printf(_(\"Unfiltered object walk.\\n\"));\n+\t\ttraverse_commit_list(rev, walken_show_commit,\n+\t\t\t\twalken_show_object, NULL);\n+\t} else {\n+\t\ttrace_printf(\n+\t\t\t_(\"Filtered object walk with filterspec 'tree:1'.\\n\"));\n+\t\t/*\n+\t\t * We can parse a tree depth of 1 to demonstrate the kind of\n+\t\t * filtering that could occur eg during shallow cloning.\n+\t\t */\n+\t\tparse_list_objects_filter(&filter_options, \"tree:1\");\n+\n+\t\ttraverse_commit_list_filtered(&filter_options, rev,\n+\t\t\twalken_show_commit, walken_show_object, NULL, NULL);\n+\t}\n+----\n+\n+`struct list_objects_filter_options` is usually built directly from a command\n+line argument, so the module provides an easy way to build one from a string.\n+Even though we aren't taking user input right now, we can still build one with\n+a hardcoded string using `parse_list_objects_filter()`.\n+\n+With the filter spec \"tree:1\", we are expecting to see _only_ the root tree for\n+each commit; therefore, the tree object count should be less than or equal to\n+the number of commits. (For an example of why that's true: `git commit --revert`\n+points to the same tree object as its grandparent.)\n+\n+=== Counting Omitted Objects\n+\n+We also have the capability to enumerate all objects which were omitted by a\n+filter, like with `git log --filter=<spec> --filter-print-omitted`. Asking\n+`traverse_commit_list_filtered()` to populate the `omitted` list means that our\n+revision walk does not perform any better than an unfiltered revision walk; all\n+reachable objects are walked in order to populate the list.\n+\n+First, add the `struct oidset` and related items we will use to iterate it:\n+\n+----\n+static void walken_object_walk(\n+\t...\n+\n+\tstruct oidset omitted;\n+\tstruct oidset_iter oit;\n+\tstruct object_id *oid = NULL;\n+\tint omitted_count = 0;\n+\toidset_init(&omitted, 0);\n+\n+\t...\n+----\n+\n+Modify the call to `traverse_commit_list_filtered()` to include your `omitted`\n+object:\n+\n+----\n+\t...\n+\n+\t\ttraverse_commit_list_filtered(&filter_options, rev,\n+\t\t\twalken_show_commit, walken_show_object, NULL, &omitted);\n+\n+\t...\n+----\n+\n+Then, after your traversal, the `oidset` traversal is pretty straightforward.\n+Count all the objects within and modify the print statement:\n+\n+----\n+\t/* Count the omitted objects. */\n+\toidset_iter_init(&omitted, &oit);\n+\n+\twhile ((oid = oidset_iter_next(&oit)))\n+\t\tomitted_count++;\n+\n+\tprintf(\"commits %d\\nblobs %d\\ntags %d\\ntrees%d\\nomitted %d\\n\",\n+\t\tcommit_count, blob_count, tag_count, tree_count, omitted_count);\n+----\n+\n+By running your walk with and without the filter, you should find that the total\n+object count in each case is identical. You can also time each invocation of\n+the `walken` subcommand, with and without `omitted` being passed in, to confirm\n+to yourself the runtime impact of tracking all omitted objects.\n+\n+=== Changing the Order\n+\n+Finally, let's demonstrate that you can also reorder walks of all objects, not\n+just walks of commits. First, we'll make our handlers chattier - modify\n+`walken_show_commit()` and `walken_show_object()` to print the object as they\n+go:\n+\n+----\n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+\ttrace_printf(\"commit: %s\\n\", oid_to_hex(&cmt->object.oid));\n+\tcommit_count++;\n+}\n+\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+\ttrace_printf(\"%s: %s\\n\", type_name(obj->type), oid_to_hex(&obj->oid));\n+\n+\t...\n+}\n+----\n+\n+NOTE: Since we will be examining this output directly as humans, we'll use\n+`trace_printf()` here. Additionally, since this change introduces a significant\n+number of printed lines, using `trace_printf()` will allow us to easily silence\n+those lines without having to recompile.\n+\n+(Leave the counter increment logic in place.)\n+\n+With only that change, run again (but save yourself some scrollback):\n+\n+----\n+$ GIT_TRACE=1 ./bin-wrappers/git walken | head -n 10\n+----\n+\n+Take a look at the top commit with `git show` and the object ID you printed; it\n+should be the same as the output of `git show HEAD`.\n+\n+Next, let's change a setting on our `struct rev_info` within\n+`walken_object_walk()`. Find where you're changing the other settings on `rev`,\n+such as `rev->tree_objects` and `rev->tree_blobs_in_commit_order`, and add the\n+`reverse` setting at the bottom:\n+\n+----\n+\t...\n+\n+\trev->tree_objects = 1;\n+\trev->blob_objects = 1;\n+\trev->tag_objects = 1;\n+\trev->tree_blobs_in_commit_order = 1;\n+\trev->exclude_promisor_objects = 1;\n+\trev->reverse = 1;\n+\n+\t...\n+----\n+\n+Now, run again, but this time, let's grab the last handful of objects instead\n+of the first handful:\n+\n+----\n+$ make\n+$ GIT_TRACE=1 ./bin-wrappers git walken | tail -n 10\n+----\n+\n+The last commit object given should have the same OID as the one we saw at the\n+top before, and running `git show <oid>` with that OID should give you again\n+the same results as `git show HEAD`. Furthermore, if you run and examine the\n+first ten lines again (with `head` instead of `tail` like we did before applying\n+the `reverse` setting), you should see that now the first commit printed is the\n+initial commit, `e83c5163`.\n+\n+== Wrapping Up\n+\n+Let's review. In this tutorial, we:\n+\n+- Built a commit walk from the ground up\n+- Enabled a grep filter for that commit walk\n+- Changed the sort order of that filtered commit walk\n+- Built an object walk (tags, commits, trees, and blobs) from the ground up\n+- Learned how to add a filter-spec to an object walk\n+- Changed the display order of the filtered object walk\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378091","messageId":"20190626235032.177551-1-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626234915.171658-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 00/13] example implementation of revwalk tutorial","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:19Z","receivedAt":"2019-06-26T23:50:53Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Since r1, made some significant changes.\n\n - Added a commit for counting the 'omitted' list, to match the new\n   section added to the tutorial.\n - Added significant comments to allow the sample to better stand on its\n   own.\n - Fixed style issues (die() formatting, etc)\n - Distinguished between human- and machine-readable output with\n   trace_printf() and printf(), to turn the command into plumbing.\n - More changes as mentioned in the tutorial patch.\n\nThanks.\n - Emily\n\nEmily Shaffer (13):\n  walken: add infrastructure for revwalk demo\n  walken: add usage to enable -h\n  walken: add placeholder to initialize defaults\n  walken: add handler to git_config\n  walken: configure rev_info and prepare for walk\n  walken: perform our basic revision walk\n  walken: filter for authors from gmail address\n  walken: demonstrate various topographical sorts\n  walken: demonstrate reversing a revision walk list\n  walken: add unfiltered object walk from HEAD\n  walken: add filtered object walk\n  walken: count omitted objects\n  walken: reverse the object walk order\n\n Makefile         |   1 +\n builtin.h        |   1 +\n builtin/walken.c | 290 +++++++++++++++++++++++++++++++++++++++++++++++\n git.c            |   1 +\n 4 files changed, 293 insertions(+)\n create mode 100644 builtin/walken.c\n\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378092","messageId":"20190626235032.177551-2-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 01/13] walken: add infrastructure for revwalk demo","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:20Z","receivedAt":"2019-06-26T23:50:59Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Begin to add scaffolding for `git walken`, a toy command which we will\nteach to perform a number of revision walks, in order to demonstrate the\nmechanics of revision walking for developers new to the Git project.\n\nThis commit is the beginning of an educational series which correspond\nto the tutorial in Documentation/MyFirstRevWalk.txt.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: I64297621919412f54701e111366e99c4ef0feae3\n---\n Makefile         |  1 +\n builtin.h        |  1 +\n builtin/walken.c | 13 +++++++++++++\n 3 files changed, 15 insertions(+)\n create mode 100644 builtin/walken.c\n\ndiff --git a/Makefile b/Makefile\nindex f58bf14c7b..5bac1dbf8d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1137,6 +1137,7 @@ BUILTIN_OBJS += builtin/var.o\n BUILTIN_OBJS += builtin/verify-commit.o\n BUILTIN_OBJS += builtin/verify-pack.o\n BUILTIN_OBJS += builtin/verify-tag.o\n+BUILTIN_OBJS += builtin/walken.o\n BUILTIN_OBJS += builtin/worktree.o\n BUILTIN_OBJS += builtin/write-tree.o\n \ndiff --git a/builtin.h b/builtin.h\nindex ec7e0954c4..c919736c36 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -242,6 +242,7 @@ int cmd_var(int argc, const char **argv, const char *prefix);\n int cmd_verify_commit(int argc, const char **argv, const char *prefix);\n int cmd_verify_tag(int argc, const char **argv, const char *prefix);\n int cmd_version(int argc, const char **argv, const char *prefix);\n+int cmd_walken(int argc, const char **argv, const char *prefix);\n int cmd_whatchanged(int argc, const char **argv, const char *prefix);\n int cmd_worktree(int argc, const char **argv, const char *prefix);\n int cmd_write_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/walken.c b/builtin/walken.c\nnew file mode 100644\nindex 0000000000..d2772a0e8f\n--- /dev/null\n+++ b/builtin/walken.c\n@@ -0,0 +1,13 @@\n+/*\n+ * \"git walken\"\n+ *\n+ * Part of the \"My First Revision Walk\" tutorial.\n+ */\n+\n+#include \"builtin.h\"\n+\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\ttrace_printf(_(\"cmd_walken incoming...\\n\"));\n+\treturn 0;\n+}\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378093","messageId":"20190626235032.177551-3-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 02/13] walken: add usage to enable -h","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:21Z","receivedAt":"2019-06-26T23:51:01Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"It's expected that Git commands support '-h' in order to provide a\nconsistent user experience (and this expectation is enforced by the\ntest suite). '-h' is captured by parse_options() by default; in order to\nsupport this flag, we add a short usage text to walken.c and invoke\nparse_options().\n\nWith this change, we can now add cmd_walken to the builtins set and\nexpect tests to pass, so we'll do so - cmd_walken is now open for\nbusiness.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: I2919dc1efadb82acb335617ea24371c84b03bbce\n---\n builtin/walken.c | 25 +++++++++++++++++++++++++\n git.c            |  1 +\n 2 files changed, 26 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex d2772a0e8f..9eea51ff70 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -5,9 +5,34 @@\n  */\n \n #include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n+/*\n+ * All builtins are expected to provide a usage to provide a consistent user\n+ * experience.\n+ */\n+const char * const walken_usage[] = {\n+\tN_(\"git walken\"),\n+\tNULL,\n+};\n \n int cmd_walken(int argc, const char **argv, const char *prefix)\n {\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\t/*\n+\t * parse_options() handles showing usage if incorrect options are\n+\t * provided, or if '-h' is passed.\n+\t */\n+\targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n+\n+\t/*\n+\t * This line is \"human-readable\" and we are writing a plumbing command,\n+\t * so we localize it and use the trace library to print only when\n+\t * the GIT_TRACE environment variable is set.\n+\t */\n \ttrace_printf(_(\"cmd_walken incoming...\\n\"));\n \treturn 0;\n }\ndiff --git a/git.c b/git.c\nindex c2eec470c9..2a7fb9714f 100644\n--- a/git.c\n+++ b/git.c\n@@ -601,6 +601,7 @@ static struct cmd_struct commands[] = {\n \t{ \"verify-pack\", cmd_verify_pack },\n \t{ \"verify-tag\", cmd_verify_tag, RUN_SETUP },\n \t{ \"version\", cmd_version },\n+\t{ \"walken\", cmd_walken, RUN_SETUP },\n \t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP },\n \t{ \"worktree\", cmd_worktree, RUN_SETUP | NO_PARSEOPT },\n \t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378094","messageId":"20190626235032.177551-4-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 03/13] walken: add placeholder to initialize defaults","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:22Z","receivedAt":"2019-06-26T23:51:04Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Eventually, we will want a good place to initialize default variables\nfor use during our revision walk(s) in `git walken`. For now, there's\nnothing to do here, but let's add the scaffolding so that it's easy to\ntell where to put the setup later on.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 15 +++++++++++++++\n 1 file changed, 15 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 9eea51ff70..daae4f811a 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -16,6 +16,19 @@ const char * const walken_usage[] = {\n \tNULL,\n };\n \n+/*\n+ * Within init_walken_defaults() we can call into other useful defaults to set\n+ * in the global scope or on the_repository. It's okay to borrow from other\n+ * functions which are doing something relatively similar to yours.\n+ */\n+static void init_walken_defaults(void)\n+{\n+\t/*\n+\t * We don't actually need the same components `git log` does; leave this\n+\t * empty for now.\n+\t */\n+}\n+\n int cmd_walken(int argc, const char **argv, const char *prefix)\n {\n \tstruct option options[] = {\n@@ -28,6 +41,8 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \t */\n \targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n \n+\tinit_walken_defaults();\n+\n \t/*\n \t * This line is \"human-readable\" and we are writing a plumbing command,\n \t * so we localize it and use the trace library to print only when\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378095","messageId":"20190626235032.177551-5-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 04/13] walken: add handler to git_config","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:23Z","receivedAt":"2019-06-26T23:51:07Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"For now, we have no configuration options we want to set up for\nourselves, but in the future we may need to. At the very least, we\nshould invoke git_default_config() for each config option; we will do so\ninside of a skeleton config callback so that we know where to add\nconfiguration handling later on when we need it.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 32 ++++++++++++++++++++++++++++++--\n 1 file changed, 30 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex daae4f811a..2474a0d7b2 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -5,6 +5,7 @@\n  */\n \n #include \"builtin.h\"\n+#include \"config.h\"\n #include \"parse-options.h\"\n \n /*\n@@ -24,11 +25,36 @@ const char * const walken_usage[] = {\n static void init_walken_defaults(void)\n {\n \t/*\n-\t * We don't actually need the same components `git log` does; leave this\n-\t * empty for now.\n+\t * We don't use any other components or have settings to initialize, so\n+\t * leave this empty.\n \t */\n }\n \n+/*\n+ * This method will be called back by git_config(). It is used to gather values\n+ * from the configuration files available to Git.\n+ *\n+ * Each time git_config() finds a configuration file entry, it calls this\n+ * callback. Then, this function should compare it to entries which concern us,\n+ * and make settings changes as necessary.\n+ *\n+ * If we are called with a config setting we care about, we should use one of\n+ * the helpers which exist in config.h to pull out the value for ourselves, i.e.\n+ * git_config_string(...) or git_config_bool(...).\n+ *\n+ * If we don't match anything, we should pass it along to another stakeholder\n+ * who may otherwise care - in log's case, grep, gpg, and diff-ui. For our case,\n+ * we'll ignore everybody else.\n+ */\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\t/*\n+\t * For now, we don't have any custom configuration, so fall back on the\n+\t * default config.\n+\t */\n+\treturn git_default_config(var, value, cb);\n+}\n+\n int cmd_walken(int argc, const char **argv, const char *prefix)\n {\n \tstruct option options[] = {\n@@ -43,6 +69,8 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \n \tinit_walken_defaults();\n \n+\tgit_config(git_walken_config, NULL);\n+\n \t/*\n \t * This line is \"human-readable\" and we are writing a plumbing command,\n \t * so we localize it and use the trace library to print only when\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378096","messageId":"20190626235032.177551-6-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 05/13] walken: configure rev_info and prepare for walk","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:24Z","receivedAt":"2019-06-26T23:51:09Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"`struct rev_info` is what's used by the struct itself.\n`repo_init_revisions()` initializes the struct; then we need to set it\nup for the walk we want to perform, which is done in\n`final_rev_info_setup()`.\n\nThe most important step here is adding the first object we want to walk\nto the pending array. Here, we take the easy road and use\n`add_head_to_pending()`; there is also a way to do it with\n`setup_revision_opt()` and `setup_revisions()` which we demonstrate but\ndo not use. If we were to forget this step, the walk would do nothing -\nthe pending queue would be checked, determined to be empty, and the walk\nwould terminate immediately.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: I76754b740227cf17a449f3f536dbbe37031e6f9a\n---\n builtin/walken.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 50 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 2474a0d7b2..c463eca843 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -5,6 +5,7 @@\n  */\n \n #include \"builtin.h\"\n+#include \"revision.h\"\n #include \"config.h\"\n #include \"parse-options.h\"\n \n@@ -30,6 +31,40 @@ static void init_walken_defaults(void)\n \t */\n }\n \n+/*\n+ * cmd_log calls a second set of init after the repo_init_revisions call. We'll\n+ * mirror those settings in post_repo_init_init.\n+ */\n+static void final_rev_info_setup(int argc, const char **argv, const char *prefix,\n+\t\tstruct rev_info *rev)\n+{\n+\t/*\n+\t * Optional:\n+\t * setup_revision_opt is used to pass options to the setup_revisions()\n+\t * call. It's got some special items for submodules and other types of\n+\t * optimizations, but for now, we'll just point it to HEAD and call it\n+\t * good. First we should make sure to reset it. This is useful for more\n+\t * complicated stuff but a decent shortcut for the first pass is\n+\t * add_head_to_pending().\n+\t */\n+\n+\t/*\n+\t * struct setup_revision_opt opt;\n+\n+\t * memset(&opt, 0, sizeof(opt));\n+\t * opt.def = \"HEAD\";\n+\t * opt.revarg_opt = REVARG_COMMITTISH;\n+\t * setup_revisions(argc, argv, rev, &opt);\n+\t */\n+\n+\t/* Let's force oneline format. */\n+\tget_commit_format(\"oneline\", rev);\n+\trev->verbose_header = 1;\n+\n+\t/* add the HEAD to pending so we can start */\n+\tadd_head_to_pending(rev);\n+}\n+\n /*\n  * This method will be called back by git_config(). It is used to gather values\n  * from the configuration files available to Git.\n@@ -61,6 +96,8 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\tstruct rev_info rev;\n+\n \t/*\n \t * parse_options() handles showing usage if incorrect options are\n \t * provided, or if '-h' is passed.\n@@ -71,6 +108,19 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \n \tgit_config(git_walken_config, NULL);\n \n+\t/*\n+\t * Time to set up the walk. repo_init_revisions sets up rev_info with\n+\t * the defaults, but then you need to make some configuration settings\n+\t * to make it do what's special about your walk.\n+\t */\n+\trepo_init_revisions(the_repository, &rev, prefix);\n+\n+\t/*\n+\t * Before we do the walk, we need to set a starting point by giving it\n+\t * something to go in `pending` - that happens in here\n+\t */\n+\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n+\n \t/*\n \t * This line is \"human-readable\" and we are writing a plumbing command,\n \t * so we localize it and use the trace library to print only when\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378097","messageId":"20190626235032.177551-7-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 06/13] walken: perform our basic revision walk","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:25Z","receivedAt":"2019-06-26T23:51:11Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Add the final steps needed and implement the walk loop itself. We add a\nmethod walken_commit_walk() which performs the final setup to revision.c\nand then iterates over commits from get_revision().\n\nThis basic walk only prints the subject line of each commit in the\nhistory. It is nearly equivalent to `git log --oneline`.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: If6dc5f3c9d14df077b99e42806cf790c96191582\n---\n builtin/walken.c | 43 +++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 43 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex c463eca843..335dcb6b21 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -6,8 +6,11 @@\n \n #include \"builtin.h\"\n #include \"revision.h\"\n+#include \"commit.h\"\n #include \"config.h\"\n #include \"parse-options.h\"\n+#include \"pretty.h\"\n+#include \"line-log.h\"\n \n /*\n  * All builtins are expected to provide a usage to provide a consistent user\n@@ -90,6 +93,41 @@ static int git_walken_config(const char *var, const char *value, void *cb)\n \treturn git_default_config(var, value, cb);\n }\n \n+/*\n+ * walken_commit_walk() is invoked by cmd_walken() after initialization. It\n+ * does the commit walk only.\n+ */\n+static void walken_commit_walk(struct rev_info *rev)\n+{\n+\tstruct commit *commit;\n+\tstruct strbuf prettybuf = STRBUF_INIT;\n+\n+\t/*\n+         * prepare_revision_walk() gets the final steps ready for a revision\n+\t * walk. We check the return value for errors.\n+         */\n+\tif (prepare_revision_walk(rev)) {\n+\t\tdie(_(\"revision walk setup failed\"));\n+\t}\n+\n+\t/*\n+         * Now we can start the real commit walk. get_revision grabs the next\n+\t * revision based on the contents of rev.\n+\t */\n+\trev->diffopt.close_file = 0;\n+\twhile ((commit = get_revision(rev))) {\n+\t\tif (!commit)\n+\t\t\tcontinue;\n+\t\tstrbuf_reset(&prettybuf);\n+\t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);\n+\t\t/*\n+\t\t * We expect this part of the output to be machine-parseable -\n+\t\t * one commit message per line - so we must not localize it.\n+\t\t */\n+\t\tputs(prettybuf.buf);\n+\t}\n+}\n+\n int cmd_walken(int argc, const char **argv, const char *prefix)\n {\n \tstruct option options[] = {\n@@ -115,12 +153,17 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \t */\n \trepo_init_revisions(the_repository, &rev, prefix);\n \n+\t/* We can set our traversal flags here. */\n+\trev.always_show_header = 1;\n+\n \t/*\n \t * Before we do the walk, we need to set a starting point by giving it\n \t * something to go in `pending` - that happens in here\n \t */\n \tfinal_rev_info_setup(argc, argv, prefix, &rev);\n \n+\twalken_commit_walk(&rev);\n+\n \t/*\n \t * This line is \"human-readable\" and we are writing a plumbing command,\n \t * so we localize it and use the trace library to print only when\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378098","messageId":"20190626235032.177551-8-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 07/13] walken: filter for authors from gmail address","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:26Z","receivedAt":"2019-06-26T23:51:14Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"In order to demonstrate how to create grep filters for revision walks,\nfilter the walk performed by cmd_walken() to print only commits which\nare authored by someone with a gmail address.\n\nThis commit demonstrates how to append a grep pattern to a\nrev_info.grep_filter, to teach new contributors how to create their own\nmore generalized grep filters during revision walks.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 335dcb6b21..da2d197914 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -11,6 +11,7 @@\n #include \"parse-options.h\"\n #include \"pretty.h\"\n #include \"line-log.h\"\n+#include \"grep.h\"\n \n /*\n  * All builtins are expected to provide a usage to provide a consistent user\n@@ -28,10 +29,8 @@ const char * const walken_usage[] = {\n  */\n static void init_walken_defaults(void)\n {\n-\t/*\n-\t * We don't use any other components or have settings to initialize, so\n-\t * leave this empty.\n-\t */\n+\t/* Needed by our grep filter. */\n+\tinit_grep_defaults(the_repository);\n }\n \n /*\n@@ -60,6 +59,10 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n \t * setup_revisions(argc, argv, rev, &opt);\n \t */\n \n+\t/* Add a grep pattern to the author line in the header. */\n+\tappend_header_grep_pattern(&rev->grep_filter, GREP_HEADER_AUTHOR, \"gmail\");\n+\tcompile_grep_patterns(&rev->grep_filter);\n+\n \t/* Let's force oneline format. */\n \tget_commit_format(\"oneline\", rev);\n \trev->verbose_header = 1;\n@@ -86,10 +89,7 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n  */\n static int git_walken_config(const char *var, const char *value, void *cb)\n {\n-\t/*\n-\t * For now, we don't have any custom configuration, so fall back on the\n-\t * default config.\n-\t */\n+\tgrep_config(var, value, cb);\n \treturn git_default_config(var, value, cb);\n }\n \n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378099","messageId":"20190626235032.177551-9-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 08/13] walken: demonstrate various topographical sorts","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:27Z","receivedAt":"2019-06-26T23:51:17Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Order the revision walk by author or commit dates, to demonstrate how to\napply topo_sort to a revision walk.\n\nWhile following the tutorial, new contributors are guided to run a walk\nwith each sort and compare the results.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: I7ce2f3e8a77c42001293637ae209087afec4ce2c\n---\n builtin/walken.c | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex da2d197914..6cc451324a 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -69,6 +69,13 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n \n \t/* add the HEAD to pending so we can start */\n \tadd_head_to_pending(rev);\n+\t\n+\t/* Let's play with the sort order. */\n+\trev->topo_order = 1;\n+\n+\t/* Toggle between these and observe the difference. */\n+\trev->sort_order = REV_SORT_BY_COMMIT_DATE;\n+\t/* rev->sort_order = REV_SORT_BY_AUTHOR_DATE; */\n }\n \n /*\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378100","messageId":"20190626235032.177551-10-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 09/13] walken: demonstrate reversing a revision walk list","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:28Z","receivedAt":"2019-06-26T23:51:19Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"The final installment in the tutorial about sorting revision walk\noutputs. This commit reverses the commit list, so that we see newer\ncommits last (handy since we aren't using a pager).\n\nIt's important to note that rev->reverse needs to be set after\nadd_head_to_pending() or before setup_revisions(). (This is mentioned in\nthe accompanying tutorial.)\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 3 +++\n 1 file changed, 3 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 6cc451324a..958923c172 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -69,6 +69,9 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n \n \t/* add the HEAD to pending so we can start */\n \tadd_head_to_pending(rev);\n+\n+\t/* Reverse the order */\n+\trev->reverse = 1;\n \t\n \t/* Let's play with the sort order. */\n \trev->topo_order = 1;\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378101","messageId":"20190626235032.177551-11-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 10/13] walken: add unfiltered object walk from HEAD","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:29Z","receivedAt":"2019-06-26T23:51:22Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Provide a demonstration of a revision walk which traverses all types of\nobject, not just commits. This type of revision walk is used for\noperations such as creating packfiles and performing fetches or clones,\nso it's useful to teach new developers how it works. For starters, only\ndemonstrate the unfiltered version, as this will make the tutorial\neasier to follow.\n\nThis commit is part of a tutorial on revision walking.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: If3b11652ba011b28d29b1c3984dac4a3f80a5f53\n---\n builtin/walken.c | 88 +++++++++++++++++++++++++++++++++++++++++++-----\n 1 file changed, 79 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 958923c172..42b23ba1ec 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -11,6 +11,7 @@\n #include \"parse-options.h\"\n #include \"pretty.h\"\n #include \"line-log.h\"\n+#include \"list-objects.h\"\n #include \"grep.h\"\n \n /*\n@@ -22,6 +23,11 @@ const char * const walken_usage[] = {\n \tNULL,\n };\n \n+static int commit_count;\n+static int tag_count;\n+static int blob_count;\n+static int tree_count;\n+\n /*\n  * Within init_walken_defaults() we can call into other useful defaults to set\n  * in the global scope or on the_repository. It's okay to borrow from other\n@@ -103,6 +109,65 @@ static int git_walken_config(const char *var, const char *value, void *cb)\n \treturn git_default_config(var, value, cb);\n }\n \n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+\tcommit_count++;\n+}\n+\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+\tswitch (obj->type) {\n+\tcase OBJ_TREE:\n+\t\ttree_count++;\n+\t\tbreak;\n+\tcase OBJ_BLOB:\n+\t\tblob_count++;\n+\t\tbreak;\n+\tcase OBJ_TAG:\n+\t\ttag_count++;\n+\t\tbreak;\n+\tcase OBJ_COMMIT:\n+\t\tdie(_(\"unexpectedly encountered a commit in \"\n+\t\t      \"walken_show_object\\n\"));\n+\t\tcommit_count++;\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(_(\"unexpected object type %s\\n\"), type_name(obj->type));\n+\t\tbreak;\n+\t}\n+}\n+\n+/*\n+ * walken_object_walk() is invoked by cmd_walken() after initialization. It does\n+ * a walk of all object types.\n+ */\n+static void walken_object_walk(struct rev_info *rev)\n+{\n+\trev->tree_objects = 1;\n+\trev->blob_objects = 1;\n+\trev->tag_objects = 1;\n+\trev->tree_blobs_in_commit_order = 1;\n+\trev->exclude_promisor_objects = 1;\n+\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tcommit_count = 0;\n+\ttag_count = 0;\n+\tblob_count = 0;\n+\ttree_count = 0;\n+\n+\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n+\n+\t/*\n+\t * This print statement is designed to be script-parseable. Script\n+\t * authors will rely on the output not to change, so we will not\n+\t * localize this string. It will go to stdout directly.\n+\t */\n+\tprintf(\"commits %d\\n blobs %d\\n tags %d\\n trees %d\\n\", commit_count,\n+\t       blob_count, tag_count, tree_count);\n+}\n+\n /*\n  * walken_commit_walk() is invoked by cmd_walken() after initialization. It\n  * does the commit walk only.\n@@ -113,15 +178,15 @@ static void walken_commit_walk(struct rev_info *rev)\n \tstruct strbuf prettybuf = STRBUF_INIT;\n \n \t/*\n-         * prepare_revision_walk() gets the final steps ready for a revision\n+\t * prepare_revision_walk() gets the final steps ready for a revision\n \t * walk. We check the return value for errors.\n-         */\n+\t */\n \tif (prepare_revision_walk(rev)) {\n \t\tdie(_(\"revision walk setup failed\"));\n \t}\n \n \t/*\n-         * Now we can start the real commit walk. get_revision grabs the next\n+\t * Now we can start the real commit walk. get_revision grabs the next\n \t * revision based on the contents of rev.\n \t */\n \trev->diffopt.close_file = 0;\n@@ -166,13 +231,18 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \t/* We can set our traversal flags here. */\n \trev.always_show_header = 1;\n \n-\t/*\n-\t * Before we do the walk, we need to set a starting point by giving it\n-\t * something to go in `pending` - that happens in here\n-\t */\n-\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n \n-\twalken_commit_walk(&rev);\n+\tif (1) {\n+\t\tadd_head_to_pending(&rev);\n+\t\twalken_object_walk(&rev);\n+\t} else {\n+\t\t/*\n+\t\t * Before we do the walk, we need to set a starting point by giving it\n+\t\t * something to go in `pending` - that happens in here\n+\t\t */\n+\t\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n+\t\twalken_commit_walk(&rev);\n+\t}\n \n \t/*\n \t * This line is \"human-readable\" and we are writing a plumbing command,\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378102","messageId":"20190626235032.177551-12-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 11/13] walken: add filtered object walk","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:30Z","receivedAt":"2019-06-26T23:51:24Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Demonstrate how filter specs can be used when performing a revision walk\nof all object types. In this case, tree depth is used. Contributors who\nare following the revision walking tutorial will be encouraged to run\nthe revision walk with and without the filter in order to compare the\nnumber of objects seen in each case.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: I6d22ba153c1afbc780c261c47f1fa03ea478b5ed\n---\n builtin/walken.c | 24 +++++++++++++++++++++++-\n 1 file changed, 23 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 42b23ba1ec..a744d042d8 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -12,6 +12,7 @@\n #include \"pretty.h\"\n #include \"line-log.h\"\n #include \"list-objects.h\"\n+#include \"list-objects-filter-options.h\"\n #include \"grep.h\"\n \n /*\n@@ -143,6 +144,10 @@ static void walken_show_object(struct object *obj, const char *str, void *buf)\n  */\n static void walken_object_walk(struct rev_info *rev)\n {\n+\tstruct list_objects_filter_options filter_options = {};\n+\n+\tprintf(\"walken_object_walk beginning...\\n\");\n+\n \trev->tree_objects = 1;\n \trev->blob_objects = 1;\n \trev->tag_objects = 1;\n@@ -157,7 +162,24 @@ static void walken_object_walk(struct rev_info *rev)\n \tblob_count = 0;\n \ttree_count = 0;\n \n-\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n+\tif (1) {\n+\t\t/* Unfiltered: */\n+\t\ttrace_printf(_(\"Unfiltered object walk.\\n\"));\n+\t\ttraverse_commit_list(rev, walken_show_commit,\n+\t\t\t\twalken_show_object, NULL);\n+\t} else {\n+\t\ttrace_printf(_(\"Filtered object walk with filterspec \"\n+\t\t\t\t\"'tree:1'.\\n\"));\n+\t\t/*\n+\t\t * We can parse a tree depth of 1 to demonstrate the kind of\n+\t\t * filtering that could occur during various operations (see\n+\t\t * `git help rev-list` and read the entry on `--filter`).\n+\t\t */\n+\t\tparse_list_objects_filter(&filter_options, \"tree:1\");\n+\n+\t\ttraverse_commit_list_filtered(&filter_options, rev,\n+\t\t\twalken_show_commit, walken_show_object, NULL, NULL);\n+\t}\n \n \t/*\n \t * This print statement is designed to be script-parseable. Script\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378103","messageId":"20190626235032.177551-13-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 12/13] walken: count omitted objects","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:31Z","receivedAt":"2019-06-26T23:51:27Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"It may be illuminating to see which objects were not included within a\ngiven filter. This also demonstrates, since filter-spec \"tree:1\" is\nused, that the 'omitted' list contains all objects which are omitted,\nnot just the first objects which were omitted - that is, it continues to\ndereference omitted trees and commits.\n\nThis is part of a tutorial on performing revision walks.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 17 ++++++++++++++---\n 1 file changed, 14 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex a744d042d8..dc59ff5009 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -45,7 +45,7 @@ static void init_walken_defaults(void)\n  * mirror those settings in post_repo_init_init.\n  */\n static void final_rev_info_setup(int argc, const char **argv, const char *prefix,\n-\t\tstruct rev_info *rev)\n+\t\t\t\t struct rev_info *rev)\n {\n \t/*\n \t * Optional:\n@@ -145,6 +145,11 @@ static void walken_show_object(struct object *obj, const char *str, void *buf)\n static void walken_object_walk(struct rev_info *rev)\n {\n \tstruct list_objects_filter_options filter_options = {};\n+\tstruct oidset omitted;\n+\tstruct oidset_iter oit;\n+\tstruct object_id *oid = NULL;\n+\tint omitted_count = 0;\n+\toidset_init(&omitted, 0);\n \n \tprintf(\"walken_object_walk beginning...\\n\");\n \n@@ -181,13 +186,19 @@ static void walken_object_walk(struct rev_info *rev)\n \t\t\twalken_show_commit, walken_show_object, NULL, NULL);\n \t}\n \n+\t/* Count the omitted objects. */\n+\toidset_iter_init(&omitted, &oit);\n+\n+\twhile ((oid = oidset_iter_next(&oit)))\n+\t\tomitted_count++;\n+\n \t/*\n \t * This print statement is designed to be script-parseable. Script\n \t * authors will rely on the output not to change, so we will not\n \t * localize this string. It will go to stdout directly.\n \t */\n-\tprintf(\"commits %d\\n blobs %d\\n tags %d\\n trees %d\\n\", commit_count,\n-\t       blob_count, tag_count, tree_count);\n+\tprintf(\"commits %d\\n blobs %d\\n tags %d\\n trees %d omitted %d\\n\",\n+\t       commit_count, blob_count, tag_count, tree_count, omitted_count);\n }\n \n /*\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378104","messageId":"20190626235032.177551-14-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"[RFC PATCH v2 13/13] walken: reverse the object walk order","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-26T23:50:32Z","receivedAt":"2019-06-26T23:51:29Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Demonstrate that just like commit walks, object walks can have their\norder reversed. Additionally, add verbose logging of objects encountered\nin order to let contributors prove to themselves that the walk has\nactually been reversed. With this commit, `git walken` becomes extremely\nchatty - it's recommended to pipe the output through `head` or `tail` or\nto redirect it into a file.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: I91883b209a61ae4d87855878291e487fe36220c4\n---\n builtin/walken.c | 3 +++\n 1 file changed, 3 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex dc59ff5009..37b02887a5 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -112,11 +112,13 @@ static int git_walken_config(const char *var, const char *value, void *cb)\n \n static void walken_show_commit(struct commit *cmt, void *buf)\n {\n+\tprintf(\"commit: %s\\n\", oid_to_hex(&cmt->object.oid));\n \tcommit_count++;\n }\n \n static void walken_show_object(struct object *obj, const char *str, void *buf)\n {\n+\tprintf(\"%s: %s\\n\", type_name(obj->type), oid_to_hex(&obj->oid));\n \tswitch (obj->type) {\n \tcase OBJ_TREE:\n \t\ttree_count++;\n@@ -158,6 +160,7 @@ static void walken_object_walk(struct rev_info *rev)\n \trev->tag_objects = 1;\n \trev->tree_blobs_in_commit_order = 1;\n \trev->exclude_promisor_objects = 1;\n+\trev->reverse = 1;\n \n \tif (prepare_revision_walk(rev))\n \t\tdie(_(\"revision walk setup failed\"));\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378112","messageId":"CAPig+cQxt7rZRkQLpUZabwfLfWpzvVrWNd11RkQvt83K8zPZ7w@mail.gmail.com","threadId":"51257","inReplyTo":"20190626235032.177551-3-emilyshaffer@google.com","subject":"Re: [RFC PATCH v2 02/13] walken: add usage to enable -h","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-27T04:47:25Z","receivedAt":"2019-06-27T04:47:39Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> It's expected that Git commands support '-h' in order to provide a\n> consistent user experience (and this expectation is enforced by the\n> test suite). '-h' is captured by parse_options() by default; in order to\n> support this flag, we add a short usage text to walken.c and invoke\n> parse_options().\n> [...]\n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> ---\n> diff --git a/builtin/walken.c b/builtin/walken.c\n> @@ -5,9 +5,34 @@\n> +const char * const walken_usage[] = {\n> +       N_(\"git walken\"),\n> +       NULL,\n> +};\n\nUnless you expect to reference this from multiple functions, there is\nno need for it to reside here; instead, it can live inside\ncmd_walken(). (And, if you do leave it in the global scope for some\nreason, it should be 'static'.)\n\n>  int cmd_walken(int argc, const char **argv, const char *prefix)\n>  {\n> +       struct option options[] = {\n> +               OPT_END()\n> +       };\n"},{"id":"378113","messageId":"CAPig+cTk9=sd7iqwDT=ynRuAvyh3gj3NKQwkv9_tBo1v28Xysw@mail.gmail.com","threadId":"51257","inReplyTo":"20190626235032.177551-3-emilyshaffer@google.com","subject":"Re: [RFC PATCH v2 02/13] walken: add usage to enable -h","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-27T04:50:28Z","receivedAt":"2019-06-27T04:50:42Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> It's expected that Git commands support '-h' in order to provide a\n> consistent user experience (and this expectation is enforced by the\n> test suite). '-h' is captured by parse_options() by default; in order to\n> support this flag, we add a short usage text to walken.c and invoke\n> parse_options().\n> [...]\n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> ---\n> diff --git a/builtin/walken.c b/builtin/walken.c\n> @@ -5,9 +5,34 @@\n>  int cmd_walken(int argc, const char **argv, const char *prefix)\n>  {\n> +       [...]\n> +       /*\n> +        * This line is \"human-readable\" and we are writing a plumbing command,\n> +        * so we localize it and use the trace library to print only when\n> +        * the GIT_TRACE environment variable is set.\n> +        */\n>         trace_printf(_(\"cmd_walken incoming...\\n\"));\n\nAlso, this in-code comment should have been introduced in patch 1/13,\nnot here in 2/13.\n"},{"id":"378114","messageId":"CAPig+cT4=qMers5QTGCCPJx9=ej6rPUHjA94SqChxXBdJDTdmg@mail.gmail.com","threadId":"51257","inReplyTo":"20190626235032.177551-5-emilyshaffer@google.com","subject":"Re: [RFC PATCH v2 04/13] walken: add handler to git_config","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-27T04:54:15Z","receivedAt":"2019-06-27T04:54:29Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> For now, we have no configuration options we want to set up for\n> ourselves, but in the future we may need to. At the very least, we\n> should invoke git_default_config() for each config option; we will do so\n> inside of a skeleton config callback so that we know where to add\n> configuration handling later on when we need it.\n>\n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> ---\n> diff --git a/builtin/walken.c b/builtin/walken.c\n> @@ -24,11 +25,36 @@ const char * const walken_usage[] = {\n>  static void init_walken_defaults(void)\n>  {\n>         /*\n> -        * We don't actually need the same components `git log` does; leave this\n> -        * empty for now.\n> +        * We don't use any other components or have settings to initialize, so\n> +        * leave this empty.\n>          */\n>  }\n\nMeh, I don't think this change has anything to do with this patch. If\nthe rewritten text is the one you prefer, then just introduce it like\nthat in patch 3/13 where the function itself was introduced.\n"},{"id":"378115","messageId":"CAPig+cRduaOcVyrvhXc2moKSUsZa7-7HLh197xYT035HTQp61w@mail.gmail.com","threadId":"51257","inReplyTo":"20190626235032.177551-6-emilyshaffer@google.com","subject":"Re: [RFC PATCH v2 05/13] walken: configure rev_info and prepare for walk","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-27T05:06:32Z","receivedAt":"2019-06-27T05:06:47Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> `struct rev_info` is what's used by the struct itself.\n\nWhat \"struct itself\"? Do you mean 'struct rev_info' is used by the\n_walk_ itself? Or something?\n\n> `repo_init_revisions()` initializes the struct; then we need to set it\n> up for the walk we want to perform, which is done in\n> `final_rev_info_setup()`.\n> [...]\n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> ---\n> diff --git a/builtin/walken.c b/builtin/walken.c\n> @@ -30,6 +31,40 @@ static void init_walken_defaults(void)\n> +/*\n> + * cmd_log calls a second set of init after the repo_init_revisions call. We'll\n> + * mirror those settings in post_repo_init_init.\n> + */\n\nWhat is 'post_repo_init_init'?\n\nI found the reference to cmd_log() confusing because I was looking for\nit in this patch (as if it was being introduced here). Newcomers might\nbe even more confused. Perhaps if you state explicitly that you're\nreferring to existing code in an existing file, it might be clearer.\nMaybe:\n\n    builtin/log.c:cmd_log() calls a second ...\n\nOverall, I find this entire function comment mystifying.\n\n> +static void final_rev_info_setup(int argc, const char **argv, const char *prefix,\n> +               struct rev_info *rev)\n> +{\n> +       /*\n> +        * Optional:\n> +        * setup_revision_opt is used to pass options to the setup_revisions()\n> +        * call. It's got some special items for submodules and other types of\n> +        * optimizations, but for now, we'll just point it to HEAD and call it\n> +        * good. First we should make sure to reset it. This is useful for more\n> +        * complicated stuff but a decent shortcut for the first pass is\n> +        * add_head_to_pending().\n> +        */\n\nI had to pause over \"call it good\" for several seconds (since I\ncouldn't understand why someone would want to write \"bad\" code) until\nI figured out you meant \"do nothing else\". It would be clearer simply\nto drop that, ending the sentence at \"HEAD\":\n\n    ..., but for now, we'll just point it at HEAD.\n\n> +       /*\n> +        * struct setup_revision_opt opt;\n> +\n> +        * memset(&opt, 0, sizeof(opt));\n> +        * opt.def = \"HEAD\";\n> +        * opt.revarg_opt = REVARG_COMMITTISH;\n> +        * setup_revisions(argc, argv, rev, &opt);\n> +        */\n> +\n> +       /* Let's force oneline format. */\n> +       get_commit_format(\"oneline\", rev);\n> +       rev->verbose_header = 1;\n> +\n> +       /* add the HEAD to pending so we can start */\n> +       add_head_to_pending(rev);\n> +}\n\nIt would be easier for the reader to associate the\nadd_head_to_pending() invocation with the commented-out setting of\n\"HEAD\" via 'setup_revision_opt' if the two bits abutted one another\nwithout being separated by the \"oneline\" gunk.\n"},{"id":"378116","messageId":"CAPig+cQAf6ds6Fobcf+mux-93D09WQJACUXSvwgdyoOznNjsUg@mail.gmail.com","threadId":"51257","inReplyTo":"20190626235032.177551-7-emilyshaffer@google.com","subject":"Re: [RFC PATCH v2 06/13] walken: perform our basic revision walk","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-27T05:16:58Z","receivedAt":"2019-06-27T05:17:12Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> Add the final steps needed and implement the walk loop itself. We add a\n> method walken_commit_walk() which performs the final setup to revision.c\n> and then iterates over commits from get_revision().\n> [...]\n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> ---\n> diff --git a/builtin/walken.c b/builtin/walken.c\n> +/*\n> + * walken_commit_walk() is invoked by cmd_walken() after initialization. It\n> + * does the commit walk only.\n> + */\n\n\"only\" as opposed to what? Maybe just say:\n\n    ... after initialization. It performs the actual commit walk.\n\n> +static void walken_commit_walk(struct rev_info *rev)\n> +{\n> +       struct commit *commit;\n> +       struct strbuf prettybuf = STRBUF_INIT;\n> +\n> +       /*\n> +         * prepare_revision_walk() gets the final steps ready for a revision\n> +        * walk. We check the return value for errors.\n> +         */\n\nYou have some funky mix of spaces and tabs indenting the comment\nlines. Same for the next comment block.\n\n> +       if (prepare_revision_walk(rev)) {\n> +               die(_(\"revision walk setup failed\"));\n> +       }\n> +\n> +       /*\n> +         * Now we can start the real commit walk. get_revision grabs the next\n> +        * revision based on the contents of rev.\n> +        */\n\ns/get_revision/&()/\n\n> +       rev->diffopt.close_file = 0;\n\nWhy this? And, why isn't it set up where other 'rev' options are initialized?\n\n> +       while ((commit = get_revision(rev))) {\n> +               if (!commit)\n> +                       continue;\n\nIf get_revision() returns NULL, then the while-loop exits, which means\nthat the \"if (!commit)\" condition will never be satisfied, thus is\nunnecessary code.\n\n> +               strbuf_reset(&prettybuf);\n> +               pp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);\n> +               /*\n> +                * We expect this part of the output to be machine-parseable -\n> +                * one commit message per line - so we must not localize it.\n> +                */\n> +               puts(prettybuf.buf);\n\nMeh, but there isn't any literal text here to localize anyway, so the\ncomment talking about not localizing it is just confusing.\n\n> +       }\n\nLeaking 'prettybuf'. Add here:\n\n    strbuf_release(&prettybuf);\n\n> +}\n"},{"id":"378117","messageId":"CAPig+cQQCXA-WsbQrC6XoejjF-roqhmcB7iVH1nA+zUDYTN3vQ@mail.gmail.com","threadId":"51257","inReplyTo":"20190626235032.177551-8-emilyshaffer@google.com","subject":"Re: [RFC PATCH v2 07/13] walken: filter for authors from gmail address","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-27T05:20:25Z","receivedAt":"2019-06-27T05:20:39Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> In order to demonstrate how to create grep filters for revision walks,\n> filter the walk performed by cmd_walken() to print only commits which\n> are authored by someone with a gmail address.\n> [...]\n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> ---\n> diff --git a/builtin/walken.c b/builtin/walken.c\n> @@ -60,6 +59,10 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n> +       /* Add a grep pattern to the author line in the header. */\n\nThis sounds as if we are adding something to the author line, which is\nconfusing. Maybe say instead:\n\n    /* Apply a 'grep' pattern to the author header. */\n\n> +       append_header_grep_pattern(&rev->grep_filter, GREP_HEADER_AUTHOR, \"gmail\");\n> +       compile_grep_patterns(&rev->grep_filter);\n"},{"id":"378118","messageId":"CAPig+cQGcNS476e+eDqyyfeVWDsi4=BGS=aTvYZ3=6BdP-0_Uw@mail.gmail.com","threadId":"51257","inReplyTo":"20190626235032.177551-9-emilyshaffer@google.com","subject":"Re: [RFC PATCH v2 08/13] walken: demonstrate various topographical sorts","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-27T05:22:10Z","receivedAt":"2019-06-27T05:22:24Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> Order the revision walk by author or commit dates, to demonstrate how to\n\ns/,//\n\n> apply topo_sort to a revision walk.\n>\n> While following the tutorial, new contributors are guided to run a walk\n> with each sort and compare the results.\n>\n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n"},{"id":"378120","messageId":"CAPig+cQh2rEW271KziUyYGZyPXrFV8z_OV6UzY=-e4pOrF_zsw@mail.gmail.com","threadId":"51257","inReplyTo":"20190626235032.177551-10-emilyshaffer@google.com","subject":"Re: [RFC PATCH v2 09/13] walken: demonstrate reversing a revision walk list","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-27T05:26:19Z","receivedAt":"2019-06-27T05:26:33Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> The final installment in the tutorial about sorting revision walk\n> outputs. This commit reverses the commit list, so that we see newer\n> commits last (handy since we aren't using a pager).\n>\n> It's important to note that rev->reverse needs to be set after\n> add_head_to_pending() or before setup_revisions(). (This is mentioned in\n> the accompanying tutorial.)\n\nThis leaves the reader wondering \"why that requirement?\". Is it\nbecause those functions may change the value or otherwise depend upon\nthe value?\n\nAlso, something this important probably deserves an in-code comment\n(and need not be mentioned in the commit message if the in-code\ncomment explains it well.)\n\n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> ---\n> diff --git a/builtin/walken.c b/builtin/walken.c\n> @@ -69,6 +69,9 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n>         /* add the HEAD to pending so we can start */\n>         add_head_to_pending(rev);\n> +\n> +       /* Reverse the order */\n> +       rev->reverse = 1;\n"},{"id":"378121","messageId":"CAPig+cR7rHokaTtZcJJg8trJ14xO3hdeWHqLnB4aOnEUak051w@mail.gmail.com","threadId":"51257","inReplyTo":"20190626235032.177551-11-emilyshaffer@google.com","subject":"Re: [RFC PATCH v2 10/13] walken: add unfiltered object walk from HEAD","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-27T05:37:58Z","receivedAt":"2019-06-27T05:38:13Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> Provide a demonstration of a revision walk which traverses all types of\n> object, not just commits. This type of revision walk is used for\n> operations such as creating packfiles and performing fetches or clones,\n> so it's useful to teach new developers how it works. For starters, only\n> demonstrate the unfiltered version, as this will make the tutorial\n> easier to follow.\n> [...]\n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> ---\n> diff --git a/builtin/walken.c b/builtin/walken.c\n> @@ -103,6 +109,65 @@ static int git_walken_config(const char *var, const char *value, void *cb)\n> +static void walken_show_commit(struct commit *cmt, void *buf)\n> +{\n> +       commit_count++;\n> +}\n> +\n> +static void walken_show_object(struct object *obj, const char *str, void *buf)\n> +{\n> +       switch (obj->type) {\n> +       [...]\n> +       case OBJ_TAG:\n> +               tag_count++;\n> +               break;\n> +       case OBJ_COMMIT:\n> +               die(_(\"unexpectedly encountered a commit in \"\n> +                     \"walken_show_object\\n\"));\n> +               commit_count++;\n\nThe \"commit_count++\" line is not only dead code, but it also confuses\nthe reader (or makes the reader think that the author of this code\ndoesn't understand C programming). You should drop this line.\n\n> +               break;\n> +       default:\n> +               die(_(\"unexpected object type %s\\n\"), type_name(obj->type));\n> +               break;\n\nLikewise, this \"break\" (and the one in the OBJ_COMMIT case) are dead\ncode which should be dropped to avoid confusing the reader.\n\nDon't localize the die() message via _() here or in the preceding\nOBJ_COMMIT case.\n\nThe two die() messages are unnecessarily dissimilar. Try to unify them\nso that they read in the same way.\n\n> +       }\n> +}> @@ -113,15 +178,15 @@ static void walken_commit_walk(struct rev_info *rev)\n>         /*\n> -         * prepare_revision_walk() gets the final steps ready for a revision\n> +        * prepare_revision_walk() gets the final steps ready for a revision\n>          * walk. We check the return value for errors.\n> -         */\n> +        */\n>         /*\n> -         * Now we can start the real commit walk. get_revision grabs the next\n> +        * Now we can start the real commit walk. get_revision grabs the next\n>          * revision based on the contents of rev.\n>          */\n\nI think these are just correcting whitespace/indentation errors I\npointed out in an earlier patch (so they are unnecessary noise in this\npatch).\n"},{"id":"378122","messageId":"CAPig+cSJLbeVawxRhHwqLEXBr6-3QoaYd+1zF169m5N=kq4J4Q@mail.gmail.com","threadId":"51257","inReplyTo":"20190626235032.177551-12-emilyshaffer@google.com","subject":"Re: [RFC PATCH v2 11/13] walken: add filtered object walk","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-27T05:42:45Z","receivedAt":"2019-06-27T05:42:59Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> Demonstrate how filter specs can be used when performing a revision walk\n> of all object types. In this case, tree depth is used. Contributors who\n> are following the revision walking tutorial will be encouraged to run\n> the revision walk with and without the filter in order to compare the\n> number of objects seen in each case.\n>\n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> ---\n> diff --git a/builtin/walken.c b/builtin/walken.c\n> @@ -143,6 +144,10 @@ static void walken_show_object(struct object *obj, const char *str, void *buf)\n>  static void walken_object_walk(struct rev_info *rev)\n>  {\n> +       struct list_objects_filter_options filter_options = {};\n> +\n> +       printf(\"walken_object_walk beginning...\\n\");\n\nIs this debugging code which you accidentally left in? Or is it meant\nto use trace_printf()? Or something else? If it is a genuine message,\nshould it be localizable?\n\n> @@ -157,7 +162,24 @@ static void walken_object_walk(struct rev_info *rev)\n>         blob_count = 0;\n>         tree_count = 0;\n>\n> -       traverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n> +       if (1) {\n> +               /* Unfiltered: */\n\nThe subject talks about adding a _filtered_ object walk (which is in\nthe 'else' arm), so should this be \"if (0)\" instead?\n\n> +               trace_printf(_(\"Unfiltered object walk.\\n\"));\n> +               traverse_commit_list(rev, walken_show_commit,\n> +                               walken_show_object, NULL);\n> +       } else {\n> +               trace_printf(_(\"Filtered object walk with filterspec \"\n> +                               \"'tree:1'.\\n\"));\n> +               /*\n> +                * We can parse a tree depth of 1 to demonstrate the kind of\n> +                * filtering that could occur during various operations (see\n> +                * `git help rev-list` and read the entry on `--filter`).\n> +                */\n> +               parse_list_objects_filter(&filter_options, \"tree:1\");\n> +\n> +               traverse_commit_list_filtered(&filter_options, rev,\n> +                       walken_show_commit, walken_show_object, NULL, NULL);\n> +       }\n"},{"id":"378123","messageId":"CAPig+cTQ3HG6tvWD=7ZqXF-HKTfqUjqmOW95d-LOaGv3T9HpbQ@mail.gmail.com","threadId":"51257","inReplyTo":"20190626235032.177551-13-emilyshaffer@google.com","subject":"Re: [RFC PATCH v2 12/13] walken: count omitted objects","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-27T05:44:57Z","receivedAt":"2019-06-27T05:45:10Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> It may be illuminating to see which objects were not included within a\n> given filter. This also demonstrates, since filter-spec \"tree:1\" is\n> used, that the 'omitted' list contains all objects which are omitted,\n> not just the first objects which were omitted - that is, it continues to\n> dereference omitted trees and commits.\n> [...]\n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> ---\n> diff --git a/builtin/walken.c b/builtin/walken.c\n> @@ -45,7 +45,7 @@ static void init_walken_defaults(void)\n>  static void final_rev_info_setup(int argc, const char **argv, const char *prefix,\n> -               struct rev_info *rev)\n> +                                struct rev_info *rev)\n\nUse the correct indentation in the patch which introduces this code\nrather than adjusting it in this patch.\n"},{"id":"378184","messageId":"20190627184051.GA245941@google.com","threadId":"51257","inReplyTo":"CAPig+cQxt7rZRkQLpUZabwfLfWpzvVrWNd11RkQvt83K8zPZ7w@mail.gmail.com","subject":"Re: [RFC PATCH v2 02/13] walken: add usage to enable -h","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-27T18:40:51Z","receivedAt":"2019-06-27T18:40:59Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Thu, Jun 27, 2019 at 12:47:25AM -0400, Eric Sunshine wrote:\n> On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > It's expected that Git commands support '-h' in order to provide a\n> > consistent user experience (and this expectation is enforced by the\n> > test suite). '-h' is captured by parse_options() by default; in order to\n> > support this flag, we add a short usage text to walken.c and invoke\n> > parse_options().\n> > [...]\n> > Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> > ---\n> > diff --git a/builtin/walken.c b/builtin/walken.c\n> > @@ -5,9 +5,34 @@\n> > +const char * const walken_usage[] = {\n> > +       N_(\"git walken\"),\n> > +       NULL,\n> > +};\n> \n> Unless you expect to reference this from multiple functions, there is\n> no need for it to reside here; instead, it can live inside\n> cmd_walken(). (And, if you do leave it in the global scope for some\n> reason, it should be 'static'.)\n\nThanks, done.\n\n> \n> >  int cmd_walken(int argc, const char **argv, const char *prefix)\n> >  {\n> > +       struct option options[] = {\n> > +               OPT_END()\n> > +       };\n\nFixed the comment from your other mail too.\n\nThanks!\n"},{"id":"378186","messageId":"20190627184707.GB245941@google.com","threadId":"51257","inReplyTo":"CAPig+cT4=qMers5QTGCCPJx9=ej6rPUHjA94SqChxXBdJDTdmg@mail.gmail.com","subject":"Re: [RFC PATCH v2 04/13] walken: add handler to git_config","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-27T18:47:07Z","receivedAt":"2019-06-27T18:47:14Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Thu, Jun 27, 2019 at 12:54:15AM -0400, Eric Sunshine wrote:\n> On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > For now, we have no configuration options we want to set up for\n> > ourselves, but in the future we may need to. At the very least, we\n> > should invoke git_default_config() for each config option; we will do so\n> > inside of a skeleton config callback so that we know where to add\n> > configuration handling later on when we need it.\n> >\n> > Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> > ---\n> > diff --git a/builtin/walken.c b/builtin/walken.c\n> > @@ -24,11 +25,36 @@ const char * const walken_usage[] = {\n> >  static void init_walken_defaults(void)\n> >  {\n> >         /*\n> > -        * We don't actually need the same components `git log` does; leave this\n> > -        * empty for now.\n> > +        * We don't use any other components or have settings to initialize, so\n> > +        * leave this empty.\n> >          */\n> >  }\n> \n> Meh, I don't think this change has anything to do with this patch. If\n> the rewritten text is the one you prefer, then just introduce it like\n> that in patch 3/13 where the function itself was introduced.\n\nWhoops, yeah. I removed this change, I don't think it's significant.\n"},{"id":"378189","messageId":"20190627185627.GC245941@google.com","threadId":"51257","inReplyTo":"CAPig+cRduaOcVyrvhXc2moKSUsZa7-7HLh197xYT035HTQp61w@mail.gmail.com","subject":"Re: [RFC PATCH v2 05/13] walken: configure rev_info and prepare for walk","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-27T18:56:27Z","receivedAt":"2019-06-27T18:56:35Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Thu, Jun 27, 2019 at 01:06:32AM -0400, Eric Sunshine wrote:\n> On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > `struct rev_info` is what's used by the struct itself.\n> \n> What \"struct itself\"? Do you mean 'struct rev_info' is used by the\n> _walk_ itself? Or something?\n\nYep, that's the one. Thanks for the fresh eyes.\n\n> \n> > `repo_init_revisions()` initializes the struct; then we need to set it\n> > up for the walk we want to perform, which is done in\n> > `final_rev_info_setup()`.\n> > [...]\n> > Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> > ---\n> > diff --git a/builtin/walken.c b/builtin/walken.c\n> > @@ -30,6 +31,40 @@ static void init_walken_defaults(void)\n> > +/*\n> > + * cmd_log calls a second set of init after the repo_init_revisions call. We'll\n> > + * mirror those settings in post_repo_init_init.\n> > + */\n> \n> What is 'post_repo_init_init'?\n> \n> I found the reference to cmd_log() confusing because I was looking for\n> it in this patch (as if it was being introduced here). Newcomers might\n> be even more confused. Perhaps if you state explicitly that you're\n> referring to existing code in an existing file, it might be clearer.\n> Maybe:\n> \n>     builtin/log.c:cmd_log() calls a second ...\n> \n> Overall, I find this entire function comment mystifying.\n\nYeah, this is very stale and never got updated when I realized cmd_log()\nwas calling two init functions for apparently legacy reasons, and I\ndidn't need to mirror it here. Again a case where fresh eyes caught\nsomething that became invisible after I stared at it for weeks. I really\nappreciate you doing the deep review, Eric.\n\nI've replaced it:\n\n /*\n  * Perform configuration for commit walk here. Within this function we set a\n  * starting point, and can customize our walk in various ways.\n  */\n\n> \n> > +static void final_rev_info_setup(int argc, const char **argv, const char *prefix,\n> > +               struct rev_info *rev)\n> > +{\n> > +       /*\n> > +        * Optional:\n> > +        * setup_revision_opt is used to pass options to the setup_revisions()\n> > +        * call. It's got some special items for submodules and other types of\n> > +        * optimizations, but for now, we'll just point it to HEAD and call it\n> > +        * good. First we should make sure to reset it. This is useful for more\n> > +        * complicated stuff but a decent shortcut for the first pass is\n> > +        * add_head_to_pending().\n> > +        */\n> \n> I had to pause over \"call it good\" for several seconds (since I\n> couldn't understand why someone would want to write \"bad\" code) until\n> I figured out you meant \"do nothing else\". It would be clearer simply\n> to drop that, ending the sentence at \"HEAD\":\n> \n>     ..., but for now, we'll just point it at HEAD.\n\nDefinitely. Done.\n\n> \n> > +       /*\n> > +        * struct setup_revision_opt opt;\n> > +\n> > +        * memset(&opt, 0, sizeof(opt));\n> > +        * opt.def = \"HEAD\";\n> > +        * opt.revarg_opt = REVARG_COMMITTISH;\n> > +        * setup_revisions(argc, argv, rev, &opt);\n> > +        */\n> > +\n> > +       /* Let's force oneline format. */\n> > +       get_commit_format(\"oneline\", rev);\n> > +       rev->verbose_header = 1;\n> > +\n> > +       /* add the HEAD to pending so we can start */\n> > +       add_head_to_pending(rev);\n> > +}\n> \n> It would be easier for the reader to associate the\n> add_head_to_pending() invocation with the commented-out setting of\n> \"HEAD\" via 'setup_revision_opt' if the two bits abutted one another\n> without being separated by the \"oneline\" gunk.\n\nGood point, done.\n"},{"id":"378197","messageId":"20190627205439.GD245941@google.com","threadId":"51257","inReplyTo":"CAPig+cQAf6ds6Fobcf+mux-93D09WQJACUXSvwgdyoOznNjsUg@mail.gmail.com","subject":"Re: [RFC PATCH v2 06/13] walken: perform our basic revision walk","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-27T20:54:39Z","receivedAt":"2019-06-27T20:54:47Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Thu, Jun 27, 2019 at 01:16:58AM -0400, Eric Sunshine wrote:\n> On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > Add the final steps needed and implement the walk loop itself. We add a\n> > method walken_commit_walk() which performs the final setup to revision.c\n> > and then iterates over commits from get_revision().\n> > [...]\n> > Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> > ---\n> > diff --git a/builtin/walken.c b/builtin/walken.c\n> > +/*\n> > + * walken_commit_walk() is invoked by cmd_walken() after initialization. It\n> > + * does the commit walk only.\n> > + */\n> \n> \"only\" as opposed to what? Maybe just say:\n> \n>     ... after initialization. It performs the actual commit walk.\n\nDone.\n\n> \n> > +static void walken_commit_walk(struct rev_info *rev)\n> > +{\n> > +       struct commit *commit;\n> > +       struct strbuf prettybuf = STRBUF_INIT;\n> > +\n> > +       /*\n> > +         * prepare_revision_walk() gets the final steps ready for a revision\n> > +        * walk. We check the return value for errors.\n> > +         */\n> \n> You have some funky mix of spaces and tabs indenting the comment\n> lines. Same for the next comment block.\n\nDone.\n\n> \n> > +       if (prepare_revision_walk(rev)) {\n> > +               die(_(\"revision walk setup failed\"));\n> > +       }\n> > +\n> > +       /*\n> > +         * Now we can start the real commit walk. get_revision grabs the next\n> > +        * revision based on the contents of rev.\n> > +        */\n> \n> s/get_revision/&()/\n\nDone.\n\n> \n> > +       rev->diffopt.close_file = 0;\n> \n> Why this? And, why isn't it set up where other 'rev' options are initialized?\n\nRemoved. Artifact of closely mirroring log.\n\n> \n> > +       while ((commit = get_revision(rev))) {\n> > +               if (!commit)\n> > +                       continue;\n> \n> If get_revision() returns NULL, then the while-loop exits, which means\n> that the \"if (!commit)\" condition will never be satisfied, thus is\n> unnecessary code.\n\nYep, removed.\n\n> \n> > +               strbuf_reset(&prettybuf);\n> > +               pp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);\n> > +               /*\n> > +                * We expect this part of the output to be machine-parseable -\n> > +                * one commit message per line - so we must not localize it.\n> > +                */\n> > +               puts(prettybuf.buf);\n> \n> Meh, but there isn't any literal text here to localize anyway, so the\n> comment talking about not localizing it is just confusing.\n\nYeah, you're right. I'll change to \"so we send it to stdout\", which is\nless obvious from puts().\n\n> \n> > +       }\n> \n> Leaking 'prettybuf'. Add here:\n> \n>     strbuf_release(&prettybuf);\n\nThanks, done.\n"},{"id":"378198","messageId":"20190627205811.GE245941@google.com","threadId":"51257","inReplyTo":"CAPig+cQQCXA-WsbQrC6XoejjF-roqhmcB7iVH1nA+zUDYTN3vQ@mail.gmail.com","subject":"Re: [RFC PATCH v2 07/13] walken: filter for authors from gmail address","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-27T20:58:11Z","receivedAt":"2019-06-27T20:58:20Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Thu, Jun 27, 2019 at 01:20:25AM -0400, Eric Sunshine wrote:\n> On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > In order to demonstrate how to create grep filters for revision walks,\n> > filter the walk performed by cmd_walken() to print only commits which\n> > are authored by someone with a gmail address.\n> > [...]\n> > Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> > ---\n> > diff --git a/builtin/walken.c b/builtin/walken.c\n> > @@ -60,6 +59,10 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n> > +       /* Add a grep pattern to the author line in the header. */\n> \n> This sounds as if we are adding something to the author line, which is\n> confusing. Maybe say instead:\n> \n>     /* Apply a 'grep' pattern to the author header. */\n> \n\nI also s/author/'&'/; thanks.\n\n> > +       append_header_grep_pattern(&rev->grep_filter, GREP_HEADER_AUTHOR, \"gmail\");\n> > +       compile_grep_patterns(&rev->grep_filter);\n"},{"id":"378206","messageId":"20190627221201.GF245941@google.com","threadId":"51257","inReplyTo":"CAPig+cQGcNS476e+eDqyyfeVWDsi4=BGS=aTvYZ3=6BdP-0_Uw@mail.gmail.com","subject":"Re: [RFC PATCH v2 08/13] walken: demonstrate various topographical sorts","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-27T22:12:01Z","receivedAt":"2019-06-27T22:12:10Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Thu, Jun 27, 2019 at 01:22:10AM -0400, Eric Sunshine wrote:\n> On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > Order the revision walk by author or commit dates, to demonstrate how to\n> \n> s/,//\n\nDone.\n"},{"id":"378207","messageId":"20190627222032.GG245941@google.com","threadId":"51257","inReplyTo":"CAPig+cQh2rEW271KziUyYGZyPXrFV8z_OV6UzY=-e4pOrF_zsw@mail.gmail.com","subject":"Re: [RFC PATCH v2 09/13] walken: demonstrate reversing a revision walk list","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-27T22:20:32Z","receivedAt":"2019-06-27T22:20:40Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Thu, Jun 27, 2019 at 01:26:19AM -0400, Eric Sunshine wrote:\n> On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > The final installment in the tutorial about sorting revision walk\n> > outputs. This commit reverses the commit list, so that we see newer\n> > commits last (handy since we aren't using a pager).\n> >\n> > It's important to note that rev->reverse needs to be set after\n> > add_head_to_pending() or before setup_revisions(). (This is mentioned in\n> > the accompanying tutorial.)\n> \n> This leaves the reader wondering \"why that requirement?\". Is it\n> because those functions may change the value or otherwise depend upon\n> the value?\n> \n> Also, something this important probably deserves an in-code comment\n> (and need not be mentioned in the commit message if the in-code\n> comment explains it well.)\n\nThis I will remove. I removed from the tutorial as it turned out I was\nincorrect. Thanks.\n\n> \n> > Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> > ---\n> > diff --git a/builtin/walken.c b/builtin/walken.c\n> > @@ -69,6 +69,9 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n> >         /* add the HEAD to pending so we can start */\n> >         add_head_to_pending(rev);\n> > +\n> > +       /* Reverse the order */\n> > +       rev->reverse = 1;\n"},{"id":"378209","messageId":"20190627223110.GH245941@google.com","threadId":"51257","inReplyTo":"CAPig+cR7rHokaTtZcJJg8trJ14xO3hdeWHqLnB4aOnEUak051w@mail.gmail.com","subject":"Re: [RFC PATCH v2 10/13] walken: add unfiltered object walk from HEAD","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-27T22:31:10Z","receivedAt":"2019-06-27T22:31:17Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Thu, Jun 27, 2019 at 01:37:58AM -0400, Eric Sunshine wrote:\n> On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > Provide a demonstration of a revision walk which traverses all types of\n> > object, not just commits. This type of revision walk is used for\n> > operations such as creating packfiles and performing fetches or clones,\n> > so it's useful to teach new developers how it works. For starters, only\n> > demonstrate the unfiltered version, as this will make the tutorial\n> > easier to follow.\n> > [...]\n> > Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> > ---\n> > diff --git a/builtin/walken.c b/builtin/walken.c\n> > @@ -103,6 +109,65 @@ static int git_walken_config(const char *var, const char *value, void *cb)\n> > +static void walken_show_commit(struct commit *cmt, void *buf)\n> > +{\n> > +       commit_count++;\n> > +}\n> > +\n> > +static void walken_show_object(struct object *obj, const char *str, void *buf)\n> > +{\n> > +       switch (obj->type) {\n> > +       [...]\n> > +       case OBJ_TAG:\n> > +               tag_count++;\n> > +               break;\n> > +       case OBJ_COMMIT:\n> > +               die(_(\"unexpectedly encountered a commit in \"\n> > +                     \"walken_show_object\\n\"));\n> > +               commit_count++;\n> \n> The \"commit_count++\" line is not only dead code, but it also confuses\n> the reader (or makes the reader think that the author of this code\n> doesn't understand C programming). You should drop this line.\n\nOw, yes. Removed. This is stale (pre-die()).\n\n> \n> > +               break;\n> > +       default:\n> > +               die(_(\"unexpected object type %s\\n\"), type_name(obj->type));\n> > +               break;\n> \n> Likewise, this \"break\" (and the one in the OBJ_COMMIT case) are dead\n> code which should be dropped to avoid confusing the reader.\n\nDone.\n\n> \n> Don't localize the die() message via _() here or in the preceding\n> OBJ_COMMIT case.\n\nI'm a little surprised by that. Is it because die() is expected to only\nbe seen by the developer? It seems like a poor user experience if\nsomeone in non-English locale encounters a bug that Git team didn't\nfind, and needed to try to translate the English die() string and figure\nout if a workaround is possible.\n\n> \n> The two die() messages are unnecessarily dissimilar. Try to unify them\n> so that they read in the same way.\n\nI'm a little surprised by this too; it seems to me the root cause of\neach would be different. In the former case, I'd guess that\ntraverse_commit_list()'s behavior changed, and in the latter case I'd\nguess that a new object type was recently added to the model. Can you\nhelp me understand the motivation for making the messages similar?\n\n(I don't think, though, that I did a good job of indicating either root\ncause in the die() messages as they are now.)\n\n> \n> > +       }\n> > +}> @@ -113,15 +178,15 @@ static void walken_commit_walk(struct rev_info *rev)\n> >         /*\n> > -         * prepare_revision_walk() gets the final steps ready for a revision\n> > +        * prepare_revision_walk() gets the final steps ready for a revision\n> >          * walk. We check the return value for errors.\n> > -         */\n> > +        */\n> >         /*\n> > -         * Now we can start the real commit walk. get_revision grabs the next\n> > +        * Now we can start the real commit walk. get_revision grabs the next\n> >          * revision based on the contents of rev.\n> >          */\n> \n> I think these are just correcting whitespace/indentation errors I\n> pointed out in an earlier patch (so they are unnecessary noise in this\n> patch).\n\nACK\n"},{"id":"378210","messageId":"20190627223322.GI245941@google.com","threadId":"51257","inReplyTo":"CAPig+cSJLbeVawxRhHwqLEXBr6-3QoaYd+1zF169m5N=kq4J4Q@mail.gmail.com","subject":"Re: [RFC PATCH v2 11/13] walken: add filtered object walk","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-27T22:33:22Z","receivedAt":"2019-06-27T22:33:29Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Thu, Jun 27, 2019 at 01:42:45AM -0400, Eric Sunshine wrote:\n> On Wed, Jun 26, 2019 at 7:51 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > Demonstrate how filter specs can be used when performing a revision walk\n> > of all object types. In this case, tree depth is used. Contributors who\n> > are following the revision walking tutorial will be encouraged to run\n> > the revision walk with and without the filter in order to compare the\n> > number of objects seen in each case.\n> >\n> > Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> > ---\n> > diff --git a/builtin/walken.c b/builtin/walken.c\n> > @@ -143,6 +144,10 @@ static void walken_show_object(struct object *obj, const char *str, void *buf)\n> >  static void walken_object_walk(struct rev_info *rev)\n> >  {\n> > +       struct list_objects_filter_options filter_options = {};\n> > +\n> > +       printf(\"walken_object_walk beginning...\\n\");\n> \n> Is this debugging code which you accidentally left in? Or is it meant\n> to use trace_printf()? Or something else? If it is a genuine message,\n> should it be localizable?\n\nThe former. Removed.\n\n> \n> > @@ -157,7 +162,24 @@ static void walken_object_walk(struct rev_info *rev)\n> >         blob_count = 0;\n> >         tree_count = 0;\n> >\n> > -       traverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n> > +       if (1) {\n> > +               /* Unfiltered: */\n> \n> The subject talks about adding a _filtered_ object walk (which is in\n> the 'else' arm), so should this be \"if (0)\" instead?\n\nDone.\n"},{"id":"378222","messageId":"20190627225652.GJ245941@google.com","threadId":"51257","inReplyTo":"20190626235032.177551-1-emilyshaffer@google.com","subject":"Re: [RFC PATCH v2 00/13] example implementation of revwalk tutorial","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-06-27T22:56:52Z","receivedAt":"2019-06-27T22:56:59Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Wed, Jun 26, 2019 at 04:50:19PM -0700, Emily Shaffer wrote:\n> Since r1, made some significant changes.\n> \n>  - Added a commit for counting the 'omitted' list, to match the new\n>    section added to the tutorial.\n>  - Added significant comments to allow the sample to better stand on its\n>    own.\n>  - Fixed style issues (die() formatting, etc)\n>  - Distinguished between human- and machine-readable output with\n>    trace_printf() and printf(), to turn the command into plumbing.\n>  - More changes as mentioned in the tutorial patch.\n> \n> Thanks.\n>  - Emily\n\nI have a v3 ready with changes based on Eric's suggestions. However,\nsince they're almost all verbatim changes, I'm going to hold off on\nsending it for a couple days to see if anybody else wants to peek\nthough. I'll likely be sending v3 on Monday.\n\n - Emily\n"},{"id":"378234","messageId":"CAPig+cRB2=3j6BfuU00YWj1iSMi0qxE+0P7LCXHMtYpWdgam-A@mail.gmail.com","threadId":"51257","inReplyTo":"20190627223110.GH245941@google.com","subject":"Re: [RFC PATCH v2 10/13] walken: add unfiltered object walk from HEAD","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-06-28T00:48:31Z","receivedAt":"2019-06-28T00:48:45Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Jun 27, 2019 at 6:31 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> On Thu, Jun 27, 2019 at 01:37:58AM -0400, Eric Sunshine wrote:\n> > Don't localize the die() message via _() here or in the preceding\n> > OBJ_COMMIT case.\n>\n> I'm a little surprised by that. Is it because die() is expected to only\n> be seen by the developer?\n\nSorry, I was reading those as BUG(), not die(), and we don't localize\nBUG() text. But, why aren't those BUG()? Can those cases arise in\npractice? (Genuine question; I haven't familiarized myself with that\ncode yet.)\n\nIf they legitimately should be die(), then ignore my comment about not\nlocalizing them.\n\n> > The two die() messages are unnecessarily dissimilar. Try to unify them\n> > so that they read in the same way.\n>\n> I'm a little surprised by this too; it seems to me the root cause of\n> each would be different. In the former case, I'd guess that\n> traverse_commit_list()'s behavior changed, and in the latter case I'd\n> guess that a new object type was recently added to the model. Can you\n> help me understand the motivation for making the messages similar?\n\nBoth causes you describe here sound like BUG() cases, not die(). If\nI'm understanding correctly, they could only trigger if someone made\nsome breaking or behavior changing modifications within Git and failed\nto update all the code in the project impacted by the change. In other\nwords, these can't be triggered by user input, hence they would be\nBUG()s indicating that a Git developer needs to fix the code.\n\nAs for the messages themselves, I was referring to the grammatical\ndissimilarity of \"unexpectedly\" and \"unexpected\", and I also don't\nunderstand why one messages mentions walken_show_object() explicitly,\nwhereas the other does not.\n"},{"id":"378426","messageId":"20190701191959.GK245941@google.com","threadId":"51257","inReplyTo":"CAPig+cRB2=3j6BfuU00YWj1iSMi0qxE+0P7LCXHMtYpWdgam-A@mail.gmail.com","subject":"Re: [RFC PATCH v2 10/13] walken: add unfiltered object walk from HEAD","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T19:19:59Z","receivedAt":"2019-07-01T19:20:06Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Thu, Jun 27, 2019 at 08:48:31PM -0400, Eric Sunshine wrote:\n> On Thu, Jun 27, 2019 at 6:31 PM Emily Shaffer <emilyshaffer@google.com> wrote:\n> > On Thu, Jun 27, 2019 at 01:37:58AM -0400, Eric Sunshine wrote:\n> > > Don't localize the die() message via _() here or in the preceding\n> > > OBJ_COMMIT case.\n> >\n> > I'm a little surprised by that. Is it because die() is expected to only\n> > be seen by the developer?\n> \n> Sorry, I was reading those as BUG(), not die(), and we don't localize\n> BUG() text. But, why aren't those BUG()? Can those cases arise in\n> practice? (Genuine question; I haven't familiarized myself with that\n> code yet.)\n> \n> If they legitimately should be die(), then ignore my comment about not\n> localizing them.\n\nHmmm. Yeah, I'll switch them to BUG() - I think there are other\ninstances of die() in the example and it'd be good to describe yet\nanother way of reporting behavior.\n\n> \n> > > The two die() messages are unnecessarily dissimilar. Try to unify them\n> > > so that they read in the same way.\n> >\n> > I'm a little surprised by this too; it seems to me the root cause of\n> > each would be different. In the former case, I'd guess that\n> > traverse_commit_list()'s behavior changed, and in the latter case I'd\n> > guess that a new object type was recently added to the model. Can you\n> > help me understand the motivation for making the messages similar?\n> \n> Both causes you describe here sound like BUG() cases, not die(). If\n> I'm understanding correctly, they could only trigger if someone made\n> some breaking or behavior changing modifications within Git and failed\n> to update all the code in the project impacted by the change. In other\n> words, these can't be triggered by user input, hence they would be\n> BUG()s indicating that a Git developer needs to fix the code.\n> \n> As for the messages themselves, I was referring to the grammatical\n> dissimilarity of \"unexpectedly\" and \"unexpected\", and I also don't\n> understand why one messages mentions walken_show_object() explicitly,\n> whereas the other does not.\n\nI see - ok, I have reworded.\n\n\nThanks!\n - Emily\n"},{"id":"378431","messageId":"20190701201934.30321-1-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190626234915.171658-1-emilyshaffer@google.com","subject":"[PATCH v3] documentation: add tutorial for revision walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:19:34Z","receivedAt":"2019-07-01T20:19:48Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Existing documentation on revision walks seems to be primarily intended\nas a reference for those already familiar with the procedure. This\ntutorial attempts to give an entry-level guide to a couple of bare-bones\nrevision walks so that new Git contributors can learn the concepts\nwithout having to wade through options parsing or special casing.\n\nThe target audience is a Git contributor who is just getting started\nwith the concept of revision walking. The goal is to prepare this\ncontributor to be able to understand and modify existing commands which\nperform revision walks more easily, although it will also prepare\ncontributors to create new commands which perform walks.\n\nThe tutorial covers a basic overview of the structs involved during\nrevision walk, setting up a basic commit walk, setting up a basic\nall-object walk, and adding some configuration changes to both walk\ntypes. It intentionally does not cover how to create new commands or\nsearch for options from the command line or gitconfigs.\n\nThere is an associated patchset at\nhttps://github.com/nasamuffin/git/tree/revwalk that contains a reference\nimplementation of the code generated by this tutorial.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\n---\nSince v2, responded to Eric's substantiative review of the sample\ncodebase. In most cases that review pointed to comments in the sample\ncode, but in a couple cases the code itself changed, and that's\nreflected here.\n\n - Move usage and options struct to local scope of cmd_walken().\n - Use BUG() to complain when an unexpected object is found in\n   show_object() during object walk.\n - Fix a hardcoded switch between commit and object walks pointing the\n   wrong way for its place in the tutorial.\n - Fix a memory leak during commit walk.\n\n Documentation/Makefile           |   1 +\n Documentation/MyFirstRevWalk.txt | 908 +++++++++++++++++++++++++++++++\n 2 files changed, 909 insertions(+)\n create mode 100644 Documentation/MyFirstRevWalk.txt\n\ndiff --git a/Documentation/Makefile b/Documentation/Makefile\nindex 76f2ecfc1b..91e5da67c4 100644\n--- a/Documentation/Makefile\n+++ b/Documentation/Makefile\n@@ -78,6 +78,7 @@ SP_ARTICLES += $(API_DOCS)\n \n TECH_DOCS += MyFirstContribution\n TECH_DOCS += SubmittingPatches\n+TECH_DOCS += MyFirstRevWalk\n TECH_DOCS += technical/hash-function-transition\n TECH_DOCS += technical/http-protocol\n TECH_DOCS += technical/index-format\ndiff --git a/Documentation/MyFirstRevWalk.txt b/Documentation/MyFirstRevWalk.txt\nnew file mode 100644\nindex 0000000000..6a432f35a0\n--- /dev/null\n+++ b/Documentation/MyFirstRevWalk.txt\n@@ -0,0 +1,908 @@\n+My First Revision Walk\n+======================\n+\n+== What's a Revision Walk?\n+\n+The revision walk is a key concept in Git - this is the process that underpins\n+operations like `git log`, `git blame`, and `git reflog`. Beginning at HEAD, the\n+list of objects is found by walking parent relationships between objects. The\n+revision walk can also be used to determine whether or not a given object is\n+reachable from the current HEAD pointer.\n+\n+=== Related Reading\n+\n+- `Documentation/user-manual.txt` under \"Hacking Git\" contains some coverage of\n+  the revision walker in its various incarnations.\n+- `Documentation/technical/api-revision-walking.txt`\n+- https://eagain.net/articles/git-for-computer-scientists/[Git for Computer Scientists]\n+  gives a good overview of the types of objects in Git and what your revision\n+  walk is really describing.\n+\n+== Setting Up\n+\n+Create a new branch from `master`.\n+\n+----\n+git checkout -b revwalk origin/master\n+----\n+\n+We'll put our fiddling into a new command. For fun, let's name it `git walken`.\n+Open up a new file `builtin/walken.c` and set up the command handler:\n+\n+----\n+/*\n+ * \"git walken\"\n+ *\n+ * Part of the \"My First Revision Walk\" tutorial.\n+ */\n+\n+#include \"builtin.h\"\n+\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\ttrace_printf(_(\"cmd_walken incoming...\\n\"));\n+\treturn 0;\n+}\n+----\n+\n+NOTE: `trace_printf()` differs from `printf()` in that it can be turned on or\n+off at runtime. For the purposes of this tutorial, we will write `walken` as\n+though it is intended for use as a \"plumbing\" command: that is, a command which\n+is used primarily in scripts, rather than interactively by humans (a \"porcelain\"\n+command). So we will send our debug output to `trace_printf()` instead. When\n+running, enable trace output by setting the environment variable `GIT_TRACE`.\n+\n+Add usage text and `-h` handling, like all subcommands should consistently do\n+(our test suite will notice and complain if you fail to do so).\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\tconst char * const walken_usage[] = {\n+\t\tN_(\"git walken\"),\n+\t\tNULL,\n+\t}\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n+\n+\t...\n+}\n+----\n+\n+Also add the relevant line in `builtin.h` near `cmd_whatchanged()`:\n+\n+----\n+extern int cmd_walken(int argc, const char **argv, const char *prefix);\n+----\n+\n+Include the command in `git.c` in `commands[]` near the entry for `whatchanged`,\n+maintaining alphabetical ordering:\n+\n+----\n+{ \"walken\", cmd_walken, RUN_SETUP },\n+----\n+\n+Add it to the `Makefile` near the line for `builtin\\worktree.o`:\n+\n+----\n+BUILTIN_OBJS += builtin/walken.o\n+----\n+\n+Build and test out your command, without forgetting to ensure the `DEVELOPER`\n+flag is set, and with `GIT_TRACE` enabled so the debug output can be seen:\n+\n+----\n+$ echo DEVELOPER=1 >>config.mak\n+$ make\n+$ GIT_TRACE=1 ./bin-wrappers/git walken\n+----\n+\n+NOTE: For a more exhaustive overview of the new command process, take a look at\n+`Documentation/MyFirstContribution.txt`.\n+\n+NOTE: A reference implementation can be found at TODO LINK.\n+\n+=== `struct rev_cmdline_info`\n+\n+The definition of `struct rev_cmdline_info` can be found in `revision.h`.\n+\n+This struct is contained within the `rev_info` struct and is used to reflect\n+parameters provided by the user over the CLI.\n+\n+`nr` represents the number of `rev_cmdline_entry` present in the array.\n+\n+`alloc` is used by the `ALLOC_GROW` macro. Check\n+`Documentation/technical/api-allocation-growing.txt` - this variable is used to\n+track the allocated size of the list.\n+\n+Per entry, we find:\n+\n+`item` is the object provided upon which to base the revision walk. Items in Git\n+can be blobs, trees, commits, or tags. (See `Documentation/gittutorial-2.txt`.)\n+\n+`name` is the object ID (OID) of the object - a hex string you may be familiar\n+with from using Git to organize your source in the past. Check the tutorial\n+mentioned above towards the top for a discussion of where the OID can come\n+from.\n+\n+`whence` indicates some information about what to do with the parents of the\n+specified object. We'll explore this flag more later on; take a look at\n+`Documentation/revisions.txt` to get an idea of what could set the `whence`\n+value.\n+\n+`flags` are used to hint the beginning of the revision walk and are the first\n+block under the `#include`s in `revision.h`. The most likely ones to be set in\n+the `rev_cmdline_info` are `UNINTERESTING` and `BOTTOM`, but these same flags\n+can be used during the walk, as well.\n+\n+=== `struct rev_info`\n+\n+This one is quite a bit longer, and many fields are only used during the walk\n+by `revision.c` - not configuration options. Most of the configurable flags in\n+`struct rev_info` have a mirror in `Documentation/rev-list-options.txt`. It's a\n+good idea to take some time and read through that document.\n+\n+== Basic Commit Walk\n+\n+First, let's see if we can replicate the output of `git log --oneline`. We'll\n+refer back to the implementation frequently to discover norms when performing\n+a revision walk of our own.\n+\n+To do so, we'll first find all the commits, in order, which preceded the current\n+commit. We'll extract the name and subject of the commit from each.\n+\n+Ideally, we will also be able to find out which ones are currently at the tip of\n+various branches.\n+\n+=== Setting Up\n+\n+Preparing for your revision walk has some distinct stages.\n+\n+1. Perform default setup for this mode, and others which may be invoked.\n+2. Check configuration files for relevant settings.\n+3. Set up the `rev_info` struct.\n+4. Tweak the initialized `rev_info` to suit the current walk.\n+5. Prepare the `rev_info` for the walk.\n+6. Iterate over the objects, processing each one.\n+\n+==== Default Setups\n+\n+Before examining configuration files which may modify command behavior, set up\n+default state for switches or options your command may have. If your command\n+utilizes other Git components, ask them to set up their default states as well.\n+For instance, `git log` takes advantage of `grep` and `diff` functionality, so\n+its `init_log_defaults()` sets its own state (`decoration_style`) and asks\n+`grep` and `diff` to initialize themselves by calling each of their\n+initialization functions.\n+\n+For our purposes, within `git walken`, for the first example we don't intend to\n+use any other components within Git, and we don't have any configuration to do.\n+However, we may want to add some later, so for now, we can add an empty\n+placeholder. Create a new function in `builtin/walken.c`:\n+\n+----\n+static void init_walken_defaults(void)\n+{\n+\t/*\n+\t * We don't actually need the same components `git log` does; leave this\n+\t * empty for now.\n+\t */\n+}\n+----\n+\n+Make sure to add a line invoking it inside of `cmd_walken()`.\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\tinit_walken_defaults();\n+}\n+----\n+\n+==== Configuring From `.gitconfig`\n+\n+Next, we should have a look at any relevant configuration settings (i.e.,\n+settings readable and settable from `git config`). This is done by providing a\n+callback to `git_config()`; within that callback, you can also invoke methods\n+from other components you may need that need to intercept these options. Your\n+callback will be invoked once per each configuration value which Git knows about\n+(global, local, worktree, etc.).\n+\n+Similarly to the default values, we don't have anything to do here yet\n+ourselves; however, we should call `git_default_config()` if we aren't calling\n+any other existing config callbacks.\n+\n+Add a new function to `builtin/walken.c`:\n+\n+----\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\t/*\n+\t * For now, we don't have any custom configuration, so fall back to\n+\t * the default config.\n+\t */\n+\treturn git_default_config(var, value, cb);\n+}\n+----\n+\n+Make sure to invoke `git_config()` with it in your `cmd_walken()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t...\n+\n+\tgit_config(git_walken_config, NULL);\n+\n+\t...\n+}\n+----\n+\n+// TODO: Checking CLI options\n+\n+==== Setting Up `rev_info`\n+\n+Now that we've gathered external configuration and options, it's time to\n+initialize the `rev_info` object which we will use to perform the walk. This is\n+typically done by calling `repo_init_revisions()` with the repository you intend\n+to target, as well as the `prefix` argument of `cmd_walken` and your `rev_info`\n+struct.\n+\n+Add the `struct rev_info` and the `repo_init_revisions()` call:\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t/* This can go wherever you like in your declarations.*/\n+\tstruct rev_info rev;\n+\t...\n+\n+\t/* This should go after the git_config() call. */\n+\trepo_init_revisions(the_repository, &rev, prefix);\n+\n+\t...\n+}\n+----\n+\n+==== Tweaking `rev_info` For the Walk\n+\n+We're getting close, but we're still not quite ready to go. Now that `rev` is\n+initialized, we can modify it to fit our needs. This is usually done within a\n+helper for clarity, so let's add one:\n+\n+----\n+static void final_rev_info_setup(struct rev_info *rev)\n+{\n+\t/*\n+\t * We want to mimic the appearance of `git log --oneline`, so let's\n+\t * force oneline format.\n+\t */\n+\tget_commit_format(\"oneline\", rev);\n+\n+\t/* Start our revision walk at HEAD. */\n+\tadd_head_to_pending(rev);\n+}\n+----\n+\n+[NOTE]\n+====\n+Instead of using the shorthand `add_head_to_pending()`, you could do\n+something like this:\n+----\n+\tstruct setup_revision_opt opt;\n+\n+\tmemset(&opt, 0, sizeof(opt));\n+\topt.def = \"HEAD\";\n+\topt.revarg_opt = REVARG_COMMITTISH;\n+\tsetup_revisions(argc, argv, rev, &opt);\n+----\n+Using a `setup_revision_opt` gives you finer control over your walk's starting\n+point.\n+====\n+\n+Then let's invoke `final_rev_info_setup()` after the call to\n+`repo_init_revisions()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t...\n+\n+\tfinal_rev_info_setup(&rev);\n+\n+\t...\n+}\n+----\n+\n+Later, we may wish to add more arguments to `final_rev_info_setup()`. But for\n+now, this is all we need.\n+\n+==== Preparing `rev_info` For the Walk\n+\n+Now that `rev` is all initialized and configured, we've got one more setup step\n+before we get rolling. We can do this in a helper, which will both prepare the\n+`rev_info` for the walk, and perform the walk itself. Let's start the helper\n+with the call to `prepare_revision_walk()`, which can return an error without\n+dying on its own:\n+\n+----\n+static void walken_commit_walk(struct rev_info *rev)\n+{\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+}\n+----\n+\n+NOTE: `die()` prints to `stderr` and exits the program. Since it will print to\n+`stderr` it's likely to be seen by a human, so we will localize it.\n+\n+==== Performing the Walk!\n+\n+Finally! We are ready to begin the walk itself. Now we can see that `rev_info`\n+can also be used as an iterator; we move to the next item in the walk by using\n+`get_revision()` repeatedly. Add the listed variable declarations at the top and\n+the walk loop below the `prepare_revision_walk()` call within your\n+`walken_commit_walk()`:\n+\n+----\n+static void walken_commit_walk(struct rev_info *rev)\n+{\n+\tstruct commit *commit;\n+\tstruct strbuf prettybuf = STRBUF_INIT;\n+\n+\t...\n+\n+\twhile ((commit = get_revision(rev))) {\n+\t\tif (!commit)\n+\t\t\tcontinue;\n+\n+\t\tstrbuf_reset(&prettybuf);\n+\t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);\n+\t\tputs(prettybuf.buf);\n+\t}\n+\tstrbuf_release(&prettybuf);\n+}\n+----\n+\n+NOTE: `puts()` prints a `char*` to `stdout`. Since this is the part of the\n+command we expect to be machine-parsed, we're sending it directly to stdout.\n+\n+Give it a shot.\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken\n+----\n+\n+You should see all of the subject lines of all the commits in\n+your tree's history, in order, ending with the initial commit, \"Initial revision\n+of \"git\", the information manager from hell\". Congratulations! You've written\n+your first revision walk. You can play with printing some additional fields\n+from each commit if you're curious; have a look at the functions available in\n+`commit.h`.\n+\n+=== Adding a Filter\n+\n+Next, let's try to filter the commits we see based on their author. This is\n+equivalent to running `git log --author=<pattern>`. We can add a filter by\n+modifying `rev_info.grep_filter`, which is a `struct grep_opt`.\n+\n+First some setup. Add `init_grep_defaults()` to `init_walken_defaults()` and add\n+`grep_config()` to `git_walken_config()`:\n+\n+----\n+static void init_walken_defaults(void)\n+{\n+\tinit_grep_defaults(the_repository);\n+}\n+\n+...\n+\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\tgrep_config(var, value, cb);\n+\treturn git_default_config(var, value, cb);\n+}\n+----\n+\n+Next, we can modify the `grep_filter`. This is done with convenience functions\n+found in `grep.h`. For fun, we're filtering to only commits from folks using a\n+`gmail.com` email address - a not-very-precise guess at who may be working on\n+Git as a hobby. Since we're checking the author, which is a specific line in the\n+header, we'll use the `append_header_grep_pattern()` helper. We can use\n+the `enum grep_header_field` to indicate which part of the commit header we want\n+to search.\n+\n+In `final_rev_info_setup()`, add your filter line:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\tappend_header_grep_pattern(&rev->grep_filter, GREP_HEADER_AUTHOR,\n+\t\t\"gmail\");\n+\tcompile_grep_patterns(&rev->grep_filter);\n+\n+\t...\n+}\n+----\n+\n+`append_header_grep_pattern()` adds your new \"gmail\" pattern to `rev_info`, but\n+it won't work unless we compile it with `compile_grep_patterns()`.\n+\n+NOTE: If you are using `setup_revisions()` (for example, if you are passing a\n+`setup_revision_opt` instead of using `add_head_to_pending()`), you don't need\n+to call `compile_grep_patterns()` because `setup_revisions()` calls it for you.\n+\n+NOTE: We could add the same filter via the `append_grep_pattern()` helper if we\n+wanted to, but `append_header_grep_pattern()` adds the `enum grep_context` and\n+`enum grep_pat_token` for us.\n+\n+=== Changing the Order\n+\n+There are a few ways that we can change the order of the commits during a\n+revision walk. Firstly, we can use the `enum rev_sort_order` to choose from some\n+sane orderings.\n+\n+`topo_order` is the same as `git log --topo-order`: we avoid showing a parent\n+before all of its children have been shown, and we avoid mixing commits which\n+are in different lines of history. (`git help log`'s section on `--topo-order`\n+has a very nice diagram to illustrate this.)\n+\n+Let's see what happens when we run with `REV_SORT_BY_COMMIT_DATE` as opposed to\n+`REV_SORT_BY_AUTHOR_DATE`. Add the following:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\trev->topo_order = 1;\n+\trev->sort_order = REV_SORT_BY_COMMIT_DATE;\n+\n+\t...\n+}\n+----\n+\n+Let's output this into a file so we can easily diff it with the walk sorted by\n+author date.\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken > commit-date.txt\n+----\n+\n+Then, let's sort by author date and run it again.\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\trev->topo_order = 1;\n+\trev->sort_order = REV_SORT_BY_AUTHOR_DATE;\n+\n+\t...\n+}\n+----\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken > author-date.txt\n+----\n+\n+Finally, compare the two. This is a little less helpful without object names or\n+dates, but hopefully we get the idea.\n+\n+----\n+$ diff -u commit-date.txt author-date.txt\n+----\n+\n+This display indicates that commits can be reordered after they're written, for\n+example with `git rebase`.\n+\n+Let's try one more reordering of commits. `rev_info` exposes a `reverse` flag.\n+Set that flag somewhere inside of `final_rev_info_setup()`:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv, const char *prefix,\n+\t\tstruct rev_info *rev)\n+{\n+\t...\n+\n+\trev->reverse = 1;\n+\n+\t...\n+}\n+----\n+\n+Run your walk again and note the difference in order. (If you remove the grep\n+pattern, you should see the last commit this call gives you as your current\n+HEAD.)\n+\n+== Basic Object Walk\n+\n+So far we've been walking only commits. But Git has more types of objects than\n+that! Let's see if we can walk _all_ objects, and find out some information\n+about each one.\n+\n+We can base our work on an example. `git pack-objects` prepares all kinds of\n+objects for packing into a bitmap or packfile. The work we are interested in\n+resides in `builtins/pack-objects.c:get_object_list()`; examination of that\n+function shows that the all-object walk is being performed by\n+`traverse_commit_list()` or `traverse_commit_list_filtered()`. Those two\n+functions reside in `list-objects.c`; examining the source shows that, despite\n+the name, these functions traverse all kinds of objects. Let's have a look at\n+the arguments to `traverse_commit_list_filtered()`, which are a superset of the\n+arguments to the unfiltered version.\n+\n+- `struct list_objects_filter_options *filter_options`: This is a struct which\n+  stores a filter-spec as outlined in `Documentation/rev-list-options.txt`.\n+- `struct rev_info *revs`: This is the `rev_info` used for the walk.\n+- `show_commit_fn show_commit`: A callback which will be used to handle each\n+  individual commit object.\n+- `show_object_fn show_object`: A callback which will be used to handle each\n+  non-commit object (so each blob, tree, or tag).\n+- `void *show_data`: A context buffer which is passed in turn to `show_commit`\n+  and `show_object`.\n+- `struct oidset *omitted`: A linked-list of object IDs which the provided\n+  filter caused to be omitted.\n+\n+It looks like this `traverse_commit_list_filtered()` uses callbacks we provide\n+instead of needing us to call it repeatedly ourselves. Cool! Let's add the\n+callbacks first.\n+\n+For the sake of this tutorial, we'll simply keep track of how many of each kind\n+of object we find. At file scope in `builtin/walken.c` add the following\n+tracking variables:\n+\n+----\n+static int commit_count;\n+static int tag_count;\n+static int blob_count;\n+static int tree_count;\n+----\n+\n+Commits are handled by a different callback than other objects; let's do that\n+one first:\n+\n+----\n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+\tcommit_count++;\n+}\n+----\n+\n+The `cmt` argument is fairly self-explanatory. But it's worth mentioning that\n+the `buf` argument is actually the context buffer that we can provide to the\n+traversal calls - `show_data`, which we mentioned a moment ago.\n+\n+Since we have the `struct commit` object, we can look at all the same parts that\n+we looked at in our earlier commit-only walk. For the sake of this tutorial,\n+though, we'll just increment the commit counter and move on.\n+\n+The callback for non-commits is a little different, as we'll need to check\n+which kind of object we're dealing with:\n+\n+----\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+\tswitch (obj->type) {\n+\tcase OBJ_TREE:\n+\t\ttree_count++;\n+\t\tbreak;\n+\tcase OBJ_BLOB:\n+\t\tblob_count++;\n+\t\tbreak;\n+\tcase OBJ_TAG:\n+\t\ttag_count++;\n+\t\tbreak;\n+\tcase OBJ_COMMIT:\n+\t\tBUG(\"unexpected commit object in walken_show_object\\n\");\n+\tdefault:\n+\t\tBUG(\"unexpected object type %s in walken_show_object\\n\",\n+\t\t\ttype_name(obj->type));\n+\t}\n+}\n+----\n+\n+Again, `obj` is fairly self-explanatory, and we can guess that `buf` is the same\n+context pointer that `walken_show_commit()` receives: the `show_data` argument\n+to `traverse_commit_list()` and `traverse_commit_list_filtered()`. Finally,\n+`str` contains the name of the object, which ends up being something like\n+`foo.txt` (blob), `bar/baz` (tree), or `v1.2.3` (tag).\n+\n+To help assure us that we aren't double-counting commits, we'll include some\n+complaining if a commit object is routed through our non-commit callback; we'll\n+also complain if we see an invalid object type. Since those two cases should be\n+unreachable, and would only change in the event of a semantic change to the Git\n+codebase, we complain by using `BUG()` - which is a signal to a developer that\n+the change they made caused unintended consequences, and the rest of the\n+codebase needs to be updated to understand that change. `BUG()` is not intended\n+to be seen by the public, so it is not localized.\n+\n+Our main object walk implementation is substantially different from our commit\n+walk implementation, so let's make a new function to perform the object walk. We\n+can perform setup which is applicable to all objects here, too, to keep separate\n+from setup which is applicable to commit-only walks.\n+\n+We'll start by enabling all types of objects in the `struct rev_info`. Unless\n+you cloned or fetched your repository earlier with a filter,\n+`exclude_promisor_objects` is unlikely to make a difference, but we'll turn it\n+on just to make sure our lives are simple. We'll also turn on\n+`tree_blobs_in_commit_order`, which means that we will walk a commit's tree and\n+everything it points to immediately after we find each commit, as opposed to\n+waiting for the end and walking through all trees after the commit history has\n+been discovered. With the appropriate settings configured, we are ready to call\n+`prepare_revision_walk()`.\n+\n+----\n+static void walken_object_walk(struct rev_info *rev)\n+{\n+\trev->tree_objects = 1;\n+\trev->blob_objects = 1;\n+\trev->tag_objects = 1;\n+\trev->tree_blobs_in_commit_order = 1;\n+\trev->exclude_promisor_objects = 1;\n+\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tcommit_count = 0;\n+\ttag_count = 0;\n+\tblob_count = 0;\n+\ttree_count = 0;\n+----\n+\n+Let's start by calling just the unfiltered walk and reporting our counts.\n+Complete your implementation of `walken_object_walk()`:\n+\n+----\n+\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n+\n+\tprintf(\"commits %d\\nblobs %d\\ntags %d\\ntrees %d\\n\", commit_count,\n+\t\tblob_count, tag_count, tree_count);\n+}\n+----\n+\n+NOTE: This output is intended to be machine-parsed. Therefore, we are not\n+sending it to `trace_printf()`, and we are not localizing it - we need scripts\n+to be able to count on the formatting to be exactly the way it is shown here.\n+If we were intending this output to be read by humans, we would need to localize\n+it with `_()`.\n+\n+Finally, we'll ask `cmd_walken()` to use the object walk instead. Discussing\n+command line options is out of scope for this tutorial, so we'll just hardcode\n+a branch we can change at compile time. Where you call `final_rev_info_setup()`\n+and `walken_commit_walk()`, instead branch like so:\n+\n+----\n+\tif (1) {\n+\t\tadd_head_to_pending(&rev);\n+\t\twalken_object_walk(&rev);\n+\t} else {\n+\t\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n+\t\twalken_commit_walk(&rev);\n+\t}\n+----\n+\n+NOTE: For simplicity, we've avoided all the filters and sorts we applied in\n+`final_rev_info_setup()` and simply added `HEAD` to our pending queue. If you\n+want, you can certainly use the filters we added before by moving\n+`final_rev_info_setup()` out of the conditional and removing the call to\n+`add_head_to_pending()`.\n+\n+Now we can try to run our command! It should take noticeably longer than the\n+commit walk, but an examination of the output will give you an idea why. Your\n+output should look similar to this example, but with different counts:\n+\n+----\n+Object walk completed. Found 55733 commits, 100274 blobs, 0 tags, and 104210 trees.\n+----\n+\n+This makes sense. We have more trees than commits because the Git project has\n+lots of subdirectories which can change, plus at least one tree per commit. We\n+have no tags because we started on a commit (`HEAD`) and while tags can point to\n+commits, commits can't point to tags.\n+\n+NOTE: You will have different counts when you run this yourself! The number of\n+objects grows along with the Git project.\n+\n+=== Adding a Filter\n+\n+There are a handful of filters that we can apply to the object walk laid out in\n+`Documentation/rev-list-options.txt`. These filters are typically useful for\n+operations such as creating packfiles or performing a partial clone. They are\n+defined in `list-objects-filter-options.h`. For the purposes of this tutorial we\n+will use the \"tree:1\" filter, which causes the walk to omit all trees and blobs\n+which are not directly referenced by commits reachable from the commit in\n+`pending` when the walk begins. (`pending` is the list of objects which need to\n+be traversed during a walk; you can imagine a breadth-first tree traversal to\n+help understand. In our case, that means we omit trees and blobs not directly\n+referenced by `HEAD` or `HEAD`'s history, because we begin the walk with only\n+`HEAD` in the `pending` list.)\n+\n+First, we'll need to `#include \"list-objects-filter-options.h`\" and set up the\n+`struct list_objects_filter_options` at the top of the function.\n+\n+----\n+static void walken_object_walk(struct rev_info *rev)\n+{\n+\tstruct list_objects_filter_options filter_options = {};\n+\n+\t...\n+----\n+\n+For now, we are not going to track the omitted objects, so we'll replace those\n+parameters with `NULL`. For the sake of simplicity, we'll add a simple\n+build-time branch to use our filter or not. Replace the line calling\n+`traverse_commit_list()` with the following, which will remind us which kind of\n+walk we've just performed:\n+\n+----\n+\tif (0) {\n+\t\t/* Unfiltered: */\n+\t\ttrace_printf(_(\"Unfiltered object walk.\\n\"));\n+\t\ttraverse_commit_list(rev, walken_show_commit,\n+\t\t\t\twalken_show_object, NULL);\n+\t} else {\n+\t\ttrace_printf(\n+\t\t\t_(\"Filtered object walk with filterspec 'tree:1'.\\n\"));\n+\t\tparse_list_objects_filter(&filter_options, \"tree:1\");\n+\n+\t\ttraverse_commit_list_filtered(&filter_options, rev,\n+\t\t\twalken_show_commit, walken_show_object, NULL, NULL);\n+\t}\n+----\n+\n+`struct list_objects_filter_options` is usually built directly from a command\n+line argument, so the module provides an easy way to build one from a string.\n+Even though we aren't taking user input right now, we can still build one with\n+a hardcoded string using `parse_list_objects_filter()`.\n+\n+With the filter spec \"tree:1\", we are expecting to see _only_ the root tree for\n+each commit; therefore, the tree object count should be less than or equal to\n+the number of commits. (For an example of why that's true: `git commit --revert`\n+points to the same tree object as its grandparent.)\n+\n+=== Counting Omitted Objects\n+\n+We also have the capability to enumerate all objects which were omitted by a\n+filter, like with `git log --filter=<spec> --filter-print-omitted`. Asking\n+`traverse_commit_list_filtered()` to populate the `omitted` list means that our\n+revision walk does not perform any better than an unfiltered revision walk; all\n+reachable objects are walked in order to populate the list.\n+\n+First, add the `struct oidset` and related items we will use to iterate it:\n+\n+----\n+static void walken_object_walk(\n+\t...\n+\n+\tstruct oidset omitted;\n+\tstruct oidset_iter oit;\n+\tstruct object_id *oid = NULL;\n+\tint omitted_count = 0;\n+\toidset_init(&omitted, 0);\n+\n+\t...\n+----\n+\n+Modify the call to `traverse_commit_list_filtered()` to include your `omitted`\n+object:\n+\n+----\n+\t...\n+\n+\t\ttraverse_commit_list_filtered(&filter_options, rev,\n+\t\t\twalken_show_commit, walken_show_object, NULL, &omitted);\n+\n+\t...\n+----\n+\n+Then, after your traversal, the `oidset` traversal is pretty straightforward.\n+Count all the objects within and modify the print statement:\n+\n+----\n+\t/* Count the omitted objects. */\n+\toidset_iter_init(&omitted, &oit);\n+\n+\twhile ((oid = oidset_iter_next(&oit)))\n+\t\tomitted_count++;\n+\n+\tprintf(\"commits %d\\nblobs %d\\ntags %d\\ntrees%d\\nomitted %d\\n\",\n+\t\tcommit_count, blob_count, tag_count, tree_count, omitted_count);\n+----\n+\n+By running your walk with and without the filter, you should find that the total\n+object count in each case is identical. You can also time each invocation of\n+the `walken` subcommand, with and without `omitted` being passed in, to confirm\n+to yourself the runtime impact of tracking all omitted objects.\n+\n+=== Changing the Order\n+\n+Finally, let's demonstrate that you can also reorder walks of all objects, not\n+just walks of commits. First, we'll make our handlers chattier - modify\n+`walken_show_commit()` and `walken_show_object()` to print the object as they\n+go:\n+\n+----\n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+\ttrace_printf(\"commit: %s\\n\", oid_to_hex(&cmt->object.oid));\n+\tcommit_count++;\n+}\n+\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+\ttrace_printf(\"%s: %s\\n\", type_name(obj->type), oid_to_hex(&obj->oid));\n+\n+\t...\n+}\n+----\n+\n+NOTE: Since we will be examining this output directly as humans, we'll use\n+`trace_printf()` here. Additionally, since this change introduces a significant\n+number of printed lines, using `trace_printf()` will allow us to easily silence\n+those lines without having to recompile.\n+\n+(Leave the counter increment logic in place.)\n+\n+With only that change, run again (but save yourself some scrollback):\n+\n+----\n+$ GIT_TRACE=1 ./bin-wrappers/git walken | head -n 10\n+----\n+\n+Take a look at the top commit with `git show` and the object ID you printed; it\n+should be the same as the output of `git show HEAD`.\n+\n+Next, let's change a setting on our `struct rev_info` within\n+`walken_object_walk()`. Find where you're changing the other settings on `rev`,\n+such as `rev->tree_objects` and `rev->tree_blobs_in_commit_order`, and add the\n+`reverse` setting at the bottom:\n+\n+----\n+\t...\n+\n+\trev->tree_objects = 1;\n+\trev->blob_objects = 1;\n+\trev->tag_objects = 1;\n+\trev->tree_blobs_in_commit_order = 1;\n+\trev->exclude_promisor_objects = 1;\n+\trev->reverse = 1;\n+\n+\t...\n+----\n+\n+Now, run again, but this time, let's grab the last handful of objects instead\n+of the first handful:\n+\n+----\n+$ make\n+$ GIT_TRACE=1 ./bin-wrappers git walken | tail -n 10\n+----\n+\n+The last commit object given should have the same OID as the one we saw at the\n+top before, and running `git show <oid>` with that OID should give you again\n+the same results as `git show HEAD`. Furthermore, if you run and examine the\n+first ten lines again (with `head` instead of `tail` like we did before applying\n+the `reverse` setting), you should see that now the first commit printed is the\n+initial commit, `e83c5163`.\n+\n+== Wrapping Up\n+\n+Let's review. In this tutorial, we:\n+\n+- Built a commit walk from the ground up\n+- Enabled a grep filter for that commit walk\n+- Changed the sort order of that filtered commit walk\n+- Built an object walk (tags, commits, trees, and blobs) from the ground up\n+- Learned how to add a filter-spec to an object walk\n+- Changed the display order of the filtered object walk\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378432","messageId":"20190701202014.34480-1-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701201934.30321-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 00/13] example implementation of revwalk tutorial","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:01Z","receivedAt":"2019-07-01T20:20:24Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Since v2, mostly reworded comments, plus fixed the issues mentioned in\nthe tutorial itself. Thanks Eric for the review.\n\nEmily Shaffer (13):\n  walken: add infrastructure for revwalk demo\n  walken: add usage to enable -h\n  walken: add placeholder to initialize defaults\n  walken: add handler to git_config\n  walken: configure rev_info and prepare for walk\n  walken: perform our basic revision walk\n  walken: filter for authors from gmail address\n  walken: demonstrate various topographical sorts\n  walken: demonstrate reversing a revision walk list\n  walken: add unfiltered object walk from HEAD\n  walken: add filtered object walk\n  walken: count omitted objects\n  walken: reverse the object walk order\n\n Makefile         |   1 +\n builtin.h        |   1 +\n builtin/walken.c | 297 +++++++++++++++++++++++++++++++++++++++++++++++\n git.c            |   1 +\n 4 files changed, 300 insertions(+)\n create mode 100644 builtin/walken.c\n\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378433","messageId":"20190701202014.34480-2-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 01/13] walken: add infrastructure for revwalk demo","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:02Z","receivedAt":"2019-07-01T20:20:26Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Begin to add scaffolding for `git walken`, a toy command which we will\nteach to perform a number of revision walks, in order to demonstrate the\nmechanics of revision walking for developers new to the Git project.\n\nThis commit is the beginning of an educational series which correspond\nto the tutorial in Documentation/MyFirstRevWalk.txt.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: I64297621919412f54701e111366e99c4ef0feae3\n---\n Makefile         |  1 +\n builtin.h        |  1 +\n builtin/walken.c | 18 ++++++++++++++++++\n 3 files changed, 20 insertions(+)\n create mode 100644 builtin/walken.c\n\ndiff --git a/Makefile b/Makefile\nindex f58bf14c7b..5bac1dbf8d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1137,6 +1137,7 @@ BUILTIN_OBJS += builtin/var.o\n BUILTIN_OBJS += builtin/verify-commit.o\n BUILTIN_OBJS += builtin/verify-pack.o\n BUILTIN_OBJS += builtin/verify-tag.o\n+BUILTIN_OBJS += builtin/walken.o\n BUILTIN_OBJS += builtin/worktree.o\n BUILTIN_OBJS += builtin/write-tree.o\n \ndiff --git a/builtin.h b/builtin.h\nindex ec7e0954c4..c919736c36 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -242,6 +242,7 @@ int cmd_var(int argc, const char **argv, const char *prefix);\n int cmd_verify_commit(int argc, const char **argv, const char *prefix);\n int cmd_verify_tag(int argc, const char **argv, const char *prefix);\n int cmd_version(int argc, const char **argv, const char *prefix);\n+int cmd_walken(int argc, const char **argv, const char *prefix);\n int cmd_whatchanged(int argc, const char **argv, const char *prefix);\n int cmd_worktree(int argc, const char **argv, const char *prefix);\n int cmd_write_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/walken.c b/builtin/walken.c\nnew file mode 100644\nindex 0000000000..db3ca50b04\n--- /dev/null\n+++ b/builtin/walken.c\n@@ -0,0 +1,18 @@\n+/*\n+ * \"git walken\"\n+ *\n+ * Part of the \"My First Revision Walk\" tutorial.\n+ */\n+\n+#include \"builtin.h\"\n+\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t/*\n+\t * This line is \"human-readable\" and we are writing a plumbing command,\n+\t * so we localize it and use the trace library to print only when\n+\t * the GIT_TRACE environment variable is set.\n+\t */\n+\ttrace_printf(_(\"cmd_walken incoming...\\n\"));\n+\treturn 0;\n+}\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378434","messageId":"20190701202014.34480-3-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 02/13] walken: add usage to enable -h","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:03Z","receivedAt":"2019-07-01T20:20:29Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"It's expected that Git commands support '-h' in order to provide a\nconsistent user experience (and this expectation is enforced by the\ntest suite). '-h' is captured by parse_options() by default; in order to\nsupport this flag, we add a short usage text to walken.c and invoke\nparse_options().\n\nWith this change, we can now add cmd_walken to the builtins set and\nexpect tests to pass, so we'll do so - cmd_walken is now open for\nbusiness.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: I2919dc1efadb82acb335617ea24371c84b03bbce\n---\n builtin/walken.c | 21 +++++++++++++++++++++\n git.c            |  1 +\n 2 files changed, 22 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex db3ca50b04..dd55f3b350 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -5,9 +5,30 @@\n  */\n \n #include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n \n int cmd_walken(int argc, const char **argv, const char *prefix)\n {\n+\t/*\n+\t * All builtins are expected to provide a usage to provide a consistent user\n+\t * experience.\n+\t */\n+\tconst char * const walken_usage[] = {\n+\t\tN_(\"git walken\"),\n+\t\tNULL,\n+\t};\n+\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\t/*\n+\t * parse_options() handles showing usage if incorrect options are\n+\t * provided, or if '-h' is passed.\n+\t */\n+\targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n+\n \t/*\n \t * This line is \"human-readable\" and we are writing a plumbing command,\n \t * so we localize it and use the trace library to print only when\ndiff --git a/git.c b/git.c\nindex c2eec470c9..2a7fb9714f 100644\n--- a/git.c\n+++ b/git.c\n@@ -601,6 +601,7 @@ static struct cmd_struct commands[] = {\n \t{ \"verify-pack\", cmd_verify_pack },\n \t{ \"verify-tag\", cmd_verify_tag, RUN_SETUP },\n \t{ \"version\", cmd_version },\n+\t{ \"walken\", cmd_walken, RUN_SETUP },\n \t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP },\n \t{ \"worktree\", cmd_worktree, RUN_SETUP | NO_PARSEOPT },\n \t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378435","messageId":"20190701202014.34480-4-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 03/13] walken: add placeholder to initialize defaults","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:04Z","receivedAt":"2019-07-01T20:20:31Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Eventually, we will want a good place to initialize default variables\nfor use during our revision walk(s) in `git walken`. For now, there's\nnothing to do here, but let's add the scaffolding so that it's easy to\ntell where to put the setup later on.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 15 +++++++++++++++\n 1 file changed, 15 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex dd55f3b350..19657b5e31 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -8,6 +8,19 @@\n #include \"parse-options.h\"\n \n \n+/*\n+ * Within init_walken_defaults() we can call into other useful defaults to set\n+ * in the global scope or on the_repository. It's okay to borrow from other\n+ * functions which are doing something relatively similar to yours.\n+ */\n+static void init_walken_defaults(void)\n+{\n+\t/*\n+\t * We don't actually need the same components `git log` does; leave this\n+\t * empty for now.\n+\t */\n+}\n+\n int cmd_walken(int argc, const char **argv, const char *prefix)\n {\n \t/*\n@@ -29,6 +42,8 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \t */\n \targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n \n+\tinit_walken_defaults();\n+\n \t/*\n \t * This line is \"human-readable\" and we are writing a plumbing command,\n \t * so we localize it and use the trace library to print only when\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378436","messageId":"20190701202014.34480-5-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 04/13] walken: add handler to git_config","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:05Z","receivedAt":"2019-07-01T20:20:33Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"For now, we have no configuration options we want to set up for\nourselves, but in the future we may need to. At the very least, we\nshould invoke git_default_config() for each config option; we will do so\ninside of a skeleton config callback so that we know where to add\nconfiguration handling later on when we need it.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 32 ++++++++++++++++++++++++++++++--\n 1 file changed, 30 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 19657b5e31..e53c42ea18 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -5,6 +5,7 @@\n  */\n \n #include \"builtin.h\"\n+#include \"config.h\"\n #include \"parse-options.h\"\n \n \n@@ -16,11 +17,36 @@\n static void init_walken_defaults(void)\n {\n \t/*\n-\t * We don't actually need the same components `git log` does; leave this\n-\t * empty for now.\n+\t * We don't use any other components or have settings to initialize, so\n+\t * leave this empty.\n \t */\n }\n \n+/*\n+ * This method will be called back by git_config(). It is used to gather values\n+ * from the configuration files available to Git.\n+ *\n+ * Each time git_config() finds a configuration file entry, it calls this\n+ * callback. Then, this function should compare it to entries which concern us,\n+ * and make settings changes as necessary.\n+ *\n+ * If we are called with a config setting we care about, we should use one of\n+ * the helpers which exist in config.h to pull out the value for ourselves, i.e.\n+ * git_config_string(...) or git_config_bool(...).\n+ *\n+ * If we don't match anything, we should pass it along to another stakeholder\n+ * who may otherwise care - in log's case, grep, gpg, and diff-ui. For our case,\n+ * we'll ignore everybody else.\n+ */\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\t/*\n+\t * For now, we don't have any custom configuration, so fall back on the\n+\t * default config.\n+\t */\n+\treturn git_default_config(var, value, cb);\n+}\n+\n int cmd_walken(int argc, const char **argv, const char *prefix)\n {\n \t/*\n@@ -44,6 +70,8 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \n \tinit_walken_defaults();\n \n+\tgit_config(git_walken_config, NULL);\n+\n \t/*\n \t * This line is \"human-readable\" and we are writing a plumbing command,\n \t * so we localize it and use the trace library to print only when\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378437","messageId":"20190701202014.34480-6-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 05/13] walken: configure rev_info and prepare for walk","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:06Z","receivedAt":"2019-07-01T20:20:40Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"`struct rev_info` is what's used by the walk itself.\n`repo_init_revisions()` initializes the struct; then we need to set it\nup for the walk we want to perform, which is done in\n`final_rev_info_setup()`.\n\nThe most important step here is adding the first object we want to walk\nto the pending array. Here, we take the easy road and use\n`add_head_to_pending()`; there is also a way to do it with\n`setup_revision_opt()` and `setup_revisions()` which we demonstrate but\ndo not use. If we were to forget this step, the walk would do nothing -\nthe pending queue would be checked, determined to be empty, and the walk\nwould terminate immediately.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: I76754b740227cf17a449f3f536dbbe37031e6f9a\n---\n builtin/walken.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 50 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex e53c42ea18..333d9ecc5e 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -5,6 +5,7 @@\n  */\n \n #include \"builtin.h\"\n+#include \"revision.h\"\n #include \"config.h\"\n #include \"parse-options.h\"\n \n@@ -22,6 +23,40 @@ static void init_walken_defaults(void)\n \t */\n }\n \n+/*\n+ * Perform configuration for commit walk here. Within this function we set a\n+ * starting point, and can customize our walk in various ways.\n+ */\n+static void final_rev_info_setup(int argc, const char **argv, const char *prefix,\n+\t\tstruct rev_info *rev)\n+{\n+\t/*\n+\t * Optional:\n+\t * setup_revision_opt is used to pass options to the setup_revisions()\n+\t * call. It's got some special items for submodules and other types of\n+\t * optimizations, but for now, we'll just point it to HEAD. First we\n+\t * should make sure to reset it. This is useful for more complicated\n+\t * stuff but a decent shortcut for the first pass is\n+\t * add_head_to_pending().\n+\t */\n+\n+\t/*\n+\t * struct setup_revision_opt opt;\n+\n+\t * memset(&opt, 0, sizeof(opt));\n+\t * opt.def = \"HEAD\";\n+\t * opt.revarg_opt = REVARG_COMMITTISH;\n+\t * setup_revisions(argc, argv, rev, &opt);\n+\t */\n+\n+\t/* add the HEAD to pending so we can start */\n+\tadd_head_to_pending(rev);\n+\n+\t/* Let's force oneline format. */\n+\tget_commit_format(\"oneline\", rev);\n+\trev->verbose_header = 1;\n+}\n+\n /*\n  * This method will be called back by git_config(). It is used to gather values\n  * from the configuration files available to Git.\n@@ -62,6 +97,8 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\tstruct rev_info rev;\n+\n \t/*\n \t * parse_options() handles showing usage if incorrect options are\n \t * provided, or if '-h' is passed.\n@@ -72,6 +109,19 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \n \tgit_config(git_walken_config, NULL);\n \n+\t/*\n+\t * Time to set up the walk. repo_init_revisions sets up rev_info with\n+\t * the defaults, but then you need to make some configuration settings\n+\t * to make it do what's special about your walk.\n+\t */\n+\trepo_init_revisions(the_repository, &rev, prefix);\n+\n+\t/*\n+\t * Before we do the walk, we need to set a starting point by giving it\n+\t * something to go in `pending` - that happens in here\n+\t */\n+\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n+\n \t/*\n \t * This line is \"human-readable\" and we are writing a plumbing command,\n \t * so we localize it and use the trace library to print only when\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378438","messageId":"20190701202014.34480-7-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 06/13] walken: perform our basic revision walk","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:07Z","receivedAt":"2019-07-01T20:20:40Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Add the final steps needed and implement the walk loop itself. We add a\nmethod walken_commit_walk() which performs the final setup to revision.c\nand then iterates over commits from get_revision().\n\nThis basic walk only prints the subject line of each commit in the\nhistory. It is nearly equivalent to `git log --oneline`.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: If6dc5f3c9d14df077b99e42806cf790c96191582\n---\n builtin/walken.c | 42 ++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 42 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 333d9ecc5e..f116bb6fca 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -6,8 +6,11 @@\n \n #include \"builtin.h\"\n #include \"revision.h\"\n+#include \"commit.h\"\n #include \"config.h\"\n #include \"parse-options.h\"\n+#include \"pretty.h\"\n+#include \"line-log.h\"\n \n \n /*\n@@ -82,6 +85,40 @@ static int git_walken_config(const char *var, const char *value, void *cb)\n \treturn git_default_config(var, value, cb);\n }\n \n+/*\n+ * walken_commit_walk() is invoked by cmd_walken() after initialization. It\n+ * performs the actual commit walk.\n+ */\n+static void walken_commit_walk(struct rev_info *rev)\n+{\n+\tstruct commit *commit;\n+\tstruct strbuf prettybuf = STRBUF_INIT;\n+\n+\t/*\n+\t * prepare_revision_walk() gets the final steps ready for a revision\n+\t * walk. We check the return value for errors.\n+\t */\n+\tif (prepare_revision_walk(rev)) {\n+\t\tdie(_(\"revision walk setup failed\"));\n+\t}\n+\n+\t/*\n+\t * Now we can start the real commit walk. get_revision() grabs the next\n+\t * revision based on the contents of rev.\n+\t */\n+\twhile ((commit = get_revision(rev))) {\n+\t\tstrbuf_reset(&prettybuf);\n+\t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);\n+\t\t/*\n+\t\t * We expect this part of the output to be machine-parseable -\n+\t\t * one commit message per line - so we send it to stdout.\n+\t\t */\n+\t\tputs(prettybuf.buf);\n+\t}\n+\n+\tstrbuf_release(&prettybuf);\n+}\n+\n int cmd_walken(int argc, const char **argv, const char *prefix)\n {\n \t/*\n@@ -116,12 +153,17 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \t */\n \trepo_init_revisions(the_repository, &rev, prefix);\n \n+\t/* We can set our traversal flags here. */\n+\trev.always_show_header = 1;\n+\n \t/*\n \t * Before we do the walk, we need to set a starting point by giving it\n \t * something to go in `pending` - that happens in here\n \t */\n \tfinal_rev_info_setup(argc, argv, prefix, &rev);\n \n+\twalken_commit_walk(&rev);\n+\n \t/*\n \t * This line is \"human-readable\" and we are writing a plumbing command,\n \t * so we localize it and use the trace library to print only when\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378439","messageId":"20190701202014.34480-8-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 07/13] walken: filter for authors from gmail address","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:08Z","receivedAt":"2019-07-01T20:20:43Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"In order to demonstrate how to create grep filters for revision walks,\nfilter the walk performed by cmd_walken() to print only commits which\nare authored by someone with a gmail address.\n\nThis commit demonstrates how to append a grep pattern to a\nrev_info.grep_filter, to teach new contributors how to create their own\nmore generalized grep filters during revision walks.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex f116bb6fca..a600f88cf6 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -11,6 +11,7 @@\n #include \"parse-options.h\"\n #include \"pretty.h\"\n #include \"line-log.h\"\n+#include \"grep.h\"\n \n \n /*\n@@ -20,10 +21,8 @@\n  */\n static void init_walken_defaults(void)\n {\n-\t/*\n-\t * We don't use any other components or have settings to initialize, so\n-\t * leave this empty.\n-\t */\n+\t/* Needed by our grep filter. */\n+\tinit_grep_defaults(the_repository);\n }\n \n /*\n@@ -55,6 +54,10 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n \t/* add the HEAD to pending so we can start */\n \tadd_head_to_pending(rev);\n \n+\t/* Apply a 'grep' pattern to the 'author' header. */\n+\tappend_header_grep_pattern(&rev->grep_filter, GREP_HEADER_AUTHOR, \"gmail\");\n+\tcompile_grep_patterns(&rev->grep_filter);\n+\n \t/* Let's force oneline format. */\n \tget_commit_format(\"oneline\", rev);\n \trev->verbose_header = 1;\n@@ -78,10 +81,7 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n  */\n static int git_walken_config(const char *var, const char *value, void *cb)\n {\n-\t/*\n-\t * For now, we don't have any custom configuration, so fall back on the\n-\t * default config.\n-\t */\n+\tgrep_config(var, value, cb);\n \treturn git_default_config(var, value, cb);\n }\n \n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378440","messageId":"20190701202014.34480-9-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 08/13] walken: demonstrate various topographical sorts","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:09Z","receivedAt":"2019-07-01T20:20:44Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Order the revision walk by author or commit dates to demonstrate how to\napply topo_sort to a revision walk.\n\nWhile following the tutorial, new contributors are guided to run a walk\nwith each sort and compare the results.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: I7ce2f3e8a77c42001293637ae209087afec4ce2c\n---\n builtin/walken.c | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex a600f88cf6..b334f61e69 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -61,6 +61,13 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n \t/* Let's force oneline format. */\n \tget_commit_format(\"oneline\", rev);\n \trev->verbose_header = 1;\n+\t\n+\t/* Let's play with the sort order. */\n+\trev->topo_order = 1;\n+\n+\t/* Toggle between these and observe the difference. */\n+\trev->sort_order = REV_SORT_BY_COMMIT_DATE;\n+\t/* rev->sort_order = REV_SORT_BY_AUTHOR_DATE; */\n }\n \n /*\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378441","messageId":"20190701202014.34480-10-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 09/13] walken: demonstrate reversing a revision walk list","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:10Z","receivedAt":"2019-07-01T20:20:47Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"The final installment in the tutorial about sorting revision walk\noutputs. This commit reverses the commit list, so that we see newer\ncommits last (handy since we aren't using a pager).\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 3 +++\n 1 file changed, 3 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex b334f61e69..03a50158fb 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -61,6 +61,9 @@ static void final_rev_info_setup(int argc, const char **argv, const char *prefix\n \t/* Let's force oneline format. */\n \tget_commit_format(\"oneline\", rev);\n \trev->verbose_header = 1;\n+\n+\t/* Reverse the order */\n+\trev->reverse = 1;\n \t\n \t/* Let's play with the sort order. */\n \trev->topo_order = 1;\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378442","messageId":"20190701202014.34480-11-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 10/13] walken: add unfiltered object walk from HEAD","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:11Z","receivedAt":"2019-07-01T20:20:49Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Provide a demonstration of a revision walk which traverses all types of\nobject, not just commits. This type of revision walk is used for\noperations such as creating packfiles and performing fetches or clones,\nso it's useful to teach new developers how it works. For starters, only\ndemonstrate the unfiltered version, as this will make the tutorial\neasier to follow.\n\nThis commit is part of a tutorial on revision walking.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: If3b11652ba011b28d29b1c3984dac4a3f80a5f53\n---\n builtin/walken.c | 91 ++++++++++++++++++++++++++++++++++++++++++++----\n 1 file changed, 85 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 03a50158fb..b613102cfb 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -11,9 +11,15 @@\n #include \"parse-options.h\"\n #include \"pretty.h\"\n #include \"line-log.h\"\n+#include \"list-objects.h\"\n #include \"grep.h\"\n \n \n+static int commit_count;\n+static int tag_count;\n+static int blob_count;\n+static int tree_count;\n+\n /*\n  * Within init_walken_defaults() we can call into other useful defaults to set\n  * in the global scope or on the_repository. It's okay to borrow from other\n@@ -95,6 +101,74 @@ static int git_walken_config(const char *var, const char *value, void *cb)\n \treturn git_default_config(var, value, cb);\n }\n \n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+\tcommit_count++;\n+}\n+\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+\tswitch (obj->type) {\n+\tcase OBJ_TREE:\n+\t\ttree_count++;\n+\t\tbreak;\n+\tcase OBJ_BLOB:\n+\t\tblob_count++;\n+\t\tbreak;\n+\tcase OBJ_TAG:\n+\t\ttag_count++;\n+\t\tbreak;\n+\tcase OBJ_COMMIT:\n+\t\t/*\n+\t\t * BUG() is used to warn developers when they've made a change\n+\t\t * which breaks some relied-upon behavior of Git. In this case,\n+\t\t * we're telling developers that we don't expect commits to be\n+\t\t * routed as objects during an object walk. BUG() messages\n+\t\t * should not be localized.\n+\t\t */\n+\t\tBUG(\"unexpected commit object in walken_show_object\\n\");\n+\tdefault:\n+\t\t/*\n+\t\t * This statement will only be hit if a new object type is added\n+\t\t * to Git; we BUG() to tell developers that the new object type\n+\t\t * needs to be handled and counted here.\n+\t\t */\n+\t\tBUG(\"unexpected object type %s in walken_show_object\\n\"),\n+\t\t\t\ttype_name(obj->type);\n+\t}\n+}\n+\n+/*\n+ * walken_object_walk() is invoked by cmd_walken() after initialization. It does\n+ * a walk of all object types.\n+ */\n+static void walken_object_walk(struct rev_info *rev)\n+{\n+\trev->tree_objects = 1;\n+\trev->blob_objects = 1;\n+\trev->tag_objects = 1;\n+\trev->tree_blobs_in_commit_order = 1;\n+\trev->exclude_promisor_objects = 1;\n+\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tcommit_count = 0;\n+\ttag_count = 0;\n+\tblob_count = 0;\n+\ttree_count = 0;\n+\n+\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n+\n+\t/*\n+\t * This print statement is designed to be script-parseable. Script\n+\t * authors will rely on the output not to change, so we will not\n+\t * localize this string. It will go to stdout directly.\n+\t */\n+\tprintf(\"commits %d\\n blobs %d\\n tags %d\\n trees %d\\n\", commit_count,\n+\t       blob_count, tag_count, tree_count);\n+}\n+\n /*\n  * walken_commit_walk() is invoked by cmd_walken() after initialization. It\n  * performs the actual commit walk.\n@@ -166,13 +240,18 @@ int cmd_walken(int argc, const char **argv, const char *prefix)\n \t/* We can set our traversal flags here. */\n \trev.always_show_header = 1;\n \n-\t/*\n-\t * Before we do the walk, we need to set a starting point by giving it\n-\t * something to go in `pending` - that happens in here\n-\t */\n-\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n \n-\twalken_commit_walk(&rev);\n+\tif (1) {\n+\t\tadd_head_to_pending(&rev);\n+\t\twalken_object_walk(&rev);\n+\t} else {\n+\t\t/*\n+\t\t * Before we do the walk, we need to set a starting point by giving it\n+\t\t * something to go in `pending` - that happens in here\n+\t\t */\n+\t\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n+\t\twalken_commit_walk(&rev);\n+\t}\n \n \t/*\n \t * This line is \"human-readable\" and we are writing a plumbing command,\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378443","messageId":"20190701202014.34480-12-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 11/13] walken: add filtered object walk","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:12Z","receivedAt":"2019-07-01T20:20:51Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Demonstrate how filter specs can be used when performing a revision walk\nof all object types. In this case, tree depth is used. Contributors who\nare following the revision walking tutorial will be encouraged to run\nthe revision walk with and without the filter in order to compare the\nnumber of objects seen in each case.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: I6d22ba153c1afbc780c261c47f1fa03ea478b5ed\n---\n builtin/walken.c | 22 +++++++++++++++++++++-\n 1 file changed, 21 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex b613102cfb..7b46377a2e 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -12,6 +12,7 @@\n #include \"pretty.h\"\n #include \"line-log.h\"\n #include \"list-objects.h\"\n+#include \"list-objects-filter-options.h\"\n #include \"grep.h\"\n \n \n@@ -144,6 +145,8 @@ static void walken_show_object(struct object *obj, const char *str, void *buf)\n  */\n static void walken_object_walk(struct rev_info *rev)\n {\n+\tstruct list_objects_filter_options filter_options = {};\n+\n \trev->tree_objects = 1;\n \trev->blob_objects = 1;\n \trev->tag_objects = 1;\n@@ -158,7 +161,24 @@ static void walken_object_walk(struct rev_info *rev)\n \tblob_count = 0;\n \ttree_count = 0;\n \n-\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n+\tif (0) {\n+\t\t/* Unfiltered: */\n+\t\ttrace_printf(_(\"Unfiltered object walk.\\n\"));\n+\t\ttraverse_commit_list(rev, walken_show_commit,\n+\t\t\t\twalken_show_object, NULL);\n+\t} else {\n+\t\ttrace_printf(_(\"Filtered object walk with filterspec \"\n+\t\t\t\t\"'tree:1'.\\n\"));\n+\t\t/*\n+\t\t * We can parse a tree depth of 1 to demonstrate the kind of\n+\t\t * filtering that could occur during various operations (see\n+\t\t * `git help rev-list` and read the entry on `--filter`).\n+\t\t */\n+\t\tparse_list_objects_filter(&filter_options, \"tree:1\");\n+\n+\t\ttraverse_commit_list_filtered(&filter_options, rev,\n+\t\t\twalken_show_commit, walken_show_object, NULL, NULL);\n+\t}\n \n \t/*\n \t * This print statement is designed to be script-parseable. Script\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378444","messageId":"20190701202014.34480-13-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 12/13] walken: count omitted objects","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:13Z","receivedAt":"2019-07-01T20:20:54Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"It may be illuminating to see which objects were not included within a\ngiven filter. This also demonstrates, since filter-spec \"tree:1\" is\nused, that the 'omitted' list contains all objects which are omitted,\nnot just the first objects which were omitted - that is, it continues to\ndereference omitted trees and commits.\n\nThis is part of a tutorial on performing revision walks.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\n---\n builtin/walken.c | 15 +++++++++++++--\n 1 file changed, 13 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 7b46377a2e..1638f679f2 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -146,6 +146,11 @@ static void walken_show_object(struct object *obj, const char *str, void *buf)\n static void walken_object_walk(struct rev_info *rev)\n {\n \tstruct list_objects_filter_options filter_options = {};\n+\tstruct oidset omitted;\n+\tstruct oidset_iter oit;\n+\tstruct object_id *oid = NULL;\n+\tint omitted_count = 0;\n+\toidset_init(&omitted, 0);\n \n \trev->tree_objects = 1;\n \trev->blob_objects = 1;\n@@ -180,13 +185,19 @@ static void walken_object_walk(struct rev_info *rev)\n \t\t\twalken_show_commit, walken_show_object, NULL, NULL);\n \t}\n \n+\t/* Count the omitted objects. */\n+\toidset_iter_init(&omitted, &oit);\n+\n+\twhile ((oid = oidset_iter_next(&oit)))\n+\t\tomitted_count++;\n+\n \t/*\n \t * This print statement is designed to be script-parseable. Script\n \t * authors will rely on the output not to change, so we will not\n \t * localize this string. It will go to stdout directly.\n \t */\n-\tprintf(\"commits %d\\n blobs %d\\n tags %d\\n trees %d\\n\", commit_count,\n-\t       blob_count, tag_count, tree_count);\n+\tprintf(\"commits %d\\n blobs %d\\n tags %d\\n trees %d omitted %d\\n\",\n+\t       commit_count, blob_count, tag_count, tree_count, omitted_count);\n }\n \n /*\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378445","messageId":"20190701202014.34480-14-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"[RFC PATCH v3 13/13] walken: reverse the object walk order","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-01T20:20:14Z","receivedAt":"2019-07-01T20:20:56Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Demonstrate that just like commit walks, object walks can have their\norder reversed. Additionally, add verbose logging of objects encountered\nin order to let contributors prove to themselves that the walk has\nactually been reversed. With this commit, `git walken` becomes extremely\nchatty - it's recommended to pipe the output through `head` or `tail` or\nto redirect it into a file.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nChange-Id: I91883b209a61ae4d87855878291e487fe36220c4\n---\n builtin/walken.c | 3 +++\n 1 file changed, 3 insertions(+)\n\ndiff --git a/builtin/walken.c b/builtin/walken.c\nindex 1638f679f2..2eb12f92ed 100644\n--- a/builtin/walken.c\n+++ b/builtin/walken.c\n@@ -104,11 +104,13 @@ static int git_walken_config(const char *var, const char *value, void *cb)\n \n static void walken_show_commit(struct commit *cmt, void *buf)\n {\n+\tprintf(\"commit: %s\\n\", oid_to_hex(&cmt->object.oid));\n \tcommit_count++;\n }\n \n static void walken_show_object(struct object *obj, const char *str, void *buf)\n {\n+\tprintf(\"%s: %s\\n\", type_name(obj->type), oid_to_hex(&obj->oid));\n \tswitch (obj->type) {\n \tcase OBJ_TREE:\n \t\ttree_count++;\n@@ -157,6 +159,7 @@ static void walken_object_walk(struct rev_info *rev)\n \trev->tag_objects = 1;\n \trev->tree_blobs_in_commit_order = 1;\n \trev->exclude_promisor_objects = 1;\n+\trev->reverse = 1;\n \n \tif (prepare_revision_walk(rev))\n \t\tdie(_(\"revision walk setup failed\"));\n-- \n2.22.0.410.gd8fdbe21b5-goog\n\n"},{"id":"378915","messageId":"20190713003948.GA43313@google.com","threadId":"51257","inReplyTo":"20190620210649.GA87385@google.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2019-07-13T00:39:48Z","receivedAt":"2019-07-13T00:39:57Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2019.06.20 14:06, Emily Shaffer wrote:\n> On Wed, Jun 19, 2019 at 08:17:29AM -0700, Junio C Hamano wrote:\n> > Emily Shaffer <emilyshaffer@google.com> writes:\n> > \n> > > Maybe there's a case for storing them as a set of patch files that are\n> > > revision-controlled somewhere within Documentation/? There was some\n> > > discussion on the IRC a few weeks ago about trying to organize these\n> > > tutorials into their own directory to form a sort of \"Git Contribution\n> > > 101\" course, maybe it makes sense to store there?\n> > >\n> > >   Documentation/contributing/myfirstcontrib/MyFirstContrib.txt\n> > >   Documentation/contributing/myfirstcontrib/sample/*.patch\n> > >   Documentation/contributing/myfirstrevwalk/MyFirstRevWalk.txt\n> > >   Documentation/contributing/myfirstrevwalk/sample/*.patch\n> > >\n> > > I don't love the idea of maintaining text patches with the expectation\n> > > that they should cleanly apply always,...\n> > \n> > Well, I actually think the above organization does match the intent\n> > of the \"My first contribution codelab\" perfectly.  When the codebase,\n> > the workflow used by the project, and/or the coding or documentation\n> > guideline gets updated, the text that documents how to contribute to\n> > the project as well as the sample patches must be updated to match\n> > the updated reality.\n> > \n> > I agree with you that maintaining the *.patch files to always\n> > cleanly apply is less than ideal.  A topic to update the sample\n> > patches and tutorial text may be competing with another topic that\n> > updates the very API the tutorials are teaching, and the sample\n> > patches may not apply cleanly when two topics are merged together,\n> > even if the \"update sample patches and tutorial text\" topic does\n> > update them to match the API at the tip of the topic branch itself.\n> > One thing we _could_ do is to pin the target version of the codebase\n> > for the sake of tutorial.  IOW, the sample/*.patch may not apply\n> > cleanly to the version of the tree these patches were taken from,\n> > but would always apply cleanly to the most recent released version\n> > before the last update to the tutorial, or something like that.\n> > \n> > Also having to review the patch to sample/*.patch files will be\n> > unpleasant.\n> \n> I wonder if we can ease some pain for both of the above issues by\n> including some scripts to \"inflate\" the patch files into a topic branch,\n> or figure out some more easily-reviewed (but more complicated, I\n> suppose) method for sending updates to the sample/*.patch files.\n> \n> Imagining workflows like this:\n> \n> Doing the tutorial:\n>  - In worktree a/.\n>  - Run a magic script which creates a worktree with the sample code, b/.\n>  - Read through a/Documentation/MyFirstContribution.txt and generate\n>    a/builtins/psuh.c, referring to b/builtins/psuh.c if confused.\n> \n> Rebasing the tutorial patches:\n>  - In worktree a/.\n>  - Run a magic script which checks out a new branch at the last known\n>    good base for the patchset, then applies all the patches.\n>  - Now faced with, likely, a topic branch based on v<n-1> (where n is\n>    latest release).\n>  - `git rebase v<n> -x (make && ./bin-wrappers/git psuh)`\n>  - Interactively fix conflicts\n>  - Run a script to generate a magic interdiff from the old version of\n>    patches\n>  - Mail out magic interdiff to list and get approval\n>  - (Maybe maintainer does this when interdiff is happy? Maybe updater\n>    does this when review looks good?) Run a magic script to regenerate\n>    patches from rebased branch, and note somewhere they are based on\n>    v<n>\n>  - Mail sample/*.patch (based on v<n>) to list (if maintainer rolled the\n>    patches after interdiff approval, this step can be skipped)\n> \n> (This seems to still be a lot of steps, even with the magic script..)\n> \n> Alternatively, for the same process:\n>  Updater: Run a magic script to create topic branch based on v<n-1>\n>    (like before)\n>  U: `git rebase v<n> -x (make && ./bin-wrappers/git psuh)`\n>  U: Interactively fix conflicts\n>  U: Run a script to turn topic branch back into sample/*.patch\n>  U: Send email with changes to sample/*.patch (this will be ugly and\n>     unreadable) - message ID <M1>\n>  Reviewer: Run a magic script, providing <M1> argument, which grabs the\n>     diff-of-.patch and generates an interdiff, or a topic branch based\n>     on v<n>\n>  R: Send comments explaining where issue is (tricky to find where to\n>     inline in the diff-of-.patch)\n>  U: Reroll diff-of-.patch email\n>  R: Accepts\n>  Maintainer: Applies diff-of-.patch email normally\n> \n>  I suppose for the first suggestion, there ends up being quite a lot of\n>  onus on the maintainer, and a lot of trust that there is no difference\n>  between the RFC easy-to-read interdiff patchset. For the second\n>  suggestion, there ends up being onus on the reviewers to run some\n>  magical script. Maybe we can split the difference by expecting Updater\n>  to provide the interdiff below the --- line? Maybe in practice the\n>  diff-of-.patch isn't so unreadable, if it's only minor changes needed\n>  to bring the tutorial up to latest?\n> \n>  I'm not sure there's a way to make this totally painless using email\n>  tools.\n\nRandom thought about the \"magic scripts\": if we keep an mbox instead of\na directory of *.patch files, then it seems like git-format-patch and\ngit-am would solve the bulk of this. I don't think dealing with\ndiffs-of-patches-in-mbox is much worse than dealing with\ndiffs-of-patches-in-multiple-files. And for the \"Doing the tutorial\"\nworkflow, it nudges the new contributor to learn git-am.\n\nBut I guess the hard part here is the reviewing diffs-of-diffs part.\nI'm leaning towards the second option here; I personally would not feel\ntoo troubled as a reviewer by having to run an extra script. And as you\nsay, diff-of-diffs may not be so bad in practice. Reviewers already see\nthese whenever someone includes a range-diff in their v>=2 emails.\n"},{"id":"378956","messageId":"20190716000628.GF113966@google.com","threadId":"51257","inReplyTo":"20190713003948.GA43313@google.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-07-16T00:06:28Z","receivedAt":"2019-07-16T00:06:37Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Fri, Jul 12, 2019 at 05:39:48PM -0700, Josh Steadmon wrote:\n> On 2019.06.20 14:06, Emily Shaffer wrote:\n> > On Wed, Jun 19, 2019 at 08:17:29AM -0700, Junio C Hamano wrote:\n> > > Emily Shaffer <emilyshaffer@google.com> writes:\n> > > \n> > > > Maybe there's a case for storing them as a set of patch files that are\n> > > > revision-controlled somewhere within Documentation/? There was some\n> > > > discussion on the IRC a few weeks ago about trying to organize these\n> > > > tutorials into their own directory to form a sort of \"Git Contribution\n> > > > 101\" course, maybe it makes sense to store there?\n> > > >\n> > > >   Documentation/contributing/myfirstcontrib/MyFirstContrib.txt\n> > > >   Documentation/contributing/myfirstcontrib/sample/*.patch\n> > > >   Documentation/contributing/myfirstrevwalk/MyFirstRevWalk.txt\n> > > >   Documentation/contributing/myfirstrevwalk/sample/*.patch\n> > > >\n> > > > I don't love the idea of maintaining text patches with the expectation\n> > > > that they should cleanly apply always,...\n> > > \n> > > Well, I actually think the above organization does match the intent\n> > > of the \"My first contribution codelab\" perfectly.  When the codebase,\n> > > the workflow used by the project, and/or the coding or documentation\n> > > guideline gets updated, the text that documents how to contribute to\n> > > the project as well as the sample patches must be updated to match\n> > > the updated reality.\n> > > \n> > > I agree with you that maintaining the *.patch files to always\n> > > cleanly apply is less than ideal.  A topic to update the sample\n> > > patches and tutorial text may be competing with another topic that\n> > > updates the very API the tutorials are teaching, and the sample\n> > > patches may not apply cleanly when two topics are merged together,\n> > > even if the \"update sample patches and tutorial text\" topic does\n> > > update them to match the API at the tip of the topic branch itself.\n> > > One thing we _could_ do is to pin the target version of the codebase\n> > > for the sake of tutorial.  IOW, the sample/*.patch may not apply\n> > > cleanly to the version of the tree these patches were taken from,\n> > > but would always apply cleanly to the most recent released version\n> > > before the last update to the tutorial, or something like that.\n> > > \n> > > Also having to review the patch to sample/*.patch files will be\n> > > unpleasant.\n> > \n> > I wonder if we can ease some pain for both of the above issues by\n> > including some scripts to \"inflate\" the patch files into a topic branch,\n> > or figure out some more easily-reviewed (but more complicated, I\n> > suppose) method for sending updates to the sample/*.patch files.\n> > \n> > Imagining workflows like this:\n> > \n> > Doing the tutorial:\n> >  - In worktree a/.\n> >  - Run a magic script which creates a worktree with the sample code, b/.\n> >  - Read through a/Documentation/MyFirstContribution.txt and generate\n> >    a/builtins/psuh.c, referring to b/builtins/psuh.c if confused.\n> > \n> > Rebasing the tutorial patches:\n> >  - In worktree a/.\n> >  - Run a magic script which checks out a new branch at the last known\n> >    good base for the patchset, then applies all the patches.\n> >  - Now faced with, likely, a topic branch based on v<n-1> (where n is\n> >    latest release).\n> >  - `git rebase v<n> -x (make && ./bin-wrappers/git psuh)`\n> >  - Interactively fix conflicts\n> >  - Run a script to generate a magic interdiff from the old version of\n> >    patches\n> >  - Mail out magic interdiff to list and get approval\n> >  - (Maybe maintainer does this when interdiff is happy? Maybe updater\n> >    does this when review looks good?) Run a magic script to regenerate\n> >    patches from rebased branch, and note somewhere they are based on\n> >    v<n>\n> >  - Mail sample/*.patch (based on v<n>) to list (if maintainer rolled the\n> >    patches after interdiff approval, this step can be skipped)\n> > \n> > (This seems to still be a lot of steps, even with the magic script..)\n> > \n> > Alternatively, for the same process:\n> >  Updater: Run a magic script to create topic branch based on v<n-1>\n> >    (like before)\n> >  U: `git rebase v<n> -x (make && ./bin-wrappers/git psuh)`\n> >  U: Interactively fix conflicts\n> >  U: Run a script to turn topic branch back into sample/*.patch\n> >  U: Send email with changes to sample/*.patch (this will be ugly and\n> >     unreadable) - message ID <M1>\n> >  Reviewer: Run a magic script, providing <M1> argument, which grabs the\n> >     diff-of-.patch and generates an interdiff, or a topic branch based\n> >     on v<n>\n> >  R: Send comments explaining where issue is (tricky to find where to\n> >     inline in the diff-of-.patch)\n> >  U: Reroll diff-of-.patch email\n> >  R: Accepts\n> >  Maintainer: Applies diff-of-.patch email normally\n> > \n> >  I suppose for the first suggestion, there ends up being quite a lot of\n> >  onus on the maintainer, and a lot of trust that there is no difference\n> >  between the RFC easy-to-read interdiff patchset. For the second\n> >  suggestion, there ends up being onus on the reviewers to run some\n> >  magical script. Maybe we can split the difference by expecting Updater\n> >  to provide the interdiff below the --- line? Maybe in practice the\n> >  diff-of-.patch isn't so unreadable, if it's only minor changes needed\n> >  to bring the tutorial up to latest?\n> > \n> >  I'm not sure there's a way to make this totally painless using email\n> >  tools.\n> \n> Random thought about the \"magic scripts\": if we keep an mbox instead of\n> a directory of *.patch files, then it seems like git-format-patch and\n> git-am would solve the bulk of this. I don't think dealing with\n> diffs-of-patches-in-mbox is much worse than dealing with\n> diffs-of-patches-in-multiple-files. And for the \"Doing the tutorial\"\n> workflow, it nudges the new contributor to learn git-am.\n> \n> But I guess the hard part here is the reviewing diffs-of-diffs part.\n> I'm leaning towards the second option here; I personally would not feel\n> too troubled as a reviewer by having to run an extra script. And as you\n> say, diff-of-diffs may not be so bad in practice. Reviewers already see\n> these whenever someone includes a range-diff in their v>=2 emails.\n\nThere was also some suggestion of instead checking in ed scripts or\nsimilar to populate the changes. On one hand, it might be nicer, as\nthere aren't diff markers on the front of all the code... but on the\nother hand, I'm not sure how many folks are familiar with ed (I know I'm\nnot) and it might be complex to indicate where to insert changes.\n\nI have been in a position of reviewing diff-of-.patch in a past life,\nalbeit via Gerrit, and it's not the worst when the code is simple (as we\nshould always hope this example tutorial code would be).\n\n - Emily\n"},{"id":"378980","messageId":"xmqqwogh3nbb.fsf@gitster-ct.c.googlers.com","threadId":"51257","inReplyTo":"20190716000628.GF113966@google.com","subject":"Re: [PATCH] documentation: add tutorial for revision walking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-07-16T17:24:08Z","receivedAt":"2019-07-16T17:24:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Emily Shaffer <emilyshaffer@google.com> writes:\n\n> I have been in a position of reviewing diff-of-.patch in a past life,\n> albeit via Gerrit, and it's not the worst when the code is simple (as we\n> should always hope this example tutorial code would be).\n\nI personally think a directory full of patch files is OK.  I am not\nsure if they (together with this rev walk tutorial) belong to the\nmain part of the project, though.\n"},{"id":"379231","messageId":"20190724231128.GD43313@google.com","threadId":"51257","inReplyTo":"20190701201934.30321-1-emilyshaffer@google.com","subject":"Re: [PATCH v3] documentation: add tutorial for revision walking","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2019-07-24T23:11:28Z","receivedAt":"2019-07-24T23:11:36Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2019.07.01 13:19, Emily Shaffer wrote:\n> Existing documentation on revision walks seems to be primarily intended\n> as a reference for those already familiar with the procedure. This\n> tutorial attempts to give an entry-level guide to a couple of bare-bones\n> revision walks so that new Git contributors can learn the concepts\n> without having to wade through options parsing or special casing.\n> \n> The target audience is a Git contributor who is just getting started\n> with the concept of revision walking. The goal is to prepare this\n> contributor to be able to understand and modify existing commands which\n> perform revision walks more easily, although it will also prepare\n> contributors to create new commands which perform walks.\n> \n> The tutorial covers a basic overview of the structs involved during\n> revision walk, setting up a basic commit walk, setting up a basic\n> all-object walk, and adding some configuration changes to both walk\n> types. It intentionally does not cover how to create new commands or\n> search for options from the command line or gitconfigs.\n> \n> There is an associated patchset at\n> https://github.com/nasamuffin/git/tree/revwalk that contains a reference\n> implementation of the code generated by this tutorial.\n> \n> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n> Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n\nThis looks good to me; as a new Git developer I found it informative and\nwish it had been present before I took my first look at the rev-walk\ncode.\n"},{"id":"379232","messageId":"20190724233253.208318-1-jonathantanmy@google.com","threadId":"51257","inReplyTo":"20190701201934.30321-1-emilyshaffer@google.com","subject":"Re: [PATCH v3] documentation: add tutorial for revision walking","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2019-07-24T23:32:53Z","receivedAt":"2019-07-24T23:32:59Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"Thanks - I think this is a useful guide to what can be a complicated\ntopic. It looks good overall; I just have some minor comments below.\n\n> diff --git a/Documentation/Makefile b/Documentation/Makefile\n> index 76f2ecfc1b..91e5da67c4 100644\n> --- a/Documentation/Makefile\n> +++ b/Documentation/Makefile\n> @@ -78,6 +78,7 @@ SP_ARTICLES += $(API_DOCS)\n>  \n>  TECH_DOCS += MyFirstContribution\n>  TECH_DOCS += SubmittingPatches\n> +TECH_DOCS += MyFirstRevWalk\n\nAny reason why this is not in alphabetical order?\n\n> +Also add the relevant line in `builtin.h` near `cmd_whatchanged()`:\n> +\n> +----\n> +extern int cmd_walken(int argc, const char **argv, const char *prefix);\n> +----\n\nbuiltin.h no longer has \"extern\", so we can delete it.\n\n> +Add it to the `Makefile` near the line for `builtin\\worktree.o`:\n> +\n> +----\n> +BUILTIN_OBJS += builtin/walken.o\n> +----\n\nIn the first line, change the backslash to a slash. (The line in\nMakefile for \"builtin/worktree.o\" uses a forward slash as expected.)\n\n> +NOTE: For a more exhaustive overview of the new command process, take a look at\n> +`Documentation/MyFirstContribution.txt`.\n> +\n> +NOTE: A reference implementation can be found at TODO LINK.\n\nI think you have a reference implementation at\nhttps://github.com/nasamuffin/git/tree/revwalk?\n\n> +We'll start by enabling all types of objects in the `struct rev_info`. Unless\n> +you cloned or fetched your repository earlier with a filter,\n> +`exclude_promisor_objects` is unlikely to make a difference, but we'll turn it\n> +on just to make sure our lives are simple. We'll also turn on\n> +`tree_blobs_in_commit_order`, which means that we will walk a commit's tree and\n> +everything it points to immediately after we find each commit, as opposed to\n> +waiting for the end and walking through all trees after the commit history has\n> +been discovered. With the appropriate settings configured, we are ready to call\n> +`prepare_revision_walk()`.\n> +\n> +----\n> +static void walken_object_walk(struct rev_info *rev)\n> +{\n> +\trev->tree_objects = 1;\n> +\trev->blob_objects = 1;\n> +\trev->tag_objects = 1;\n> +\trev->tree_blobs_in_commit_order = 1;\n> +\trev->exclude_promisor_objects = 1;\n\nOptional: I think we should not bother with exclude_promisor_objects. If\nthe user really cloned with a filter, then every object would be a\npromisor object and the revision walk should output nothing, which is\nvery confusing.\n"},{"id":"379245","messageId":"nycvar.QRO.7.76.6.1907251122260.21907@tvgsbejvaqbjf.bet","threadId":"51257","inReplyTo":"20190701202014.34480-1-emilyshaffer@google.com","subject":"Re: [RFC PATCH v3 00/13] example implementation of revwalk tutorial","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-07-25T09:25:02Z","receivedAt":"2019-07-25T09:25:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Emily,\n\nOn Mon, 1 Jul 2019, Emily Shaffer wrote:\n\n> Since v2, mostly reworded comments, plus fixed the issues mentioned in\n> the tutorial itself. Thanks Eric for the review.\n>\n> Emily Shaffer (13):\n>   walken: add infrastructure for revwalk demo\n>   walken: add usage to enable -h\n>   walken: add placeholder to initialize defaults\n>   walken: add handler to git_config\n>   walken: configure rev_info and prepare for walk\n>   walken: perform our basic revision walk\n>   walken: filter for authors from gmail address\n>   walken: demonstrate various topographical sorts\n>   walken: demonstrate reversing a revision walk list\n>   walken: add unfiltered object walk from HEAD\n>   walken: add filtered object walk\n>   walken: count omitted objects\n>   walken: reverse the object walk order\n>\n>  Makefile         |   1 +\n>  builtin.h        |   1 +\n>  builtin/walken.c | 297 +++++++++++++++++++++++++++++++++++++++++++++++\n\nSince this is not really intended to be an end user-facing command, I\nthink it should not become a built-in, to be carried into every Git\nuser's setup.\n\nInstead, I would recommend to implement this as a test helper.\n\nThis would have the following advantages:\n\n- it won't clutter the end user installations,\n\n- it will still be compile-tested with every build (guaranteeing that\n  the tutorial won't become stale over time as so many other tutorials),\n\n- it really opens the door very wide to follow up with another tutorial\n  to guide new contributors to write stellar regression tests.\n\nThanks,\nDscho\n\n>  git.c            |   1 +\n>  4 files changed, 300 insertions(+)\n>  create mode 100644 builtin/walken.c\n>\n> --\n> 2.22.0.410.gd8fdbe21b5-goog\n>\n>\n>\n"},{"id":"380050","messageId":"20190806231050.GA130824@google.com","threadId":"51257","inReplyTo":"20190724233253.208318-1-jonathantanmy@google.com","subject":"Re: [PATCH v3] documentation: add tutorial for revision walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-08-06T23:10:50Z","receivedAt":"2019-08-06T23:10:58Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Wed, Jul 24, 2019 at 04:32:53PM -0700, Jonathan Tan wrote:\n> Thanks - I think this is a useful guide to what can be a complicated\n> topic. It looks good overall; I just have some minor comments below.\n> \n> > diff --git a/Documentation/Makefile b/Documentation/Makefile\n> > index 76f2ecfc1b..91e5da67c4 100644\n> > --- a/Documentation/Makefile\n> > +++ b/Documentation/Makefile\n> > @@ -78,6 +78,7 @@ SP_ARTICLES += $(API_DOCS)\n> >  \n> >  TECH_DOCS += MyFirstContribution\n> >  TECH_DOCS += SubmittingPatches\n> > +TECH_DOCS += MyFirstRevWalk\n> \n> Any reason why this is not in alphabetical order?\n\nNo reason, will fix.\n\n> \n> > +Also add the relevant line in `builtin.h` near `cmd_whatchanged()`:\n> > +\n> > +----\n> > +extern int cmd_walken(int argc, const char **argv, const char *prefix);\n> > +----\n> \n> builtin.h no longer has \"extern\", so we can delete it.\n\nDone.\n\n> \n> > +Add it to the `Makefile` near the line for `builtin\\worktree.o`:\n> > +\n> > +----\n> > +BUILTIN_OBJS += builtin/walken.o\n> > +----\n> \n> In the first line, change the backslash to a slash. (The line in\n> Makefile for \"builtin/worktree.o\" uses a forward slash as expected.)\n\nDone, not sure how this got in there. Thanks!\n\n> \n> > +NOTE: For a more exhaustive overview of the new command process, take a look at\n> > +`Documentation/MyFirstContribution.txt`.\n> > +\n> > +NOTE: A reference implementation can be found at TODO LINK.\n> \n> I think you have a reference implementation at\n> https://github.com/nasamuffin/git/tree/revwalk?\n\nYep, although it's not very fresh. I was hoping to wait for a way for us\nto check in the reference implementation to Git source, although that\ncan wait and the off-project branch is maybe OK for now.\n\n> \n> > +We'll start by enabling all types of objects in the `struct rev_info`. Unless\n> > +you cloned or fetched your repository earlier with a filter,\n> > +`exclude_promisor_objects` is unlikely to make a difference, but we'll turn it\n> > +on just to make sure our lives are simple. We'll also turn on\n> > +`tree_blobs_in_commit_order`, which means that we will walk a commit's tree and\n> > +everything it points to immediately after we find each commit, as opposed to\n> > +waiting for the end and walking through all trees after the commit history has\n> > +been discovered. With the appropriate settings configured, we are ready to call\n> > +`prepare_revision_walk()`.\n> > +\n> > +----\n> > +static void walken_object_walk(struct rev_info *rev)\n> > +{\n> > +\trev->tree_objects = 1;\n> > +\trev->blob_objects = 1;\n> > +\trev->tag_objects = 1;\n> > +\trev->tree_blobs_in_commit_order = 1;\n> > +\trev->exclude_promisor_objects = 1;\n> \n> Optional: I think we should not bother with exclude_promisor_objects. If\n> the user really cloned with a filter, then every object would be a\n> promisor object and the revision walk should output nothing, which is\n> very confusing.\n\nSure, that makes sense. Ok, I removed it.\n\n\nThanks for looking - and for the patience with the latency on the reply.\n"},{"id":"380051","messageId":"20190806231328.GB130824@google.com","threadId":"51257","inReplyTo":"nycvar.QRO.7.76.6.1907251122260.21907@tvgsbejvaqbjf.bet","subject":"Re: [RFC PATCH v3 00/13] example implementation of revwalk tutorial","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-08-06T23:13:28Z","receivedAt":"2019-08-06T23:13:35Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Thu, Jul 25, 2019 at 11:25:02AM +0200, Johannes Schindelin wrote:\n> Hi Emily,\n> \n> On Mon, 1 Jul 2019, Emily Shaffer wrote:\n> \n> > Since v2, mostly reworded comments, plus fixed the issues mentioned in\n> > the tutorial itself. Thanks Eric for the review.\n> >\n> > Emily Shaffer (13):\n> >   walken: add infrastructure for revwalk demo\n> >   walken: add usage to enable -h\n> >   walken: add placeholder to initialize defaults\n> >   walken: add handler to git_config\n> >   walken: configure rev_info and prepare for walk\n> >   walken: perform our basic revision walk\n> >   walken: filter for authors from gmail address\n> >   walken: demonstrate various topographical sorts\n> >   walken: demonstrate reversing a revision walk list\n> >   walken: add unfiltered object walk from HEAD\n> >   walken: add filtered object walk\n> >   walken: count omitted objects\n> >   walken: reverse the object walk order\n> >\n> >  Makefile         |   1 +\n> >  builtin.h        |   1 +\n> >  builtin/walken.c | 297 +++++++++++++++++++++++++++++++++++++++++++++++\n> \n> Since this is not really intended to be an end user-facing command, I\n> think it should not become a built-in, to be carried into every Git\n> user's setup.\n\nIt's not intended to be checked into Git source as-is.\n\n> \n> Instead, I would recommend to implement this as a test helper.\n\nI'm not sure I follow how you imagine this looking, but the drawback I\nsee of implementing this in a different way than you would typically do\nwhen writing a real feature for the project is that it becomes less\nuseful as a reference for new contributors.\n\n> \n> This would have the following advantages:\n> \n> - it won't clutter the end user installations,\n> \n> - it will still be compile-tested with every build (guaranteeing that\n>   the tutorial won't become stale over time as so many other tutorials),\n\nThis part of your suggestion appeals to me; so I'm really curious how\nyou would do it. Do you have something else written in the way you're\nsuggesting in mind?\n\n> \n> - it really opens the door very wide to follow up with another tutorial\n>   to guide new contributors to write stellar regression tests.\n> \n> Thanks,\n> Dscho\n> \n> >  git.c            |   1 +\n> >  4 files changed, 300 insertions(+)\n> >  create mode 100644 builtin/walken.c\n> >\n> > --\n> > 2.22.0.410.gd8fdbe21b5-goog\n> >\n> >\n> >\n"},{"id":"380052","messageId":"20190806231952.39155-1-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190701201934.30321-1-emilyshaffer@google.com","subject":"[PATCH v4] documentation: add tutorial for revision walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-08-06T23:19:52Z","receivedAt":"2019-08-06T23:20:26Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Existing documentation on revision walks seems to be primarily intended\nas a reference for those already familiar with the procedure. This\ntutorial attempts to give an entry-level guide to a couple of bare-bones\nrevision walks so that new Git contributors can learn the concepts\nwithout having to wade through options parsing or special casing.\n\nThe target audience is a Git contributor who is just getting started\nwith the concept of revision walking. The goal is to prepare this\ncontributor to be able to understand and modify existing commands which\nperform revision walks more easily, although it will also prepare\ncontributors to create new commands which perform walks.\n\nThe tutorial covers a basic overview of the structs involved during\nrevision walk, setting up a basic commit walk, setting up a basic\nall-object walk, and adding some configuration changes to both walk\ntypes. It intentionally does not cover how to create new commands or\nsearch for options from the command line or gitconfigs.\n\nThere is an associated patchset at\nhttps://github.com/nasamuffin/git/tree/revwalk that contains a reference\nimplementation of the code generated by this tutorial.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\n---\n\nSince v3, only a couple of minor changes from Jonathan Tan - thanks.\n\nI'm dropping the updates for the RFC set, since they're incremental from\nnow. Next time you all see them they will be in a form which we would\nhope to maintain over a long period of time, checked into source -\nlikely in the form of an mbox or dir of .patch file.\n\nI think the tutorial itself is pretty much ready; the example source is\nmore like \"supporting material\" - so I'd like to try to get this back on\npeople's minds and hopefully checked in.\n\nThanks!\n - Emily\n\n Documentation/Makefile           |   1 +\n Documentation/MyFirstRevWalk.txt | 904 +++++++++++++++++++++++++++++++\n 2 files changed, 905 insertions(+)\n create mode 100644 Documentation/MyFirstRevWalk.txt\n\ndiff --git a/Documentation/Makefile b/Documentation/Makefile\nindex 76f2ecfc1b..7d136b480c 100644\n--- a/Documentation/Makefile\n+++ b/Documentation/Makefile\n@@ -77,6 +77,7 @@ API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technica\n SP_ARTICLES += $(API_DOCS)\n \n TECH_DOCS += MyFirstContribution\n+TECH_DOCS += MyFirstRevWalk\n TECH_DOCS += SubmittingPatches\n TECH_DOCS += technical/hash-function-transition\n TECH_DOCS += technical/http-protocol\ndiff --git a/Documentation/MyFirstRevWalk.txt b/Documentation/MyFirstRevWalk.txt\nnew file mode 100644\nindex 0000000000..5aa249df5c\n--- /dev/null\n+++ b/Documentation/MyFirstRevWalk.txt\n@@ -0,0 +1,904 @@\n+My First Revision Walk\n+======================\n+\n+== What's a Revision Walk?\n+\n+The revision walk is a key concept in Git - this is the process that underpins\n+operations like `git log`, `git blame`, and `git reflog`. Beginning at HEAD, the\n+list of objects is found by walking parent relationships between objects. The\n+revision walk can also be used to determine whether or not a given object is\n+reachable from the current HEAD pointer.\n+\n+=== Related Reading\n+\n+- `Documentation/user-manual.txt` under \"Hacking Git\" contains some coverage of\n+  the revision walker in its various incarnations.\n+- `Documentation/technical/api-revision-walking.txt`\n+- https://eagain.net/articles/git-for-computer-scientists/[Git for Computer Scientists]\n+  gives a good overview of the types of objects in Git and what your revision\n+  walk is really describing.\n+\n+== Setting Up\n+\n+Create a new branch from `master`.\n+\n+----\n+git checkout -b revwalk origin/master\n+----\n+\n+We'll put our fiddling into a new command. For fun, let's name it `git walken`.\n+Open up a new file `builtin/walken.c` and set up the command handler:\n+\n+----\n+/*\n+ * \"git walken\"\n+ *\n+ * Part of the \"My First Revision Walk\" tutorial.\n+ */\n+\n+#include \"builtin.h\"\n+\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\ttrace_printf(_(\"cmd_walken incoming...\\n\"));\n+\treturn 0;\n+}\n+----\n+\n+NOTE: `trace_printf()` differs from `printf()` in that it can be turned on or\n+off at runtime. For the purposes of this tutorial, we will write `walken` as\n+though it is intended for use as a \"plumbing\" command: that is, a command which\n+is used primarily in scripts, rather than interactively by humans (a \"porcelain\"\n+command). So we will send our debug output to `trace_printf()` instead. When\n+running, enable trace output by setting the environment variable `GIT_TRACE`.\n+\n+Add usage text and `-h` handling, like all subcommands should consistently do\n+(our test suite will notice and complain if you fail to do so).\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\tconst char * const walken_usage[] = {\n+\t\tN_(\"git walken\"),\n+\t\tNULL,\n+\t}\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n+\n+\t...\n+}\n+----\n+\n+Also add the relevant line in `builtin.h` near `cmd_whatchanged()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix);\n+----\n+\n+Include the command in `git.c` in `commands[]` near the entry for `whatchanged`,\n+maintaining alphabetical ordering:\n+\n+----\n+{ \"walken\", cmd_walken, RUN_SETUP },\n+----\n+\n+Add it to the `Makefile` near the line for `builtin/worktree.o`:\n+\n+----\n+BUILTIN_OBJS += builtin/walken.o\n+----\n+\n+Build and test out your command, without forgetting to ensure the `DEVELOPER`\n+flag is set, and with `GIT_TRACE` enabled so the debug output can be seen:\n+\n+----\n+$ echo DEVELOPER=1 >>config.mak\n+$ make\n+$ GIT_TRACE=1 ./bin-wrappers/git walken\n+----\n+\n+NOTE: For a more exhaustive overview of the new command process, take a look at\n+`Documentation/MyFirstContribution.txt`.\n+\n+NOTE: A reference implementation can be found at\n+https://github.com/nasamuffin/git/tree/revwalk.\n+\n+=== `struct rev_cmdline_info`\n+\n+The definition of `struct rev_cmdline_info` can be found in `revision.h`.\n+\n+This struct is contained within the `rev_info` struct and is used to reflect\n+parameters provided by the user over the CLI.\n+\n+`nr` represents the number of `rev_cmdline_entry` present in the array.\n+\n+`alloc` is used by the `ALLOC_GROW` macro. Check\n+`Documentation/technical/api-allocation-growing.txt` - this variable is used to\n+track the allocated size of the list.\n+\n+Per entry, we find:\n+\n+`item` is the object provided upon which to base the revision walk. Items in Git\n+can be blobs, trees, commits, or tags. (See `Documentation/gittutorial-2.txt`.)\n+\n+`name` is the object ID (OID) of the object - a hex string you may be familiar\n+with from using Git to organize your source in the past. Check the tutorial\n+mentioned above towards the top for a discussion of where the OID can come\n+from.\n+\n+`whence` indicates some information about what to do with the parents of the\n+specified object. We'll explore this flag more later on; take a look at\n+`Documentation/revisions.txt` to get an idea of what could set the `whence`\n+value.\n+\n+`flags` are used to hint the beginning of the revision walk and are the first\n+block under the `#include`s in `revision.h`. The most likely ones to be set in\n+the `rev_cmdline_info` are `UNINTERESTING` and `BOTTOM`, but these same flags\n+can be used during the walk, as well.\n+\n+=== `struct rev_info`\n+\n+This one is quite a bit longer, and many fields are only used during the walk\n+by `revision.c` - not configuration options. Most of the configurable flags in\n+`struct rev_info` have a mirror in `Documentation/rev-list-options.txt`. It's a\n+good idea to take some time and read through that document.\n+\n+== Basic Commit Walk\n+\n+First, let's see if we can replicate the output of `git log --oneline`. We'll\n+refer back to the implementation frequently to discover norms when performing\n+a revision walk of our own.\n+\n+To do so, we'll first find all the commits, in order, which preceded the current\n+commit. We'll extract the name and subject of the commit from each.\n+\n+Ideally, we will also be able to find out which ones are currently at the tip of\n+various branches.\n+\n+=== Setting Up\n+\n+Preparing for your revision walk has some distinct stages.\n+\n+1. Perform default setup for this mode, and others which may be invoked.\n+2. Check configuration files for relevant settings.\n+3. Set up the `rev_info` struct.\n+4. Tweak the initialized `rev_info` to suit the current walk.\n+5. Prepare the `rev_info` for the walk.\n+6. Iterate over the objects, processing each one.\n+\n+==== Default Setups\n+\n+Before examining configuration files which may modify command behavior, set up\n+default state for switches or options your command may have. If your command\n+utilizes other Git components, ask them to set up their default states as well.\n+For instance, `git log` takes advantage of `grep` and `diff` functionality, so\n+its `init_log_defaults()` sets its own state (`decoration_style`) and asks\n+`grep` and `diff` to initialize themselves by calling each of their\n+initialization functions.\n+\n+For our purposes, within `git walken`, for the first example we don't intend to\n+use any other components within Git, and we don't have any configuration to do.\n+However, we may want to add some later, so for now, we can add an empty\n+placeholder. Create a new function in `builtin/walken.c`:\n+\n+----\n+static void init_walken_defaults(void)\n+{\n+\t/*\n+\t * We don't actually need the same components `git log` does; leave this\n+\t * empty for now.\n+\t */\n+}\n+----\n+\n+Make sure to add a line invoking it inside of `cmd_walken()`.\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\tinit_walken_defaults();\n+}\n+----\n+\n+==== Configuring From `.gitconfig`\n+\n+Next, we should have a look at any relevant configuration settings (i.e.,\n+settings readable and settable from `git config`). This is done by providing a\n+callback to `git_config()`; within that callback, you can also invoke methods\n+from other components you may need that need to intercept these options. Your\n+callback will be invoked once per each configuration value which Git knows about\n+(global, local, worktree, etc.).\n+\n+Similarly to the default values, we don't have anything to do here yet\n+ourselves; however, we should call `git_default_config()` if we aren't calling\n+any other existing config callbacks.\n+\n+Add a new function to `builtin/walken.c`:\n+\n+----\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\t/*\n+\t * For now, we don't have any custom configuration, so fall back to\n+\t * the default config.\n+\t */\n+\treturn git_default_config(var, value, cb);\n+}\n+----\n+\n+Make sure to invoke `git_config()` with it in your `cmd_walken()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t...\n+\n+\tgit_config(git_walken_config, NULL);\n+\n+\t...\n+}\n+----\n+\n+// TODO: Checking CLI options\n+\n+==== Setting Up `rev_info`\n+\n+Now that we've gathered external configuration and options, it's time to\n+initialize the `rev_info` object which we will use to perform the walk. This is\n+typically done by calling `repo_init_revisions()` with the repository you intend\n+to target, as well as the `prefix` argument of `cmd_walken` and your `rev_info`\n+struct.\n+\n+Add the `struct rev_info` and the `repo_init_revisions()` call:\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t/* This can go wherever you like in your declarations.*/\n+\tstruct rev_info rev;\n+\t...\n+\n+\t/* This should go after the git_config() call. */\n+\trepo_init_revisions(the_repository, &rev, prefix);\n+\n+\t...\n+}\n+----\n+\n+==== Tweaking `rev_info` For the Walk\n+\n+We're getting close, but we're still not quite ready to go. Now that `rev` is\n+initialized, we can modify it to fit our needs. This is usually done within a\n+helper for clarity, so let's add one:\n+\n+----\n+static void final_rev_info_setup(struct rev_info *rev)\n+{\n+\t/*\n+\t * We want to mimic the appearance of `git log --oneline`, so let's\n+\t * force oneline format.\n+\t */\n+\tget_commit_format(\"oneline\", rev);\n+\n+\t/* Start our revision walk at HEAD. */\n+\tadd_head_to_pending(rev);\n+}\n+----\n+\n+[NOTE]\n+====\n+Instead of using the shorthand `add_head_to_pending()`, you could do\n+something like this:\n+----\n+\tstruct setup_revision_opt opt;\n+\n+\tmemset(&opt, 0, sizeof(opt));\n+\topt.def = \"HEAD\";\n+\topt.revarg_opt = REVARG_COMMITTISH;\n+\tsetup_revisions(argc, argv, rev, &opt);\n+----\n+Using a `setup_revision_opt` gives you finer control over your walk's starting\n+point.\n+====\n+\n+Then let's invoke `final_rev_info_setup()` after the call to\n+`repo_init_revisions()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t...\n+\n+\tfinal_rev_info_setup(&rev);\n+\n+\t...\n+}\n+----\n+\n+Later, we may wish to add more arguments to `final_rev_info_setup()`. But for\n+now, this is all we need.\n+\n+==== Preparing `rev_info` For the Walk\n+\n+Now that `rev` is all initialized and configured, we've got one more setup step\n+before we get rolling. We can do this in a helper, which will both prepare the\n+`rev_info` for the walk, and perform the walk itself. Let's start the helper\n+with the call to `prepare_revision_walk()`, which can return an error without\n+dying on its own:\n+\n+----\n+static void walken_commit_walk(struct rev_info *rev)\n+{\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+}\n+----\n+\n+NOTE: `die()` prints to `stderr` and exits the program. Since it will print to\n+`stderr` it's likely to be seen by a human, so we will localize it.\n+\n+==== Performing the Walk!\n+\n+Finally! We are ready to begin the walk itself. Now we can see that `rev_info`\n+can also be used as an iterator; we move to the next item in the walk by using\n+`get_revision()` repeatedly. Add the listed variable declarations at the top and\n+the walk loop below the `prepare_revision_walk()` call within your\n+`walken_commit_walk()`:\n+\n+----\n+static void walken_commit_walk(struct rev_info *rev)\n+{\n+\tstruct commit *commit;\n+\tstruct strbuf prettybuf = STRBUF_INIT;\n+\n+\t...\n+\n+\twhile ((commit = get_revision(rev))) {\n+\t\tif (!commit)\n+\t\t\tcontinue;\n+\n+\t\tstrbuf_reset(&prettybuf);\n+\t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);\n+\t\tputs(prettybuf.buf);\n+\t}\n+\tstrbuf_release(&prettybuf);\n+}\n+----\n+\n+NOTE: `puts()` prints a `char*` to `stdout`. Since this is the part of the\n+command we expect to be machine-parsed, we're sending it directly to stdout.\n+\n+Give it a shot.\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken\n+----\n+\n+You should see all of the subject lines of all the commits in\n+your tree's history, in order, ending with the initial commit, \"Initial revision\n+of \"git\", the information manager from hell\". Congratulations! You've written\n+your first revision walk. You can play with printing some additional fields\n+from each commit if you're curious; have a look at the functions available in\n+`commit.h`.\n+\n+=== Adding a Filter\n+\n+Next, let's try to filter the commits we see based on their author. This is\n+equivalent to running `git log --author=<pattern>`. We can add a filter by\n+modifying `rev_info.grep_filter`, which is a `struct grep_opt`.\n+\n+First some setup. Add `init_grep_defaults()` to `init_walken_defaults()` and add\n+`grep_config()` to `git_walken_config()`:\n+\n+----\n+static void init_walken_defaults(void)\n+{\n+\tinit_grep_defaults(the_repository);\n+}\n+\n+...\n+\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\tgrep_config(var, value, cb);\n+\treturn git_default_config(var, value, cb);\n+}\n+----\n+\n+Next, we can modify the `grep_filter`. This is done with convenience functions\n+found in `grep.h`. For fun, we're filtering to only commits from folks using a\n+`gmail.com` email address - a not-very-precise guess at who may be working on\n+Git as a hobby. Since we're checking the author, which is a specific line in the\n+header, we'll use the `append_header_grep_pattern()` helper. We can use\n+the `enum grep_header_field` to indicate which part of the commit header we want\n+to search.\n+\n+In `final_rev_info_setup()`, add your filter line:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\tappend_header_grep_pattern(&rev->grep_filter, GREP_HEADER_AUTHOR,\n+\t\t\"gmail\");\n+\tcompile_grep_patterns(&rev->grep_filter);\n+\n+\t...\n+}\n+----\n+\n+`append_header_grep_pattern()` adds your new \"gmail\" pattern to `rev_info`, but\n+it won't work unless we compile it with `compile_grep_patterns()`.\n+\n+NOTE: If you are using `setup_revisions()` (for example, if you are passing a\n+`setup_revision_opt` instead of using `add_head_to_pending()`), you don't need\n+to call `compile_grep_patterns()` because `setup_revisions()` calls it for you.\n+\n+NOTE: We could add the same filter via the `append_grep_pattern()` helper if we\n+wanted to, but `append_header_grep_pattern()` adds the `enum grep_context` and\n+`enum grep_pat_token` for us.\n+\n+=== Changing the Order\n+\n+There are a few ways that we can change the order of the commits during a\n+revision walk. Firstly, we can use the `enum rev_sort_order` to choose from some\n+sane orderings.\n+\n+`topo_order` is the same as `git log --topo-order`: we avoid showing a parent\n+before all of its children have been shown, and we avoid mixing commits which\n+are in different lines of history. (`git help log`'s section on `--topo-order`\n+has a very nice diagram to illustrate this.)\n+\n+Let's see what happens when we run with `REV_SORT_BY_COMMIT_DATE` as opposed to\n+`REV_SORT_BY_AUTHOR_DATE`. Add the following:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\trev->topo_order = 1;\n+\trev->sort_order = REV_SORT_BY_COMMIT_DATE;\n+\n+\t...\n+}\n+----\n+\n+Let's output this into a file so we can easily diff it with the walk sorted by\n+author date.\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken > commit-date.txt\n+----\n+\n+Then, let's sort by author date and run it again.\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\trev->topo_order = 1;\n+\trev->sort_order = REV_SORT_BY_AUTHOR_DATE;\n+\n+\t...\n+}\n+----\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken > author-date.txt\n+----\n+\n+Finally, compare the two. This is a little less helpful without object names or\n+dates, but hopefully we get the idea.\n+\n+----\n+$ diff -u commit-date.txt author-date.txt\n+----\n+\n+This display indicates that commits can be reordered after they're written, for\n+example with `git rebase`.\n+\n+Let's try one more reordering of commits. `rev_info` exposes a `reverse` flag.\n+Set that flag somewhere inside of `final_rev_info_setup()`:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv, const char *prefix,\n+\t\tstruct rev_info *rev)\n+{\n+\t...\n+\n+\trev->reverse = 1;\n+\n+\t...\n+}\n+----\n+\n+Run your walk again and note the difference in order. (If you remove the grep\n+pattern, you should see the last commit this call gives you as your current\n+HEAD.)\n+\n+== Basic Object Walk\n+\n+So far we've been walking only commits. But Git has more types of objects than\n+that! Let's see if we can walk _all_ objects, and find out some information\n+about each one.\n+\n+We can base our work on an example. `git pack-objects` prepares all kinds of\n+objects for packing into a bitmap or packfile. The work we are interested in\n+resides in `builtins/pack-objects.c:get_object_list()`; examination of that\n+function shows that the all-object walk is being performed by\n+`traverse_commit_list()` or `traverse_commit_list_filtered()`. Those two\n+functions reside in `list-objects.c`; examining the source shows that, despite\n+the name, these functions traverse all kinds of objects. Let's have a look at\n+the arguments to `traverse_commit_list_filtered()`, which are a superset of the\n+arguments to the unfiltered version.\n+\n+- `struct list_objects_filter_options *filter_options`: This is a struct which\n+  stores a filter-spec as outlined in `Documentation/rev-list-options.txt`.\n+- `struct rev_info *revs`: This is the `rev_info` used for the walk.\n+- `show_commit_fn show_commit`: A callback which will be used to handle each\n+  individual commit object.\n+- `show_object_fn show_object`: A callback which will be used to handle each\n+  non-commit object (so each blob, tree, or tag).\n+- `void *show_data`: A context buffer which is passed in turn to `show_commit`\n+  and `show_object`.\n+- `struct oidset *omitted`: A linked-list of object IDs which the provided\n+  filter caused to be omitted.\n+\n+It looks like this `traverse_commit_list_filtered()` uses callbacks we provide\n+instead of needing us to call it repeatedly ourselves. Cool! Let's add the\n+callbacks first.\n+\n+For the sake of this tutorial, we'll simply keep track of how many of each kind\n+of object we find. At file scope in `builtin/walken.c` add the following\n+tracking variables:\n+\n+----\n+static int commit_count;\n+static int tag_count;\n+static int blob_count;\n+static int tree_count;\n+----\n+\n+Commits are handled by a different callback than other objects; let's do that\n+one first:\n+\n+----\n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+\tcommit_count++;\n+}\n+----\n+\n+The `cmt` argument is fairly self-explanatory. But it's worth mentioning that\n+the `buf` argument is actually the context buffer that we can provide to the\n+traversal calls - `show_data`, which we mentioned a moment ago.\n+\n+Since we have the `struct commit` object, we can look at all the same parts that\n+we looked at in our earlier commit-only walk. For the sake of this tutorial,\n+though, we'll just increment the commit counter and move on.\n+\n+The callback for non-commits is a little different, as we'll need to check\n+which kind of object we're dealing with:\n+\n+----\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+\tswitch (obj->type) {\n+\tcase OBJ_TREE:\n+\t\ttree_count++;\n+\t\tbreak;\n+\tcase OBJ_BLOB:\n+\t\tblob_count++;\n+\t\tbreak;\n+\tcase OBJ_TAG:\n+\t\ttag_count++;\n+\t\tbreak;\n+\tcase OBJ_COMMIT:\n+\t\tBUG(\"unexpected commit object in walken_show_object\\n\");\n+\tdefault:\n+\t\tBUG(\"unexpected object type %s in walken_show_object\\n\",\n+\t\t\ttype_name(obj->type));\n+\t}\n+}\n+----\n+\n+Again, `obj` is fairly self-explanatory, and we can guess that `buf` is the same\n+context pointer that `walken_show_commit()` receives: the `show_data` argument\n+to `traverse_commit_list()` and `traverse_commit_list_filtered()`. Finally,\n+`str` contains the name of the object, which ends up being something like\n+`foo.txt` (blob), `bar/baz` (tree), or `v1.2.3` (tag).\n+\n+To help assure us that we aren't double-counting commits, we'll include some\n+complaining if a commit object is routed through our non-commit callback; we'll\n+also complain if we see an invalid object type. Since those two cases should be\n+unreachable, and would only change in the event of a semantic change to the Git\n+codebase, we complain by using `BUG()` - which is a signal to a developer that\n+the change they made caused unintended consequences, and the rest of the\n+codebase needs to be updated to understand that change. `BUG()` is not intended\n+to be seen by the public, so it is not localized.\n+\n+Our main object walk implementation is substantially different from our commit\n+walk implementation, so let's make a new function to perform the object walk. We\n+can perform setup which is applicable to all objects here, too, to keep separate\n+from setup which is applicable to commit-only walks.\n+\n+We'll start by enabling all types of objects in the `struct rev_info`.  We'll\n+also turn on `tree_blobs_in_commit_order`, which means that we will walk a\n+commit's tree and everything it points to immediately after we find each commit,\n+as opposed to waiting for the end and walking through all trees after the commit\n+history has been discovered. With the appropriate settings configured, we are\n+ready to call `prepare_revision_walk()`.\n+\n+----\n+static void walken_object_walk(struct rev_info *rev)\n+{\n+\trev->tree_objects = 1;\n+\trev->blob_objects = 1;\n+\trev->tag_objects = 1;\n+\trev->tree_blobs_in_commit_order = 1;\n+\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tcommit_count = 0;\n+\ttag_count = 0;\n+\tblob_count = 0;\n+\ttree_count = 0;\n+----\n+\n+Let's start by calling just the unfiltered walk and reporting our counts.\n+Complete your implementation of `walken_object_walk()`:\n+\n+----\n+\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n+\n+\tprintf(\"commits %d\\nblobs %d\\ntags %d\\ntrees %d\\n\", commit_count,\n+\t\tblob_count, tag_count, tree_count);\n+}\n+----\n+\n+NOTE: This output is intended to be machine-parsed. Therefore, we are not\n+sending it to `trace_printf()`, and we are not localizing it - we need scripts\n+to be able to count on the formatting to be exactly the way it is shown here.\n+If we were intending this output to be read by humans, we would need to localize\n+it with `_()`.\n+\n+Finally, we'll ask `cmd_walken()` to use the object walk instead. Discussing\n+command line options is out of scope for this tutorial, so we'll just hardcode\n+a branch we can change at compile time. Where you call `final_rev_info_setup()`\n+and `walken_commit_walk()`, instead branch like so:\n+\n+----\n+\tif (1) {\n+\t\tadd_head_to_pending(&rev);\n+\t\twalken_object_walk(&rev);\n+\t} else {\n+\t\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n+\t\twalken_commit_walk(&rev);\n+\t}\n+----\n+\n+NOTE: For simplicity, we've avoided all the filters and sorts we applied in\n+`final_rev_info_setup()` and simply added `HEAD` to our pending queue. If you\n+want, you can certainly use the filters we added before by moving\n+`final_rev_info_setup()` out of the conditional and removing the call to\n+`add_head_to_pending()`.\n+\n+Now we can try to run our command! It should take noticeably longer than the\n+commit walk, but an examination of the output will give you an idea why. Your\n+output should look similar to this example, but with different counts:\n+\n+----\n+Object walk completed. Found 55733 commits, 100274 blobs, 0 tags, and 104210 trees.\n+----\n+\n+This makes sense. We have more trees than commits because the Git project has\n+lots of subdirectories which can change, plus at least one tree per commit. We\n+have no tags because we started on a commit (`HEAD`) and while tags can point to\n+commits, commits can't point to tags.\n+\n+NOTE: You will have different counts when you run this yourself! The number of\n+objects grows along with the Git project.\n+\n+=== Adding a Filter\n+\n+There are a handful of filters that we can apply to the object walk laid out in\n+`Documentation/rev-list-options.txt`. These filters are typically useful for\n+operations such as creating packfiles or performing a partial clone. They are\n+defined in `list-objects-filter-options.h`. For the purposes of this tutorial we\n+will use the \"tree:1\" filter, which causes the walk to omit all trees and blobs\n+which are not directly referenced by commits reachable from the commit in\n+`pending` when the walk begins. (`pending` is the list of objects which need to\n+be traversed during a walk; you can imagine a breadth-first tree traversal to\n+help understand. In our case, that means we omit trees and blobs not directly\n+referenced by `HEAD` or `HEAD`'s history, because we begin the walk with only\n+`HEAD` in the `pending` list.)\n+\n+First, we'll need to `#include \"list-objects-filter-options.h`\" and set up the\n+`struct list_objects_filter_options` at the top of the function.\n+\n+----\n+static void walken_object_walk(struct rev_info *rev)\n+{\n+\tstruct list_objects_filter_options filter_options = {};\n+\n+\t...\n+----\n+\n+For now, we are not going to track the omitted objects, so we'll replace those\n+parameters with `NULL`. For the sake of simplicity, we'll add a simple\n+build-time branch to use our filter or not. Replace the line calling\n+`traverse_commit_list()` with the following, which will remind us which kind of\n+walk we've just performed:\n+\n+----\n+\tif (0) {\n+\t\t/* Unfiltered: */\n+\t\ttrace_printf(_(\"Unfiltered object walk.\\n\"));\n+\t\ttraverse_commit_list(rev, walken_show_commit,\n+\t\t\t\twalken_show_object, NULL);\n+\t} else {\n+\t\ttrace_printf(\n+\t\t\t_(\"Filtered object walk with filterspec 'tree:1'.\\n\"));\n+\t\tparse_list_objects_filter(&filter_options, \"tree:1\");\n+\n+\t\ttraverse_commit_list_filtered(&filter_options, rev,\n+\t\t\twalken_show_commit, walken_show_object, NULL, NULL);\n+\t}\n+----\n+\n+`struct list_objects_filter_options` is usually built directly from a command\n+line argument, so the module provides an easy way to build one from a string.\n+Even though we aren't taking user input right now, we can still build one with\n+a hardcoded string using `parse_list_objects_filter()`.\n+\n+With the filter spec \"tree:1\", we are expecting to see _only_ the root tree for\n+each commit; therefore, the tree object count should be less than or equal to\n+the number of commits. (For an example of why that's true: `git commit --revert`\n+points to the same tree object as its grandparent.)\n+\n+=== Counting Omitted Objects\n+\n+We also have the capability to enumerate all objects which were omitted by a\n+filter, like with `git log --filter=<spec> --filter-print-omitted`. Asking\n+`traverse_commit_list_filtered()` to populate the `omitted` list means that our\n+revision walk does not perform any better than an unfiltered revision walk; all\n+reachable objects are walked in order to populate the list.\n+\n+First, add the `struct oidset` and related items we will use to iterate it:\n+\n+----\n+static void walken_object_walk(\n+\t...\n+\n+\tstruct oidset omitted;\n+\tstruct oidset_iter oit;\n+\tstruct object_id *oid = NULL;\n+\tint omitted_count = 0;\n+\toidset_init(&omitted, 0);\n+\n+\t...\n+----\n+\n+Modify the call to `traverse_commit_list_filtered()` to include your `omitted`\n+object:\n+\n+----\n+\t...\n+\n+\t\ttraverse_commit_list_filtered(&filter_options, rev,\n+\t\t\twalken_show_commit, walken_show_object, NULL, &omitted);\n+\n+\t...\n+----\n+\n+Then, after your traversal, the `oidset` traversal is pretty straightforward.\n+Count all the objects within and modify the print statement:\n+\n+----\n+\t/* Count the omitted objects. */\n+\toidset_iter_init(&omitted, &oit);\n+\n+\twhile ((oid = oidset_iter_next(&oit)))\n+\t\tomitted_count++;\n+\n+\tprintf(\"commits %d\\nblobs %d\\ntags %d\\ntrees%d\\nomitted %d\\n\",\n+\t\tcommit_count, blob_count, tag_count, tree_count, omitted_count);\n+----\n+\n+By running your walk with and without the filter, you should find that the total\n+object count in each case is identical. You can also time each invocation of\n+the `walken` subcommand, with and without `omitted` being passed in, to confirm\n+to yourself the runtime impact of tracking all omitted objects.\n+\n+=== Changing the Order\n+\n+Finally, let's demonstrate that you can also reorder walks of all objects, not\n+just walks of commits. First, we'll make our handlers chattier - modify\n+`walken_show_commit()` and `walken_show_object()` to print the object as they\n+go:\n+\n+----\n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+\ttrace_printf(\"commit: %s\\n\", oid_to_hex(&cmt->object.oid));\n+\tcommit_count++;\n+}\n+\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+\ttrace_printf(\"%s: %s\\n\", type_name(obj->type), oid_to_hex(&obj->oid));\n+\n+\t...\n+}\n+----\n+\n+NOTE: Since we will be examining this output directly as humans, we'll use\n+`trace_printf()` here. Additionally, since this change introduces a significant\n+number of printed lines, using `trace_printf()` will allow us to easily silence\n+those lines without having to recompile.\n+\n+(Leave the counter increment logic in place.)\n+\n+With only that change, run again (but save yourself some scrollback):\n+\n+----\n+$ GIT_TRACE=1 ./bin-wrappers/git walken | head -n 10\n+----\n+\n+Take a look at the top commit with `git show` and the object ID you printed; it\n+should be the same as the output of `git show HEAD`.\n+\n+Next, let's change a setting on our `struct rev_info` within\n+`walken_object_walk()`. Find where you're changing the other settings on `rev`,\n+such as `rev->tree_objects` and `rev->tree_blobs_in_commit_order`, and add the\n+`reverse` setting at the bottom:\n+\n+----\n+\t...\n+\n+\trev->tree_objects = 1;\n+\trev->blob_objects = 1;\n+\trev->tag_objects = 1;\n+\trev->tree_blobs_in_commit_order = 1;\n+\trev->reverse = 1;\n+\n+\t...\n+----\n+\n+Now, run again, but this time, let's grab the last handful of objects instead\n+of the first handful:\n+\n+----\n+$ make\n+$ GIT_TRACE=1 ./bin-wrappers git walken | tail -n 10\n+----\n+\n+The last commit object given should have the same OID as the one we saw at the\n+top before, and running `git show <oid>` with that OID should give you again\n+the same results as `git show HEAD`. Furthermore, if you run and examine the\n+first ten lines again (with `head` instead of `tail` like we did before applying\n+the `reverse` setting), you should see that now the first commit printed is the\n+initial commit, `e83c5163`.\n+\n+== Wrapping Up\n+\n+Let's review. In this tutorial, we:\n+\n+- Built a commit walk from the ground up\n+- Enabled a grep filter for that commit walk\n+- Changed the sort order of that filtered commit walk\n+- Built an object walk (tags, commits, trees, and blobs) from the ground up\n+- Learned how to add a filter-spec to an object walk\n+- Changed the display order of the filtered object walk\n-- \n2.22.0.770.g0f2c4a37fd-goog\n\n"},{"id":"380091","messageId":"xmqqwofohjgv.fsf@gitster-ct.c.googlers.com","threadId":"51257","inReplyTo":"20190806231952.39155-1-emilyshaffer@google.com","subject":"Re: [PATCH v4] documentation: add tutorial for revision walking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-08-07T19:19:12Z","receivedAt":"2019-08-07T19:19:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Emily Shaffer <emilyshaffer@google.com> writes:\n\n> Since v3, only a couple of minor changes from Jonathan Tan - thanks.\n>\n> I'm dropping the updates for the RFC set, since they're incremental from\n> now. Next time you all see them they will be in a form which we would\n> hope to maintain over a long period of time, checked into source -\n> likely in the form of an mbox or dir of .patch file.\n\nSure.\n\n> I think the tutorial itself is pretty much ready...\n\nA few comments after skimming this round; none of them may be a show\nstopper, but others may have different opinions.\n\n - There is still a leftover \"TODO: checking CLI options\"; is that\n   something we postpone teaching?\n\n - This is an offtopic tangent, but \"my first contribution\" being an\n   addition of an entire command probably mistakenly raised the bar\n   to contributors a bit too much.  A typical first contribution is\n   a typofix, fix to a small (e.g. off-by-one) bug, etc.\n\n - For a revision walk tutorial, not seeing any mention of pathspec\n   filtering and associated history simplification is somewhat\n   unsatisfying.  On the other hand, I expect that enumeration of\n   objects contained within commits is (hence various --filter\n   options are) totally uninteresting for end users who run the\n   command interactively and view the output of the command on\n   screen.\n\n - Enumeration of objects is useful at least in three places in Git:\n   (1) enumerate objects to be packed, with some filtering based on\n   various criteria; (2) enumerate objects that are reachable from\n   anchor points like refs, index, reflog, etc., to discover what\n   are not reachable and can be discarded; (3) enumerate objects\n   that still matter (i.e. the opposite of (2)) so that they can be\n   fed to validation mechanisms (e.g. \"cat-file --batch-check\").  If\n   this were titled \"My first object enumeration\", the reaction led\n   to the latter half of the previous point may not have occurred\n   (pathspec filtering would still be relevant, but not as\n   much---for packing to create a narrow clone, you do not want to\n   use pathspec with history simplification, but you would want to\n   use something more like \"root and intermediate trees that are\n   necessary to cover these paths\" filter in the\n   list--objects-filter layer).\n\nAnd from the point of view of the last item, I would think the new\ndocument is covering a need that is different from what we\ntraditionally would call \"revision walk\", which is more about \"git\nlog\", not the upstream of \"git pack-objects\", which this new\ndocument is more geared towards.\n\nUnless \"git walken\" is an exercise of how to write code that does\nrandom thing, use of --grep filter however may be out of place,\nthough.  I do not offhand think of a use case where --grep would be\nuseful in the revision walk/object enumeration that is placed\nupstream of \"pack-objects\".\n\nThanks.\n\n"},{"id":"380159","messageId":"nycvar.QRO.7.76.6.1908082118130.46@tvgsbejvaqbjf.bet","threadId":"51257","inReplyTo":"20190806231328.GB130824@google.com","subject":"Re: [RFC PATCH v3 00/13] example implementation of revwalk tutorial","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-08-08T19:19:18Z","receivedAt":"2019-08-08T19:19:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Emily,\n\nOn Tue, 6 Aug 2019, Emily Shaffer wrote:\n\n> On Thu, Jul 25, 2019 at 11:25:02AM +0200, Johannes Schindelin wrote:\n> >\n> > On Mon, 1 Jul 2019, Emily Shaffer wrote:\n> >\n> > > Since v2, mostly reworded comments, plus fixed the issues mentioned in\n> > > the tutorial itself. Thanks Eric for the review.\n> > >\n> > > Emily Shaffer (13):\n> > >   walken: add infrastructure for revwalk demo\n> > >   walken: add usage to enable -h\n> > >   walken: add placeholder to initialize defaults\n> > >   walken: add handler to git_config\n> > >   walken: configure rev_info and prepare for walk\n> > >   walken: perform our basic revision walk\n> > >   walken: filter for authors from gmail address\n> > >   walken: demonstrate various topographical sorts\n> > >   walken: demonstrate reversing a revision walk list\n> > >   walken: add unfiltered object walk from HEAD\n> > >   walken: add filtered object walk\n> > >   walken: count omitted objects\n> > >   walken: reverse the object walk order\n> > >\n> > >  Makefile         |   1 +\n> > >  builtin.h        |   1 +\n> > >  builtin/walken.c | 297 +++++++++++++++++++++++++++++++++++++++++++++++\n> >\n> > Since this is not really intended to be an end user-facing command, I\n> > think it should not become a built-in, to be carried into every Git\n> > user's setup.\n>\n> It's not intended to be checked into Git source as-is.\n\nThen it runs the very real danger of becoming stale: we do _not_\nguarantee a stable API, not even an internal one.\n\n> > Instead, I would recommend to implement this as a test helper.\n>\n> I'm not sure I follow how you imagine this looking, but the drawback I\n> see of implementing this in a different way than you would typically do\n> when writing a real feature for the project is that it becomes less\n> useful as a reference for new contributors.\n\nTo the contrary. Some code in `t/helper/` is intended to test\nfunctionality in a way that is copy-editable.\n\nYour use case strikes me a perfect example for such a test helper:\n\n- It guarantees that the example is valid,\n- It demonstrates how to use the API,\n- In case the API changes, the changes to the helper will inform\n  contributors how to change their copy-edited versions\n\n> > This would have the following advantages:\n> >\n> > - it won't clutter the end user installations,\n> >\n> > - it will still be compile-tested with every build (guaranteeing that\n> >   the tutorial won't become stale over time as so many other tutorials),\n>\n> This part of your suggestion appeals to me; so I'm really curious how\n> you would do it. Do you have something else written in the way you're\n> suggesting in mind?\n\nI looked at `t/helper/test-hashmap.c` and it looks _almost_ like a\nperfect example for what I have in mind: it uses a given API,\ndemonstrates how to use it properly, and is copy-editable.\n\nCiao,\nDscho\n"},{"id":"380452","messageId":"20190814183328.GA40797@google.com","threadId":"51257","inReplyTo":"xmqqwofohjgv.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v4] documentation: add tutorial for revision walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-08-14T18:33:28Z","receivedAt":"2019-08-14T18:33:37Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Wed, Aug 07, 2019 at 12:19:12PM -0700, Junio C Hamano wrote:\n> Emily Shaffer <emilyshaffer@google.com> writes:\n> \n> > Since v3, only a couple of minor changes from Jonathan Tan - thanks.\n> >\n> > I'm dropping the updates for the RFC set, since they're incremental from\n> > now. Next time you all see them they will be in a form which we would\n> > hope to maintain over a long period of time, checked into source -\n> > likely in the form of an mbox or dir of .patch file.\n> \n> Sure.\n> \n> > I think the tutorial itself is pretty much ready...\n> \n> A few comments after skimming this round; none of them may be a show\n> stopper, but others may have different opinions.\n> \n>  - There is still a leftover \"TODO: checking CLI options\"; is that\n>    something we postpone teaching?\n\nYeah, I think it would make more sense to include it into the other one\n(my first contribution) instead.\n\n> \n>  - This is an offtopic tangent, but \"my first contribution\" being an\n>    addition of an entire command probably mistakenly raised the bar\n>    to contributors a bit too much.  A typical first contribution is\n>    a typofix, fix to a small (e.g. off-by-one) bug, etc.\n\nSure, I agree with that; but I think the larger (though less common)\nproject of new command allowed me to explain more about the architecture\nof the project overall than a typo fix would. Maybe there's a clearer\nname to use?\n\n> \n>  - For a revision walk tutorial, not seeing any mention of pathspec\n>    filtering and associated history simplification is somewhat\n>    unsatisfying.  On the other hand, I expect that enumeration of\n>    objects contained within commits is (hence various --filter\n>    options are) totally uninteresting for end users who run the\n>    command interactively and view the output of the command on\n>    screen.\n> \n>  - Enumeration of objects is useful at least in three places in Git:\n>    (1) enumerate objects to be packed, with some filtering based on\n>    various criteria; (2) enumerate objects that are reachable from\n>    anchor points like refs, index, reflog, etc., to discover what\n>    are not reachable and can be discarded; (3) enumerate objects\n>    that still matter (i.e. the opposite of (2)) so that they can be\n>    fed to validation mechanisms (e.g. \"cat-file --batch-check\").  If\n>    this were titled \"My first object enumeration\", the reaction led\n>    to the latter half of the previous point may not have occurred\n>    (pathspec filtering would still be relevant, but not as\n>    much---for packing to create a narrow clone, you do not want to\n>    use pathspec with history simplification, but you would want to\n>    use something more like \"root and intermediate trees that are\n>    necessary to cover these paths\" filter in the\n>    list--objects-filter layer).\n> \n> And from the point of view of the last item, I would think the new\n> document is covering a need that is different from what we\n> traditionally would call \"revision walk\", which is more about \"git\n> log\", not the upstream of \"git pack-objects\", which this new\n> document is more geared towards.\n\nHmmm. It sounds like you're saying:\n\n- This object covers walking objects, which is surprising since it's\n  titled about \"revision walks\". Revision walks are more about commits\n  (\"git log\").\n- Using grep on objects doesn't make any sense.\n- Other filters (like pathspecs) which do make sense for object walks\n  aren't covered.\n\n> \n> Unless \"git walken\" is an exercise of how to write code that does\n> random thing, use of --grep filter however may be out of place,\n> though.  I do not offhand think of a use case where --grep would be\n> useful in the revision walk/object enumeration that is placed\n> upstream of \"pack-objects\".\n\nIn this case, it might make sense to do one of these things:\n\n- Apply the grep filter to the commit walk, and apply a more interesting\n  object filter to the object walk.\n\nOr,\n\n- Choose a different kind of filter which is interesting when applied to\n  commits alone _and_ all objects.\n\nIn the interest of covering more ground with this kind of tutorial, I'd\nlean more towards the former.\n\n\nIt's possible that the added scope will make the document large enough\nthat we'd rather split it into two (one for \"git log\"-ish, one for \"git\npack-objects\"-ish). I think that's fine if we end up there.\n\nThanks. I hope to get back to this soon...\n\n - Emily\n"},{"id":"380456","messageId":"xmqqd0h75ze8.fsf@gitster-ct.c.googlers.com","threadId":"51257","inReplyTo":"20190814183328.GA40797@google.com","subject":"Re: [PATCH v4] documentation: add tutorial for revision walking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-08-14T19:18:39Z","receivedAt":"2019-08-14T19:18:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Emily Shaffer <emilyshaffer@google.com> writes:\n\n>> > I think the tutorial itself is pretty much ready...\n>> \n>> A few comments after skimming this round; none of them may be a show\n>> stopper, but others may have different opinions.\n>>  ...\n> Hmmm. It sounds like you're saying:\n>\n> - This object covers walking objects, which is surprising since it's\n>   titled about \"revision walks\". Revision walks are more about commits\n>   (\"git log\").\n\nYes, the document does not duplicate what existing docs on \"revision\nwalk\" API would cover, which is a very good thing, as it is (or at\nleast \"feels to be\") primarly about walking objects.\n\n> - Using grep on objects doesn't make any sense.\n\nThe grep filter works on commit's log messages, and does not even\nlook at other types of objects, so while that point is true, what I\nwas driving at was that skipping commits using grep filter would\nmean showing trees and blobs related only to the chosen commits, and\nwhile it can be explained as such (i.e. \"trees and blobs contained\nonly in commits without these strings are excluded\"), the practical\nusefulness of such a \"feature\" is dubious (here I am imagining the\nprimary practical use of \"object walk\" is to feed pack-objects).\n\n> - Other filters (like pathspecs) which do make sense for object walks\n>   aren't covered.\n\nYup.  For example, \"trees and blobs that appear only outside of this\ndirectory hierarchy are excluded\" would be useful to enumerate\nobjects necessary for a narrow commits (again, to feed pack-objects).\n\n> - Apply the grep filter to the commit walk, and apply a more interesting\n>   object filter to the object walk.\n>\n> Or,\n>\n> - Choose a different kind of filter which is interesting when applied to\n>   commits alone _and_ all objects.\n>\n> In the interest of covering more ground with this kind of tutorial, I'd\n> lean more towards the former.\n\nSorry, I do not have enough imagination to cheer for either of these\ntwo options---these may be \"interesting\" in the same way as \"trees\nand blobs contained only in commits without these strings are\nexcluded\" enumeration, but I fail to see practical usefulness\n(i.e. the reason why a user may be tempted to learn how to achieve\nit).\n\nIn any case, that was my personal take and not a strong request to\nchange anything, as I said upfront.  The document just gave me an\nimpression that it was teaching coding exercise that may be\ninteresting but of dubious utility.\n\n"},{"id":"383802","messageId":"20191010151932.2716-1-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20190806231952.39155-1-emilyshaffer@google.com","subject":"[PATCH v5] documentation: add tutorial for object walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-10-10T15:19:32Z","receivedAt":"2019-10-10T15:19:45Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"The target audience is a Git contributor who is just getting started\nwith the concept of object walking. The goal is to prepare this\ncontributor to be able to understand and modify existing commands which\nperform revision walks more easily, although it will also prepare\ncontributors to create new commands which perform walks.\n\nThe tutorial covers a basic overview of the structs involved during\nobject walk, setting up a basic commit walk, setting up a basic\nall-object walk, and adding some configuration changes to both walk\ntypes. It intentionally does not cover how to create new commands or\nsearch for options from the command line or gitconfigs.\n\nThere is an associated patchset at\nhttps://github.com/nasamuffin/git/tree/revwalk that contains a reference\nimplementation of the code generated by this tutorial.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\n---\nPrimarily since v5 this is a reword to try to frame the tutorial as\n\"object walk tutorial which also covers commit-only walk\", rather than\n\"revision walk tutorial which also covers all-object walk\" - per Junio's\nsuggestion.\n\nSince I had spent some time away from the patch, I also made some small\nchanges to the wording.\n\nHere's the rangediff from gitster/es/walken-tutorial:\n\n1:  1ed29a34d1 ! 1:  554d940af7 documentation: add tutorial for revision walking\n     a => b | 0\n     1 file changed, 0 insertions(+), 0 deletions(-)\n    \n    @@ Metadata\n     Author: Emily Shaffer <emilyshaffer@google.com>\n     \n      ## Commit message ##\n    -    documentation: add tutorial for revision walking\n    +    documentation: add tutorial for object walking\n     \n    -    Existing documentation on revision walks seems to be primarily intended\n    +    Existing documentation on object walks seems to be primarily intended\n         as a reference for those already familiar with the procedure. This\n         tutorial attempts to give an entry-level guide to a couple of bare-bones\n    -    revision walks so that new Git contributors can learn the concepts\n    +    object walks so that new Git contributors can learn the concepts\n         without having to wade through options parsing or special casing.\n     \n         The target audience is a Git contributor who is just getting started\n    -    with the concept of revision walking. The goal is to prepare this\n    +    with the concept of object walking. The goal is to prepare this\n         contributor to be able to understand and modify existing commands which\n         perform revision walks more easily, although it will also prepare\n         contributors to create new commands which perform walks.\n     \n         The tutorial covers a basic overview of the structs involved during\n    -    revision walk, setting up a basic commit walk, setting up a basic\n    +    object walk, setting up a basic commit walk, setting up a basic\n         all-object walk, and adding some configuration changes to both walk\n         types. It intentionally does not cover how to create new commands or\n         search for options from the command line or gitconfigs.\n    @@ Commit message\n     \n         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>\n         Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n     \n      ## Documentation/Makefile ##\n     @@ Documentation/Makefile: API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technica\n    @@ Documentation/Makefile: API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api\n      TECH_DOCS += technical/hash-function-transition\n      TECH_DOCS += technical/http-protocol\n     \n    - ## Documentation/MyFirstRevWalk.txt (new) ##\n    + ## Documentation/MyFirstObjectWalk.txt (new) ##\n     @@\n    -+My First Revision Walk\n    ++My First Object Walk\n     +======================\n     +\n    -+== What's a Revision Walk?\n    ++== What's an Object Walk?\n     +\n    -+The revision walk is a key concept in Git - this is the process that underpins\n    ++The object walk is a key concept in Git - this is the process that underpins\n     +operations like `git log`, `git blame`, and `git reflog`. Beginning at HEAD, the\n     +list of objects is found by walking parent relationships between objects. The\n    -+revision walk can also be used to determine whether or not a given object is\n    ++object walk can also be used to determine whether or not a given object is\n     +reachable from the current HEAD pointer.\n     +\n    ++A related concept is the revision walk, which is focused on commit objects and\n    ++their relationships.\n    ++\n     +=== Related Reading\n     +\n     +- `Documentation/user-manual.txt` under \"Hacking Git\" contains some coverage of\n     +  the revision walker in its various incarnations.\n     +- `Documentation/technical/api-revision-walking.txt`\n     +- https://eagain.net/articles/git-for-computer-scientists/[Git for Computer Scientists]\n    -+  gives a good overview of the types of objects in Git and what your revision\n    ++  gives a good overview of the types of objects in Git and what your object\n     +  walk is really describing.\n     +\n     +== Setting Up\n    @@ Documentation/MyFirstRevWalk.txt (new)\n     +/*\n     + * \"git walken\"\n     + *\n    -+ * Part of the \"My First Revision Walk\" tutorial.\n    ++ * Part of the \"My First Object Walk\" tutorial.\n     + */\n     +\n     +#include \"builtin.h\"\n    @@ Documentation/MyFirstRevWalk.txt (new)\n     +\n     +Per entry, we find:\n     +\n    -+`item` is the object provided upon which to base the revision walk. Items in Git\n    ++`item` is the object provided upon which to base the object walk. Items in Git\n     +can be blobs, trees, commits, or tags. (See `Documentation/gittutorial-2.txt`.)\n     +\n     +`name` is the object ID (OID) of the object - a hex string you may be familiar\n    @@ Documentation/MyFirstRevWalk.txt (new)\n     +\n     +First, let's see if we can replicate the output of `git log --oneline`. We'll\n     +refer back to the implementation frequently to discover norms when performing\n    -+a revision walk of our own.\n    ++an object walk of our own.\n     +\n     +To do so, we'll first find all the commits, in order, which preceded the current\n     +commit. We'll extract the name and subject of the commit from each.\n    @@ Documentation/MyFirstRevWalk.txt (new)\n     +\n     +=== Setting Up\n     +\n    -+Preparing for your revision walk has some distinct stages.\n    ++Preparing for your object walk has some distinct stages.\n     +\n     +1. Perform default setup for this mode, and others which may be invoked.\n     +2. Check configuration files for relevant settings.\n    @@ Documentation/MyFirstRevWalk.txt (new)\n     +`grep` and `diff` to initialize themselves by calling each of their\n     +initialization functions.\n     +\n    -+For our purposes, within `git walken`, for the first example we don't intend to\n    -+use any other components within Git, and we don't have any configuration to do.\n    -+However, we may want to add some later, so for now, we can add an empty\n    -+placeholder. Create a new function in `builtin/walken.c`:\n    ++For our first example within `git walken`, we don't intend to use any other\n    ++components within Git, and we don't have any configuration to do.  However, we\n    ++may want to add some later, so for now, we can add an empty placeholder. Create\n    ++a new function in `builtin/walken.c`:\n     +\n     +----\n     +static void init_walken_defaults(void)\n    @@ Documentation/MyFirstRevWalk.txt (new)\n     +}\n     +----\n     +\n    -+// TODO: Checking CLI options\n    -+\n     +==== Setting Up `rev_info`\n     +\n     +Now that we've gathered external configuration and options, it's time to\n    @@ Documentation/MyFirstRevWalk.txt (new)\n     +\t */\n     +\tget_commit_format(\"oneline\", rev);\n     +\n    -+\t/* Start our revision walk at HEAD. */\n    ++\t/* Start our object walk at HEAD. */\n     +\tadd_head_to_pending(rev);\n     +}\n     +----\n    @@ Documentation/MyFirstRevWalk.txt (new)\n     +\n     +There are a few ways that we can change the order of the commits during a\n     +revision walk. Firstly, we can use the `enum rev_sort_order` to choose from some\n    -+sane orderings.\n    ++typical orderings.\n     +\n     +`topo_order` is the same as `git log --topo-order`: we avoid showing a parent\n     +before all of its children have been shown, and we avoid mixing commits which\n    @@ Documentation/MyFirstRevWalk.txt (new)\n     +We also have the capability to enumerate all objects which were omitted by a\n     +filter, like with `git log --filter=<spec> --filter-print-omitted`. Asking\n     +`traverse_commit_list_filtered()` to populate the `omitted` list means that our\n    -+revision walk does not perform any better than an unfiltered revision walk; all\n    ++object walk does not perform any better than an unfiltered object walk; all\n     +reachable objects are walked in order to populate the list.\n     +\n     +First, add the `struct oidset` and related items we will use to iterate it:\n\n<end rangediff>\n\n Documentation/Makefile              |   1 +\n Documentation/MyFirstObjectWalk.txt | 905 ++++++++++++++++++++++++++++\n 2 files changed, 906 insertions(+)\n create mode 100644 Documentation/MyFirstObjectWalk.txt\n\ndiff --git a/Documentation/Makefile b/Documentation/Makefile\nindex 06d85ad958..fc35cb29b1 100644\n--- a/Documentation/Makefile\n+++ b/Documentation/Makefile\n@@ -77,6 +77,7 @@ API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technica\n SP_ARTICLES += $(API_DOCS)\n \n TECH_DOCS += MyFirstContribution\n+TECH_DOCS += MyFirstRevWalk\n TECH_DOCS += SubmittingPatches\n TECH_DOCS += technical/hash-function-transition\n TECH_DOCS += technical/http-protocol\ndiff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt\nnew file mode 100644\nindex 0000000000..7085f17072\n--- /dev/null\n+++ b/Documentation/MyFirstObjectWalk.txt\n@@ -0,0 +1,905 @@\n+My First Object Walk\n+======================\n+\n+== What's an Object Walk?\n+\n+The object walk is a key concept in Git - this is the process that underpins\n+operations like `git log`, `git blame`, and `git reflog`. Beginning at HEAD, the\n+list of objects is found by walking parent relationships between objects. The\n+object walk can also be used to determine whether or not a given object is\n+reachable from the current HEAD pointer.\n+\n+A related concept is the revision walk, which is focused on commit objects and\n+their relationships.\n+\n+=== Related Reading\n+\n+- `Documentation/user-manual.txt` under \"Hacking Git\" contains some coverage of\n+  the revision walker in its various incarnations.\n+- `Documentation/technical/api-revision-walking.txt`\n+- https://eagain.net/articles/git-for-computer-scientists/[Git for Computer Scientists]\n+  gives a good overview of the types of objects in Git and what your object\n+  walk is really describing.\n+\n+== Setting Up\n+\n+Create a new branch from `master`.\n+\n+----\n+git checkout -b revwalk origin/master\n+----\n+\n+We'll put our fiddling into a new command. For fun, let's name it `git walken`.\n+Open up a new file `builtin/walken.c` and set up the command handler:\n+\n+----\n+/*\n+ * \"git walken\"\n+ *\n+ * Part of the \"My First Object Walk\" tutorial.\n+ */\n+\n+#include \"builtin.h\"\n+\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\ttrace_printf(_(\"cmd_walken incoming...\\n\"));\n+\treturn 0;\n+}\n+----\n+\n+NOTE: `trace_printf()` differs from `printf()` in that it can be turned on or\n+off at runtime. For the purposes of this tutorial, we will write `walken` as\n+though it is intended for use as a \"plumbing\" command: that is, a command which\n+is used primarily in scripts, rather than interactively by humans (a \"porcelain\"\n+command). So we will send our debug output to `trace_printf()` instead. When\n+running, enable trace output by setting the environment variable `GIT_TRACE`.\n+\n+Add usage text and `-h` handling, like all subcommands should consistently do\n+(our test suite will notice and complain if you fail to do so).\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\tconst char * const walken_usage[] = {\n+\t\tN_(\"git walken\"),\n+\t\tNULL,\n+\t}\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n+\n+\t...\n+}\n+----\n+\n+Also add the relevant line in `builtin.h` near `cmd_whatchanged()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix);\n+----\n+\n+Include the command in `git.c` in `commands[]` near the entry for `whatchanged`,\n+maintaining alphabetical ordering:\n+\n+----\n+{ \"walken\", cmd_walken, RUN_SETUP },\n+----\n+\n+Add it to the `Makefile` near the line for `builtin/worktree.o`:\n+\n+----\n+BUILTIN_OBJS += builtin/walken.o\n+----\n+\n+Build and test out your command, without forgetting to ensure the `DEVELOPER`\n+flag is set, and with `GIT_TRACE` enabled so the debug output can be seen:\n+\n+----\n+$ echo DEVELOPER=1 >>config.mak\n+$ make\n+$ GIT_TRACE=1 ./bin-wrappers/git walken\n+----\n+\n+NOTE: For a more exhaustive overview of the new command process, take a look at\n+`Documentation/MyFirstContribution.txt`.\n+\n+NOTE: A reference implementation can be found at\n+https://github.com/nasamuffin/git/tree/revwalk.\n+\n+=== `struct rev_cmdline_info`\n+\n+The definition of `struct rev_cmdline_info` can be found in `revision.h`.\n+\n+This struct is contained within the `rev_info` struct and is used to reflect\n+parameters provided by the user over the CLI.\n+\n+`nr` represents the number of `rev_cmdline_entry` present in the array.\n+\n+`alloc` is used by the `ALLOC_GROW` macro. Check\n+`Documentation/technical/api-allocation-growing.txt` - this variable is used to\n+track the allocated size of the list.\n+\n+Per entry, we find:\n+\n+`item` is the object provided upon which to base the object walk. Items in Git\n+can be blobs, trees, commits, or tags. (See `Documentation/gittutorial-2.txt`.)\n+\n+`name` is the object ID (OID) of the object - a hex string you may be familiar\n+with from using Git to organize your source in the past. Check the tutorial\n+mentioned above towards the top for a discussion of where the OID can come\n+from.\n+\n+`whence` indicates some information about what to do with the parents of the\n+specified object. We'll explore this flag more later on; take a look at\n+`Documentation/revisions.txt` to get an idea of what could set the `whence`\n+value.\n+\n+`flags` are used to hint the beginning of the revision walk and are the first\n+block under the `#include`s in `revision.h`. The most likely ones to be set in\n+the `rev_cmdline_info` are `UNINTERESTING` and `BOTTOM`, but these same flags\n+can be used during the walk, as well.\n+\n+=== `struct rev_info`\n+\n+This one is quite a bit longer, and many fields are only used during the walk\n+by `revision.c` - not configuration options. Most of the configurable flags in\n+`struct rev_info` have a mirror in `Documentation/rev-list-options.txt`. It's a\n+good idea to take some time and read through that document.\n+\n+== Basic Commit Walk\n+\n+First, let's see if we can replicate the output of `git log --oneline`. We'll\n+refer back to the implementation frequently to discover norms when performing\n+an object walk of our own.\n+\n+To do so, we'll first find all the commits, in order, which preceded the current\n+commit. We'll extract the name and subject of the commit from each.\n+\n+Ideally, we will also be able to find out which ones are currently at the tip of\n+various branches.\n+\n+=== Setting Up\n+\n+Preparing for your object walk has some distinct stages.\n+\n+1. Perform default setup for this mode, and others which may be invoked.\n+2. Check configuration files for relevant settings.\n+3. Set up the `rev_info` struct.\n+4. Tweak the initialized `rev_info` to suit the current walk.\n+5. Prepare the `rev_info` for the walk.\n+6. Iterate over the objects, processing each one.\n+\n+==== Default Setups\n+\n+Before examining configuration files which may modify command behavior, set up\n+default state for switches or options your command may have. If your command\n+utilizes other Git components, ask them to set up their default states as well.\n+For instance, `git log` takes advantage of `grep` and `diff` functionality, so\n+its `init_log_defaults()` sets its own state (`decoration_style`) and asks\n+`grep` and `diff` to initialize themselves by calling each of their\n+initialization functions.\n+\n+For our first example within `git walken`, we don't intend to use any other\n+components within Git, and we don't have any configuration to do.  However, we\n+may want to add some later, so for now, we can add an empty placeholder. Create\n+a new function in `builtin/walken.c`:\n+\n+----\n+static void init_walken_defaults(void)\n+{\n+\t/*\n+\t * We don't actually need the same components `git log` does; leave this\n+\t * empty for now.\n+\t */\n+}\n+----\n+\n+Make sure to add a line invoking it inside of `cmd_walken()`.\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\tinit_walken_defaults();\n+}\n+----\n+\n+==== Configuring From `.gitconfig`\n+\n+Next, we should have a look at any relevant configuration settings (i.e.,\n+settings readable and settable from `git config`). This is done by providing a\n+callback to `git_config()`; within that callback, you can also invoke methods\n+from other components you may need that need to intercept these options. Your\n+callback will be invoked once per each configuration value which Git knows about\n+(global, local, worktree, etc.).\n+\n+Similarly to the default values, we don't have anything to do here yet\n+ourselves; however, we should call `git_default_config()` if we aren't calling\n+any other existing config callbacks.\n+\n+Add a new function to `builtin/walken.c`:\n+\n+----\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\t/*\n+\t * For now, we don't have any custom configuration, so fall back to\n+\t * the default config.\n+\t */\n+\treturn git_default_config(var, value, cb);\n+}\n+----\n+\n+Make sure to invoke `git_config()` with it in your `cmd_walken()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t...\n+\n+\tgit_config(git_walken_config, NULL);\n+\n+\t...\n+}\n+----\n+\n+==== Setting Up `rev_info`\n+\n+Now that we've gathered external configuration and options, it's time to\n+initialize the `rev_info` object which we will use to perform the walk. This is\n+typically done by calling `repo_init_revisions()` with the repository you intend\n+to target, as well as the `prefix` argument of `cmd_walken` and your `rev_info`\n+struct.\n+\n+Add the `struct rev_info` and the `repo_init_revisions()` call:\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t/* This can go wherever you like in your declarations.*/\n+\tstruct rev_info rev;\n+\t...\n+\n+\t/* This should go after the git_config() call. */\n+\trepo_init_revisions(the_repository, &rev, prefix);\n+\n+\t...\n+}\n+----\n+\n+==== Tweaking `rev_info` For the Walk\n+\n+We're getting close, but we're still not quite ready to go. Now that `rev` is\n+initialized, we can modify it to fit our needs. This is usually done within a\n+helper for clarity, so let's add one:\n+\n+----\n+static void final_rev_info_setup(struct rev_info *rev)\n+{\n+\t/*\n+\t * We want to mimic the appearance of `git log --oneline`, so let's\n+\t * force oneline format.\n+\t */\n+\tget_commit_format(\"oneline\", rev);\n+\n+\t/* Start our object walk at HEAD. */\n+\tadd_head_to_pending(rev);\n+}\n+----\n+\n+[NOTE]\n+====\n+Instead of using the shorthand `add_head_to_pending()`, you could do\n+something like this:\n+----\n+\tstruct setup_revision_opt opt;\n+\n+\tmemset(&opt, 0, sizeof(opt));\n+\topt.def = \"HEAD\";\n+\topt.revarg_opt = REVARG_COMMITTISH;\n+\tsetup_revisions(argc, argv, rev, &opt);\n+----\n+Using a `setup_revision_opt` gives you finer control over your walk's starting\n+point.\n+====\n+\n+Then let's invoke `final_rev_info_setup()` after the call to\n+`repo_init_revisions()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t...\n+\n+\tfinal_rev_info_setup(&rev);\n+\n+\t...\n+}\n+----\n+\n+Later, we may wish to add more arguments to `final_rev_info_setup()`. But for\n+now, this is all we need.\n+\n+==== Preparing `rev_info` For the Walk\n+\n+Now that `rev` is all initialized and configured, we've got one more setup step\n+before we get rolling. We can do this in a helper, which will both prepare the\n+`rev_info` for the walk, and perform the walk itself. Let's start the helper\n+with the call to `prepare_revision_walk()`, which can return an error without\n+dying on its own:\n+\n+----\n+static void walken_commit_walk(struct rev_info *rev)\n+{\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+}\n+----\n+\n+NOTE: `die()` prints to `stderr` and exits the program. Since it will print to\n+`stderr` it's likely to be seen by a human, so we will localize it.\n+\n+==== Performing the Walk!\n+\n+Finally! We are ready to begin the walk itself. Now we can see that `rev_info`\n+can also be used as an iterator; we move to the next item in the walk by using\n+`get_revision()` repeatedly. Add the listed variable declarations at the top and\n+the walk loop below the `prepare_revision_walk()` call within your\n+`walken_commit_walk()`:\n+\n+----\n+static void walken_commit_walk(struct rev_info *rev)\n+{\n+\tstruct commit *commit;\n+\tstruct strbuf prettybuf = STRBUF_INIT;\n+\n+\t...\n+\n+\twhile ((commit = get_revision(rev))) {\n+\t\tif (!commit)\n+\t\t\tcontinue;\n+\n+\t\tstrbuf_reset(&prettybuf);\n+\t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);\n+\t\tputs(prettybuf.buf);\n+\t}\n+\tstrbuf_release(&prettybuf);\n+}\n+----\n+\n+NOTE: `puts()` prints a `char*` to `stdout`. Since this is the part of the\n+command we expect to be machine-parsed, we're sending it directly to stdout.\n+\n+Give it a shot.\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken\n+----\n+\n+You should see all of the subject lines of all the commits in\n+your tree's history, in order, ending with the initial commit, \"Initial revision\n+of \"git\", the information manager from hell\". Congratulations! You've written\n+your first revision walk. You can play with printing some additional fields\n+from each commit if you're curious; have a look at the functions available in\n+`commit.h`.\n+\n+=== Adding a Filter\n+\n+Next, let's try to filter the commits we see based on their author. This is\n+equivalent to running `git log --author=<pattern>`. We can add a filter by\n+modifying `rev_info.grep_filter`, which is a `struct grep_opt`.\n+\n+First some setup. Add `init_grep_defaults()` to `init_walken_defaults()` and add\n+`grep_config()` to `git_walken_config()`:\n+\n+----\n+static void init_walken_defaults(void)\n+{\n+\tinit_grep_defaults(the_repository);\n+}\n+\n+...\n+\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\tgrep_config(var, value, cb);\n+\treturn git_default_config(var, value, cb);\n+}\n+----\n+\n+Next, we can modify the `grep_filter`. This is done with convenience functions\n+found in `grep.h`. For fun, we're filtering to only commits from folks using a\n+`gmail.com` email address - a not-very-precise guess at who may be working on\n+Git as a hobby. Since we're checking the author, which is a specific line in the\n+header, we'll use the `append_header_grep_pattern()` helper. We can use\n+the `enum grep_header_field` to indicate which part of the commit header we want\n+to search.\n+\n+In `final_rev_info_setup()`, add your filter line:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\tappend_header_grep_pattern(&rev->grep_filter, GREP_HEADER_AUTHOR,\n+\t\t\"gmail\");\n+\tcompile_grep_patterns(&rev->grep_filter);\n+\n+\t...\n+}\n+----\n+\n+`append_header_grep_pattern()` adds your new \"gmail\" pattern to `rev_info`, but\n+it won't work unless we compile it with `compile_grep_patterns()`.\n+\n+NOTE: If you are using `setup_revisions()` (for example, if you are passing a\n+`setup_revision_opt` instead of using `add_head_to_pending()`), you don't need\n+to call `compile_grep_patterns()` because `setup_revisions()` calls it for you.\n+\n+NOTE: We could add the same filter via the `append_grep_pattern()` helper if we\n+wanted to, but `append_header_grep_pattern()` adds the `enum grep_context` and\n+`enum grep_pat_token` for us.\n+\n+=== Changing the Order\n+\n+There are a few ways that we can change the order of the commits during a\n+revision walk. Firstly, we can use the `enum rev_sort_order` to choose from some\n+typical orderings.\n+\n+`topo_order` is the same as `git log --topo-order`: we avoid showing a parent\n+before all of its children have been shown, and we avoid mixing commits which\n+are in different lines of history. (`git help log`'s section on `--topo-order`\n+has a very nice diagram to illustrate this.)\n+\n+Let's see what happens when we run with `REV_SORT_BY_COMMIT_DATE` as opposed to\n+`REV_SORT_BY_AUTHOR_DATE`. Add the following:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\trev->topo_order = 1;\n+\trev->sort_order = REV_SORT_BY_COMMIT_DATE;\n+\n+\t...\n+}\n+----\n+\n+Let's output this into a file so we can easily diff it with the walk sorted by\n+author date.\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken > commit-date.txt\n+----\n+\n+Then, let's sort by author date and run it again.\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\trev->topo_order = 1;\n+\trev->sort_order = REV_SORT_BY_AUTHOR_DATE;\n+\n+\t...\n+}\n+----\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken > author-date.txt\n+----\n+\n+Finally, compare the two. This is a little less helpful without object names or\n+dates, but hopefully we get the idea.\n+\n+----\n+$ diff -u commit-date.txt author-date.txt\n+----\n+\n+This display indicates that commits can be reordered after they're written, for\n+example with `git rebase`.\n+\n+Let's try one more reordering of commits. `rev_info` exposes a `reverse` flag.\n+Set that flag somewhere inside of `final_rev_info_setup()`:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv, const char *prefix,\n+\t\tstruct rev_info *rev)\n+{\n+\t...\n+\n+\trev->reverse = 1;\n+\n+\t...\n+}\n+----\n+\n+Run your walk again and note the difference in order. (If you remove the grep\n+pattern, you should see the last commit this call gives you as your current\n+HEAD.)\n+\n+== Basic Object Walk\n+\n+So far we've been walking only commits. But Git has more types of objects than\n+that! Let's see if we can walk _all_ objects, and find out some information\n+about each one.\n+\n+We can base our work on an example. `git pack-objects` prepares all kinds of\n+objects for packing into a bitmap or packfile. The work we are interested in\n+resides in `builtins/pack-objects.c:get_object_list()`; examination of that\n+function shows that the all-object walk is being performed by\n+`traverse_commit_list()` or `traverse_commit_list_filtered()`. Those two\n+functions reside in `list-objects.c`; examining the source shows that, despite\n+the name, these functions traverse all kinds of objects. Let's have a look at\n+the arguments to `traverse_commit_list_filtered()`, which are a superset of the\n+arguments to the unfiltered version.\n+\n+- `struct list_objects_filter_options *filter_options`: This is a struct which\n+  stores a filter-spec as outlined in `Documentation/rev-list-options.txt`.\n+- `struct rev_info *revs`: This is the `rev_info` used for the walk.\n+- `show_commit_fn show_commit`: A callback which will be used to handle each\n+  individual commit object.\n+- `show_object_fn show_object`: A callback which will be used to handle each\n+  non-commit object (so each blob, tree, or tag).\n+- `void *show_data`: A context buffer which is passed in turn to `show_commit`\n+  and `show_object`.\n+- `struct oidset *omitted`: A linked-list of object IDs which the provided\n+  filter caused to be omitted.\n+\n+It looks like this `traverse_commit_list_filtered()` uses callbacks we provide\n+instead of needing us to call it repeatedly ourselves. Cool! Let's add the\n+callbacks first.\n+\n+For the sake of this tutorial, we'll simply keep track of how many of each kind\n+of object we find. At file scope in `builtin/walken.c` add the following\n+tracking variables:\n+\n+----\n+static int commit_count;\n+static int tag_count;\n+static int blob_count;\n+static int tree_count;\n+----\n+\n+Commits are handled by a different callback than other objects; let's do that\n+one first:\n+\n+----\n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+\tcommit_count++;\n+}\n+----\n+\n+The `cmt` argument is fairly self-explanatory. But it's worth mentioning that\n+the `buf` argument is actually the context buffer that we can provide to the\n+traversal calls - `show_data`, which we mentioned a moment ago.\n+\n+Since we have the `struct commit` object, we can look at all the same parts that\n+we looked at in our earlier commit-only walk. For the sake of this tutorial,\n+though, we'll just increment the commit counter and move on.\n+\n+The callback for non-commits is a little different, as we'll need to check\n+which kind of object we're dealing with:\n+\n+----\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+\tswitch (obj->type) {\n+\tcase OBJ_TREE:\n+\t\ttree_count++;\n+\t\tbreak;\n+\tcase OBJ_BLOB:\n+\t\tblob_count++;\n+\t\tbreak;\n+\tcase OBJ_TAG:\n+\t\ttag_count++;\n+\t\tbreak;\n+\tcase OBJ_COMMIT:\n+\t\tBUG(\"unexpected commit object in walken_show_object\\n\");\n+\tdefault:\n+\t\tBUG(\"unexpected object type %s in walken_show_object\\n\",\n+\t\t\ttype_name(obj->type));\n+\t}\n+}\n+----\n+\n+Again, `obj` is fairly self-explanatory, and we can guess that `buf` is the same\n+context pointer that `walken_show_commit()` receives: the `show_data` argument\n+to `traverse_commit_list()` and `traverse_commit_list_filtered()`. Finally,\n+`str` contains the name of the object, which ends up being something like\n+`foo.txt` (blob), `bar/baz` (tree), or `v1.2.3` (tag).\n+\n+To help assure us that we aren't double-counting commits, we'll include some\n+complaining if a commit object is routed through our non-commit callback; we'll\n+also complain if we see an invalid object type. Since those two cases should be\n+unreachable, and would only change in the event of a semantic change to the Git\n+codebase, we complain by using `BUG()` - which is a signal to a developer that\n+the change they made caused unintended consequences, and the rest of the\n+codebase needs to be updated to understand that change. `BUG()` is not intended\n+to be seen by the public, so it is not localized.\n+\n+Our main object walk implementation is substantially different from our commit\n+walk implementation, so let's make a new function to perform the object walk. We\n+can perform setup which is applicable to all objects here, too, to keep separate\n+from setup which is applicable to commit-only walks.\n+\n+We'll start by enabling all types of objects in the `struct rev_info`.  We'll\n+also turn on `tree_blobs_in_commit_order`, which means that we will walk a\n+commit's tree and everything it points to immediately after we find each commit,\n+as opposed to waiting for the end and walking through all trees after the commit\n+history has been discovered. With the appropriate settings configured, we are\n+ready to call `prepare_revision_walk()`.\n+\n+----\n+static void walken_object_walk(struct rev_info *rev)\n+{\n+\trev->tree_objects = 1;\n+\trev->blob_objects = 1;\n+\trev->tag_objects = 1;\n+\trev->tree_blobs_in_commit_order = 1;\n+\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tcommit_count = 0;\n+\ttag_count = 0;\n+\tblob_count = 0;\n+\ttree_count = 0;\n+----\n+\n+Let's start by calling just the unfiltered walk and reporting our counts.\n+Complete your implementation of `walken_object_walk()`:\n+\n+----\n+\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n+\n+\tprintf(\"commits %d\\nblobs %d\\ntags %d\\ntrees %d\\n\", commit_count,\n+\t\tblob_count, tag_count, tree_count);\n+}\n+----\n+\n+NOTE: This output is intended to be machine-parsed. Therefore, we are not\n+sending it to `trace_printf()`, and we are not localizing it - we need scripts\n+to be able to count on the formatting to be exactly the way it is shown here.\n+If we were intending this output to be read by humans, we would need to localize\n+it with `_()`.\n+\n+Finally, we'll ask `cmd_walken()` to use the object walk instead. Discussing\n+command line options is out of scope for this tutorial, so we'll just hardcode\n+a branch we can change at compile time. Where you call `final_rev_info_setup()`\n+and `walken_commit_walk()`, instead branch like so:\n+\n+----\n+\tif (1) {\n+\t\tadd_head_to_pending(&rev);\n+\t\twalken_object_walk(&rev);\n+\t} else {\n+\t\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n+\t\twalken_commit_walk(&rev);\n+\t}\n+----\n+\n+NOTE: For simplicity, we've avoided all the filters and sorts we applied in\n+`final_rev_info_setup()` and simply added `HEAD` to our pending queue. If you\n+want, you can certainly use the filters we added before by moving\n+`final_rev_info_setup()` out of the conditional and removing the call to\n+`add_head_to_pending()`.\n+\n+Now we can try to run our command! It should take noticeably longer than the\n+commit walk, but an examination of the output will give you an idea why. Your\n+output should look similar to this example, but with different counts:\n+\n+----\n+Object walk completed. Found 55733 commits, 100274 blobs, 0 tags, and 104210 trees.\n+----\n+\n+This makes sense. We have more trees than commits because the Git project has\n+lots of subdirectories which can change, plus at least one tree per commit. We\n+have no tags because we started on a commit (`HEAD`) and while tags can point to\n+commits, commits can't point to tags.\n+\n+NOTE: You will have different counts when you run this yourself! The number of\n+objects grows along with the Git project.\n+\n+=== Adding a Filter\n+\n+There are a handful of filters that we can apply to the object walk laid out in\n+`Documentation/rev-list-options.txt`. These filters are typically useful for\n+operations such as creating packfiles or performing a partial clone. They are\n+defined in `list-objects-filter-options.h`. For the purposes of this tutorial we\n+will use the \"tree:1\" filter, which causes the walk to omit all trees and blobs\n+which are not directly referenced by commits reachable from the commit in\n+`pending` when the walk begins. (`pending` is the list of objects which need to\n+be traversed during a walk; you can imagine a breadth-first tree traversal to\n+help understand. In our case, that means we omit trees and blobs not directly\n+referenced by `HEAD` or `HEAD`'s history, because we begin the walk with only\n+`HEAD` in the `pending` list.)\n+\n+First, we'll need to `#include \"list-objects-filter-options.h`\" and set up the\n+`struct list_objects_filter_options` at the top of the function.\n+\n+----\n+static void walken_object_walk(struct rev_info *rev)\n+{\n+\tstruct list_objects_filter_options filter_options = {};\n+\n+\t...\n+----\n+\n+For now, we are not going to track the omitted objects, so we'll replace those\n+parameters with `NULL`. For the sake of simplicity, we'll add a simple\n+build-time branch to use our filter or not. Replace the line calling\n+`traverse_commit_list()` with the following, which will remind us which kind of\n+walk we've just performed:\n+\n+----\n+\tif (0) {\n+\t\t/* Unfiltered: */\n+\t\ttrace_printf(_(\"Unfiltered object walk.\\n\"));\n+\t\ttraverse_commit_list(rev, walken_show_commit,\n+\t\t\t\twalken_show_object, NULL);\n+\t} else {\n+\t\ttrace_printf(\n+\t\t\t_(\"Filtered object walk with filterspec 'tree:1'.\\n\"));\n+\t\tparse_list_objects_filter(&filter_options, \"tree:1\");\n+\n+\t\ttraverse_commit_list_filtered(&filter_options, rev,\n+\t\t\twalken_show_commit, walken_show_object, NULL, NULL);\n+\t}\n+----\n+\n+`struct list_objects_filter_options` is usually built directly from a command\n+line argument, so the module provides an easy way to build one from a string.\n+Even though we aren't taking user input right now, we can still build one with\n+a hardcoded string using `parse_list_objects_filter()`.\n+\n+With the filter spec \"tree:1\", we are expecting to see _only_ the root tree for\n+each commit; therefore, the tree object count should be less than or equal to\n+the number of commits. (For an example of why that's true: `git commit --revert`\n+points to the same tree object as its grandparent.)\n+\n+=== Counting Omitted Objects\n+\n+We also have the capability to enumerate all objects which were omitted by a\n+filter, like with `git log --filter=<spec> --filter-print-omitted`. Asking\n+`traverse_commit_list_filtered()` to populate the `omitted` list means that our\n+object walk does not perform any better than an unfiltered object walk; all\n+reachable objects are walked in order to populate the list.\n+\n+First, add the `struct oidset` and related items we will use to iterate it:\n+\n+----\n+static void walken_object_walk(\n+\t...\n+\n+\tstruct oidset omitted;\n+\tstruct oidset_iter oit;\n+\tstruct object_id *oid = NULL;\n+\tint omitted_count = 0;\n+\toidset_init(&omitted, 0);\n+\n+\t...\n+----\n+\n+Modify the call to `traverse_commit_list_filtered()` to include your `omitted`\n+object:\n+\n+----\n+\t...\n+\n+\t\ttraverse_commit_list_filtered(&filter_options, rev,\n+\t\t\twalken_show_commit, walken_show_object, NULL, &omitted);\n+\n+\t...\n+----\n+\n+Then, after your traversal, the `oidset` traversal is pretty straightforward.\n+Count all the objects within and modify the print statement:\n+\n+----\n+\t/* Count the omitted objects. */\n+\toidset_iter_init(&omitted, &oit);\n+\n+\twhile ((oid = oidset_iter_next(&oit)))\n+\t\tomitted_count++;\n+\n+\tprintf(\"commits %d\\nblobs %d\\ntags %d\\ntrees%d\\nomitted %d\\n\",\n+\t\tcommit_count, blob_count, tag_count, tree_count, omitted_count);\n+----\n+\n+By running your walk with and without the filter, you should find that the total\n+object count in each case is identical. You can also time each invocation of\n+the `walken` subcommand, with and without `omitted` being passed in, to confirm\n+to yourself the runtime impact of tracking all omitted objects.\n+\n+=== Changing the Order\n+\n+Finally, let's demonstrate that you can also reorder walks of all objects, not\n+just walks of commits. First, we'll make our handlers chattier - modify\n+`walken_show_commit()` and `walken_show_object()` to print the object as they\n+go:\n+\n+----\n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+\ttrace_printf(\"commit: %s\\n\", oid_to_hex(&cmt->object.oid));\n+\tcommit_count++;\n+}\n+\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+\ttrace_printf(\"%s: %s\\n\", type_name(obj->type), oid_to_hex(&obj->oid));\n+\n+\t...\n+}\n+----\n+\n+NOTE: Since we will be examining this output directly as humans, we'll use\n+`trace_printf()` here. Additionally, since this change introduces a significant\n+number of printed lines, using `trace_printf()` will allow us to easily silence\n+those lines without having to recompile.\n+\n+(Leave the counter increment logic in place.)\n+\n+With only that change, run again (but save yourself some scrollback):\n+\n+----\n+$ GIT_TRACE=1 ./bin-wrappers/git walken | head -n 10\n+----\n+\n+Take a look at the top commit with `git show` and the object ID you printed; it\n+should be the same as the output of `git show HEAD`.\n+\n+Next, let's change a setting on our `struct rev_info` within\n+`walken_object_walk()`. Find where you're changing the other settings on `rev`,\n+such as `rev->tree_objects` and `rev->tree_blobs_in_commit_order`, and add the\n+`reverse` setting at the bottom:\n+\n+----\n+\t...\n+\n+\trev->tree_objects = 1;\n+\trev->blob_objects = 1;\n+\trev->tag_objects = 1;\n+\trev->tree_blobs_in_commit_order = 1;\n+\trev->reverse = 1;\n+\n+\t...\n+----\n+\n+Now, run again, but this time, let's grab the last handful of objects instead\n+of the first handful:\n+\n+----\n+$ make\n+$ GIT_TRACE=1 ./bin-wrappers git walken | tail -n 10\n+----\n+\n+The last commit object given should have the same OID as the one we saw at the\n+top before, and running `git show <oid>` with that OID should give you again\n+the same results as `git show HEAD`. Furthermore, if you run and examine the\n+first ten lines again (with `head` instead of `tail` like we did before applying\n+the `reverse` setting), you should see that now the first commit printed is the\n+initial commit, `e83c5163`.\n+\n+== Wrapping Up\n+\n+Let's review. In this tutorial, we:\n+\n+- Built a commit walk from the ground up\n+- Enabled a grep filter for that commit walk\n+- Changed the sort order of that filtered commit walk\n+- Built an object walk (tags, commits, trees, and blobs) from the ground up\n+- Learned how to add a filter-spec to an object walk\n+- Changed the display order of the filtered object walk\n-- \n2.23.0.700.g56cf767bdb-goog\n\n"},{"id":"383870","messageId":"xmqqr23jhlud.fsf@gitster-ct.c.googlers.com","threadId":"51257","inReplyTo":"20191010151932.2716-1-emilyshaffer@google.com","subject":"Re: [PATCH v5] documentation: add tutorial for object walking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-10-11T05:50:34Z","receivedAt":"2019-10-11T05:50:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Emily Shaffer <emilyshaffer@google.com> writes:\n\n> @@ -77,6 +77,7 @@ API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technica\n>  SP_ARTICLES += $(API_DOCS)\n>  \n>  TECH_DOCS += MyFirstContribution\n> +TECH_DOCS += MyFirstRevWalk\n\ns/Rev/Object/ probably (if so I can locally amend).\n\n> diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt\n> new file mode 100644\n> index 0000000000..7085f17072\n> --- /dev/null\n> +++ b/Documentation/MyFirstObjectWalk.txt\n> @@ -0,0 +1,905 @@\n> +My First Object Walk\n> +======================\n> +\n> +== What's an Object Walk?\n> +\n> +The object walk is a key concept in Git - this is the process that underpins\n> +operations like `git log`, `git blame`, and `git reflog`. Beginning at HEAD, the\n> +list of objects is found by walking parent relationships between objects.\n\nThe above is more about revision walk, for which we have plenty of\ndocs already, isn't it?  Walking objects, while walking the commit\nDAG, is a lessor concept than the key \"revision walk\" concept and\nunderpins different set of operations like object transfer and fsck.\n\nAlso, the object walk, unlike the revision walk, follows containment\nrelationships between objects.\n\n> +A related concept is the revision walk, which is focused on commit objects and\n> +their relationships.\n\nYes, s/their/& parent/ perhaps, to contrast the two a bit better.\n`git log` and friends, if they need to be listed, should come on\nthis side, I think.\n\n"},{"id":"383909","messageId":"20191011175010.GO29845@szeder.dev","threadId":"51257","inReplyTo":"20191010151932.2716-1-emilyshaffer@google.com","subject":"Re: [PATCH v5] documentation: add tutorial for object walking","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-10-11T17:50:10Z","receivedAt":"2019-10-11T17:50:18Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Thu, Oct 10, 2019 at 08:19:32AM -0700, Emily Shaffer wrote:\n> diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt\n> new file mode 100644\n> index 0000000000..7085f17072\n> --- /dev/null\n> +++ b/Documentation/MyFirstObjectWalk.txt\n> @@ -0,0 +1,905 @@\n> +My First Object Walk\n> +======================\n\nIn our CI builds [1] Asciidoctor complains about the above line like\nthis:\n\n  asciidoctor: WARNING: MyFirstObjectWalk.txt: line 2: unterminated example block\n\nI have no idea what it is trying to say, but I suspect that it\ncomplains about the length of that '=====' line not matching the\nlength of the previous title line.  I kicked off a build with the\n'====' line shortened, and it did silence that warning, and the build\nsucceeded.\n\nNote, however, that we recently had a patch [2] that argued that a\ndifferent header notation is better, at least for the Git User Manual.\nI'm not sure whether that applies for this tutorial as well; just\nmentioning it for consideration.\n\n\n[1] https://travis-ci.org/git/git/jobs/596474664#L1192\n[2] fd5b820d9c (user-manual.txt: change header notation, 2019-09-22)\n\n"},{"id":"383948","messageId":"20191011232633.GB20450@google.com","threadId":"51257","inReplyTo":"xmqqr23jhlud.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5] documentation: add tutorial for object walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-10-11T23:26:33Z","receivedAt":"2019-10-11T23:26:41Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Fri, Oct 11, 2019 at 02:50:34PM +0900, Junio C Hamano wrote:\n> Emily Shaffer <emilyshaffer@google.com> writes:\n> \n> > @@ -77,6 +77,7 @@ API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technica\n> >  SP_ARTICLES += $(API_DOCS)\n> >  \n> >  TECH_DOCS += MyFirstContribution\n> > +TECH_DOCS += MyFirstRevWalk\n> \n> s/Rev/Object/ probably (if so I can locally amend).\nYes, that's the case, although I'll send a reroll shortly if you don't\nwant to amend locally - there's other stuff to fix according to you and\nSZEDER.\n\n> \n> > diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt\n> > new file mode 100644\n> > index 0000000000..7085f17072\n> > --- /dev/null\n> > +++ b/Documentation/MyFirstObjectWalk.txt\n> > @@ -0,0 +1,905 @@\n> > +My First Object Walk\n> > +======================\n> > +\n> > +== What's an Object Walk?\n> > +\n> > +The object walk is a key concept in Git - this is the process that underpins\n> > +operations like `git log`, `git blame`, and `git reflog`. Beginning at HEAD, the\n> > +list of objects is found by walking parent relationships between objects.\n> \n> The above is more about revision walk, for which we have plenty of\n> docs already, isn't it?  Walking objects, while walking the commit\n> DAG, is a lessor concept than the key \"revision walk\" concept and\n> underpins different set of operations like object transfer and fsck.\n> \n> Also, the object walk, unlike the revision walk, follows containment\n> relationships between objects.\n\nGood point; I'll rewrite this paragraph rather than trying to just edit\nit a little for the next reroll.\n\n> \n> > +A related concept is the revision walk, which is focused on commit objects and\n> > +their relationships.\n> \n> Yes, s/their/& parent/ perhaps, to contrast the two a bit better.\n> `git log` and friends, if they need to be listed, should come on\n> this side, I think.\n> \nOK.\n"},{"id":"383949","messageId":"20191011233344.GC20450@google.com","threadId":"51257","inReplyTo":"20191011175010.GO29845@szeder.dev","subject":"Re: [PATCH v5] documentation: add tutorial for object walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-10-11T23:33:44Z","receivedAt":"2019-10-11T23:33:51Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"On Fri, Oct 11, 2019 at 07:50:10PM +0200, SZEDER Gábor wrote:\n> On Thu, Oct 10, 2019 at 08:19:32AM -0700, Emily Shaffer wrote:\n> > diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt\n> > new file mode 100644\n> > index 0000000000..7085f17072\n> > --- /dev/null\n> > +++ b/Documentation/MyFirstObjectWalk.txt\n> > @@ -0,0 +1,905 @@\n> > +My First Object Walk\n> > +======================\n> \n> In our CI builds [1] Asciidoctor complains about the above line like\n> this:\n> \n>   asciidoctor: WARNING: MyFirstObjectWalk.txt: line 2: unterminated example block\n> \n> I have no idea what it is trying to say, but I suspect that it\n> complains about the length of that '=====' line not matching the\n> length of the previous title line.  I kicked off a build with the\n> '====' line shortened, and it did silence that warning, and the build\n> succeeded.\n\nOh, I'm sure it's a consequence of changing it from \"Revision\" to\n\"Object\" in the title.\n\n> \n> Note, however, that we recently had a patch [2] that argued that a\n> different header notation is better, at least for the Git User Manual.\n> I'm not sure whether that applies for this tutorial as well; just\n> mentioning it for consideration.\n\nInteresting read! It looks like we actually do use the format that that\npatch suggests is better, except for the title. I'll change the title to\ncome into line with everything else, since I'm sending a reroll anyways.\n\nThanks!\n - Emily\n"},{"id":"383951","messageId":"20191011235554.138337-1-emilyshaffer@google.com","threadId":"51257","inReplyTo":"20191010151932.2716-1-emilyshaffer@google.com","subject":"[PATCH v6] documentation: add tutorial for object walking","fromName":"Emily Shaffer","fromEmail":"emilyshaffer@google.com","sentAt":"2019-10-11T23:55:54Z","receivedAt":"2019-10-11T23:56:03Z","isPatch":true,"sender":{"key":"nasamuffin@google.com","avatar":"https://avatars.githubusercontent.com/u/1606826?v=4"},"body":"Existing documentation on object walks seems to be primarily intended\nas a reference for those already familiar with the procedure. This\ntutorial attempts to give an entry-level guide to a couple of bare-bones\nobject walks so that new Git contributors can learn the concepts\nwithout having to wade through options parsing or special casing.\n\nThe target audience is a Git contributor who is just getting started\nwith the concept of object walking. The goal is to prepare this\ncontributor to be able to understand and modify existing commands which\nperform revision walks more easily, although it will also prepare\ncontributors to create new commands which perform walks.\n\nThe tutorial covers a basic overview of the structs involved during\nobject walk, setting up a basic commit walk, setting up a basic\nall-object walk, and adding some configuration changes to both walk\ntypes. It intentionally does not cover how to create new commands or\nsearch for options from the command line or gitconfigs.\n\nThere is an associated patchset at\nhttps://github.com/nasamuffin/git/tree/revwalk that contains a reference\nimplementation of the code generated by this tutorial.\n\nSigned-off-by: Emily Shaffer <emilyshaffer@google.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\n---\nSince v5 only a couple small changes:\n\nThe opening paragraph was reworked per Junio's comments to hopefully\nmake more sense for this document's new life as an object walking\ntutorial (instead of a revision walking one).\n\nThe title formatting line was changed from \"underlining\" with === to\nbeing prepended with a single \"=\", to both silence a warning and match\nthe headers through the rest of the document.\n\nThanks!\n - Emily\n\n Documentation/Makefile              |   1 +\n Documentation/MyFirstObjectWalk.txt | 906 ++++++++++++++++++++++++++++\n 2 files changed, 907 insertions(+)\n create mode 100644 Documentation/MyFirstObjectWalk.txt\n\ndiff --git a/Documentation/Makefile b/Documentation/Makefile\nindex 06d85ad958..8fe829cc1b 100644\n--- a/Documentation/Makefile\n+++ b/Documentation/Makefile\n@@ -77,6 +77,7 @@ API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technica\n SP_ARTICLES += $(API_DOCS)\n \n TECH_DOCS += MyFirstContribution\n+TECH_DOCS += MyFirstObjectWalk\n TECH_DOCS += SubmittingPatches\n TECH_DOCS += technical/hash-function-transition\n TECH_DOCS += technical/http-protocol\ndiff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt\nnew file mode 100644\nindex 0000000000..4d24daeb9f\n--- /dev/null\n+++ b/Documentation/MyFirstObjectWalk.txt\n@@ -0,0 +1,906 @@\n+= My First Object Walk\n+\n+== What's an Object Walk?\n+\n+The object walk is a key concept in Git - this is the process that underpins\n+operations like object transfer and fsck. Beginning from a given commit, the\n+list of objects is found by walking parent relationships between commits (commit\n+X based on commit W) and containment relationships between objects (tree Y is\n+contained within commit X, and blob Z is located within tree Y, giving our\n+working tree for commit X something like `y/z.txt`).\n+\n+A related concept is the revision walk, which is focused on commit objects and\n+their parent relationships and does not delve into other object types. The\n+revision walk is used for operations like `git log`.\n+\n+=== Related Reading\n+\n+- `Documentation/user-manual.txt` under \"Hacking Git\" contains some coverage of\n+  the revision walker in its various incarnations.\n+- `Documentation/technical/api-revision-walking.txt`\n+- https://eagain.net/articles/git-for-computer-scientists/[Git for Computer Scientists]\n+  gives a good overview of the types of objects in Git and what your object\n+  walk is really describing.\n+\n+== Setting Up\n+\n+Create a new branch from `master`.\n+\n+----\n+git checkout -b revwalk origin/master\n+----\n+\n+We'll put our fiddling into a new command. For fun, let's name it `git walken`.\n+Open up a new file `builtin/walken.c` and set up the command handler:\n+\n+----\n+/*\n+ * \"git walken\"\n+ *\n+ * Part of the \"My First Object Walk\" tutorial.\n+ */\n+\n+#include \"builtin.h\"\n+\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\ttrace_printf(_(\"cmd_walken incoming...\\n\"));\n+\treturn 0;\n+}\n+----\n+\n+NOTE: `trace_printf()` differs from `printf()` in that it can be turned on or\n+off at runtime. For the purposes of this tutorial, we will write `walken` as\n+though it is intended for use as a \"plumbing\" command: that is, a command which\n+is used primarily in scripts, rather than interactively by humans (a \"porcelain\"\n+command). So we will send our debug output to `trace_printf()` instead. When\n+running, enable trace output by setting the environment variable `GIT_TRACE`.\n+\n+Add usage text and `-h` handling, like all subcommands should consistently do\n+(our test suite will notice and complain if you fail to do so).\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\tconst char * const walken_usage[] = {\n+\t\tN_(\"git walken\"),\n+\t\tNULL,\n+\t}\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, walken_usage, 0);\n+\n+\t...\n+}\n+----\n+\n+Also add the relevant line in `builtin.h` near `cmd_whatchanged()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix);\n+----\n+\n+Include the command in `git.c` in `commands[]` near the entry for `whatchanged`,\n+maintaining alphabetical ordering:\n+\n+----\n+{ \"walken\", cmd_walken, RUN_SETUP },\n+----\n+\n+Add it to the `Makefile` near the line for `builtin/worktree.o`:\n+\n+----\n+BUILTIN_OBJS += builtin/walken.o\n+----\n+\n+Build and test out your command, without forgetting to ensure the `DEVELOPER`\n+flag is set, and with `GIT_TRACE` enabled so the debug output can be seen:\n+\n+----\n+$ echo DEVELOPER=1 >>config.mak\n+$ make\n+$ GIT_TRACE=1 ./bin-wrappers/git walken\n+----\n+\n+NOTE: For a more exhaustive overview of the new command process, take a look at\n+`Documentation/MyFirstContribution.txt`.\n+\n+NOTE: A reference implementation can be found at\n+https://github.com/nasamuffin/git/tree/revwalk.\n+\n+=== `struct rev_cmdline_info`\n+\n+The definition of `struct rev_cmdline_info` can be found in `revision.h`.\n+\n+This struct is contained within the `rev_info` struct and is used to reflect\n+parameters provided by the user over the CLI.\n+\n+`nr` represents the number of `rev_cmdline_entry` present in the array.\n+\n+`alloc` is used by the `ALLOC_GROW` macro. Check\n+`Documentation/technical/api-allocation-growing.txt` - this variable is used to\n+track the allocated size of the list.\n+\n+Per entry, we find:\n+\n+`item` is the object provided upon which to base the object walk. Items in Git\n+can be blobs, trees, commits, or tags. (See `Documentation/gittutorial-2.txt`.)\n+\n+`name` is the object ID (OID) of the object - a hex string you may be familiar\n+with from using Git to organize your source in the past. Check the tutorial\n+mentioned above towards the top for a discussion of where the OID can come\n+from.\n+\n+`whence` indicates some information about what to do with the parents of the\n+specified object. We'll explore this flag more later on; take a look at\n+`Documentation/revisions.txt` to get an idea of what could set the `whence`\n+value.\n+\n+`flags` are used to hint the beginning of the revision walk and are the first\n+block under the `#include`s in `revision.h`. The most likely ones to be set in\n+the `rev_cmdline_info` are `UNINTERESTING` and `BOTTOM`, but these same flags\n+can be used during the walk, as well.\n+\n+=== `struct rev_info`\n+\n+This one is quite a bit longer, and many fields are only used during the walk\n+by `revision.c` - not configuration options. Most of the configurable flags in\n+`struct rev_info` have a mirror in `Documentation/rev-list-options.txt`. It's a\n+good idea to take some time and read through that document.\n+\n+== Basic Commit Walk\n+\n+First, let's see if we can replicate the output of `git log --oneline`. We'll\n+refer back to the implementation frequently to discover norms when performing\n+an object walk of our own.\n+\n+To do so, we'll first find all the commits, in order, which preceded the current\n+commit. We'll extract the name and subject of the commit from each.\n+\n+Ideally, we will also be able to find out which ones are currently at the tip of\n+various branches.\n+\n+=== Setting Up\n+\n+Preparing for your object walk has some distinct stages.\n+\n+1. Perform default setup for this mode, and others which may be invoked.\n+2. Check configuration files for relevant settings.\n+3. Set up the `rev_info` struct.\n+4. Tweak the initialized `rev_info` to suit the current walk.\n+5. Prepare the `rev_info` for the walk.\n+6. Iterate over the objects, processing each one.\n+\n+==== Default Setups\n+\n+Before examining configuration files which may modify command behavior, set up\n+default state for switches or options your command may have. If your command\n+utilizes other Git components, ask them to set up their default states as well.\n+For instance, `git log` takes advantage of `grep` and `diff` functionality, so\n+its `init_log_defaults()` sets its own state (`decoration_style`) and asks\n+`grep` and `diff` to initialize themselves by calling each of their\n+initialization functions.\n+\n+For our first example within `git walken`, we don't intend to use any other\n+components within Git, and we don't have any configuration to do.  However, we\n+may want to add some later, so for now, we can add an empty placeholder. Create\n+a new function in `builtin/walken.c`:\n+\n+----\n+static void init_walken_defaults(void)\n+{\n+\t/*\n+\t * We don't actually need the same components `git log` does; leave this\n+\t * empty for now.\n+\t */\n+}\n+----\n+\n+Make sure to add a line invoking it inside of `cmd_walken()`.\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\tinit_walken_defaults();\n+}\n+----\n+\n+==== Configuring From `.gitconfig`\n+\n+Next, we should have a look at any relevant configuration settings (i.e.,\n+settings readable and settable from `git config`). This is done by providing a\n+callback to `git_config()`; within that callback, you can also invoke methods\n+from other components you may need that need to intercept these options. Your\n+callback will be invoked once per each configuration value which Git knows about\n+(global, local, worktree, etc.).\n+\n+Similarly to the default values, we don't have anything to do here yet\n+ourselves; however, we should call `git_default_config()` if we aren't calling\n+any other existing config callbacks.\n+\n+Add a new function to `builtin/walken.c`:\n+\n+----\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\t/*\n+\t * For now, we don't have any custom configuration, so fall back to\n+\t * the default config.\n+\t */\n+\treturn git_default_config(var, value, cb);\n+}\n+----\n+\n+Make sure to invoke `git_config()` with it in your `cmd_walken()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t...\n+\n+\tgit_config(git_walken_config, NULL);\n+\n+\t...\n+}\n+----\n+\n+==== Setting Up `rev_info`\n+\n+Now that we've gathered external configuration and options, it's time to\n+initialize the `rev_info` object which we will use to perform the walk. This is\n+typically done by calling `repo_init_revisions()` with the repository you intend\n+to target, as well as the `prefix` argument of `cmd_walken` and your `rev_info`\n+struct.\n+\n+Add the `struct rev_info` and the `repo_init_revisions()` call:\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t/* This can go wherever you like in your declarations.*/\n+\tstruct rev_info rev;\n+\t...\n+\n+\t/* This should go after the git_config() call. */\n+\trepo_init_revisions(the_repository, &rev, prefix);\n+\n+\t...\n+}\n+----\n+\n+==== Tweaking `rev_info` For the Walk\n+\n+We're getting close, but we're still not quite ready to go. Now that `rev` is\n+initialized, we can modify it to fit our needs. This is usually done within a\n+helper for clarity, so let's add one:\n+\n+----\n+static void final_rev_info_setup(struct rev_info *rev)\n+{\n+\t/*\n+\t * We want to mimic the appearance of `git log --oneline`, so let's\n+\t * force oneline format.\n+\t */\n+\tget_commit_format(\"oneline\", rev);\n+\n+\t/* Start our object walk at HEAD. */\n+\tadd_head_to_pending(rev);\n+}\n+----\n+\n+[NOTE]\n+====\n+Instead of using the shorthand `add_head_to_pending()`, you could do\n+something like this:\n+----\n+\tstruct setup_revision_opt opt;\n+\n+\tmemset(&opt, 0, sizeof(opt));\n+\topt.def = \"HEAD\";\n+\topt.revarg_opt = REVARG_COMMITTISH;\n+\tsetup_revisions(argc, argv, rev, &opt);\n+----\n+Using a `setup_revision_opt` gives you finer control over your walk's starting\n+point.\n+====\n+\n+Then let's invoke `final_rev_info_setup()` after the call to\n+`repo_init_revisions()`:\n+\n+----\n+int cmd_walken(int argc, const char **argv, const char *prefix)\n+{\n+\t...\n+\n+\tfinal_rev_info_setup(&rev);\n+\n+\t...\n+}\n+----\n+\n+Later, we may wish to add more arguments to `final_rev_info_setup()`. But for\n+now, this is all we need.\n+\n+==== Preparing `rev_info` For the Walk\n+\n+Now that `rev` is all initialized and configured, we've got one more setup step\n+before we get rolling. We can do this in a helper, which will both prepare the\n+`rev_info` for the walk, and perform the walk itself. Let's start the helper\n+with the call to `prepare_revision_walk()`, which can return an error without\n+dying on its own:\n+\n+----\n+static void walken_commit_walk(struct rev_info *rev)\n+{\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+}\n+----\n+\n+NOTE: `die()` prints to `stderr` and exits the program. Since it will print to\n+`stderr` it's likely to be seen by a human, so we will localize it.\n+\n+==== Performing the Walk!\n+\n+Finally! We are ready to begin the walk itself. Now we can see that `rev_info`\n+can also be used as an iterator; we move to the next item in the walk by using\n+`get_revision()` repeatedly. Add the listed variable declarations at the top and\n+the walk loop below the `prepare_revision_walk()` call within your\n+`walken_commit_walk()`:\n+\n+----\n+static void walken_commit_walk(struct rev_info *rev)\n+{\n+\tstruct commit *commit;\n+\tstruct strbuf prettybuf = STRBUF_INIT;\n+\n+\t...\n+\n+\twhile ((commit = get_revision(rev))) {\n+\t\tif (!commit)\n+\t\t\tcontinue;\n+\n+\t\tstrbuf_reset(&prettybuf);\n+\t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);\n+\t\tputs(prettybuf.buf);\n+\t}\n+\tstrbuf_release(&prettybuf);\n+}\n+----\n+\n+NOTE: `puts()` prints a `char*` to `stdout`. Since this is the part of the\n+command we expect to be machine-parsed, we're sending it directly to stdout.\n+\n+Give it a shot.\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken\n+----\n+\n+You should see all of the subject lines of all the commits in\n+your tree's history, in order, ending with the initial commit, \"Initial revision\n+of \"git\", the information manager from hell\". Congratulations! You've written\n+your first revision walk. You can play with printing some additional fields\n+from each commit if you're curious; have a look at the functions available in\n+`commit.h`.\n+\n+=== Adding a Filter\n+\n+Next, let's try to filter the commits we see based on their author. This is\n+equivalent to running `git log --author=<pattern>`. We can add a filter by\n+modifying `rev_info.grep_filter`, which is a `struct grep_opt`.\n+\n+First some setup. Add `init_grep_defaults()` to `init_walken_defaults()` and add\n+`grep_config()` to `git_walken_config()`:\n+\n+----\n+static void init_walken_defaults(void)\n+{\n+\tinit_grep_defaults(the_repository);\n+}\n+\n+...\n+\n+static int git_walken_config(const char *var, const char *value, void *cb)\n+{\n+\tgrep_config(var, value, cb);\n+\treturn git_default_config(var, value, cb);\n+}\n+----\n+\n+Next, we can modify the `grep_filter`. This is done with convenience functions\n+found in `grep.h`. For fun, we're filtering to only commits from folks using a\n+`gmail.com` email address - a not-very-precise guess at who may be working on\n+Git as a hobby. Since we're checking the author, which is a specific line in the\n+header, we'll use the `append_header_grep_pattern()` helper. We can use\n+the `enum grep_header_field` to indicate which part of the commit header we want\n+to search.\n+\n+In `final_rev_info_setup()`, add your filter line:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\tappend_header_grep_pattern(&rev->grep_filter, GREP_HEADER_AUTHOR,\n+\t\t\"gmail\");\n+\tcompile_grep_patterns(&rev->grep_filter);\n+\n+\t...\n+}\n+----\n+\n+`append_header_grep_pattern()` adds your new \"gmail\" pattern to `rev_info`, but\n+it won't work unless we compile it with `compile_grep_patterns()`.\n+\n+NOTE: If you are using `setup_revisions()` (for example, if you are passing a\n+`setup_revision_opt` instead of using `add_head_to_pending()`), you don't need\n+to call `compile_grep_patterns()` because `setup_revisions()` calls it for you.\n+\n+NOTE: We could add the same filter via the `append_grep_pattern()` helper if we\n+wanted to, but `append_header_grep_pattern()` adds the `enum grep_context` and\n+`enum grep_pat_token` for us.\n+\n+=== Changing the Order\n+\n+There are a few ways that we can change the order of the commits during a\n+revision walk. Firstly, we can use the `enum rev_sort_order` to choose from some\n+typical orderings.\n+\n+`topo_order` is the same as `git log --topo-order`: we avoid showing a parent\n+before all of its children have been shown, and we avoid mixing commits which\n+are in different lines of history. (`git help log`'s section on `--topo-order`\n+has a very nice diagram to illustrate this.)\n+\n+Let's see what happens when we run with `REV_SORT_BY_COMMIT_DATE` as opposed to\n+`REV_SORT_BY_AUTHOR_DATE`. Add the following:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\trev->topo_order = 1;\n+\trev->sort_order = REV_SORT_BY_COMMIT_DATE;\n+\n+\t...\n+}\n+----\n+\n+Let's output this into a file so we can easily diff it with the walk sorted by\n+author date.\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken > commit-date.txt\n+----\n+\n+Then, let's sort by author date and run it again.\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv,\n+\t\tconst char *prefix, struct rev_info *rev)\n+{\n+\t...\n+\n+\trev->topo_order = 1;\n+\trev->sort_order = REV_SORT_BY_AUTHOR_DATE;\n+\n+\t...\n+}\n+----\n+\n+----\n+$ make\n+$ ./bin-wrappers/git walken > author-date.txt\n+----\n+\n+Finally, compare the two. This is a little less helpful without object names or\n+dates, but hopefully we get the idea.\n+\n+----\n+$ diff -u commit-date.txt author-date.txt\n+----\n+\n+This display indicates that commits can be reordered after they're written, for\n+example with `git rebase`.\n+\n+Let's try one more reordering of commits. `rev_info` exposes a `reverse` flag.\n+Set that flag somewhere inside of `final_rev_info_setup()`:\n+\n+----\n+static void final_rev_info_setup(int argc, const char **argv, const char *prefix,\n+\t\tstruct rev_info *rev)\n+{\n+\t...\n+\n+\trev->reverse = 1;\n+\n+\t...\n+}\n+----\n+\n+Run your walk again and note the difference in order. (If you remove the grep\n+pattern, you should see the last commit this call gives you as your current\n+HEAD.)\n+\n+== Basic Object Walk\n+\n+So far we've been walking only commits. But Git has more types of objects than\n+that! Let's see if we can walk _all_ objects, and find out some information\n+about each one.\n+\n+We can base our work on an example. `git pack-objects` prepares all kinds of\n+objects for packing into a bitmap or packfile. The work we are interested in\n+resides in `builtins/pack-objects.c:get_object_list()`; examination of that\n+function shows that the all-object walk is being performed by\n+`traverse_commit_list()` or `traverse_commit_list_filtered()`. Those two\n+functions reside in `list-objects.c`; examining the source shows that, despite\n+the name, these functions traverse all kinds of objects. Let's have a look at\n+the arguments to `traverse_commit_list_filtered()`, which are a superset of the\n+arguments to the unfiltered version.\n+\n+- `struct list_objects_filter_options *filter_options`: This is a struct which\n+  stores a filter-spec as outlined in `Documentation/rev-list-options.txt`.\n+- `struct rev_info *revs`: This is the `rev_info` used for the walk.\n+- `show_commit_fn show_commit`: A callback which will be used to handle each\n+  individual commit object.\n+- `show_object_fn show_object`: A callback which will be used to handle each\n+  non-commit object (so each blob, tree, or tag).\n+- `void *show_data`: A context buffer which is passed in turn to `show_commit`\n+  and `show_object`.\n+- `struct oidset *omitted`: A linked-list of object IDs which the provided\n+  filter caused to be omitted.\n+\n+It looks like this `traverse_commit_list_filtered()` uses callbacks we provide\n+instead of needing us to call it repeatedly ourselves. Cool! Let's add the\n+callbacks first.\n+\n+For the sake of this tutorial, we'll simply keep track of how many of each kind\n+of object we find. At file scope in `builtin/walken.c` add the following\n+tracking variables:\n+\n+----\n+static int commit_count;\n+static int tag_count;\n+static int blob_count;\n+static int tree_count;\n+----\n+\n+Commits are handled by a different callback than other objects; let's do that\n+one first:\n+\n+----\n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+\tcommit_count++;\n+}\n+----\n+\n+The `cmt` argument is fairly self-explanatory. But it's worth mentioning that\n+the `buf` argument is actually the context buffer that we can provide to the\n+traversal calls - `show_data`, which we mentioned a moment ago.\n+\n+Since we have the `struct commit` object, we can look at all the same parts that\n+we looked at in our earlier commit-only walk. For the sake of this tutorial,\n+though, we'll just increment the commit counter and move on.\n+\n+The callback for non-commits is a little different, as we'll need to check\n+which kind of object we're dealing with:\n+\n+----\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+\tswitch (obj->type) {\n+\tcase OBJ_TREE:\n+\t\ttree_count++;\n+\t\tbreak;\n+\tcase OBJ_BLOB:\n+\t\tblob_count++;\n+\t\tbreak;\n+\tcase OBJ_TAG:\n+\t\ttag_count++;\n+\t\tbreak;\n+\tcase OBJ_COMMIT:\n+\t\tBUG(\"unexpected commit object in walken_show_object\\n\");\n+\tdefault:\n+\t\tBUG(\"unexpected object type %s in walken_show_object\\n\",\n+\t\t\ttype_name(obj->type));\n+\t}\n+}\n+----\n+\n+Again, `obj` is fairly self-explanatory, and we can guess that `buf` is the same\n+context pointer that `walken_show_commit()` receives: the `show_data` argument\n+to `traverse_commit_list()` and `traverse_commit_list_filtered()`. Finally,\n+`str` contains the name of the object, which ends up being something like\n+`foo.txt` (blob), `bar/baz` (tree), or `v1.2.3` (tag).\n+\n+To help assure us that we aren't double-counting commits, we'll include some\n+complaining if a commit object is routed through our non-commit callback; we'll\n+also complain if we see an invalid object type. Since those two cases should be\n+unreachable, and would only change in the event of a semantic change to the Git\n+codebase, we complain by using `BUG()` - which is a signal to a developer that\n+the change they made caused unintended consequences, and the rest of the\n+codebase needs to be updated to understand that change. `BUG()` is not intended\n+to be seen by the public, so it is not localized.\n+\n+Our main object walk implementation is substantially different from our commit\n+walk implementation, so let's make a new function to perform the object walk. We\n+can perform setup which is applicable to all objects here, too, to keep separate\n+from setup which is applicable to commit-only walks.\n+\n+We'll start by enabling all types of objects in the `struct rev_info`.  We'll\n+also turn on `tree_blobs_in_commit_order`, which means that we will walk a\n+commit's tree and everything it points to immediately after we find each commit,\n+as opposed to waiting for the end and walking through all trees after the commit\n+history has been discovered. With the appropriate settings configured, we are\n+ready to call `prepare_revision_walk()`.\n+\n+----\n+static void walken_object_walk(struct rev_info *rev)\n+{\n+\trev->tree_objects = 1;\n+\trev->blob_objects = 1;\n+\trev->tag_objects = 1;\n+\trev->tree_blobs_in_commit_order = 1;\n+\n+\tif (prepare_revision_walk(rev))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tcommit_count = 0;\n+\ttag_count = 0;\n+\tblob_count = 0;\n+\ttree_count = 0;\n+----\n+\n+Let's start by calling just the unfiltered walk and reporting our counts.\n+Complete your implementation of `walken_object_walk()`:\n+\n+----\n+\ttraverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);\n+\n+\tprintf(\"commits %d\\nblobs %d\\ntags %d\\ntrees %d\\n\", commit_count,\n+\t\tblob_count, tag_count, tree_count);\n+}\n+----\n+\n+NOTE: This output is intended to be machine-parsed. Therefore, we are not\n+sending it to `trace_printf()`, and we are not localizing it - we need scripts\n+to be able to count on the formatting to be exactly the way it is shown here.\n+If we were intending this output to be read by humans, we would need to localize\n+it with `_()`.\n+\n+Finally, we'll ask `cmd_walken()` to use the object walk instead. Discussing\n+command line options is out of scope for this tutorial, so we'll just hardcode\n+a branch we can change at compile time. Where you call `final_rev_info_setup()`\n+and `walken_commit_walk()`, instead branch like so:\n+\n+----\n+\tif (1) {\n+\t\tadd_head_to_pending(&rev);\n+\t\twalken_object_walk(&rev);\n+\t} else {\n+\t\tfinal_rev_info_setup(argc, argv, prefix, &rev);\n+\t\twalken_commit_walk(&rev);\n+\t}\n+----\n+\n+NOTE: For simplicity, we've avoided all the filters and sorts we applied in\n+`final_rev_info_setup()` and simply added `HEAD` to our pending queue. If you\n+want, you can certainly use the filters we added before by moving\n+`final_rev_info_setup()` out of the conditional and removing the call to\n+`add_head_to_pending()`.\n+\n+Now we can try to run our command! It should take noticeably longer than the\n+commit walk, but an examination of the output will give you an idea why. Your\n+output should look similar to this example, but with different counts:\n+\n+----\n+Object walk completed. Found 55733 commits, 100274 blobs, 0 tags, and 104210 trees.\n+----\n+\n+This makes sense. We have more trees than commits because the Git project has\n+lots of subdirectories which can change, plus at least one tree per commit. We\n+have no tags because we started on a commit (`HEAD`) and while tags can point to\n+commits, commits can't point to tags.\n+\n+NOTE: You will have different counts when you run this yourself! The number of\n+objects grows along with the Git project.\n+\n+=== Adding a Filter\n+\n+There are a handful of filters that we can apply to the object walk laid out in\n+`Documentation/rev-list-options.txt`. These filters are typically useful for\n+operations such as creating packfiles or performing a partial clone. They are\n+defined in `list-objects-filter-options.h`. For the purposes of this tutorial we\n+will use the \"tree:1\" filter, which causes the walk to omit all trees and blobs\n+which are not directly referenced by commits reachable from the commit in\n+`pending` when the walk begins. (`pending` is the list of objects which need to\n+be traversed during a walk; you can imagine a breadth-first tree traversal to\n+help understand. In our case, that means we omit trees and blobs not directly\n+referenced by `HEAD` or `HEAD`'s history, because we begin the walk with only\n+`HEAD` in the `pending` list.)\n+\n+First, we'll need to `#include \"list-objects-filter-options.h`\" and set up the\n+`struct list_objects_filter_options` at the top of the function.\n+\n+----\n+static void walken_object_walk(struct rev_info *rev)\n+{\n+\tstruct list_objects_filter_options filter_options = {};\n+\n+\t...\n+----\n+\n+For now, we are not going to track the omitted objects, so we'll replace those\n+parameters with `NULL`. For the sake of simplicity, we'll add a simple\n+build-time branch to use our filter or not. Replace the line calling\n+`traverse_commit_list()` with the following, which will remind us which kind of\n+walk we've just performed:\n+\n+----\n+\tif (0) {\n+\t\t/* Unfiltered: */\n+\t\ttrace_printf(_(\"Unfiltered object walk.\\n\"));\n+\t\ttraverse_commit_list(rev, walken_show_commit,\n+\t\t\t\twalken_show_object, NULL);\n+\t} else {\n+\t\ttrace_printf(\n+\t\t\t_(\"Filtered object walk with filterspec 'tree:1'.\\n\"));\n+\t\tparse_list_objects_filter(&filter_options, \"tree:1\");\n+\n+\t\ttraverse_commit_list_filtered(&filter_options, rev,\n+\t\t\twalken_show_commit, walken_show_object, NULL, NULL);\n+\t}\n+----\n+\n+`struct list_objects_filter_options` is usually built directly from a command\n+line argument, so the module provides an easy way to build one from a string.\n+Even though we aren't taking user input right now, we can still build one with\n+a hardcoded string using `parse_list_objects_filter()`.\n+\n+With the filter spec \"tree:1\", we are expecting to see _only_ the root tree for\n+each commit; therefore, the tree object count should be less than or equal to\n+the number of commits. (For an example of why that's true: `git commit --revert`\n+points to the same tree object as its grandparent.)\n+\n+=== Counting Omitted Objects\n+\n+We also have the capability to enumerate all objects which were omitted by a\n+filter, like with `git log --filter=<spec> --filter-print-omitted`. Asking\n+`traverse_commit_list_filtered()` to populate the `omitted` list means that our\n+object walk does not perform any better than an unfiltered object walk; all\n+reachable objects are walked in order to populate the list.\n+\n+First, add the `struct oidset` and related items we will use to iterate it:\n+\n+----\n+static void walken_object_walk(\n+\t...\n+\n+\tstruct oidset omitted;\n+\tstruct oidset_iter oit;\n+\tstruct object_id *oid = NULL;\n+\tint omitted_count = 0;\n+\toidset_init(&omitted, 0);\n+\n+\t...\n+----\n+\n+Modify the call to `traverse_commit_list_filtered()` to include your `omitted`\n+object:\n+\n+----\n+\t...\n+\n+\t\ttraverse_commit_list_filtered(&filter_options, rev,\n+\t\t\twalken_show_commit, walken_show_object, NULL, &omitted);\n+\n+\t...\n+----\n+\n+Then, after your traversal, the `oidset` traversal is pretty straightforward.\n+Count all the objects within and modify the print statement:\n+\n+----\n+\t/* Count the omitted objects. */\n+\toidset_iter_init(&omitted, &oit);\n+\n+\twhile ((oid = oidset_iter_next(&oit)))\n+\t\tomitted_count++;\n+\n+\tprintf(\"commits %d\\nblobs %d\\ntags %d\\ntrees%d\\nomitted %d\\n\",\n+\t\tcommit_count, blob_count, tag_count, tree_count, omitted_count);\n+----\n+\n+By running your walk with and without the filter, you should find that the total\n+object count in each case is identical. You can also time each invocation of\n+the `walken` subcommand, with and without `omitted` being passed in, to confirm\n+to yourself the runtime impact of tracking all omitted objects.\n+\n+=== Changing the Order\n+\n+Finally, let's demonstrate that you can also reorder walks of all objects, not\n+just walks of commits. First, we'll make our handlers chattier - modify\n+`walken_show_commit()` and `walken_show_object()` to print the object as they\n+go:\n+\n+----\n+static void walken_show_commit(struct commit *cmt, void *buf)\n+{\n+\ttrace_printf(\"commit: %s\\n\", oid_to_hex(&cmt->object.oid));\n+\tcommit_count++;\n+}\n+\n+static void walken_show_object(struct object *obj, const char *str, void *buf)\n+{\n+\ttrace_printf(\"%s: %s\\n\", type_name(obj->type), oid_to_hex(&obj->oid));\n+\n+\t...\n+}\n+----\n+\n+NOTE: Since we will be examining this output directly as humans, we'll use\n+`trace_printf()` here. Additionally, since this change introduces a significant\n+number of printed lines, using `trace_printf()` will allow us to easily silence\n+those lines without having to recompile.\n+\n+(Leave the counter increment logic in place.)\n+\n+With only that change, run again (but save yourself some scrollback):\n+\n+----\n+$ GIT_TRACE=1 ./bin-wrappers/git walken | head -n 10\n+----\n+\n+Take a look at the top commit with `git show` and the object ID you printed; it\n+should be the same as the output of `git show HEAD`.\n+\n+Next, let's change a setting on our `struct rev_info` within\n+`walken_object_walk()`. Find where you're changing the other settings on `rev`,\n+such as `rev->tree_objects` and `rev->tree_blobs_in_commit_order`, and add the\n+`reverse` setting at the bottom:\n+\n+----\n+\t...\n+\n+\trev->tree_objects = 1;\n+\trev->blob_objects = 1;\n+\trev->tag_objects = 1;\n+\trev->tree_blobs_in_commit_order = 1;\n+\trev->reverse = 1;\n+\n+\t...\n+----\n+\n+Now, run again, but this time, let's grab the last handful of objects instead\n+of the first handful:\n+\n+----\n+$ make\n+$ GIT_TRACE=1 ./bin-wrappers git walken | tail -n 10\n+----\n+\n+The last commit object given should have the same OID as the one we saw at the\n+top before, and running `git show <oid>` with that OID should give you again\n+the same results as `git show HEAD`. Furthermore, if you run and examine the\n+first ten lines again (with `head` instead of `tail` like we did before applying\n+the `reverse` setting), you should see that now the first commit printed is the\n+initial commit, `e83c5163`.\n+\n+== Wrapping Up\n+\n+Let's review. In this tutorial, we:\n+\n+- Built a commit walk from the ground up\n+- Enabled a grep filter for that commit walk\n+- Changed the sort order of that filtered commit walk\n+- Built an object walk (tags, commits, trees, and blobs) from the ground up\n+- Learned how to add a filter-spec to an object walk\n+- Changed the display order of the filtered object walk\n-- \n2.23.0.700.g56cf767bdb-goog\n\n"}]}