threads / patch / 30732

patchgit fetch one tag only

Subject: [PATCH] git fetch one tag only

## tl;dr

9 messages between Jun 7, 2012 and Jun 8, 2012. Diffs are folded; open one to read it.

replies: 8people: 3as markdown or json

cheng renquan· Jun 7, 2012, 01:40 UTC · lore
Someone maybe like me is working in the way of following one central
git repository
while sometimes need to fetch some code or tags from a 3rd git repo,
but unfortunately the 3rd repo may contain a lot of tags not all I want
to fetch to mess up my local repo, at this time I want to fetch only one tag
from the 3rd repo, but the syntax of
  `git fetch 3rd-repo the-tag-name`

really fetched the code of the-tag-name from 3rd-repo, but forgot the tag itself; this patch enhanced the above syntax to create the tag itself;

 builtin/fetch.c |    8 ++++++++
 1 file changed, 8 insertions(+)
Show changes to builtin/fetch.c +7 −0
diff --git a/builtin/fetch.c b/builtin/fetch.c
index bb9a074..9a3ec4a 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -439,6 +439,14 @@ static int store_updated_refs(const char
*raw_url, const char *remote_name,
 			else if (!prefixcmp(rm->name, "refs/tags/")) {
 				kind = "tag";
 				what = rm->name + 10;
+				if (!ref) {
+					unsigned char sha1[20];
+					ref = alloc_ref(rm->name);
+					hashcpy(ref->new_sha1, rm->old_sha1);
+					if (!get_sha1(rm->name, sha1))
+						hashcpy(ref->old_sha1, sha1);
+				}
 			}
 			else if (!prefixcmp(rm->name, "refs/remotes/")) {
 				kind = "remote-tracking branch";
-- 
cheng renquan (程任全)
Junio C Hamano· Jun 7, 2012, 04:37 UTC · re: cheng renquan · lore

Re: [PATCH] git fetch one tag only

On Wed, Jun 6, 2012 at 6:40 PM, cheng renquan <crquan@gmail.com> wrote:
Show 11 quoted lines
>
> Someone maybe like me is working in the way of following one central
> git repository
> while sometimes need to fetch some code or tags from a 3rd git repo,
> but unfortunately the 3rd repo may contain a lot of tags not all I want
> to fetch to mess up my local repo, at this time I want to fetch only one tag
> from the 3rd repo, but the syntax of
>  `git fetch 3rd-repo the-tag-name`
>
> really fetched the code of the-tag-name from 3rd-repo, but forgot the
> tag itself;

The subject of "forgot" in that sentence is you, not "git fetch". You told "git fetch" to grab it but not store it locally in your refs/tags namespace.

There is a convenience short-hand "tag <tagname>", i.e.
  git fetch 3rd-repo tag the-tag-name
that is equivalent to
  git fetch 3rd-repo refs/tags/the-tag-name:refs/tags/the-tag-name

So I do not think your patch is necessary for your use case, and obviously it will break other people's use case where they just want to fetch (and inspect what is left in FETCH_HEAD) but do not want to store.

cheng renquan· Jun 7, 2012, 05:17 UTC · re: Junio C Hamano · lore

Re: [PATCH] git fetch one tag only

On Wed, Jun 6, 2012 at 9:37 PM, Junio C Hamano <gitster-vger@pobox.com> wrote:
Show 30 quoted lines
> On Wed, Jun 6, 2012 at 6:40 PM, cheng renquan <crquan@gmail.com> wrote:
>>
>> Someone maybe like me is working in the way of following one central
>> git repository
>> while sometimes need to fetch some code or tags from a 3rd git repo,
>> but unfortunately the 3rd repo may contain a lot of tags not all I want
>> to fetch to mess up my local repo, at this time I want to fetch only one tag
>> from the 3rd repo, but the syntax of
>>  `git fetch 3rd-repo the-tag-name`
>>
>> really fetched the code of the-tag-name from 3rd-repo, but forgot the
>> tag itself;
>
>
> The subject of "forgot" in that sentence is you, not "git fetch".
> You told "git fetch" to grab it but not store it locally in your
> refs/tags namespace.
>
> There is a convenience short-hand "tag <tagname>", i.e.
>
>   git fetch 3rd-repo tag the-tag-name
>
> that is equivalent to
>
>   git fetch 3rd-repo refs/tags/the-tag-name:refs/tags/the-tag-name
>
> So I do not think your patch is necessary for your use case, and
> obviously it will
> break other people's use case where they just want to fetch (and inspect what is
> left in FETCH_HEAD) but do not want to store.
No, I tried what you said but it doesn't work as expected:
  git fetch linux-stable tag v3.4.1

My [linus-git] is following torvalds' tree and now I want to fetch just one tag v3.4.1 from linux-stable tree, but the above command would fetch all tags from linux-stable

linus-git git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git (fetch) linux-stable git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git (fetch)

[linus-git] $ git fetch -v --dry-run linux-stable tag v3.4.1 |& head
From git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable
 = [up to date]      v3.4.1     -> v3.4.1
 * [new tag]         latest     -> latest
 * [new tag]         v2.6.12.1  -> v2.6.12.1
 * [new tag]         v2.6.12.2  -> v2.6.12.2
[...]
I didn't know this syntax before, but Yes, this syntax should serve
the one-tag-only goal better,
  `git fetch 3rd-repo tag the-tag-name`
maybe I'd better to fix that?
Junio C Hamano· Jun 7, 2012, 05:33 UTC · re: cheng renquan · lore

Re: [PATCH] git fetch one tag only

cheng renquan <crquan@gmail.com> writes:
Show 23 quoted lines
>> There is a convenience short-hand "tag <tagname>", i.e.
>>
>>  git fetch 3rd-repo tag the-tag-name
>>
>> that is equivalent to
>>
>>  git fetch 3rd-repo refs/tags/the-tag-name:refs/tags/the-tag-name
>>
>> So I do not think your patch is necessary for your use case, and
>> obviously it will break other people's use case where they just
>> want to fetch (and inspect what is left in FETCH_HEAD) but do not
>> want to store.
>
> No, I tried what you said but it doesn't work as expected:
> ...
> [linus-git] $ git fetch -v --dry-run linux-stable tag v3.4.1 |& head
> From git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable
>  = [up to date]      v3.4.1     -> v3.4.1
>  * [new tag]         latest     -> latest
>  * [new tag]         v2.6.12.1  -> v2.6.12.1
>  * [new tag]         v2.6.12.2  -> v2.6.12.2
> [...]
> maybe I'd better to fix that?

Ahh, that is auto-following of tags. Read up on that in "git fetch" manual page, and there is an option to decline auto-following also described.

The (current) rule is to grab all tags that reference commits you are fetching *IF* you are storing any refs resulting from the fetch in your refs/ namespace, and "tag v3.4.1" obviously asks for storing that tag at refs/tags/v3.4.1 in your repository, so it is expected that the auto-following kicks in.

It is a separate matter if we should add some special case to further reduce the cases where auto-following happens. I personally do not think any change is needed.

cheng renquan· Jun 7, 2012, 16:11 UTC · re: cheng renquan · lore

Re: [PATCH] git fetch one tag only

On Wed, Jun 6, 2012 at 10:47 PM, cheng renquan <crquan@gmail.com> wrote:
Show 5 quoted lines
> ok, got it:
>
> $ git fetch -v --no-tags linux-stable tag v3.4.1
> From git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable
>  = [up to date]      v3.4.1     -> v3.4.1
Here I still think the logic is wrong:

$ git fetch linux-stable tag fatal: You need to specify a tag name.

$ git fetch linux-stable tag v3.4.1 [this would actually fetch all tags]

from `git help fetch`:
           Some short-cut notations are also supported.
           ·    tag <tag> means the same as
refs/tags/<tag>:refs/tags/<tag>; it requests fetching everything up to
the given tag.

Here the documentation is saying "everything up to the given tag.", but other tags can never be all belonging to that path, for above example, in the path up to given tag v3.4.1, I'm sure other tags like v3.3.1 are not in the path;

So the `git fetch linux-stable tag v3.4.1` fetched other tags is a WRONG behavior, and why can't we fix that by disabling auto-following automatically if use tag syntax? Why to the user "--no-tags" is explicitly required?

Junio C Hamano· Jun 7, 2012, 17:08 UTC · re: Junio C Hamano · lore

Re: [PATCH] git fetch one tag only

Junio C Hamano <gitster@pobox.com> writes:
> It is a separate matter if we should add some special case to further
> reduce the cases where auto-following happens. I personally do not
> think any change is needed.
I do not care too deeply either way, but after doing
	$ git init blank && cd blank
and in an empty repository:
        $ git fetch $over_there v1.0.0

will grab the named tag and drop it in FETCH_HEAD, without adding anything to your local refs, while

	$ git fetch $over_there tag v1.0.0

will grab the named tag and copy it to your refs/tags/v1.0.0, and auto-follow all the tags that point at commits that are reachable.

To grab and store _only_ v1.0.0 in the local refs, you need to say "do not auto-follow" with

	$ git fetch $over_there --no-tags tag v1.0.0

While it may be workable, the user experience could be better in a couple of different ways.

 * It is not fair to expect anybody to guess that "--no-tags" means
   "--no-auto-follow-tags" without reading documentation.
	$ git fetch $over_there --no-auto-follow-tags tag v1.0.0
   might be an improvement, but of course, it is a bit longer to
   spell ;-).
 * The auto-follow kicks in whenever you tell "fetch" to update some
   refs locally.  Maybe if we tweak the rule and auto-follow kick in
   only when you tell "fetch" to update some refs outside refs/tags
   locally,
	$ git fetch $over_there tag v1.0.0
   will fetch and store _only_ the v1.0.0 tag.
   Of course, any behaviour change is a regression, and if done
   without an escape hatch, such a change robs people one useful
   feature: grab tag v1.0.0 and others older than that tag in one
   go.
I won't be coding any of the above; just thinking aloud. 
cheng renquan· Jun 8, 2012, 21:46 UTC · re: Junio C Hamano · lore

Re: [PATCH] git fetch one tag only

On Thu, Jun 7, 2012 at 10:08 AM, Junio C Hamano <gitster@pobox.com> wrote:
Show 15 quoted lines
>  * The auto-follow kicks in whenever you tell "fetch" to update some
>   refs locally.  Maybe if we tweak the rule and auto-follow kick in
>   only when you tell "fetch" to update some refs outside refs/tags
>   locally,
>
>        $ git fetch $over_there tag v1.0.0
>
>   will fetch and store _only_ the v1.0.0 tag.
>
>   Of course, any behaviour change is a regression, and if done
>   without an escape hatch, such a change robs people one useful
>   feature: grab tag v1.0.0 and others older than that tag in one
>   go.
>
> I won't be coding any of the above; just thinking aloud.

Here is the code to implement your 2nd approach, and I don't think this behaviour change is a regression, because if someone is really relying this fetch one tag actually fetch all tags feature he is relying on a broken feature, and should be corrected: if one really need to fetch all tags, that's what explicit "--tags" designed and documented for

Show changes to builtin/fetch.c +2 −1
diff --git a/builtin/fetch.c b/builtin/fetch.c
index bb9a074..b6d7ef3 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -158,7 +158,8 @@ static struct ref *get_ref_map(struct transport *transport,
 	if (ref_count || tags == TAGS_SET) {
 		for (i = 0; i < ref_count; i++) {
 			get_fetch_map(remote_refs, &refs[i], &tail, 0);
-			if (refs[i].dst && refs[i].dst[0])
+			if (refs[i].dst && refs[i].dst[0]
+			    && prefixcmp(refs[i].dst, "refs/tags/"))
 				*autotags = 1;
 		}
 		/* Merge everything on the command line, but not --tags */
-- 
cheng renquan (程任全)
Junio C Hamano· Jun 8, 2012, 22:22 UTC · re: cheng renquan · lore

Re: [PATCH] git fetch one tag only

cheng renquan <crquan@gmail.com> writes:
Show 18 quoted lines
> On Thu, Jun 7, 2012 at 10:08 AM, Junio C Hamano <gitster@pobox.com> wrote:
>>  * The auto-follow kicks in whenever you tell "fetch" to update some
>>   refs locally.  Maybe if we tweak the rule and auto-follow kick in
>>   only when you tell "fetch" to update some refs outside refs/tags
>>   locally,
>>
>>        $ git fetch $over_there tag v1.0.0
>>
>>   will fetch and store _only_ the v1.0.0 tag.
>>
>>   Of course, any behaviour change is a regression, and if done
>>   without an escape hatch, such a change robs people one useful
>>   feature: grab tag v1.0.0 and others older than that tag in one
>>   go.
>>
>> I won't be coding any of the above; just thinking aloud.
>
> Here is the code to implement your 2nd approach,
The patch is obviously and trivially correct ;-)
> and I don't think this behaviour change is a regression,
> because if someone is really relying this fetch one tag actually fetch
> all tags feature
> he is relying on a broken feature,

I do not think the old behaviour is broken. "tag v1.0.0" asks for refs/tags/v1.0.0 at the remote and store it at refs/tags/v1.0.0 locally. And when you ask "git fetch" to store something in refs/ hierarchy like that, without --no-tags, you are also asking tags on commits reachable from v1.0.0 (e.g. v0.99, but not v1.1.0).

	Side note: I just double-checked this.
	$ git init junk && cd junk
        $ git fetch ../git.git tag v1.0.0
	Of course, I get v1.0.0, many tags in v0.99 series and
	v1.0rc tags, but nothing newer.

I would grant you that "asking for only tags v1.0.0 and everything before" is not a very common thing to do, but I do not think it is a wrong feature in any way.

In any case, when we discuss regression, "I think the old behaviour is broken" does not matter. Change in behaviour is change in behaviour, even if the new one is superiour than the old one (and I tend to think that if I were doing Git from scratch, I probably would have coded the auto-follow part to ignore refs/tags hierarchy).

The problem of introducing such a change _today_ is that it robs a way to ask for tags v1.0.0 and all tags before it, which people can do with Git without the patch. A new --auto-follow-tags option that undoes what the patch does could remedy it, but without such an escape hatch, the change will be a true regression as it not just changes behaviour but it loses capability.

← back to recent threads