revision.h: introduce blob/tree walking in order of the commits

The functionality to list tree objects in the order they were seen while traversing the commits will be used in one of the next commits, where we teach `git describe` to describe not only commits, but blobs, too. The change in list-objects.c is rather minimal as we'll be re-using the infrastructure put in place of the revision walking machinery. For example one could expect that add_pending_tree is not called, but rather commit->tree is directly passed to the tree traversal function. This however requires a lot more code than just emptying the queue containing trees after each commit. Signed-off-by: Stefan Beller <sbeller@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Stefan Beller committed Nov 15, 2017 at 18:00 UTC ce5b6f9be84690ba38eba10c42b3f7c7e2511abb
5 files changed +94 -1
Documentation/rev-list-options.txt
+5
@@ -686,6 +686,11 @@ ifdef::git-rev-list[]
686 all object IDs which I need to download if I have the commit
687 object _bar_ but not _foo_''.
688
689 +--in-commit-order::
690 + Print tree and blob ids in order of the commits. The tree
691 + and blob ids are printed after they are first referenced
692 + by a commit.
693 +
694 --objects-edge::
695 Similar to `--objects`, but also print the IDs of excluded
696 commits prefixed with a ``-'' character. This is used by
list-objects.c
+8
@@ -239,6 +239,14 @@ void traverse_commit_list(struct rev_info *revs,
239 if (commit->tree)
240 add_pending_tree(revs, commit->tree);
241 show_commit(commit, data);
242 +
243 + if (revs->tree_blobs_in_commit_order)
244 + /*
245 + * NEEDSWORK: Adding the tree and then flushing it here
246 + * needs a reallocation for each commit. Can we pass the
247 + * tree directory without allocation churn?
248 + */
249 + traverse_trees_and_blobs(revs, &csp, show_object, data);
250 }
251 traverse_trees_and_blobs(revs, &csp, show_object, data);
252
revision.c
+2
@@ -1845,6 +1845,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
1845 revs->dense = 0;
1846 } else if (!strcmp(arg, "--show-all")) {
1847 revs->show_all = 1;
1848 + } else if (!strcmp(arg, "--in-commit-order")) {
1849 + revs->tree_blobs_in_commit_order = 1;
1850 } else if (!strcmp(arg, "--remove-empty")) {
1851 revs->remove_empty_trees = 1;
1852 } else if (!strcmp(arg, "--merges")) {
revision.h
+2 -1
@@ -121,7 +121,8 @@ struct rev_info {
121 bisect:1,
122 ancestry_path:1,
123 first_parent_only:1,
124 - line_level_traverse:1;
124 + line_level_traverse:1,
125 + tree_blobs_in_commit_order:1;
126
127 /* Diff flags */
128 unsigned int diff:1,
t/t6100-rev-list-in-order.sh new
+77
@@ -0,0 +1,77 @@
1 +#!/bin/sh
2 +
3 +test_description='rev-list testing in-commit-order'
4 +
5 +. ./test-lib.sh
6 +
7 +test_expect_success 'setup a commit history with trees, blobs' '
8 + for x in one two three four
9 + do
10 + echo $x >$x &&
11 + git add $x &&
12 + git commit -m "add file $x" ||
13 + return 1
14 + done &&
15 + for x in four three
16 + do
17 + git rm $x &&
18 + git commit -m "remove $x" ||
19 + return 1
20 + done
21 +'
22 +
23 +test_expect_success 'rev-list --in-commit-order' '
24 + git rev-list --in-commit-order --objects HEAD >actual.raw &&
25 + cut -c 1-40 >actual <actual.raw &&
26 +
27 + git cat-file --batch-check="%(objectname)" >expect.raw <<-\EOF &&
28 + HEAD^{commit}
29 + HEAD^{tree}
30 + HEAD^{tree}:one
31 + HEAD^{tree}:two
32 + HEAD~1^{commit}
33 + HEAD~1^{tree}
34 + HEAD~1^{tree}:three
35 + HEAD~2^{commit}
36 + HEAD~2^{tree}
37 + HEAD~2^{tree}:four
38 + HEAD~3^{commit}
39 + # HEAD~3^{tree} skipped, same as HEAD~1^{tree}
40 + HEAD~4^{commit}
41 + # HEAD~4^{tree} skipped, same as HEAD^{tree}
42 + HEAD~5^{commit}
43 + HEAD~5^{tree}
44 + EOF
45 + grep -v "#" >expect <expect.raw &&
46 +
47 + test_cmp expect actual
48 +'
49 +
50 +test_expect_success 'rev-list lists blobs and trees after commits' '
51 + git rev-list --objects HEAD >actual.raw &&
52 + cut -c 1-40 >actual <actual.raw &&
53 +
54 + git cat-file --batch-check="%(objectname)" >expect.raw <<-\EOF &&
55 + HEAD^{commit}
56 + HEAD~1^{commit}
57 + HEAD~2^{commit}
58 + HEAD~3^{commit}
59 + HEAD~4^{commit}
60 + HEAD~5^{commit}
61 + HEAD^{tree}
62 + HEAD^{tree}:one
63 + HEAD^{tree}:two
64 + HEAD~1^{tree}
65 + HEAD~1^{tree}:three
66 + HEAD~2^{tree}
67 + HEAD~2^{tree}:four
68 + # HEAD~3^{tree} skipped, same as HEAD~1^{tree}
69 + # HEAD~4^{tree} skipped, same as HEAD^{tree}
70 + HEAD~5^{tree}
71 + EOF
72 + grep -v "#" >expect <expect.raw &&
73 +
74 + test_cmp expect actual
75 +'
76 +
77 +test_done