Skip to content

Commit f45022d

Browse files
pks-tgitster
authored andcommitted
connected: do not sort input revisions
In order to compute whether objects reachable from a set of tips are all connected, we do a revision walk with these tips as positive references and `--not --all`. `--not --all` will cause the revision walk to load all preexisting references as uninteresting, which can be very expensive in repositories with many references. Benchmarking the git-rev-list(1) command highlights that by far the most expensive single phase is initial sorting of the input revisions: after all references have been loaded, we first sort commits by author date. In a real-world repository with about 2.2 million references, it makes up about 40% of the total runtime of git-rev-list(1). Ultimately, the connectivity check shouldn't really bother about the order of input revisions at all. We only care whether we can actually walk all objects until we hit the cut-off point. So sorting the input is a complete waste of time. Introduce a new "--unsorted-input" flag to git-rev-list(1) which will cause it to not sort the commits and adjust the connectivity check to always pass the flag. This results in the following speedups, executed in a clone of gitlab-org/gitlab [1]: Benchmark #1: git rev-list --objects --quiet --not --all --not $(cat newrev) Time (mean ± σ): 7.639 s ± 0.065 s [User: 7.304 s, System: 0.335 s] Range (min … max): 7.543 s … 7.742 s 10 runs Benchmark #2: git rev-list --unsorted-input --objects --quiet --not --all --not $newrev Time (mean ± σ): 4.995 s ± 0.044 s [User: 4.657 s, System: 0.337 s] Range (min … max): 4.909 s … 5.048 s 10 runs Summary 'git rev-list --unsorted-input --objects --quiet --not --all --not $(cat newrev)' ran 1.53 ± 0.02 times faster than 'git rev-list --objects --quiet --not --all --not $newrev' [1]: https://gitlab.com/gitlab-org/gitlab.git. Note that not all refs are visible to clients. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 29ef1f2 commit f45022d

File tree

4 files changed

+48
-1
lines changed

4 files changed

+48
-1
lines changed

Documentation/rev-list-options.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -968,14 +968,20 @@ list of the missing objects. Object IDs are prefixed with a ``?'' character.
968968
objects.
969969
endif::git-rev-list[]
970970

971+
--unsorted-input::
972+
Show commits in the order they were given on the command line instead
973+
of sorting them in reverse chronological order by commit time. Cannot
974+
be combined with `--no-walk` or `--no-walk=sorted`.
975+
971976
--no-walk[=(sorted|unsorted)]::
972977
Only show the given commits, but do not traverse their ancestors.
973978
This has no effect if a range is specified. If the argument
974979
`unsorted` is given, the commits are shown in the order they were
975980
given on the command line. Otherwise (if `sorted` or no argument
976981
was given), the commits are shown in reverse chronological order
977982
by commit time.
978-
Cannot be combined with `--graph`.
983+
Cannot be combined with `--graph`. Cannot be combined with
984+
`--unsorted-input` if `sorted` or no argument was given.
979985

980986
--do-walk::
981987
Overrides a previous `--no-walk`.

connected.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
106106
if (opt->progress)
107107
strvec_pushf(&rev_list.args, "--progress=%s",
108108
_("Checking connectivity"));
109+
strvec_push(&rev_list.args, "--unsorted-input");
109110

110111
rev_list.git_cmd = 1;
111112
rev_list.env = opt->env;

revision.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2256,6 +2256,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
22562256
} else if (!strcmp(arg, "--author-date-order")) {
22572257
revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
22582258
revs->topo_order = 1;
2259+
} else if (!strcmp(arg, "--unsorted-input")) {
2260+
if (revs->no_walk)
2261+
die(_("--unsorted-input is incompatible with --no-walk"));
2262+
revs->unsorted_input = 1;
22592263
} else if (!strcmp(arg, "--early-output")) {
22602264
revs->early_output = 100;
22612265
revs->topo_order = 1;
@@ -2651,8 +2655,13 @@ static int handle_revision_pseudo_opt(const char *submodule,
26512655
} else if (!strcmp(arg, "--not")) {
26522656
*flags ^= UNINTERESTING | BOTTOM;
26532657
} else if (!strcmp(arg, "--no-walk")) {
2658+
if (!revs->no_walk && revs->unsorted_input)
2659+
die(_("--no-walk is incompatible with --unsorted-input"));
26542660
revs->no_walk = 1;
26552661
} else if (skip_prefix(arg, "--no-walk=", &optarg)) {
2662+
if (!revs->no_walk && revs->unsorted_input)
2663+
die(_("--no-walk is incompatible with --unsorted-input"));
2664+
26562665
/*
26572666
* Detached form ("--no-walk X" as opposed to "--no-walk=X")
26582667
* not allowed, since the argument is optional.

t/t6000-rev-list-misc.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,35 @@ test_expect_success 'rev-list --count --objects' '
169169
test_line_count = $count actual
170170
'
171171

172+
test_expect_success 'rev-list --unsorted-input results in different sorting' '
173+
git rev-list --unsorted-input HEAD HEAD~ >first &&
174+
git rev-list --unsorted-input HEAD~ HEAD >second &&
175+
! test_cmp first second &&
176+
sort first >first.sorted &&
177+
sort second >second.sorted &&
178+
test_cmp first.sorted second.sorted
179+
'
180+
181+
test_expect_success 'rev-list --unsorted-input incompatible with --no-walk' '
182+
cat >expect <<-EOF &&
183+
fatal: --no-walk is incompatible with --unsorted-input
184+
EOF
185+
test_must_fail git rev-list --unsorted-input --no-walk HEAD 2>error &&
186+
test_cmp expect error &&
187+
test_must_fail git rev-list --unsorted-input --no-walk=sorted HEAD 2>error &&
188+
test_cmp expect error &&
189+
test_must_fail git rev-list --unsorted-input --no-walk=unsorted HEAD 2>error &&
190+
test_cmp expect error &&
191+
192+
cat >expect <<-EOF &&
193+
fatal: --unsorted-input is incompatible with --no-walk
194+
EOF
195+
test_must_fail git rev-list --no-walk --unsorted-input HEAD 2>error &&
196+
test_cmp expect error &&
197+
test_must_fail git rev-list --no-walk=sorted --unsorted-input HEAD 2>error &&
198+
test_cmp expect error &&
199+
test_must_fail git rev-list --no-walk=unsorted --unsorted-input HEAD 2>error &&
200+
test_cmp expect error
201+
'
202+
172203
test_done

0 commit comments

Comments
 (0)