Re: How to check new commit availability without full fetch?
On Mon, 11 Jan 2010, Junio C Hamano wrote:
Show 24 quoted lines
> Ahh, you are right. It walks from objects the remote side told us are at
> the tip, and stops at what we know are complete (i.e. reachable from our
> tip of objects); immediately after --dry-run slurped objects, the next
> fetch will prove everything is locally available and complete before going
> over the network.
>
> But either I am very confused or the use of fields from "struct ref" is
> unintuitive in this codepath.
>
> Why does it feed ref->old_sha1? We are feeding _their_ tip commits to:
>
> rev-list --objects --stdin --not --all
>
> and expecting it to report failure when some of their tip commits lead to
> what we don't have yet. The reason why we have old_sha1[] vs new_sha1[]
> is because we want to report what changed from what, and also to protect
> us from simultaneous updates by doing compare-and-swap using the value we
> read from our refs when we started in old_sha1[], so I would have expected
> that ref_map elements would have _their_ commits on the new_sha1[] side,
> but apparently that is not what is happening, and it has been this way for
> a long time. The use of old_sha1[] came from 4191c35 (git-fetch: avoid
> local fetching from alternate (again), 2007-11-11), so it is a lot more
> likely that I am confused than the code is wrong and nobody noticed so
> far.
Very confusing indeed. I first discovered about quickfetch() myself when I fixed shallow clone leading to commit 86386829.
If old_sha1[] was our refs then quickfetch() would always succeed and we'd never fetch anything.
> What am I missing?
Digging a bit, it looks like get_remote_heads() is storing the remote's heads into old_sha1. And so is performed in get_refs_from_bundle(), and in insert_packed_refs() from get_refs_via_rsync(), etc.
Looking at the struct ref definition, we can see:
struct ref {
struct ref *next;
unsigned char old_sha1[20];
unsigned char new_sha1[20];
[...]
struct ref *peer_ref; /* when renaming */
[...]
};And apparently store_updated_refs() ends up using that peer_ref like this:
for (rm = ref_map; rm; rm = rm->next) {
struct ref *ref = NULL; if (rm->peer_ref) {
ref = xcalloc(1, sizeof(*ref) + strlen(rm->peer_ref->name) + 1);
strcpy(ref->name, rm->peer_ref->name);
hashcpy(ref->old_sha1, rm->peer_ref->old_sha1);
hashcpy(ref->new_sha1, rm->old_sha1);
ref->force = rm->peer_ref->force;
}So.... Doesn't this all look like a total mess of needless (and even leaked in this case) allocations and duplications, besides being completely unintuitive? Both hashcpy() above certainly throw my sense of logic aside...
Nicolas