Skip to content

Commit 36cfda1

Browse files
committed
refs_from_alternate: helper to use refs from alternates
The receiving end of "git push" advertises the objects that the repository itself does not use, but are at the tips of refs in other repositories whose object databases are used as alternates for it. This helps it avoid having to receive (and the pusher having to send) objects that are already available to the receiving repository via the alternates mechanism. Tweak the helper function that implements this feature, and move it to transport.[ch] for future reuse by other programs. The additional test demonstrates how this optimization is helping "git push", and "git fetch" is ignorant about it. Signed-off-by: Junio C Hamano <gitster@pobox.com> Acked-by: Shawn O. Pearce <spearce@spearce.org>
1 parent ea2c69e commit 36cfda1

File tree

4 files changed

+106
-32
lines changed

4 files changed

+106
-32
lines changed

builtin/receive-pack.c

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -731,43 +731,14 @@ static int delete_only(struct command *commands)
731731
return 1;
732732
}
733733

734-
static int add_refs_from_alternate(struct alternate_object_database *e, void *unused)
734+
static void add_one_alternate_ref(const struct ref *ref, void *unused)
735735
{
736-
char *other;
737-
size_t len;
738-
struct remote *remote;
739-
struct transport *transport;
740-
const struct ref *extra;
741-
742-
e->name[-1] = '\0';
743-
other = xstrdup(make_absolute_path(e->base));
744-
e->name[-1] = '/';
745-
len = strlen(other);
746-
747-
while (other[len-1] == '/')
748-
other[--len] = '\0';
749-
if (len < 8 || memcmp(other + len - 8, "/objects", 8))
750-
return 0;
751-
/* Is this a git repository with refs? */
752-
memcpy(other + len - 8, "/refs", 6);
753-
if (!is_directory(other))
754-
return 0;
755-
other[len - 8] = '\0';
756-
remote = remote_get(other);
757-
transport = transport_get(remote, other);
758-
for (extra = transport_get_remote_refs(transport);
759-
extra;
760-
extra = extra->next) {
761-
add_extra_ref(".have", extra->old_sha1, 0);
762-
}
763-
transport_disconnect(transport);
764-
free(other);
765-
return 0;
736+
add_extra_ref(".have", ref->old_sha1, 0);
766737
}
767738

768739
static void add_alternate_refs(void)
769740
{
770-
foreach_alt_odb(add_refs_from_alternate, NULL);
741+
foreach_alt_odb(refs_from_alternate_cb, add_one_alternate_ref);
771742
}
772743

773744
int cmd_receive_pack(int argc, const char **argv, const char *prefix)

t/t5501-fetch-push-alternates.sh

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/bin/sh
2+
3+
test_description='fetch/push involving alternates'
4+
. ./test-lib.sh
5+
6+
count_objects () {
7+
loose=0 inpack=0
8+
eval "$(
9+
git count-objects -v |
10+
sed -n -e 's/^count: \(.*\)/loose=\1/p' \
11+
-e 's/^in-pack: \(.*\)/inpack=\1/p'
12+
)" &&
13+
echo $(( $loose + $inpack ))
14+
}
15+
16+
17+
test_expect_success setup '
18+
(
19+
git init original &&
20+
cd original &&
21+
i=0 &&
22+
while test $i -le 100
23+
do
24+
echo "$i" >count &&
25+
git add count &&
26+
git commit -m "$i" || exit
27+
i=$(($i + 1))
28+
done
29+
) &&
30+
(
31+
git clone --reference=original "file:///$(pwd)/original" one &&
32+
cd one &&
33+
echo Z >count &&
34+
git add count &&
35+
git commit -m Z &&
36+
count_objects >../one.count
37+
) &&
38+
A=$(pwd)/original/.git/objects &&
39+
git init receiver &&
40+
echo "$A" >receiver/.git/objects/info/alternates &&
41+
git init fetcher &&
42+
echo "$A" >fetcher/.git/objects/info/alternates
43+
'
44+
45+
test_expect_success 'pushing into a repository with the same alternate' '
46+
(
47+
cd one &&
48+
git push ../receiver master:refs/heads/it
49+
) &&
50+
(
51+
cd receiver &&
52+
count_objects >../receiver.count
53+
) &&
54+
test_cmp one.count receiver.count
55+
'
56+
57+
test_expect_failure 'fetching from a repository with the same alternate' '
58+
(
59+
cd fetcher &&
60+
git fetch ../one master:refs/heads/it &&
61+
count_objects >../fetcher.count
62+
) &&
63+
test_cmp one.count fetcher.count
64+
'
65+
66+
test_done

transport.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,3 +1189,37 @@ char *transport_anonymize_url(const char *url)
11891189
literal_copy:
11901190
return xstrdup(url);
11911191
}
1192+
1193+
int refs_from_alternate_cb(struct alternate_object_database *e, void *cb)
1194+
{
1195+
char *other;
1196+
size_t len;
1197+
struct remote *remote;
1198+
struct transport *transport;
1199+
const struct ref *extra;
1200+
alternate_ref_fn *ref_fn = cb;
1201+
1202+
e->name[-1] = '\0';
1203+
other = xstrdup(make_absolute_path(e->base));
1204+
e->name[-1] = '/';
1205+
len = strlen(other);
1206+
1207+
while (other[len-1] == '/')
1208+
other[--len] = '\0';
1209+
if (len < 8 || memcmp(other + len - 8, "/objects", 8))
1210+
return 0;
1211+
/* Is this a git repository with refs? */
1212+
memcpy(other + len - 8, "/refs", 6);
1213+
if (!is_directory(other))
1214+
return 0;
1215+
other[len - 8] = '\0';
1216+
remote = remote_get(other);
1217+
transport = transport_get(remote, other);
1218+
for (extra = transport_get_remote_refs(transport);
1219+
extra;
1220+
extra = extra->next)
1221+
ref_fn(extra, NULL);
1222+
transport_disconnect(transport);
1223+
free(other);
1224+
return 0;
1225+
}

transport.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,7 @@ int transport_refs_pushed(struct ref *ref);
166166
void transport_print_push_status(const char *dest, struct ref *refs,
167167
int verbose, int porcelain, int *nonfastforward);
168168

169+
typedef void alternate_ref_fn(const struct ref *, void *);
170+
extern int refs_from_alternate_cb(struct alternate_object_database *e, void *cb);
171+
169172
#endif

0 commit comments

Comments
 (0)