git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: Question regarding git fetch

From
Jeff King <peff@peff.net>
Date
Aug 27, 2009, 21:34 UTC
Message-ID
<20090827213426.GD4399@coredump.intra.peff.net>
In-Reply-To
<20090827204835.GC4399@coredump.intra.peff.net>
On Thu, Aug 27, 2009 at 04:48:35PM -0400, Jeff King wrote:
Show 5 quoted lines
> 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;
Previous: Jeff KingNext: Junio C Hamano
Message 8 of 17 in “Question regarding git fetch”
  1. Tom LambdaAug 27, 2009
  2. Avery PennarunAug 27, 2009
  3. Eric RaibleAug 27, 2009
  4. Avery PennarunAug 27, 2009
  5. Björn SteinbrinkAug 27, 2009
  6. Avery PennarunAug 27, 2009
  7. Jeff KingAug 27, 2009
  8. Jeff KingAug 27, 2009
  9. Junio C HamanoAug 27, 2009
  10. Jeff KingAug 27, 2009
  11. Jeff KingAug 27, 2009
  12. Avery PennarunAug 27, 2009
  13. Jeff KingAug 27, 2009
  14. Avery PennarunAug 27, 2009
  15. Junio C HamanoAug 27, 2009
  16. Jeff KingAug 27, 2009
  17. Tom LambdaAug 28, 2009

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.