On Thu, Aug 27, 2009 at 04:48:35PM -0400, Jeff King wrote: > And this matching is not really any different than what the fetch code > does when applying the refspec to what the remote offers. So I don't > think it should be any significant new code; it's just a matter of > activating that matching and updating the local tracking refs based on > what we actually fetched, instead of what the remote advertised. Sure enough, here is a really simple proof of concept. This is the first time I have really looked into the fetch code, so I hope I'm not totally breaking something else. :) Basically it works like this. Both "git fetch remote" and "git fetch remote refspec" work by making a "ref map" via get_ref_map. This is a list of refs, with some refs having a "peer ref" that points to the local counterpart. Those without a peer are just stored in FETCH_HEAD. The function works by starting with a list of possible remote refs, and then applying the refspecs to it. Calling fetch without explicit refspecs means we will create the map using the configured refspecs. Calling with commandline refspecs means we will use those. So what this patch does is to first apply the commandline refspecs to narrow the list, and then apply the configured refspecs on top of that to make a new list, and then concatenate the lists. So you will end up with "two" refs to fetch, which just happen to match the same source. One will go to FETCH_HEAD, and one will go to the tracking ref. I.e.: $ git remote add origin ~/compile/git $ git fetch origin next From /home/peff/compile/git * branch next -> FETCH_HEAD * [new branch] next -> origin/next And it should work the same if you supply a more interesting refspec, too: $ git fetch -v origin next:foo From /home/peff/compile/git * [new branch] next -> foo = [up to date] next -> origin/next or even a wildcard: $ git fetch -v origin refs/heads/*:refs/foo/* From /home/peff/compile/git * [new branch] master -> refs/foo/master * [new branch] next -> refs/foo/next * [new branch] master -> origin/master = [up to date] next -> origin/next I haven't thought long enough to be convinced there aren't any bad side effects, though. I guess if you had non-traditional configured refspecs, you might be surprised to see them applied in this case. Something like: # we usually fetch the remote's master straight into our production # branch for deployment $ git config remote.origin.fetch refs/heads/master:refs/heads/production # but today let's demo it first $ git fetch origin master:demo I don't know if people are actually using refspecs in that way. But we do need to consider that this is a potentially destructive change. Anyway, here is the patch. --- diff --git a/builtin-fetch.c b/builtin-fetch.c index 817dd6b..d9e44c7 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -119,6 +119,9 @@ static struct ref *get_ref_map(struct transport *transport, const struct ref *remote_refs = transport_get_remote_refs(transport); if (ref_count || tags == TAGS_SET) { + struct ref *tracking_refs = NULL; + struct ref **tracking_tail = &tracking_refs; + for (i = 0; i < ref_count; i++) { get_fetch_map(remote_refs, &refs[i], &tail, 0); if (refs[i].dst && refs[i].dst[0]) @@ -129,6 +132,12 @@ static struct ref *get_ref_map(struct transport *transport, rm->merge = 1; if (tags == TAGS_SET) get_fetch_map(remote_refs, tag_refspec, &tail, 0); + + for (i = 0; i < transport->remote->fetch_refspec_nr; i++) + get_fetch_map(ref_map, &transport->remote->fetch[i], + &tracking_tail, 0); + *tail = tracking_refs; + tail = tracking_tail; } else { /* Use the defaults */ struct remote *remote = transport->remote; -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html