Skip to content

Commit 39b44ba

Browse files
peffgitster
authored andcommitted
check_everything_connected: assume alternate ref tips are valid
When we receive a remote ref update to sha1 "X", we want to check that we have all of the objects needed by "X". We can assume that our repository is not currently corrupted, and therefore if we have a ref pointing at "Y", we have all of its objects. So we can stop our traversal from "X" as soon as we hit "Y". If we make the same non-corruption assumption about any repositories we use to store alternates, then we can also use their ref tips to shorten the traversal. This is especially useful when cloning with "--reference", as we otherwise do not have any local refs to check against, and have to traverse the whole history, even though the other side may have sent us few or no objects. Here are results for the included perf test (which shows off more or less the maximal savings, getting one new commit and sharing the whole history): Test HEAD^ HEAD -------------------------------------------------------------------- [on git.git] 5600.3: clone --reference 2.94(2.86+0.08) 0.09(0.08+0.01) -96.9% [on linux.git] 5600.3: clone --reference 45.74(45.34+0.41) 0.36(0.30+0.08) -99.2% Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 709dfa6 commit 39b44ba

File tree

5 files changed

+125
-0
lines changed

5 files changed

+125
-0
lines changed

Documentation/rev-list-options.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ explicitly.
182182
Pretend as if all objects mentioned by reflogs are listed on the
183183
command line as `<commit>`.
184184

185+
--alternate-refs::
186+
Pretend as if all objects mentioned as ref tips of alternate
187+
repositories were listed on the command line. An alternate
188+
repository is any repository whose object directory is specified
189+
in `objects/info/alternates`. The set of included objects may
190+
be modified by `core.alternateRefsCommand`, etc. See
191+
linkgit:git-config[1].
192+
185193
--single-worktree::
186194
By default, all working trees will be examined by the
187195
following options when there are more than one (see

connected.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
8080
argv_array_push(&rev_list.args, "--all");
8181
}
8282
argv_array_push(&rev_list.args, "--quiet");
83+
argv_array_push(&rev_list.args, "--alternate-refs");
8384
if (opt->progress)
8485
argv_array_pushf(&rev_list.args, "--progress=%s",
8586
_("Checking connectivity"));

revision.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,6 +1552,32 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
15521552
free_worktrees(worktrees);
15531553
}
15541554

1555+
struct add_alternate_refs_data {
1556+
struct rev_info *revs;
1557+
unsigned int flags;
1558+
};
1559+
1560+
static void add_one_alternate_ref(const struct object_id *oid,
1561+
void *vdata)
1562+
{
1563+
const char *name = ".alternate";
1564+
struct add_alternate_refs_data *data = vdata;
1565+
struct object *obj;
1566+
1567+
obj = get_reference(data->revs, name, oid, data->flags);
1568+
add_rev_cmdline(data->revs, obj, name, REV_CMD_REV, data->flags);
1569+
add_pending_object(data->revs, obj, name);
1570+
}
1571+
1572+
static void add_alternate_refs_to_pending(struct rev_info *revs,
1573+
unsigned int flags)
1574+
{
1575+
struct add_alternate_refs_data data;
1576+
data.revs = revs;
1577+
data.flags = flags;
1578+
for_each_alternate_ref(add_one_alternate_ref, &data);
1579+
}
1580+
15551581
static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
15561582
int exclude_parent)
15571583
{
@@ -1954,6 +1980,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
19541980
!strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
19551981
!strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
19561982
!strcmp(arg, "--indexed-objects") ||
1983+
!strcmp(arg, "--alternate-refs") ||
19571984
starts_with(arg, "--exclude=") ||
19581985
starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
19591986
starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
@@ -2440,6 +2467,8 @@ static int handle_revision_pseudo_opt(const char *submodule,
24402467
add_reflogs_to_pending(revs, *flags);
24412468
} else if (!strcmp(arg, "--indexed-objects")) {
24422469
add_index_objects_to_pending(revs, *flags);
2470+
} else if (!strcmp(arg, "--alternate-refs")) {
2471+
add_alternate_refs_to_pending(revs, *flags);
24432472
} else if (!strcmp(arg, "--not")) {
24442473
*flags ^= UNINTERESTING | BOTTOM;
24452474
} else if (!strcmp(arg, "--no-walk")) {

t/perf/p5600-clone-reference.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/sh
2+
3+
test_description='speed of clone --reference'
4+
. ./perf-lib.sh
5+
6+
test_perf_default_repo
7+
8+
test_expect_success 'create shareable repository' '
9+
git clone --bare . shared.git
10+
'
11+
12+
test_expect_success 'advance base repository' '
13+
# Do not use test_commit here; its test_tick will
14+
# use some ancient hard-coded date. The resulting clock
15+
# skew will cause pack-objects to traverse in a very
16+
# sub-optimal order, skewing the results.
17+
echo content >new-file-that-does-not-exist &&
18+
git add new-file-that-does-not-exist &&
19+
git commit -m "new commit"
20+
'
21+
22+
test_perf 'clone --reference' '
23+
rm -rf dst.git &&
24+
git clone --no-local --bare --reference shared.git . dst.git
25+
'
26+
27+
test_done

t/t5618-alternate-refs.sh

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/bin/sh
2+
3+
test_description='test handling of --alternate-refs traversal'
4+
. ./test-lib.sh
5+
6+
# Avoid test_commit because we want a specific and known set of refs:
7+
#
8+
# base -- one
9+
# \ \
10+
# two -- merged
11+
#
12+
# where "one" and "two" are on separate refs, and "merged" is available only in
13+
# the dependent child repository.
14+
test_expect_success 'set up local refs' '
15+
git checkout -b one &&
16+
test_tick &&
17+
git commit --allow-empty -m base &&
18+
test_tick &&
19+
git commit --allow-empty -m one &&
20+
git checkout -b two HEAD^ &&
21+
test_tick &&
22+
git commit --allow-empty -m two
23+
'
24+
25+
# We'll enter the child repository after it's set up since that's where
26+
# all of the subsequent tests will want to run (and it's easy to forget a
27+
# "-C child" and get nonsense results).
28+
test_expect_success 'set up shared clone' '
29+
git clone -s . child &&
30+
cd child &&
31+
git merge origin/one
32+
'
33+
34+
test_expect_success 'rev-list --alternate-refs' '
35+
git rev-list --remotes=origin >expect &&
36+
git rev-list --alternate-refs >actual &&
37+
test_cmp expect actual
38+
'
39+
40+
test_expect_success 'rev-list --not --alternate-refs' '
41+
git rev-parse HEAD >expect &&
42+
git rev-list HEAD --not --alternate-refs >actual &&
43+
test_cmp expect actual
44+
'
45+
46+
test_expect_success 'limiting with alternateRefsPrefixes' '
47+
test_config core.alternateRefsPrefixes refs/heads/one &&
48+
git rev-list origin/one >expect &&
49+
git rev-list --alternate-refs >actual &&
50+
test_cmp expect actual
51+
'
52+
53+
test_expect_success 'log --source shows .alternate marker' '
54+
git log --oneline --source --remotes=origin >expect.orig &&
55+
sed "s/origin.* /.alternate /" <expect.orig >expect &&
56+
git log --oneline --source --alternate-refs >actual &&
57+
test_cmp expect actual
58+
'
59+
60+
test_done

0 commit comments

Comments
 (0)