Skip to content

Commit

Permalink
refs_from_alternate: helper to use refs from alternates
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Junio C Hamano committed Mar 17, 2011
1 parent ea2c69e commit 36cfda1
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 32 deletions.
35 changes: 3 additions & 32 deletions builtin/receive-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -731,43 +731,14 @@ static int delete_only(struct command *commands)
return 1;
}

static int add_refs_from_alternate(struct alternate_object_database *e, void *unused)
static void add_one_alternate_ref(const struct ref *ref, void *unused)
{
char *other;
size_t len;
struct remote *remote;
struct transport *transport;
const struct ref *extra;

e->name[-1] = '\0';
other = xstrdup(make_absolute_path(e->base));
e->name[-1] = '/';
len = strlen(other);

while (other[len-1] == '/')
other[--len] = '\0';
if (len < 8 || memcmp(other + len - 8, "/objects", 8))
return 0;
/* Is this a git repository with refs? */
memcpy(other + len - 8, "/refs", 6);
if (!is_directory(other))
return 0;
other[len - 8] = '\0';
remote = remote_get(other);
transport = transport_get(remote, other);
for (extra = transport_get_remote_refs(transport);
extra;
extra = extra->next) {
add_extra_ref(".have", extra->old_sha1, 0);
}
transport_disconnect(transport);
free(other);
return 0;
add_extra_ref(".have", ref->old_sha1, 0);
}

static void add_alternate_refs(void)
{
foreach_alt_odb(add_refs_from_alternate, NULL);
foreach_alt_odb(refs_from_alternate_cb, add_one_alternate_ref);
}

int cmd_receive_pack(int argc, const char **argv, const char *prefix)
Expand Down
66 changes: 66 additions & 0 deletions t/t5501-fetch-push-alternates.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/bin/sh

test_description='fetch/push involving alternates'
. ./test-lib.sh

count_objects () {
loose=0 inpack=0
eval "$(
git count-objects -v |
sed -n -e 's/^count: \(.*\)/loose=\1/p' \
-e 's/^in-pack: \(.*\)/inpack=\1/p'
)" &&
echo $(( $loose + $inpack ))
}


test_expect_success setup '
(
git init original &&
cd original &&
i=0 &&
while test $i -le 100
do
echo "$i" >count &&
git add count &&
git commit -m "$i" || exit
i=$(($i + 1))
done
) &&
(
git clone --reference=original "file:///$(pwd)/original" one &&
cd one &&
echo Z >count &&
git add count &&
git commit -m Z &&
count_objects >../one.count
) &&
A=$(pwd)/original/.git/objects &&
git init receiver &&
echo "$A" >receiver/.git/objects/info/alternates &&
git init fetcher &&
echo "$A" >fetcher/.git/objects/info/alternates
'

test_expect_success 'pushing into a repository with the same alternate' '
(
cd one &&
git push ../receiver master:refs/heads/it
) &&
(
cd receiver &&
count_objects >../receiver.count
) &&
test_cmp one.count receiver.count
'

test_expect_failure 'fetching from a repository with the same alternate' '
(
cd fetcher &&
git fetch ../one master:refs/heads/it &&
count_objects >../fetcher.count
) &&
test_cmp one.count fetcher.count
'

test_done
34 changes: 34 additions & 0 deletions transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -1189,3 +1189,37 @@ char *transport_anonymize_url(const char *url)
literal_copy:
return xstrdup(url);
}

int refs_from_alternate_cb(struct alternate_object_database *e, void *cb)
{
char *other;
size_t len;
struct remote *remote;
struct transport *transport;
const struct ref *extra;
alternate_ref_fn *ref_fn = cb;

e->name[-1] = '\0';
other = xstrdup(make_absolute_path(e->base));
e->name[-1] = '/';
len = strlen(other);

while (other[len-1] == '/')
other[--len] = '\0';
if (len < 8 || memcmp(other + len - 8, "/objects", 8))
return 0;
/* Is this a git repository with refs? */
memcpy(other + len - 8, "/refs", 6);
if (!is_directory(other))
return 0;
other[len - 8] = '\0';
remote = remote_get(other);
transport = transport_get(remote, other);
for (extra = transport_get_remote_refs(transport);
extra;
extra = extra->next)
ref_fn(extra, NULL);
transport_disconnect(transport);
free(other);
return 0;
}
3 changes: 3 additions & 0 deletions transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,7 @@ int transport_refs_pushed(struct ref *ref);
void transport_print_push_status(const char *dest, struct ref *refs,
int verbose, int porcelain, int *nonfastforward);

typedef void alternate_ref_fn(const struct ref *, void *);
extern int refs_from_alternate_cb(struct alternate_object_database *e, void *cb);

#endif

0 comments on commit 36cfda1

Please sign in to comment.