{"thread":{"id":"19341","subject":"git-tag bug? confusing git fast-export with double tag objects","startedAt":"2009-05-14T00:53:42Z","lastAt":"2009-05-19T11:29:14Z","messageCount":30,"participants":["Matthias Andree","Junio C Hamano","Michael J Gruber","Alex Riesen","Sverre Rabbelier","Jeff King","Brandon Casey","Jakub Narebski","Johannes Sixt","Daniel Cheng","Andreas Ericsson"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"113860","messageId":"op.utv93sdo1e62zd@merlin.emma.line.org","threadId":"19341","inReplyTo":null,"subject":"git-tag bug? confusing git fast-export with double tag objects","fromName":"Matthias Andree","fromEmail":"matthias.andree@gmx.de","sentAt":"2009-05-14T00:53:42Z","receivedAt":"2009-05-14T00:53:42Z","isPatch":false,"sender":{"key":"matthias.andree@gmx.de","avatar":null},"body":"Greetings,\n\nI found a way to break git fast-export accidentally. I'm looking at the  \nmaster branch, currently v1.6.3.1-9-g95405ba for me.\n\nThe short story is, I tried to regenerate signed tag objects after  \ndoctoring the history in a repo to fix b0rked addresses after conversion,  \ndoing something like:\n\n\tgit tag -f -s foo foo\n\nwhen I should have done\n\n\tgit tag -f -s foo foo^{commit}\n\nNow I have  tag \"foo\" twice in my repo, and this screws some operations  \nroyally.\n\nHere's a script to generate such a b0rked repo:\n\n#! /bin/sh\n#\n# On your marks\nset -eu\nIFS=$(printf '\\n\\t')\n#\n# Set\ndir=$(mktemp -d)\ncd $dir\ngit init\necho foo >bar\ngit add bar\ngit commit -m \"add bar\"\ngit tag -s baz -m \"tag bar as baz\"\n#\n# Go - this is correct, but we'll do wrong\n#git tag -f -s baz baz^{commit} -m \"regenerate tag\"\n#\n# This is wrong and confuses git fast-export:\ngit tag -f -s baz baz -m \"regenerate tag\"\n#\n# Print the result\ngit show baz\n\n\nThis is quite prominent in git fast-export --all --signed-tags=strip  \noutput:\n\n...\ntag baz\n from :2\ntagger Matthias Andree <matthias.andree@gmx.de> 1242259705 +0200\ndata 15\ntag bar as baz\n\ntag baz\n from :0\ntagger Matthias Andree <matthias.andree@gmx.de> 1242259705 +0200\ndata 11\nregenerate\n\nAnd the \"from :0\" hunk kills git fast-import afterwards as the mark :0  \nisn't defined. Not sure if it could cope with a duplicate tag otherwise.  \nProbably not how git-tag should behave.\n\nQuestions:\n\n1. how do I get a list of all such tags? git tag -l doesn't work. git  \nrev-list --all is a bit unspecific for my taste, and not very helpful...\n\n2. how do I trash the accidentally created 2nd \"baz\" tag object, i. e.  \nremove it from the (packed) object database? Of course, I can hack some  \nscript (or use a text editor) to grind this git-fast-export into shape and  \nre-importing it...\n\n3. is this a shortcoming in git tag that doesn't properly resolve its 2nd  \nnon-option argument to a commit?\n\nThanks.\n\n-- \nMatthias Andree\n"},{"id":"113862","messageId":"op.utwdsutn1e62zd@merlin.emma.line.org","threadId":"19341","inReplyTo":"op.utv93sdo1e62zd@merlin.emma.line.org","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Matthias Andree","fromEmail":"matthias.andree@gmx.de","sentAt":"2009-05-14T02:13:32Z","receivedAt":"2009-05-14T02:13:32Z","isPatch":false,"sender":{"key":"matthias.andree@gmx.de","avatar":null},"body":"Am 14.05.2009, 02:53 Uhr, schrieb Matthias Andree <matthias.andree@gmx.de>:\n\n\n> 2. how do I trash the accidentally created 2nd \"baz\" tag object, i. e.  \n> remove it from the (packed) object database? Of course, I can hack some  \n> script (or use a text editor) to grind this git-fast-export into shape  \n> and re-importing it...\n\nOK, that worked: I traced (with git cat-file) the tree through all tagged  \ntag until the first tagged commit, and hack packed-refs (or refs/tags/foo)  \nto point to the commit object, and afterwards prune the dangling tag.\n\nHowever, the other questions remain. I'd think git tag should dereference  \nits 2nd non-option argument to a commit before laying down the tag...\n\n-- \nMatthias Andree\n"},{"id":"113864","messageId":"7v8wl01iev.fsf@alter.siamese.dyndns.org","threadId":"19341","inReplyTo":"op.utwdsutn1e62zd@merlin.emma.line.org","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-14T03:18:32Z","receivedAt":"2009-05-14T03:18:32Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Matthias Andree\" <matthias.andree@gmx.de> writes:\n\n> Am 14.05.2009, 02:53 Uhr, schrieb Matthias Andree <matthias.andree@gmx.de>:\n>\n>> 2. how do I trash the accidentally created 2nd \"baz\" tag object,\n>> i. e.  remove it from the (packed) object database? Of course, I can\n>> hack some  script (or use a text editor) to grind this\n>> git-fast-export into shape  and re-importing it...\n>\n> OK, that worked: I traced (with git cat-file) the tree through all\n> tagged  tag until the first tagged commit, and hack packed-refs (or\n> refs/tags/foo)  to point to the commit object, and afterwards prune\n> the dangling tag.\n>\n> However, the other questions remain. I'd think git tag should\n> dereference  its 2nd non-option argument to a commit before laying\n> down the tag...\n\nNo.  You can tag any object, and a tag is an object.  You can point a\nsigned tag with your own signed tag to attest your own belief on that\nother guy's tag, be it \"it's genuine\", \"the tagged commit suits my need\",\netc.\n\nI thought there was a breakage report followed by a fix to the fast-export\nthat mishandled a tag that points at another tag not too long ago.  Do you\nhave 1982467 (builtin-fast-export.c: handle nested tags, 2009-03-23)?\n"},{"id":"113914","messageId":"op.utwyczlf1e62zd@merlin.emma.line.org","threadId":"19341","inReplyTo":"7v8wl01iev.fsf@alter.siamese.dyndns.org","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Matthias Andree","fromEmail":"matthias.andree@gmx.de","sentAt":"2009-05-14T09:37:37Z","receivedAt":"2009-05-14T09:37:37Z","isPatch":false,"sender":{"key":"matthias.andree@gmx.de","avatar":null},"body":"Am 14.05.2009, 05:18 Uhr, schrieb Junio C Hamano <gitster@pobox.com>:\n\n> \"Matthias Andree\" <matthias.andree@gmx.de> writes:\n>\n>> Am 14.05.2009, 02:53 Uhr, schrieb Matthias Andree  \n>> <matthias.andree@gmx.de>:\n>>\n>>> 2. how do I trash the accidentally created 2nd \"baz\" tag object,\n>>> i. e.  remove it from the (packed) object database? Of course, I can\n>>> hack some  script (or use a text editor) to grind this\n>>> git-fast-export into shape  and re-importing it...\n>>\n>> OK, that worked: I traced (with git cat-file) the tree through all\n>> tagged  tag until the first tagged commit, and hack packed-refs (or\n>> refs/tags/foo)  to point to the commit object, and afterwards prune\n>> the dangling tag.\n>>\n>> However, the other questions remain. I'd think git tag should\n>> dereference  its 2nd non-option argument to a commit before laying\n>> down the tag...\n>\n> No.  You can tag any object, and a tag is an object.  You can point a\n> signed tag with your own signed tag to attest your own belief on that\n> other guy's tag, be it \"it's genuine\", \"the tagged commit suits my need\",\n> etc.\n\nOK, so I can tag/sign any object, fine.\n\nHOWEVER, I see two problems here (yes, they are corner cases):\n\n#1: git tag -f (\"replace tag\") fails to \"replace\" a heaviweight tag if I  \ntry to replace a tag by itself (or create a cycle by some other means).\n\nThe new \"foo\" is unique in refs (OK), but it's *not unique* in objects  \n(FAIL), as the old \"foo\" is referenced by the new \"foo\" and bears the same  \ntag name.\n\nIt screws the repo, breaking the uniqueness of tags. Basically, git tag -f  \nis implementing a half-baked, non-working \"rebase tag objects\"  \nfunctionality.\n\n\n\n#2: related: git tag -d cannot reliably delete tag objects\n\nSame here: if another tag object references the tag object I'm deleting,  \nwe only delete the ref, but not the tag object. It doesn't (cannot) become  \ndangling.\n\n\n\nWatch:\n\n$ cd $(mktemp -d)\n$ git init\nInitialized empty Git repository in /tmp/tmp.GBjHED4Xj8/.git/\n$ date >a\n$ git add a\n$ git commit -m \"new file a\" -a\n[master (root-commit) 4481a15] new file a\n  1 files changed, 1 insertions(+), 0 deletions(-)\n  create mode 100644 a\n$ LANG=C git tag foo -m \"add tag foo\" -s\n[GPG passphrase query]\n$ LANG=C git tag foo foo -m \"add tag foo\" -s\nfatal: tag 'foo' already exists\n\n-> this is ok, now let's break uniqueness:\n\n$ LANG=C git tag foo foo -m \"add tag foo\" -s -f\n[GPG passphrase query]\n$ git rev-list --objects --all\n4481a15d999b1b13066fe932e35ea05b8b1027a6\n72f3463f5a8089ac91001d458ceffb6d4e1056ee foo\n2e326d8a210536b7cd1f2bc77e3e29d7231f9ec4 foo\n995773fc9b649922936e110207e6abb904cc18e8\n15a9779d8f787428e57830410c7842e5449dfd33 a\n$ git show-ref\n4481a15d999b1b13066fe932e35ea05b8b1027a6 refs/heads/master\n72f3463f5a8089ac91001d458ceffb6d4e1056ee refs/tags/foo\n$ git cat-file tag 72f346\nobject 2e326d8a210536b7cd1f2bc77e3e29d7231f9ec4\ntype tag\ntag foo\ntagger Matthias Andree <matthias.andree@gmx.de> 1242289836 +0200\n\nadd tag foo\n-----BEGIN PGP SIGNATURE-----\n...\n$\n$ git cat-file tag 2e326d\nobject 4481a15d999b1b13066fe932e35ea05b8b1027a6\ntype commit\ntag foo\ntagger Matthias Andree <matthias.andree@gmx.de> 1242289732 +0200\n\nadd tag foo\n-----BEGIN PGP SIGNATURE-----\n...\n\n\nSo what we get is (root/parents first, then children):\n\nobjects:  4481a1 (commit) <- 2e326d (tag \"foo\") <- 72f346 (tag \"foo\")\nrefs:     heads/master                             tags/foo\n\nWhoops. \"foo\" is there twice, and it's referenced from a current ref.\nWe have *not* *replaced* it. *If* we did, we should have got:\n\nobjects:  4481a1 (commit) <- 72f346 (tag \"foo\")\nrefs:     heads/master       tags/foo\nwith a dangling tag 2e326d\n\n> I thought there was a breakage report followed by a fix to the  \n> fast-export that mishandled a tag that points at another tag not too  \n> long ago.  Do you have 1982467 (builtin-fast-export.c: handle nested  \n> tags, 2009-03-23)?\n\nI have that beast (how do I QUICKLY check if that is reachable from  \nrefs/master? git log | grep isn't exactly quick), but I think that's  \nunrelated. The real problem is the tag name is no longer unique, and we  \nmust prevent that.\n\nLet's screw with the tag objects even more (fresh repo, some \"otherfile\"):\n\n$ git tag -m \"old tag1\" -a tag1\n$ git tag -m \"tag2\" -a tag2 tag1\n$ git tag -m \"new tag1\" tag1 tag2\nfatal: tag 'tag1' already exists\n$ git tag -f -m \"new tag1\" tag1 tag2\n$ git rev-list --objects tag1\n69bf327c5d172fc8e4f63acf4d2e01c474824ce4\n8e7a1997726fc5158954569134d2cafad710f6fe tag1\n38aea56fec319d8c259a80157dde2432d2d09b2b tag2\n9756f6fa98a5cce2aab1f6a6e7dd4de515626e19 tag1\nd758baa57a7ef20d44df0535bef1a91bb3dc4f62\nd3d8863b140f43f7c07050b9f2e210d41e73edb1 otherfile\n$ git show-ref\n69bf327c5d172fc8e4f63acf4d2e01c474824ce4 refs/heads/master\n8e7a1997726fc5158954569134d2cafad710f6fe refs/tags/tag1\n38aea56fec319d8c259a80157dde2432d2d09b2b refs/tags/tag2\n$ gitk\n$ git cat-file tag 8e7a\nobject 38aea56fec319d8c259a80157dde2432d2d09b2b\ntype tag\ntag tag1\ntagger Matthias Andree <matthias.andree@gmx.de> 1242292320 +0200\n\nnew tag1\n$ git cat-file tag 38ae\nobject 9756f6fa98a5cce2aab1f6a6e7dd4de515626e19\ntype tag\ntag tag2\ntagger Matthias Andree <matthias.andree@gmx.de> 1242292301 +0200\n\ntag2\n$ git cat-file tag 9756\nobject 69bf327c5d172fc8e4f63acf4d2e01c474824ce4\ntype commit\ntag tag1\ntagger Matthias Andree <matthias.andree@gmx.de> 1242292293 +0200\n\nold tag1\n\nHu, there's a nice cycle:\n\n69bf (commit) <- 9756 ('old' tag1) <- 38ae (tag2) <- 8e7a (tag1)\n\nNow, more fun - watch the inconsistency:\n\n$ git tag -d tag1\nDeleted tag 'tag1'\n$ git tag -d tag1\nerror: tag 'tag1' not found.\n\nHa! As if... now watch this:\n$ git rev-list --objects  --all | while read a b ; do echo \"$a $(git  \ncat-file -t $a) $b\" ; done\n69bf327c5d172fc8e4f63acf4d2e01c474824ce4 commit\n38aea56fec319d8c259a80157dde2432d2d09b2b tag tag2\n9756f6fa98a5cce2aab1f6a6e7dd4de515626e19 tag tag1\nd758baa57a7ef20d44df0535bef1a91bb3dc4f62 tree\nd3d8863b140f43f7c07050b9f2e210d41e73edb1 blob otherfile\n\nThe tag object \"tag1\" is still there. WHOOPS!!!\n\nI appreciate that this isn't trivial to solve, but I presume anything that  \nwalks the object database and uses tags can fail - including, but not  \nlimited to, git fast-export.\n\nEither git tag -f/-d should complain and refuse if it cannot  \nreplace/remove the tag because it wouldn't become dangling (other  \ndependencies on it, best to list them), or it would have to recursively  \ntrash all its children, too - and perhaps require -f -f be specified for  \nthis recursive replacing/removal.\n\n-- \nMatthias Andree\n"},{"id":"113920","messageId":"4A0C07D9.8030401@drmicha.warpmail.net","threadId":"19341","inReplyTo":"op.utwyczlf1e62zd@merlin.emma.line.org","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Michael J Gruber","fromEmail":"git@drmicha.warpmail.net","sentAt":"2009-05-14T12:00:25Z","receivedAt":"2009-05-14T12:00:25Z","isPatch":false,"sender":{"key":"git@grubix.eu","avatar":"https://avatars.githubusercontent.com/u/233215?v=4"},"body":"Matthias Andree venit, vidit, dixit 14.05.2009 11:37:\n> Am 14.05.2009, 05:18 Uhr, schrieb Junio C Hamano <gitster@pobox.com>:\n> \n>> \"Matthias Andree\" <matthias.andree@gmx.de> writes:\n>>\n>>> Am 14.05.2009, 02:53 Uhr, schrieb Matthias Andree  \n>>> <matthias.andree@gmx.de>:\n>>>\n>>>> 2. how do I trash the accidentally created 2nd \"baz\" tag object,\n>>>> i. e.  remove it from the (packed) object database? Of course, I can\n>>>> hack some  script (or use a text editor) to grind this\n>>>> git-fast-export into shape  and re-importing it...\n>>>\n>>> OK, that worked: I traced (with git cat-file) the tree through all\n>>> tagged  tag until the first tagged commit, and hack packed-refs (or\n>>> refs/tags/foo)  to point to the commit object, and afterwards prune\n>>> the dangling tag.\n>>>\n>>> However, the other questions remain. I'd think git tag should\n>>> dereference  its 2nd non-option argument to a commit before laying\n>>> down the tag...\n>>\n>> No.  You can tag any object, and a tag is an object.  You can point a\n>> signed tag with your own signed tag to attest your own belief on that\n>> other guy's tag, be it \"it's genuine\", \"the tagged commit suits my need\",\n>> etc.\n> \n> OK, so I can tag/sign any object, fine.\n> \n> HOWEVER, I see two problems here (yes, they are corner cases):\n> \n> #1: git tag -f (\"replace tag\") fails to \"replace\" a heaviweight tag if I  \n> try to replace a tag by itself (or create a cycle by some other means).\n> \n> The new \"foo\" is unique in refs (OK), but it's *not unique* in objects  \n> (FAIL), as the old \"foo\" is referenced by the new \"foo\" and bears the same  \n> tag name.\n> \n> It screws the repo, breaking the uniqueness of tags. Basically, git tag -f  \n> is implementing a half-baked, non-working \"rebase tag objects\"  \n> functionality.\n> \n> \n> \n> #2: related: git tag -d cannot reliably delete tag objects\n> \n> Same here: if another tag object references the tag object I'm deleting,  \n> we only delete the ref, but not the tag object. It doesn't (cannot) become  \n> dangling.\n> \n> \n> \n> Watch:\n> \n> $ cd $(mktemp -d)\n> $ git init\n> Initialized empty Git repository in /tmp/tmp.GBjHED4Xj8/.git/\n> $ date >a\n> $ git add a\n> $ git commit -m \"new file a\" -a\n> [master (root-commit) 4481a15] new file a\n>   1 files changed, 1 insertions(+), 0 deletions(-)\n>   create mode 100644 a\n> $ LANG=C git tag foo -m \"add tag foo\" -s\n> [GPG passphrase query]\n> $ LANG=C git tag foo foo -m \"add tag foo\" -s\n> fatal: tag 'foo' already exists\n> \n> -> this is ok, now let's break uniqueness:\n> \n> $ LANG=C git tag foo foo -m \"add tag foo\" -s -f\n> [GPG passphrase query]\n> $ git rev-list --objects --all\n> 4481a15d999b1b13066fe932e35ea05b8b1027a6\n> 72f3463f5a8089ac91001d458ceffb6d4e1056ee foo\n> 2e326d8a210536b7cd1f2bc77e3e29d7231f9ec4 foo\n> 995773fc9b649922936e110207e6abb904cc18e8\n> 15a9779d8f787428e57830410c7842e5449dfd33 a\n> $ git show-ref\n> 4481a15d999b1b13066fe932e35ea05b8b1027a6 refs/heads/master\n> 72f3463f5a8089ac91001d458ceffb6d4e1056ee refs/tags/foo\n> $ git cat-file tag 72f346\n> object 2e326d8a210536b7cd1f2bc77e3e29d7231f9ec4\n> type tag\n> tag foo\n> tagger Matthias Andree <matthias.andree@gmx.de> 1242289836 +0200\n> \n> add tag foo\n> -----BEGIN PGP SIGNATURE-----\n> ...\n> $\n> $ git cat-file tag 2e326d\n> object 4481a15d999b1b13066fe932e35ea05b8b1027a6\n> type commit\n> tag foo\n> tagger Matthias Andree <matthias.andree@gmx.de> 1242289732 +0200\n> \n> add tag foo\n> -----BEGIN PGP SIGNATURE-----\n> ...\n> \n> \n> So what we get is (root/parents first, then children):\n> \n> objects:  4481a1 (commit) <- 2e326d (tag \"foo\") <- 72f346 (tag \"foo\")\n> refs:     heads/master                             tags/foo\n> \n> Whoops. \"foo\" is there twice, and it's referenced from a current ref.\n> We have *not* *replaced* it. *If* we did, we should have got:\n> \n> objects:  4481a1 (commit) <- 72f346 (tag \"foo\")\n> refs:     heads/master       tags/foo\n> with a dangling tag 2e326d\n> \n>> I thought there was a breakage report followed by a fix to the  \n>> fast-export that mishandled a tag that points at another tag not too  \n>> long ago.  Do you have 1982467 (builtin-fast-export.c: handle nested  \n>> tags, 2009-03-23)?\n> \n> I have that beast (how do I QUICKLY check if that is reachable from  \n> refs/master? git log | grep isn't exactly quick), \n\n\"git branch --contains 1982467\" gives you all branches which have that.\n\"git rev-list -1 \"master..1982467|wc -l\" checks whether 1982467 is\ncontained in master.\n\nbut I think that's\n> unrelated. The real problem is the tag name is no longer unique, and we  \n> must prevent that.\n> \n> Let's screw with the tag objects even more (fresh repo, some \"otherfile\"):\n> \n> $ git tag -m \"old tag1\" -a tag1\n> $ git tag -m \"tag2\" -a tag2 tag1\n> $ git tag -m \"new tag1\" tag1 tag2\n> fatal: tag 'tag1' already exists\n> $ git tag -f -m \"new tag1\" tag1 tag2\n> $ git rev-list --objects tag1\n> 69bf327c5d172fc8e4f63acf4d2e01c474824ce4\n> 8e7a1997726fc5158954569134d2cafad710f6fe tag1\n> 38aea56fec319d8c259a80157dde2432d2d09b2b tag2\n> 9756f6fa98a5cce2aab1f6a6e7dd4de515626e19 tag1\n> d758baa57a7ef20d44df0535bef1a91bb3dc4f62\n> d3d8863b140f43f7c07050b9f2e210d41e73edb1 otherfile\n> $ git show-ref\n> 69bf327c5d172fc8e4f63acf4d2e01c474824ce4 refs/heads/master\n> 8e7a1997726fc5158954569134d2cafad710f6fe refs/tags/tag1\n> 38aea56fec319d8c259a80157dde2432d2d09b2b refs/tags/tag2\n> $ gitk\n> $ git cat-file tag 8e7a\n> object 38aea56fec319d8c259a80157dde2432d2d09b2b\n> type tag\n> tag tag1\n> tagger Matthias Andree <matthias.andree@gmx.de> 1242292320 +0200\n> \n> new tag1\n> $ git cat-file tag 38ae\n> object 9756f6fa98a5cce2aab1f6a6e7dd4de515626e19\n> type tag\n> tag tag2\n> tagger Matthias Andree <matthias.andree@gmx.de> 1242292301 +0200\n> \n> tag2\n> $ git cat-file tag 9756\n> object 69bf327c5d172fc8e4f63acf4d2e01c474824ce4\n> type commit\n> tag tag1\n> tagger Matthias Andree <matthias.andree@gmx.de> 1242292293 +0200\n> \n> old tag1\n> \n> Hu, there's a nice cycle:\n> \n> 69bf (commit) <- 9756 ('old' tag1) <- 38ae (tag2) <- 8e7a (tag1)\n> \n> Now, more fun - watch the inconsistency:\n> \n> $ git tag -d tag1\n> Deleted tag 'tag1'\n> $ git tag -d tag1\n> error: tag 'tag1' not found.\n> \n> Ha! As if... now watch this:\n> $ git rev-list --objects  --all | while read a b ; do echo \"$a $(git  \n> cat-file -t $a) $b\" ; done\n> 69bf327c5d172fc8e4f63acf4d2e01c474824ce4 commit\n> 38aea56fec319d8c259a80157dde2432d2d09b2b tag tag2\n> 9756f6fa98a5cce2aab1f6a6e7dd4de515626e19 tag tag1\n> d758baa57a7ef20d44df0535bef1a91bb3dc4f62 tree\n> d3d8863b140f43f7c07050b9f2e210d41e73edb1 blob otherfile\n> \n> The tag object \"tag1\" is still there. WHOOPS!!!\n> \n> I appreciate that this isn't trivial to solve, but I presume anything that  \n> walks the object database and uses tags can fail - including, but not  \n> limited to, git fast-export.\n> \n> Either git tag -f/-d should complain and refuse if it cannot  \n> replace/remove the tag because it wouldn't become dangling (other  \n> dependencies on it, best to list them), or it would have to recursively  \n> trash all its children, too - and perhaps require -f -f be specified for  \n> this recursive replacing/removal.\n> \n"},{"id":"113922","messageId":"81b0412b0905140516k4bc84606scb71981936966caf@mail.gmail.com","threadId":"19341","inReplyTo":"op.utwyczlf1e62zd@merlin.emma.line.org","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2009-05-14T12:16:31Z","receivedAt":"2009-05-14T12:16:31Z","isPatch":false,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"2009/5/14 Matthias Andree <matthias.andree@gmx.de>:\n> Am 14.05.2009, 05:18 Uhr, schrieb Junio C Hamano <gitster@pobox.com>:\n>> No.  You can tag any object, and a tag is an object.  You can point a\n>> signed tag with your own signed tag to attest your own belief on that\n>> other guy's tag, be it \"it's genuine\", \"the tagged commit suits my need\",\n>> etc.\n>\n> OK, so I can tag/sign any object, fine.\n>\n> HOWEVER, I see two problems here (yes, they are corner cases):\n>\n> #1: git tag -f (\"replace tag\") fails to \"replace\" a heaviweight tag if I try\n> to replace a tag by itself (or create a cycle by some other means).\n\nIt is not a \"cycle\" (\"loop\"?) The tags information is the SHA1, not\nthe tag's name.\n\n> The new \"foo\" is unique in refs (OK), but it's *not unique* in objects\n> (FAIL), as the old \"foo\" is referenced by the new \"foo\" and bears the same\n> tag name.\n\nOf course it is unique. Look at tag's SHA1.\n\n> #2: related: git tag -d cannot reliably delete tag objects\n>\n> Same here: if another tag object references the tag object I'm deleting, we\n> only delete the ref, but not the tag object. It doesn't (cannot) become\n> dangling.\n\nAs soon as an object is not referenced anymore by any reference (including\nreferences from refs/tags/), reference log or index it will be removed by\ngarbage collection (gc, prune) at the next opportunity.\n"},{"id":"113924","messageId":"op.utw7buoi1e62zd@balu","threadId":"19341","inReplyTo":"81b0412b0905140516k4bc84606scb71981936966caf@mail.gmail.com","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Matthias Andree","fromEmail":"matthias.andree@gmx.de","sentAt":"2009-05-14T12:51:20Z","receivedAt":"2009-05-14T12:51:20Z","isPatch":false,"sender":{"key":"matthias.andree@gmx.de","avatar":null},"body":"Am 14.05.2009, 14:16 Uhr, schrieb Alex Riesen <raa.lkml@gmail.com>:\n\n> 2009/5/14 Matthias Andree <matthias.andree@gmx.de>:\n>> Am 14.05.2009, 05:18 Uhr, schrieb Junio C Hamano <gitster@pobox.com>:\n>>> No.  You can tag any object, and a tag is an object.  You can point a\n>>> signed tag with your own signed tag to attest your own belief on that\n>>> other guy's tag, be it \"it's genuine\", \"the tagged commit suits my  \n>>> need\",\n>>> etc.\n>>\n>> OK, so I can tag/sign any object, fine.\n>>\n>> HOWEVER, I see two problems here (yes, they are corner cases):\n>>\n>> #1: git tag -f (\"replace tag\") fails to \"replace\" a heaviweight tag if  \n>> I try\n>> to replace a tag by itself (or create a cycle by some other means).\n>\n> It is not a \"cycle\" (\"loop\"?) The tags information is the SHA1, not\n> the tag's name.\n>\n>> The new \"foo\" is unique in refs (OK), but it's *not unique* in objects\n>> (FAIL), as the old \"foo\" is referenced by the new \"foo\" and bears the  \n>> same\n>> tag name.\n>\n> Of course it is unique. Look at tag's SHA1.\n\nHi Alex,\n\nI'm sorry to say this is irrelevant. Please read my earlier message again,  \nand completely this time - you appear to have missed crucial parts, as  \nyour next paragraph suggests:\n\n>> #2: related: git tag -d cannot reliably delete tag objects\n>>\n>> Same here: if another tag object references the tag object I'm  \n>> deleting, we\n>> only delete the ref, but not the tag object. It doesn't (cannot) become\n>> dangling.\n>\n> As soon as an object is not referenced anymore by any reference  \n> (including references from refs/tags/), reference log or index it will  \n> be removed by\n> garbage collection (gc, prune) at the next opportunity.\n\nIrrelevant, because your assumption \"not referenced anymore\" is false.  \nThis was clearly written in my earlier message, which please see.\n\n-- \nMatthias Andree\n"},{"id":"113927","messageId":"81b0412b0905140616h69ac2919j26734f02455a5f5c@mail.gmail.com","threadId":"19341","inReplyTo":"op.utw7buoi1e62zd@balu","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2009-05-14T13:16:26Z","receivedAt":"2009-05-14T13:16:26Z","isPatch":false,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"2009/5/14 Matthias Andree <matthias.andree@gmx.de>:\n> Am 14.05.2009, 14:16 Uhr, schrieb Alex Riesen <raa.lkml@gmail.com>:\n>>\n>> As soon as an object is not referenced anymore by any reference (including\n>> references from refs/tags/), reference log or index it will be removed by\n>> garbage collection (gc, prune) at the next opportunity.\n>\n> Irrelevant, because your assumption \"not referenced anymore\" is false. This\n> was clearly written in my earlier message, which please see.\n\nYes, in your case it stays referenced, through the new tag which the reference\nrefs/tags/foo _now_ points to. Tough luck. In this particular case you could\njust remove the reference (that's refs/tags/foo now), repack and re-tag\n(properly).\n\nIt may be not what your wanted, but it is how it is expected to work. If git tag\nwould reduce its arguments down to commits, it would be impossible to sign\ntags at all (strictly speaking: it would be impossible to create a\ntag, referencing\nanother tag). Which is useful thing to have.\n"},{"id":"113928","messageId":"op.utw9khqa1e62zd@balu","threadId":"19341","inReplyTo":"81b0412b0905140616h69ac2919j26734f02455a5f5c@mail.gmail.com","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Matthias Andree","fromEmail":"matthias.andree@gmx.de","sentAt":"2009-05-14T13:39:43Z","receivedAt":"2009-05-14T13:39:43Z","isPatch":false,"sender":{"key":"matthias.andree@gmx.de","avatar":null},"body":"Am 14.05.2009, 15:16 Uhr, schrieb Alex Riesen <raa.lkml@gmail.com>:\n\n> It may be not what your wanted, but it is how it is expected to work. If  \n> git tag would reduce its arguments down to commits, it would be  \n> impossible to sign tags at all (strictly speaking: it would be  \n> impossible to create a\n> tag, referencing another tag). Which is useful thing to have.\n\nI'll kindly ask you again: please read my messages completely and  \ncarefully.\n\nI gave up the idea of reducing tag objects to referencing commits two  \nmessages ago. That was one simple, early, and insufficient suggestion of  \nmine to address the bug. It is no longer brought forward.\n\nThe bug itself (references to 'deleted' or 'replaced' tag objects remain  \nreachable rather than becoming dangling) is still there without a  \nsuggestion to the solution, and you're uselessly the bug.\n\nI may be wrong, but I do believe you still haven't understood my report -  \nthis may well be a problem in my way of phrasing it. So let's try this:\n\nIf you do not understand parts of the problem, please ask specific  \nquestions.\n\nIf you do not understand enough of this problem to ask such questions,  \nplease ignore this thread.\n\nPlease do not waste someone else's time by keeping up a discussion of  \narguments I've withdrawn hours ago.\nI'm not going to repeat earlier reasons either.\n\n-- \nMatthias Andree\n"},{"id":"113929","messageId":"fabb9a1e0905140642x26bf5e2ala604a36d0fe520a6@mail.gmail.com","threadId":"19341","inReplyTo":"op.utw9khqa1e62zd@balu","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2009-05-14T13:42:45Z","receivedAt":"2009-05-14T13:42:45Z","isPatch":false,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\nOn Thu, May 14, 2009 at 15:39, Matthias Andree <matthias.andree@gmx.de> wrote:\n> The bug itself (references to 'deleted' or 'replaced' tag objects remain\n> reachable rather than becoming dangling) is still there without a suggestion\n> to the solution, and you're uselessly the bug.\n\nI believe Alex is saying that this is not a bug, but intended\nbehavior, and Matthias is saying that we should change that behavior\nso that users are at least aware that they are creating such a\nsituation, is that correct?\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"113944","messageId":"op.utxlqej91e62zd@balu","threadId":"19341","inReplyTo":"fabb9a1e0905140642x26bf5e2ala604a36d0fe520a6@mail.gmail.com","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Matthias Andree","fromEmail":"matthias.andree@gmx.de","sentAt":"2009-05-14T18:02:28Z","receivedAt":"2009-05-14T18:02:28Z","isPatch":false,"sender":{"key":"matthias.andree@gmx.de","avatar":null},"body":"Am 14.05.2009, 15:42 Uhr, schrieb Sverre Rabbelier <srabbelier@gmail.com>:\n\n> Heya,\n>\n> On Thu, May 14, 2009 at 15:39, Matthias Andree <matthias.andree@gmx.de>  \n> wrote:\n>> The bug itself (references to 'deleted' or 'replaced' tag objects remain\n>> reachable rather than becoming dangling) is still there without a  \n>> suggestion\n>> to the solution, and you're uselessly the bug.\n>\n> I believe Alex is saying that this is not a bug, but intended\n> behavior, and Matthias is saying that we should change that behavior\n> so that users are at least aware that they are creating such a\n> situation, is that correct?\n\nI think my statements are:\n\n1- git tag -d and git tag -f do not work as advertised for tag objects (as\nopposed to lightweight tags); evidence in the longish mail\n\n2- I presume that the bug cannot be really fixed (signed tags created by\nsomebody else), we then have several solutions:\n  2a- warn the user and refuse\n  2b- warn the user and continue nonetheless\n  2c- warn the user and add options to force the user should at least be\nwarned that he may be doing something which doesn't work as intended, or\n  2d- give the user a possibility to force git to do stupid things.\n\n-- \nMatthias Andree\n"},{"id":"113946","messageId":"20090514182249.GA11919@sigill.intra.peff.net","threadId":"19341","inReplyTo":"op.utwyczlf1e62zd@merlin.emma.line.org","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-05-14T18:22:49Z","receivedAt":"2009-05-14T18:22:49Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, May 14, 2009 at 11:37:37AM +0200, Matthias Andree wrote:\n\n> HOWEVER, I see two problems here (yes, they are corner cases):\n>\n> #1: git tag -f (\"replace tag\") fails to \"replace\" a heaviweight tag if I  \n> try to replace a tag by itself (or create a cycle by some other means).\n>\n> The new \"foo\" is unique in refs (OK), but it's *not unique* in objects  \n> (FAIL), as the old \"foo\" is referenced by the new \"foo\" and bears the same \n> tag name.\n>\n> It screws the repo, breaking the uniqueness of tags. Basically, git tag -f \n> is implementing a half-baked, non-working \"rebase tag objects\"  \n> functionality.\n\nCan you explain how this \"screws the repo\"? The refs are unique, and in\nyour examples, a tag object replaced by \"git tag -f\" no longer has a ref\npointing to it (but in your example of making a tag of a tag, then of\ncourse the original is still reachable indirectly). The \"unique in\nobjects\" you refer to is that the tag itself says \"here is the name\nunder which I was tagged\". That name is purely informative and has\nnothing to do with ref lookup or reachability.\n\nIn your examples, I don't see any behavior that is causing actual\nproblems.\n\n> #2: related: git tag -d cannot reliably delete tag objects\n>\n> Same here: if another tag object references the tag object I'm deleting,  \n> we only delete the ref, but not the tag object. It doesn't (cannot) become \n> dangling.\n\nDeleting the ref makes it dangling, unless something else is referencing\nit. In your examples, since you tag the tag, the original tag is still\nreferenced.\n\n> $ git rev-list --objects --all\n> 4481a15d999b1b13066fe932e35ea05b8b1027a6\n> 72f3463f5a8089ac91001d458ceffb6d4e1056ee foo\n> 2e326d8a210536b7cd1f2bc77e3e29d7231f9ec4 foo\n> 995773fc9b649922936e110207e6abb904cc18e8\n> 15a9779d8f787428e57830410c7842e5449dfd33 a\n\nThe right-hand side of this output is a purely informative \"here is a\nname that may be useful for packing heuristics\". It has nothing to do\nwith the refs (for blobs, the pathname through which we reached the blob\nwill be printed -- obviously this is not going to be unique, as you will\nhave many versions of each file).\n\n> So what we get is (root/parents first, then children):\n>\n> objects:  4481a1 (commit) <- 2e326d (tag \"foo\") <- 72f346 (tag \"foo\")\n> refs:     heads/master                             tags/foo\n>\n> Whoops. \"foo\" is there twice, and it's referenced from a current ref.\n> We have *not* *replaced* it. *If* we did, we should have got:\n>\n> objects:  4481a1 (commit) <- 72f346 (tag \"foo\")\n> refs:     heads/master       tags/foo\n> with a dangling tag 2e326d\n\nRight. Because you didn't ask to replace it. You asked to tag the tag.\n\n> Hu, there's a nice cycle:\n>\n> 69bf (commit) <- 9756 ('old' tag1) <- 38ae (tag2) <- 8e7a (tag1)\n\nYou keep calling these cycles, but they're not (at least in terms of the\ngit graph). The fact that two distinct objects both contain the string\n\"tag tag1\" is not any more a cycle than two commit objects with the same\ncommit message. They are both distinct objects with distinct hashes, and\nthe hashes are how the git graph is built.\n\n> Now, more fun - watch the inconsistency:\n>\n> $ git tag -d tag1\n> Deleted tag 'tag1'\n> $ git tag -d tag1\n> error: tag 'tag1' not found.\n\nOK, so you deleted the ref tag1.\n\n> Ha! As if... now watch this:\n> $ git rev-list --objects  --all | while read a b ; do echo \"$a $(git  \n> cat-file -t $a) $b\" ; done\n> 69bf327c5d172fc8e4f63acf4d2e01c474824ce4 commit\n> 38aea56fec319d8c259a80157dde2432d2d09b2b tag tag2\n> 9756f6fa98a5cce2aab1f6a6e7dd4de515626e19 tag tag1\n> d758baa57a7ef20d44df0535bef1a91bb3dc4f62 tree\n> d3d8863b140f43f7c07050b9f2e210d41e73edb1 blob otherfile\n>\n> The tag object \"tag1\" is still there. WHOOPS!!!\n\nOf course the _old_ tag1 is still there. It is referenced by tag2, which\nstill has a ref. Again you are confusing the right-hand side of \"git\nrev-list --objects\" with actual ref names.\n\n> I appreciate that this isn't trivial to solve, but I presume anything that \n> walks the object database and uses tags can fail - including, but not  \n> limited to, git fast-export.\n\nI am not ruling out the possibility that there is some piece of code\nthat will be confused by the situation you have created, but it has\nnothing to do with graph walking. It would have to be a piece of code\nwhich cares about the uniqueness of informative names inside tag\nobjects.\n\n-Peff\n"},{"id":"113952","messageId":"qIGyi7O683pM7kzjmlY6QeiakFbPlBEHw9e9bG_SQhtXpvaqdek-Bw@cipher.nrlssc.navy.mil","threadId":"19341","inReplyTo":"op.utxlqej91e62zd@balu","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Brandon Casey","fromEmail":"casey@nrlssc.navy.mil","sentAt":"2009-05-14T19:01:40Z","receivedAt":"2009-05-14T19:01:40Z","isPatch":false,"sender":{"key":"drafnel@gmail.com","avatar":"https://avatars.githubusercontent.com/u/921167?v=4"},"body":"Matthias Andree wrote:\n> Am 14.05.2009, 15:42 Uhr, schrieb Sverre Rabbelier <srabbelier@gmail.com>:\n> \n>> Heya,\n>>\n>> On Thu, May 14, 2009 at 15:39, Matthias Andree\n>> <matthias.andree@gmx.de> wrote:\n>>> The bug itself (references to 'deleted' or 'replaced' tag objects remain\n>>> reachable rather than becoming dangling) is still there without a\n>>> suggestion\n>>> to the solution, and you're uselessly the bug.\n>>\n>> I believe Alex is saying that this is not a bug, but intended\n>> behavior, and Matthias is saying that we should change that behavior\n>> so that users are at least aware that they are creating such a\n>> situation, is that correct?\n> \n> I think my statements are:\n> \n> 1- git tag -d and git tag -f do not work as advertised for tag objects (as\n> opposed to lightweight tags); evidence in the longish mail\n\nBoth of these do indeed work.\n\nIn your examples 'git tag -d' worked as intended.  The tag was deleted even\nthough the object still remained in the object database.  The tag subcommand\ndoes not remove objects.  Objects which are not used anymore are removed by\nrunning the 'git gc' command.  This happens automatically periodically.  If\nyou want to see them disappear now, run 'git gc --prune=now'.\n\nIn your examples, 'git tag -f' worked as intended.  The \"object\" referenced\nby the command line arguments was tagged, and the existing tag was replaced\nby a new tag with the same name.\n\nSo when you do\n\n  $ git tag -f -m 'add tag foo' foo foo\n\nthe second foo is dereferenced, and it's object id is what is tagged.  So it\nis equivalent to the following:\n\n  $ git tag -f -m 'add tag foo' foo 2e326d8a210536b7cd1f2bc77e3e29d7231f9ec4\n\nThis object happens to be a tag object which points to a commit.\n\nYour graph:\n\n  objects:  4481a1 (commit) <- 2e326d (tag \"foo\") <- 72f346 (tag \"foo\")\n\nis perfectly correct and valid.  The middle tag object does not exist in\nthe tag namespace though.  Its name is embedded in the tag object and is\nnecessary for validating the tag object.\n\n> 2- I presume that the bug cannot be really fixed (signed tags created by\n> somebody else), we then have several solutions:\n>  2a- warn the user and refuse\n>  2b- warn the user and continue nonetheless\n>  2c- warn the user and add options to force the user should at least be\n> warned that he may be doing something which doesn't work as intended, or\n>  2d- give the user a possibility to force git to do stupid things.\n\nThere are no 'cycles', there is no inconsistency, there is no bug, except\nperhaps in git fast-export.\n\n-brandon\n"},{"id":"113970","messageId":"op.utxydvnu1e62zd@merlin.emma.line.org","threadId":"19341","inReplyTo":"20090514182249.GA11919@sigill.intra.peff.net","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Matthias Andree","fromEmail":"matthias.andree@gmx.de","sentAt":"2009-05-14T22:35:45Z","receivedAt":"2009-05-14T22:35:45Z","isPatch":false,"sender":{"key":"matthias.andree@gmx.de","avatar":null},"body":"Am 14.05.2009, 20:22 Uhr, schrieb Jeff King <peff@peff.net>:\n\n> On Thu, May 14, 2009 at 11:37:37AM +0200, Matthias Andree wrote:\n>\n>> HOWEVER, I see two problems here (yes, they are corner cases):\n>>\n>> #1: git tag -f (\"replace tag\") fails to \"replace\" a heaviweight tag if I\n>> try to replace a tag by itself (or create a cycle by some other means).\n>>\n>> The new \"foo\" is unique in refs (OK), but it's *not unique* in objects\n>> (FAIL), as the old \"foo\" is referenced by the new \"foo\" and bears the  \n>> same\n>> tag name.\n>>\n>> It screws the repo, breaking the uniqueness of tags. Basically, git tag  \n>> -f\n>> is implementing a half-baked, non-working \"rebase tag objects\"\n>> functionality.\n>\n> Can you explain how this \"screws the repo\"? The refs are unique, and in\n> your examples, a tag object replaced by \"git tag -f\" no longer has a ref\n> pointing to it (but in your example of making a tag of a tag, then of\n> course the original is still reachable indirectly). The \"unique in\n> objects\" you refer to is that the tag itself says \"here is the name\n> under which I was tagged\". That name is purely informative and has\n> nothing to do with ref lookup or reachability.\n>\n> In your examples, I don't see any behavior that is causing actual\n> problems.\n\nHi Jeff,\n\nso you, Alex and Brandon say git doesn't malfunction here. Hope you don't  \nmind my insisting, and to keep this concise, I'll send only one answer to  \nseveral posts (the whole discussion is too verbose already).\n\nI agree that the resulting object structure is still a directed acyclic  \ngraph, so I'll not criticize the object structure.\n\nThe semantic meaning however is missing. Let me take a different vantage  \nand look at the same situation, leaving the object graph aside.\n\nThis \"git tag -f -s same same\" operation gives me a signed nothing, and  \nthe ref indeed no longer points to the old tag.  But what's the new  \nsignature or tag good for?  I tagged & signed an object that was removed  \nin the process.  I have a tagged and signed nothing. (Yes, there is an  \nunderlying object, but it takes lots of fiddling with the LL tools to get  \nat it.)\n\n> Deleting the ref makes it dangling, unless something else is referencing\n> it. In your examples, since you tag the tag, the original tag is still\n> referenced.\n\nThat's what I see, yes. But again, what good is the signed nothing?\n\n> Of course the _old_ tag1 is still there. It is referenced by tag2, which\n> still has a ref. Again you are confusing the right-hand side of \"git\n> rev-list --objects\" with actual ref names.\n\nIndeed it's not git show-ref...\n\n> I am not ruling out the possibility that there is some piece of code\n> that will be confused by the situation you have created, but it has\n> nothing to do with graph walking. It would have to be a piece of code\n> which cares about the uniqueness of informative names inside tag\n> objects.\n\nThat's true, and apparently git fast-export is one of those pieces of code.\n\nThanks\n\n-- \nMatthias Andree\n"},{"id":"113983","messageId":"20090515020206.GA12451@coredump.intra.peff.net","threadId":"19341","inReplyTo":"op.utxydvnu1e62zd@merlin.emma.line.org","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-05-15T02:02:06Z","receivedAt":"2009-05-15T02:02:06Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, May 15, 2009 at 12:35:45AM +0200, Matthias Andree wrote:\n\n> The semantic meaning however is missing. Let me take a different vantage  \n> and look at the same situation, leaving the object graph aside.\n>\n> This \"git tag -f -s same same\" operation gives me a signed nothing, and  \n> the ref indeed no longer points to the old tag.\n\n\nIt doesn't give you a signed nothing. It gives you a signed tag pointing\nto the old signed tag (which in turn could point to another tag, a\ncommit, etc).\n\nAnd yes, the ref no longer points to the old tag. That has nothing to do\nwith what you are pointing the tag out, but the fact that you are\n_overwriting_ the old tag. And we already have a safety valve there: you\nmust specify \"-f\" to overwrite an existing tag.\n\n> But what's the new signature or tag good for?\n\nTagging a tag is good for saying something about the original tag, as\nopposed to saying something about the commit that the original tag\npoints to.\n\n> I tagged & signed an object that was removed  in the process.  I have\n> a tagged and signed nothing. (Yes, there is an  underlying object, but\n> it takes lots of fiddling with the LL tools to get  at it.)\n\nThe object wasn't removed, any more than making a new commit that\nadvances a branch head removes the old history. Git objects form a DAG,\nand refs are named objects in the DAG. But we only consider objects\n\"removed\" when they are not reachable from any ref (and I say \"removed\"\nbecause we call this \"dangling\", and then actually remove them after\nthey have been dangling for a period of time, assuming that nobody is\ninterested in them any longer). And it is not the case here that the\nresult is dangling.\n\nI wouldn't be surprised if working with tags of tags is less convenient,\nbecause people aren't using them as often as tags of commits, and there\nmay be some unexercised corner cases. But you can certainly see them via\n\"git show\":\n\n  $ git init && echo content > file && git add . && git commit -m one\n  $ git tag -m two tag1\n  $ git tag -m three tag2 tag1\n  $ git show tag2\n\nshould show something like:\n\n  tag tag2\n  Tagger: Jeff King <peff@peff.net>\n  Date:   Thu May 14 21:06:09 2009 -0400\n\n\n  three\n  tag tag1\n  Tagger: Jeff King <peff@peff.net>\n  Date:   Thu May 14 21:05:44 2009 -0400\n\n\n  two\n  commit 395553770322cbfc8326eb6edc9c3ab73334c541\n  Author: Jeff King <peff@peff.net>\n  Date:   Thu May 14 21:05:27 2009 -0400\n\n      one\n\n  diff...\n\nArguably the spacing could be more readable (one less blank line between\nthe tag header and its message, and an extra space between the message\nand the start of the next header), but I wouldn't call the tag\ninaccessible or removed.\n\nAnd you can repeat the same exercise with\n\n  $ git tag -f -m four tag2 tag2\n\nwhich will show you the chain of tag2 -> old_tag2 -> tag1 -> commit.\n\n>> Of course the _old_ tag1 is still there. It is referenced by tag2, which\n>> still has a ref. Again you are confusing the right-hand side of \"git\n>> rev-list --objects\" with actual ref names.\n>\n> Indeed it's not git show-ref...\n\nRight. No ref points to it any more. But that doesn't mean it's not\nthere. Just like older commits have no ref pointing to them, and we have\nto give them names based on the refs we do have, like HEAD~5. There is\nnot a shorthand syntax for saying \"peel exactly N layers of this tag\nchain\", but I think that is because nobody has really needed one at this\npoint.\n\n>> I am not ruling out the possibility that there is some piece of code\n>> that will be confused by the situation you have created, but it has\n>> nothing to do with graph walking. It would have to be a piece of code\n>> which cares about the uniqueness of informative names inside tag\n>> objects.\n>\n> That's true, and apparently git fast-export is one of those pieces of code.\n\nSorry, I thought from your last email (\"I presume anything that walks\nthe object database and uses tags can fail\") that you were just\nspeculating on fast-export mishandling.\n\nGoing back to your original message, though, it looks like you do have a\nspecific problem with fast-export. However, I think it has nothing to do\nwith a _renamed_ tag, but in general of tags pointing to tags. The\noutermost tag ends up pointing to a bogus mark of \":0\".\n\nI would have thought that would be dealt with by 198246, but I can\nreplicate the problem even on the current 'next'. I think we just need\nto be setting a mark for tag objects. The patch below makes your\nfast-export example better, but it still looks like we end up printing\nout one of the tag objects twice.\n\n---\ndiff --git a/builtin-fast-export.c b/builtin-fast-export.c\nindex 6731713..2349c8d 100644\n--- a/builtin-fast-export.c\n+++ b/builtin-fast-export.c\n@@ -293,6 +293,9 @@ static void handle_tag(const char *name, struct tag *tag)\n \tbuf = read_sha1_file(tag->object.sha1, &type, &size);\n \tif (!buf)\n \t\tdie (\"Could not read tag %s\", sha1_to_hex(tag->object.sha1));\n+\n+\tmark_next_object(&tag->object);\n+\n \tmessage = memmem(buf, size, \"\\n\\n\", 2);\n \tif (message) {\n \t\tmessage += 2;\n@@ -335,8 +338,8 @@ static void handle_tag(const char *name, struct tag *tag)\n \n \tif (!prefixcmp(name, \"refs/tags/\"))\n \t\tname += 10;\n-\tprintf(\"tag %s\\nfrom :%d\\n%.*s%sdata %d\\n%.*s\\n\",\n-\t       name, get_object_mark(tag->tagged),\n+\tprintf(\"tag %s\\nmark :%\"PRIu32\"\\nfrom :%d\\n%.*s%sdata %d\\n%.*s\\n\",\n+\t       name, last_idnum, get_object_mark(tag->tagged),\n \t       (int)(tagger_end - tagger), tagger,\n \t       tagger == tagger_end ? \"\" : \"\\n\",\n \t       (int)message_size, (int)message_size, message ? message : \"\");\n"},{"id":"113993","messageId":"op.uty0pjb51e62zd@balu","threadId":"19341","inReplyTo":"20090515020206.GA12451@coredump.intra.peff.net","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Matthias Andree","fromEmail":"matthias.andree@gmx.de","sentAt":"2009-05-15T12:23:33Z","receivedAt":"2009-05-15T12:23:33Z","isPatch":false,"sender":{"key":"matthias.andree@gmx.de","avatar":null},"body":"Am 15.05.2009, 04:02 Uhr, schrieb Jeff King <peff@peff.net>:\n\n>> But what's the new signature or tag good for?\n>\n> Tagging a tag is good for saying something about the original tag, as\n> opposed to saying something about the commit that the original tag\n> points to.\n\nYes, I agree to that since Junio's first reply.\n\nClear reminder up front: this thread is *not* about tagging tagA with  \nanother tagB (I'll see if git fast-export has issues with that and perhaps  \nconcoct a test script), but this thread *is* about replacing tagA with  \nitself.\n\nThis raises semantic and hence usability concerns.\n\nSo let's shift object relations aside for a while, no need to discuss what  \nwe agree about.\n\nLet's narrow down the discussion to signed tag objects (git tag -s/git tag  \n-u GPG-ID). They are a bit different as there's some extended *meaning*  \nthat lies in the signature. I have no trouble with this. A <--signed-by--  \nB is implemented by \"git tag -s B A.\"\n\nYour example is:\n\n\tcommit <--signed-by-- tag1 <--signed-by-- tag2.\n\nTag2 is useful in an \"approved by me, too\" meaning or similar. Point taken.\n\nIf I do \"git tag -f -s -m three tag1 tag1\" (as opposed to... tag2 tag1),  \nthen I'll have trouble seeing or explaning the meaning or use cases of the  \nresult:\n\n\tcommit <-- signed-by-- NIL (removed) <--signed-by-- tag1.\n\nIn this particular corner case (replacing a heaviweight tag object with  \nitself), tag1 has no meaning, because the tag \"destination\" gets deleted.  \nIt's as though your luggage trolley disappeared the very moment that you  \nmoved your address label from one handle to the other (presuming it has  \none side handle to carry it and one top handle to haul it while on its  \nwheels). That's just useless. Why do I want to sign the phantom?\n\nFor this particular corner case, \"git tag -f tag tag\" (where I really use  \nthe same tag name twice) could warn along the lines\n\n\"Warning: you are trying to replace a tag to [old reference] by a tag to  \nitself.\nHowever, [old reference] will be removed as per your request,  \nconsequentially, the new tag will reference a deleted [type of old  \nreference].\nIf you just want to relocate the tag and want the new tag to point to the  \noriginal [type , use git tag -f tagname tagname^{}.\nIf you really want to create a tag of a tag, add another -f.\"\n\nSimilarly, git tag -d could complain if the tag I'm removing is a tag  \nobject and has children (usually other tags, I believe).\n\n> Arguably the spacing could be more readable (one less blank line between\n> the tag header and its message, and an extra space between the message\n> and the start of the next header), but I wouldn't call the tag\n> inaccessible or removed.\n\nI'd describe the current output as irritating.\n\n> And you can repeat the same exercise with\n>\n>   $ git tag -f -m four tag2 tag2\n>\n> which will show you the chain of tag2 -> old_tag2 -> tag1 -> commit.\n\nThat's the object tree, but not the semantic meaning: semantically,  \nold_tag2 was supposed to be removed/replaced (tag -d/-f respectively). But  \nit has to remain in place for syntactic reasons, i. e. to guarantee the  \nobject graph (= syntax) integrity.\nAnd that's what is confusing.\n\n> Right. No ref points to it any more. But that doesn't mean it's not\n> there. Just like older commits have no ref pointing to them, and we have\n> to give them names based on the refs we do have, like HEAD~5. There is\n> not a shorthand syntax for saying \"peel exactly N layers of this tag\n> chain\", but I think that is because nobody has really needed one at this\n> point.\n\nThat's plausible.\n\n> Going back to your original message, though, it looks like you do have a\n> specific problem with fast-export. However, I think it has nothing to do\n> with a _renamed_ tag, but in general of tags pointing to tags. The\n> outermost tag ends up pointing to a bogus mark of \":0\".\n>\n> I would have thought that would be dealt with by 198246, but I can\n> replicate the problem even on the current 'next'. I think we just need\n> to be setting a mark for tag objects. The patch below makes your\n> fast-export example better, but it still looks like we end up printing\n> out one of the tag objects twice.\n\nIf fast-import knows how to handle that (by forcibly moving the tag), that  \nmight work.\n\nThanks for the patch, I'll try it tomorrow and see what I get.\n\nAlso thanks for keeping the discussion constructive, rather than fetching  \nthe \"troll\" punch and dismiss. :-)\n\n> ---\n> diff --git a/builtin-fast-export.c b/builtin-fast-export.c\n> index 6731713..2349c8d 100644\n> --- a/builtin-fast-export.c\n> +++ b/builtin-fast-export.c\n> @@ -293,6 +293,9 @@ static void handle_tag(const char *name, struct tag  \n> *tag)\n>  \tbuf = read_sha1_file(tag->object.sha1, &type, &size);\n>  \tif (!buf)\n>  \t\tdie (\"Could not read tag %s\", sha1_to_hex(tag->object.sha1));\n> +\n> +\tmark_next_object(&tag->object);\n> +\n>  \tmessage = memmem(buf, size, \"\\n\\n\", 2);\n>  \tif (message) {\n>  \t\tmessage += 2;\n> @@ -335,8 +338,8 @@ static void handle_tag(const char *name, struct tag  \n> *tag)\n> \tif (!prefixcmp(name, \"refs/tags/\"))\n>  \t\tname += 10;\n> -\tprintf(\"tag %s\\nfrom :%d\\n%.*s%sdata %d\\n%.*s\\n\",\n> -\t       name, get_object_mark(tag->tagged),\n> +\tprintf(\"tag %s\\nmark :%\"PRIu32\"\\nfrom :%d\\n%.*s%sdata %d\\n%.*s\\n\",\n> +\t       name, last_idnum, get_object_mark(tag->tagged),\n>  \t       (int)(tagger_end - tagger), tagger,\n>  \t       tagger == tagger_end ? \"\" : \"\\n\",\n>  \t       (int)message_size, (int)message_size, message ? message : \"\");\n\n\n\n-- \nMatthias Andree\n"},{"id":"113994","messageId":"m34ovmlcve.fsf@localhost.localdomain","threadId":"19341","inReplyTo":"op.uty0pjb51e62zd@balu","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2009-05-15T13:22:37Z","receivedAt":"2009-05-15T13:22:37Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"\"Matthias Andree\" <matthias.andree@gmx.de> writes:\n> Am 15.05.2009, 04:02 Uhr, schrieb Jeff King <peff@peff.net>: \n\n>>> But what's the new signature or tag good for?\n>>\n>> Tagging a tag is good for saying something about the original tag, as\n>> opposed to saying something about the commit that the original tag\n>> points to.\n> \n> Yes, I agree to that since Junio's first reply.\n> \n> Clear reminder up front: this thread is *not* about tagging tagA with\n> another tagB (I'll see if git fast-export has issues with that and\n> perhaps  concoct a test script), but this thread *is* about replacing\n> tagA with  itself.\n> \n> This raises semantic and hence usability concerns.\n> \n> So let's shift object relations aside for a while, no need to discuss\n> what  we agree about.\n> \n> Let's narrow down the discussion to signed tag objects (git tag -s/git\n> tag  -u GPG-ID). They are a bit different as there's some extended\n> *meaning*  that lies in the signature. I have no trouble with this. A\n> <--signed-by--\n> B is implemented by \"git tag -s B A.\"\n> \n> Your example is:\n> \n> \tcommit <--signed-by-- tag1 <--signed-by-- tag2.\n> \n> Tag2 is useful in an \"approved by me, too\" meaning or similar. Point taken.\n> \n> If I do \"git tag -f -s -m three tag1 tag1\" (as opposed to... tag2\n> tag1),  then I'll have trouble seeing or explaning the meaning or use\n> cases of the  result:\n> \n> \tcommit <-- signed-by-- NIL (removed) <--signed-by-- tag1.\n\n[...]\n> For this particular corner case, \"git tag -f tag tag\" (where I really\n> use the same tag name twice) could warn along the lines [...]\n\n[cut]\n\nTHIS IS A FEATURE, NOT A BUG.\n\n\nPlease note that the name of tag (heavyweight tag, i.e. tag object)\nis stored in two places: in the tag object itself as a contents of\n'tag' header (you can see it in output of \"git show <tag>\" and also\nin output of \"git cat-file -p <tag>\", where <tag> is heavyweight tag,\ne.g. v1.6.3 in git.git repository), and also is default name of tag\nreference (reference in \"refs/tags/*\" namespace) pointing to a tag\nobject.\n\nSo when you create signed tag 'A', you have the following situation\n(assuming that it points at some commit)\n\n  35805ce   <--- 5b7b4ead  <=== refs/tags/A\n  (commit)       tag A\n                 (tag)\n  \nPlease also note that \"git tag -f A A\" (notice the absence of options\nforcing it to be an annotated tag) is a noop - it doesn't change the\nsituation.\n\nIf you do \"git tag -f -s A A\": note that you _force_ owerwriting a tag\n(so git assumes that you know what you are doing), and that one of \n-s / -a / -m options is used to force annotated tag (creation of tag\nobject), you will get the following situation\n\n  35805ce   <--- 5b7b4ea  <--- ada8ddc  <=== refs/tags/A\n  (commit)       tag A         tag A\n                 (tag)         (tag)\n\nWhat is unclear about this situation? How would you want to change it:\nforce user to use 'git tag -f -f' (I really know what I am doing)?\n\nNote also that \"git show A\" would show the whole chain down to the\nnon-tag object...\n\nNote also that the tag _reference_ (appropriate reference in the\n\"refs/tags/*\" namespace) is purely _local_ matter; what one repository\nhas in 'refs/tags/v0.1.3', other can have in 'refs/tags/sub/v0.1.3'\nfor example.\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"113995","messageId":"4A0D8211.5010806@viscovery.net","threadId":"19341","inReplyTo":"m34ovmlcve.fsf@localhost.localdomain","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2009-05-15T14:54:09Z","receivedAt":"2009-05-15T14:54:09Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Jakub Narebski schrieb:\n> \"Matthias Andree\" <matthias.andree@gmx.de> writes:\n>> \tcommit <-- signed-by-- NIL (removed) <--signed-by-- tag1.\n> \n> THIS IS A FEATURE, NOT A BUG.\n\nPlease stop it. Everone agrees about this.\n\nMatthias only wants a patch like below. Matthias, if you are serious about\nit, please pick this up and turn it into a proper submission. I don't care\nenough.\n\n-- Hannes\n\n\ndiff --git a/builtin-tag.c b/builtin-tag.c\nindex 01e7374..35d39a2 100644\n--- a/builtin-tag.c\n+++ b/builtin-tag.c\n@@ -367,6 +367,7 @@ int cmd_tag(int argc, const char **argv, const char\n*prefix)\n \tunsigned char object[20], prev[20];\n \tchar ref[PATH_MAX];\n \tconst char *object_ref, *tag;\n+\tstruct tag *tag_object;\n \tstruct ref_lock *lock;\n\n \tint annotate = 0, sign = 0, force = 0, lines = -1,\n@@ -472,6 +473,15 @@ int cmd_tag(int argc, const char **argv, const char\n \telse if (!force)\n \t\tdie(\"tag '%s' already exists\", tag);\n\n+\tif ((tag_object = (struct tag *)parse_object(object)) &&\n+\t    tag_object->object.type == OBJ_TAG &&\n+\t    tag_object->tag &&\n+\t    !strcmp(tag_object->tag, tag)) {\n+\t\terror(\"A tag cannot tag itself. If you meant to tag the commit\");\n+\t\terror(\"that the tag refers to, use 'git tag %s %s^{}'.\", tag, object_ref);\n+\t\texit(1);\n+\t}\n+\n \tif (annotate)\n \t\tcreate_tag(object, tag, &buf, msg.given || msgfile,\n \t\t\t   sign, prev, object);\n"},{"id":"113997","messageId":"81b0412b0905150851q232b3f6s95df89e72d4dc381@mail.gmail.com","threadId":"19341","inReplyTo":"4A0D8211.5010806@viscovery.net","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2009-05-15T15:51:09Z","receivedAt":"2009-05-15T15:51:09Z","isPatch":false,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"2009/5/15 Johannes Sixt <j.sixt@viscovery.net>:\n> Jakub Narebski schrieb:\n>> \"Matthias Andree\" <matthias.andree@gmx.de> writes:\n>>>      commit <-- signed-by-- NIL (removed) <--signed-by-- tag1.\n>>\n>> THIS IS A FEATURE, NOT A BUG.\n>\n> Please stop it. Everone agrees about this.\n>\n> Matthias only wants a patch like below. Matthias, if you are serious about\n> it, please pick this up and turn it into a proper submission. I don't care\n> enough.\n>\n...\n> +       if ((tag_object = (struct tag *)parse_object(object)) &&\n> +           tag_object->object.type == OBJ_TAG &&\n> +           tag_object->tag &&\n> +           !strcmp(tag_object->tag, tag)) {\n> +               error(\"A tag cannot tag itself. If you meant to tag the commit\");\n\nIf it ever turned into submission, I'll always patch this out. It is stupid.\n"},{"id":"113998","messageId":"guk3i3$dri$1@ger.gmane.org","threadId":"19341","inReplyTo":"op.utwyczlf1e62zd@merlin.emma.line.org","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Daniel Cheng","fromEmail":"j16sdiz+freenet@gmail.com","sentAt":"2009-05-15T16:00:02Z","receivedAt":"2009-05-15T16:00:02Z","isPatch":false,"sender":{"key":"j16sdiz+freenet@gmail.com","avatar":"https://gravatar.com/avatar/3e796e8a156ee86e305bfc1fbe01608302554ee3b5fa7eb8d1213b8877bfcabd?d=mp&s=160"},"body":"On 14/5/2009 17:37, Matthias Andree wrote:\n[...]\n> #2: related: git tag -d cannot reliably delete tag objects\n>\n> Same here: if another tag object references the tag object I'm deleting,\n> we only delete the ref, but not the tag object.\n\n> It doesn't (cannot)  become dangling.\n\nThis show you have no idea how git works internally.\n\n[...]\n"},{"id":"114000","messageId":"op.utzbdtb91e62zd@merlin.emma.line.org","threadId":"19341","inReplyTo":"81b0412b0905150851q232b3f6s95df89e72d4dc381@mail.gmail.com","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Matthias Andree","fromEmail":"matthias.andree@gmx.de","sentAt":"2009-05-15T16:14:07Z","receivedAt":"2009-05-15T16:14:07Z","isPatch":false,"sender":{"key":"matthias.andree@gmx.de","avatar":null},"body":"Am 15.05.2009, 17:51 Uhr, schrieb Alex Riesen <raa.lkml@gmail.com>:\n\n> 2009/5/15 Johannes Sixt <j.sixt@viscovery.net>:\n>> Jakub Narebski schrieb:\n>>> \"Matthias Andree\" <matthias.andree@gmx.de> writes:\n>>>>      commit <-- signed-by-- NIL (removed) <--signed-by-- tag1.\n>>>\n>>> THIS IS A FEATURE, NOT A BUG.\n>>\n>> Please stop it. Everone agrees about this.\n>>\n>> Matthias only wants a patch like below. Matthias, if you are serious  \n>> about it, please pick this up and turn it into a proper submission. I  \n>> don't care enough.\n>>\n> ...\n>> +       if ((tag_object = (struct tag *)parse_object(object)) &&\n>> +           tag_object->object.type == OBJ_TAG &&\n>> +           tag_object->tag &&\n>> +           !strcmp(tag_object->tag, tag)) {\n>> +               error(\"A tag cannot tag itself. If you meant to tag the  \n>> commit\");\n>\n> If it ever turned into submission, I'll always patch this out. It is  \n> stupid.\n\nI seem to lack intermediate messages, probably queued somewhere, yet I'll  \nrespond already.\n\nMoving a tag on top of itself is just stupid. The result of git -f doesn't  \nproperly match documentation IMO. There is no clear consensus if it's  \n\"gone\". It's gone from the refs/ namespace, but kept in the object space,  \nso there's a split meaning of \"replace\" or \"delete\" here.\n\nArguably, we already need to say -f once, but nothing prevents me from  \nusing git tag -d first and then tag the dangling old_tag1 object to revive  \nit.\n\nNobody has shown valid reasons of existence for such tags, or valid  \nsemantics, or use cases.\n\nIt's confusing => usability problem => let's put a warning there. I'm not  \nsure if \"error()\" is the right function to call here, since I don't have  \nthe full patch to look at.\n\nAt any rate, a policy of obstruction is as invalid as anything.\n\n-- \nMatthias Andree\n"},{"id":"114001","messageId":"4A0D9696.1040805@op5.se","threadId":"19341","inReplyTo":"81b0412b0905150851q232b3f6s95df89e72d4dc381@mail.gmail.com","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2009-05-15T16:21:42Z","receivedAt":"2009-05-15T16:21:42Z","isPatch":false,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Alex Riesen wrote:\n> 2009/5/15 Johannes Sixt <j.sixt@viscovery.net>:\n>> Jakub Narebski schrieb:\n>>> \"Matthias Andree\" <matthias.andree@gmx.de> writes:\n>>>>      commit <-- signed-by-- NIL (removed) <--signed-by-- tag1.\n>>> THIS IS A FEATURE, NOT A BUG.\n>> Please stop it. Everone agrees about this.\n>>\n>> Matthias only wants a patch like below. Matthias, if you are serious about\n>> it, please pick this up and turn it into a proper submission. I don't care\n>> enough.\n>>\n> ...\n>> +       if ((tag_object = (struct tag *)parse_object(object)) &&\n>> +           tag_object->object.type == OBJ_TAG &&\n>> +           tag_object->tag &&\n>> +           !strcmp(tag_object->tag, tag)) {\n>> +               error(\"A tag cannot tag itself. If you meant to tag the commit\");\n> \n> If it ever turned into submission, I'll always patch this out. It is stupid.\n\nIs it? Does it really make sense to have a tag named \"foo\" point to a tag object\nthat in turn points to a tag object without a tag ref? I mean, if you're signing\na tag, it makes sense to want to keep the original tag around so people can\nreference it. If you want to *replace* a tag, it doesn't make sense to create\nthis chain which, iiuc, goes something like this:\n\n   tag ref -> tag object -> tag object without ref -> something\n\nHonestly, I can see how this turned out to be confusing, as you end up with a\ntag object without a tag, but a new tag in its place. Not to mention that the\nnew tag won't be push-able without --force in case the old tag was pushed earlier.\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n\nRegister now for Nordic Meet on Nagios, June 3-4 in Stockholm\n http://nordicmeetonnagios.op5.org/\n\nConsidering the successes of the wars on alcohol, poverty, drugs and\nterror, I think we should give some serious thought to declaring war\non peace.\n"},{"id":"114004","messageId":"7v3ab6uuw4.fsf@alter.siamese.dyndns.org","threadId":"19341","inReplyTo":"4A0D9696.1040805@op5.se","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-15T17:40:43Z","receivedAt":"2009-05-15T17:40:43Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andreas Ericsson <ae@op5.se> writes:\n\n> Is it? Does it really make sense to have a tag named \"foo\" point to a tag object\n> that in turn points to a tag object without a tag ref? I mean, if you're signing\n> a tag, it makes sense to want to keep the original tag around so people can\n> reference it. If you want to *replace* a tag, it doesn't make sense to create\n> this chain which, iiuc, goes something like this:\n>\n>   tag ref -> tag object -> tag object without ref -> something\n>\n> Honestly, I can see how this turned out to be confusing, as you end up with a\n> tag object without a tag, but a new tag in its place. Not to mention that the\n> new tag won't be push-able without --force in case the old tag was pushed earlier.\n\nSuppose the gpg key used to sign v1.6.3 somehow gets compromised, and I\ncome up with a new gpg key.  I could reassure people that the commit the\nold v1.6.3 tagged is genuine if I re-tag with the new key like this:\n\n\tgit tag -f v1.6.3 v1.6.3^{commit}\n\nBut what should I do if I would want to reassure people that both the old\nv1.6.3 was tagged by _me_ (with the old key that later was compromised)\nand that the commit that old tag tags is genuine?\n"},{"id":"114073","messageId":"20090516050736.GB7330@sigio.peff.net","threadId":"19341","inReplyTo":"op.uty0pjb51e62zd@balu","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-05-16T05:07:36Z","receivedAt":"2009-05-16T05:07:36Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, May 15, 2009 at 02:23:33PM +0200, Matthias Andree wrote:\n\n> Clear reminder up front: this thread is *not* about tagging tagA with  \n> another tagB (I'll see if git fast-export has issues with that and perhaps \n> concoct a test script), but this thread *is* about replacing tagA with  \n> itself.\n\nI'm pretty sure git fast-export does have a problem just with tags of\ntags; that was what I was looking at fixing with the patch in my\nprevious mail.\n\n> If I do \"git tag -f -s -m three tag1 tag1\" (as opposed to... tag2 tag1),  \n> then I'll have trouble seeing or explaning the meaning or use cases of the \n> result:\n>\n> \tcommit <-- signed-by-- NIL (removed) <--signed-by-- tag1.\n\nI think this is where we diverge. Removing a ref to an object (in my\nmental model) doesn't make it \"NIL\" or \"removed\". It simply does not\nhave a direct name anymore.\n\nNow, you _will_ have problems rewriting your tags all of the time,\nbecause (IIRC) git does not fetch tags that it already has. So anybody\nfetching from you will not ever see the new tag. But that is a problem\nwith _any_ tag overwriting, not just overwriting with a tag chain. And\nthat is why such overwriting is protected by \"-f\".\n\n> In this particular corner case (replacing a heaviweight tag object with  \n> itself), tag1 has no meaning, because the tag \"destination\" gets deleted.  \n> It's as though your luggage trolley disappeared the very moment that you  \n> moved your address label from one handle to the other (presuming it has  \n> one side handle to carry it and one top handle to haul it while on its  \n> wheels). That's just useless. Why do I want to sign the phantom?\n\nAgain, I disagree that it is a phantom. It is now a link in a chain of\nsignatures. The head of the chain has a name, but that doesn't mean\nevery link needs to.\n\nSo I think the reason that you are seeing \"there is no bug, it is a\nfeature\" responses from git people is that we have a different mental\nmodel of what is going on. I don't know whether that means your model is\n_wrong_, or simply _different_.\n\n> For this particular corner case, \"git tag -f tag tag\" (where I really use  \n> the same tag name twice) could warn along the lines\n>\n> \"Warning: you are trying to replace a tag to [old reference] by a tag to  \n> itself.\n> However, [old reference] will be removed as per your request,  \n> consequentially, the new tag will reference a deleted [type of old  \n> reference].\n> If you just want to relocate the tag and want the new tag to point to the  \n> original [type , use git tag -f tagname tagname^{}.\n> If you really want to create a tag of a tag, add another -f.\"\n\nI am torn on whether such a warning is a good idea. Yes, it can prevent\na user from shooting themselves in the foot. But it also is something\nyou may legitimately want to be doing, and having git yell at you is a\nbad thing (and I actually do disagree with some of the wording, but I\nthink the real point in contention is whether this should be flagged as\na dangerous and potentially wrong operation).\n\nYour original problem seemed to come from thinking that \"git tag -f tag\ntag\" would \"redo\" the tagging (when what you really wanted was \"git tag\n-f tag tag^{commit}\"). I wonder if it would be helpful to provide a more\nobvious and constructive way of doing that, like\n\n  git tag --amend tag\n\nThe benefits would be:\n\n  - it is a more natural way of saying what you want to do, and it\n    mirrors the \"git commit --amend\" command\n\n  - it could also populate the editor with the original tag message,\n    which is probably useful if you are trying to fix up any issues.\n\nThe obvious drawback to me is that it might encourage rewriting commits,\nwhich is something that it is not really sane to do after they have been\npublished (of course, we already encourage rewriting _commits_ via\n--amend, which have similar if somewhat lesser problems).\n\nAnd of course it doesn't actually _solve_ the problem you mentioned, it\nmerely makes it more likely that the user will see our handy safe\nversion and not use the error-prone \"git tag -f\" version. So maybe that\nis good enough and maybe not.\n\n> Similarly, git tag -d could complain if the tag I'm removing is a tag  \n> object and has children (usually other tags, I believe).\n\nI'm not sure what you mean here. Why would removing a tag to a tag to a\ncommit be any more or less dangerous than removing a tag to a commit?\n\n>> Arguably the spacing could be more readable (one less blank line between\n>> the tag header and its message, and an extra space between the message\n>> and the start of the next header), but I wouldn't call the tag\n>> inaccessible or removed.\n>\n> I'd describe the current output as irritating.\n\nAgreed, though it really has nothing to do with the issue at hand. Even\na tag to a commit looks ugly (try, e.g., \"git show v1.6.2.1\" in the git\nrepo -- extra space before the tag text, no space between the PGP\nsignature and the commit).\n\n>> And you can repeat the same exercise with\n>>\n>>   $ git tag -f -m four tag2 tag2\n>>\n>> which will show you the chain of tag2 -> old_tag2 -> tag1 -> commit.\n>\n> That's the object tree, but not the semantic meaning: semantically,  \n> old_tag2 was supposed to be removed/replaced (tag -d/-f respectively). But \n> it has to remain in place for syntactic reasons, i. e. to guarantee the  \n> object graph (= syntax) integrity.\n> And that's what is confusing.\n\nAgain, I think \"semantic meaning\" here is in the eye of the beholder.\nThe intended semantics are not \"make tag2 a tag to tag2\" but\n\"dereference what is currently named tag2, and make tag2 a tag to that\".\nSo what happens makes sense under that mental model.\n\n>> I would have thought that would be dealt with by 198246, but I can\n>> replicate the problem even on the current 'next'. I think we just need\n>> to be setting a mark for tag objects. The patch below makes your\n>> fast-export example better, but it still looks like we end up printing\n>> out one of the tag objects twice.\n>\n> If fast-import knows how to handle that (by forcibly moving the tag), that \n> might work.\n\nI don't think it needs to forcibly move the tag. The fast-export output\ncontains two \"tag\" stanzas, but those are only about creating the tag\nobjects themselves. The actual ref is set only once with lines like:\n\n  reset refs/tags/foo\n  from :2\n\nAt least that is my quick reading of the output; I admit I am not much\nof a fast-export/import person, so there may be something subtle I am\nmissing.\n\n> Also thanks for keeping the discussion constructive, rather than fetching  \n> the \"troll\" punch and dismiss. :-)\n\nWell, I _do_ like a good flamefest... ;)\n\n-Peff\n"},{"id":"114077","messageId":"4A0E67E9.3020208@op5.se","threadId":"19341","inReplyTo":"7v3ab6uuw4.fsf@alter.siamese.dyndns.org","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2009-05-16T07:14:49Z","receivedAt":"2009-05-16T07:14:49Z","isPatch":false,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Junio C Hamano wrote:\n> Andreas Ericsson <ae@op5.se> writes:\n> \n>> Is it? Does it really make sense to have a tag named \"foo\" point to a tag object\n>> that in turn points to a tag object without a tag ref? I mean, if you're signing\n>> a tag, it makes sense to want to keep the original tag around so people can\n>> reference it. If you want to *replace* a tag, it doesn't make sense to create\n>> this chain which, iiuc, goes something like this:\n>>\n>>   tag ref -> tag object -> tag object without ref -> something\n>>\n>> Honestly, I can see how this turned out to be confusing, as you end up with a\n>> tag object without a tag, but a new tag in its place. Not to mention that the\n>> new tag won't be push-able without --force in case the old tag was pushed earlier.\n> \n> Suppose the gpg key used to sign v1.6.3 somehow gets compromised, and I\n> come up with a new gpg key.  I could reassure people that the commit the\n> old v1.6.3 tagged is genuine if I re-tag with the new key like this:\n> \n> \tgit tag -f v1.6.3 v1.6.3^{commit}\n> \n> But what should I do if I would want to reassure people that both the old\n> v1.6.3 was tagged by _me_ (with the old key that later was compromised)\n> and that the commit that old tag tags is genuine?\n> \n\nAdd a tag with a new name, pointing to the original tag. Try doing what\nMatthias did and then run \"git show $tagname\". It won't show the original\ntag at all, so people have to resort to low-level commands in order to\nsee it, but it will still exist as an object.\n\nThe main point though is that re-creating a ref with different content\nadds major headaches when distributing it. People who have the old tag\nand fetches from a new repo won't get the new tag stored in a ref. If\nthe object is transferred, it will be garbage-collected.\n\nSo let's examine the scenario you described, with your gpg key being\ncompromised.\n\nYou re-create all your tag refs with same-name tags that point to the\nold tags.\nJoe Dev fetches from you, but your tags do not get stored as refs.\nJoe Dev publishes a repo somewhere with a bunch of topic-branches and\nrequests you merge from those repositories.\nYou fetch from Joe.\n\nNow we have two opposite problems.\nIf tags aren't updated when Joe Dev fetches from you, his refs will\nnot match yours when you fetch from him, and anyone cloning from him\neven after the re-sign will never get the new tags at all.\nIf tag refs *do* get updated when fetching from a repo when we already\nhave another tag ref with the same name, you fetching from Joe Dev\ncould undo all your re-created tags and make the new tag-objects\ngarbage-collectable. This assumes Joe Dev published his repo before\nfetching your new tags though.\n\nPerhaps I'm missing something. It's 9AM here and I woke up ten minutes\nago, but it seems to me that what will happen and what should happen\nis not entirely clear when one creates tag refs that already exist and\nare published.\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n\nRegister now for Nordic Meet on Nagios, June 3-4 in Stockholm\n http://nordicmeetonnagios.op5.org/\n\nConsidering the successes of the wars on alcohol, poverty, drugs and\nterror, I think we should give some serious thought to declaring war\non peace.\n"},{"id":"114080","messageId":"200905160956.27417.jnareb@gmail.com","threadId":"19341","inReplyTo":"4A0E67E9.3020208@op5.se","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2009-05-16T07:56:24Z","receivedAt":"2009-05-16T07:56:24Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Sat, 16 May 2009, Andreas Ericsson wrote:\n\n> Add a tag with a new name, pointing to the original tag. Try doing what\n> Matthias did and then run \"git show $tagname\". It won't show the original\n> tag at all, so people have to resort to low-level commands in order to\n> see it, but it will still exist as an object.\n\nNot true. Did you check that?\n\n\"git show <tag>\" shows the whole chain of objects down to non-tag object.\n\n  tag tag1\n  Tagger: Jakub Narebski <jnareb@gmail.com>\n  Date:   Sat May 16 09:55:13 2009 +0200\n\n\n  tag1 (retagged)\n  tag tag1\n  Tagger: Jakub Narebski <jnareb@gmail.com>\n  Date:   Sat May 16 09:54:58 2009 +0200\n  \n  \n  tag1\n  commit 6d3eee4f5e9fde51f3213320b98bda5f325000e4\n  [...]\n\nTrue, the separation between objects could have been made more explicit...\n\n-- \nJakub Narebski\nPoland\n"},{"id":"114081","messageId":"4A0E7333.3020104@op5.se","threadId":"19341","inReplyTo":"200905160956.27417.jnareb@gmail.com","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2009-05-16T08:02:59Z","receivedAt":"2009-05-16T08:02:59Z","isPatch":false,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Jakub Narebski wrote:\n> On Sat, 16 May 2009, Andreas Ericsson wrote:\n> \n>> Add a tag with a new name, pointing to the original tag. Try doing what\n>> Matthias did and then run \"git show $tagname\". It won't show the original\n>> tag at all, so people have to resort to low-level commands in order to\n>> see it, but it will still exist as an object.\n> \n> Not true. Did you check that?\n> \n\nYes, but with light-weight tags on a repo after cvs import. Mea culpa, so\nthis is a non-issue. Thanks for politely pointing out my error :-)\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n\nRegister now for Nordic Meet on Nagios, June 3-4 in Stockholm\n http://nordicmeetonnagios.op5.org/\n\nConsidering the successes of the wars on alcohol, poverty, drugs and\nterror, I think we should give some serious thought to declaring war\non peace.\n"},{"id":"114104","messageId":"7vtz3lnf1x.fsf@alter.siamese.dyndns.org","threadId":"19341","inReplyTo":"4A0E67E9.3020208@op5.se","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-16T17:16:58Z","receivedAt":"2009-05-16T17:16:58Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andreas Ericsson <ae@op5.se> writes:\n\n> You re-create all your tag refs with same-name tags that point to the\n> old tags.\n> Joe Dev fetches from you, but your tags do not get stored as refs.\n> Joe Dev publishes a repo somewhere with a bunch of topic-branches and\n> requests you merge from those repositories.\n> You fetch from Joe.\n>\n> Now we have two opposite problems.\n> If tags aren't updated when Joe Dev fetches from you, his refs will\n> not match yours when you fetch from him, and anyone cloning from him\n> even after the re-sign will never get the new tags at all.\n> If tag refs *do* get updated when fetching from a repo when we already\n> have another tag ref with the same name, you fetching from Joe Dev\n> could undo all your re-created tags and make the new tag-objects\n> garbage-collectable. This assumes Joe Dev published his repo before\n> fetching your new tags though.\n>\n> Perhaps I'm missing something....\n\nNo, you did not miss anything.  It illustrates the problem space very\nwell.\n\nThere are two \"names\" to a tag.  The name of a tag is recorded in the\nobject itself, in its \"tag\" header.\n\n        $ git cat-file tag v1.6.3\n        object f01f1099f40f24fe6f7802185340a6fa3a3d4f35\n        type commit\n        tag v1.6.3\n        tagger Junio C Hamano <gitster@pobox.com> 1241659007 -0700\n\n        GIT 1.6.3\n        -----BEGIN PGP SIGNATURE-----\n        Version: GnuPG v1.4.9 (GNU/Linux)\n        ...\n\nAnybody can forge, even down to the trailing signature part with a\ncompromised key, such a tag object that claims to be \"tag v1.6.3\" and\npoint anything with it.\n\nBut the \"name\" you usually access a tag object with is not this one.\nInstead, you use the name of the ref in refs/tags hierarchy.  These two\nnames are supposed to match by convention, and I think recent fsck even\nchecks it.\n\nUnlike the name recorded in tag objects, you can have only one v1.6.3 in\nyour ref namespace.  And the way git protects users from maliciously made\ntags has been by not re-fetching what already exist unless explicitly\nasked.  The consequence of this is that you somehow obtained a forged or\nvulnerable one first, you will not get corrected ones automatically.\n\nBut that is a feature in the current set-up.\n\nIf the key that signs release tags were compromised and the tags got\nre-signed with a new key (whether I re-tag by pointing at the commit\nobjects, or pointing at the old-genuine tag objects), that fact needs to\nbe advertised (\"Sorry, but I had to re-tag; if you have old tags, please\nre-fetch\"), and the user who is currently protected by this \"no automatic\nre-fetching\" mechanism has to somehow assert that \"Sorry\" is really from\nme, and allow git to re-fetch.\n\nThe workflow for a such case would be:\n\n (0) I notice the signing key was somehow compromised; roll a new key,\n     re-sign the tags, and send out a \"I had to re-tag, and here is a list\n     of the old and new tag object names you can use to verify\" message;\n\n (1) You read such a message,  You do \"git for-each-ref refs/tags\" to see\n     the object names to check with my message, and realize that you have\n     stale tags.  So does Joe Dev but he may be slower to react;\n\n (2) You fetch (or ls-remote) from Joe Dev which is your preferrerd mirror\n     of my tree and notice he hasn't updated, and let him know.  In the\n     meantime you fetch \"git fetch --tags\" from me, and verify the result\n     against my message.\n\n (3) Joe Dev would do the same.\n\nThat's largely manual, cumbersome, and makes everybody involved painfully\naware of what is going on, which may be an advantage over silently\nupdating with a new tag without telling anybody.\n\nBut you can improve the situation without losing security by doing\nsomething like this.\n\n * Introduce a concept of \"trusted signing keys\" (similar to the way\n   distros sign their binary packages), whose fingerprints are probably\n   stored in .git/config of the receiving repositories;\n\n * Upon 'git tag -v <name>', verify that the signature was made with one\n   of the trusted signing keys;\n\n * Inside 'git fetch':\n\n   - before starting to fetch, see if there are signed tags that exist\n     locally but not signed with any of the trusted keys;\n\n   - for the signed tags we find in the above step, if the remote end has\n     different tag object at the same refname, ask for them;\n\n   - perform the main 'git fetch' transfer and store things according to\n     the refspec as usual (but do not store the tags re-fetched only\n     because of the new logic yet);\n\n   - for the tags re-fetched with the new logic, see if they are signed by\n     trusted keys, and if so replace the stale tags with them.\n\nThe step (0) to issue a \"Sorry but I had to re-tag\" message with \"here is\nthe fingerprint of new signing key\" is still necessary, and you need to\nreact to it by replacing the old trusted signing key with the fingerprint\nof the new key, but after that everything can be made automatic.\n"},{"id":"114253","messageId":"op.ut6ciwjl1e62zd@balu.cs.uni-paderborn.de","threadId":"19341","inReplyTo":"7vtz3lnf1x.fsf@alter.siamese.dyndns.org","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Matthias Andree","fromEmail":"matthias.andree@gmx.de","sentAt":"2009-05-19T11:21:58Z","receivedAt":"2009-05-19T11:21:58Z","isPatch":false,"sender":{"key":"matthias.andree@gmx.de","avatar":null},"body":"Am 16.05.2009, 19:16 Uhr, schrieb Junio C Hamano <gitster@pobox.com>:\n\n\n> The workflow for a such case would be:\n>\n>  (0) I notice the signing key was somehow compromised; roll a new key,\n>      re-sign the tags, and send out a \"I had to re-tag, and here is a  \n> list\n>      of the old and new tag object names you can use to verify\" message;\n>\n>  (1) You read such a message,  You do \"git for-each-ref refs/tags\" to see\n>      the object names to check with my message, and realize that you have\n>      stale tags.  So does Joe Dev but he may be slower to react;\n>\n>  (2) You fetch (or ls-remote) from Joe Dev which is your preferrerd  \n> mirror\n>      of my tree and notice he hasn't updated, and let him know.  In the\n>      meantime you fetch \"git fetch --tags\" from me, and verify the result\n>      against my message.\n>\n>  (3) Joe Dev would do the same.\n>\n> That's largely manual, cumbersome, and makes everybody involved painfully\n> aware of what is going on, which may be an advantage over silently\n> updating with a new tag without telling anybody.\n>\n> But you can improve the situation without losing security by doing\n> something like this.\n\nLet's do things step by step and fix the current issue - and I fear there  \nwon't be an easy technical solution, so let's amend to the documentation  \nfor the nonce.\n\nOK, what I was trying to do is rewrite history to fix up some b0rked  \ninternal addresses. That's a repository for a mostly frozen project, which  \nis more a reference point than a basis for development. I had to recreate  \nthe few tag signatures they were, and hence I used \"git tag -f\" without  \nthinking too much. I had seen the section on re-tagging, and am aware of  \nit, but it somehow didn't apply to my situation.\n\nI think we ought\n\n(1) to fix the git tag -h output and manual page for consistency, and\n\n(2) to add a note to make users aware that they can also tag tags (the  \n[<object>] in SYNOPSIS may not be hint enough, as Git seems to differ  \nsubstantially from other SCM systems in this respect - so this is a  \nusability concern that deserves documentation).\n\nI'll suggest something, but that can take a couple of days.\n\nWhat else can we tag in Git? Commits and Tags.  Is it sensible and does it  \nwork to tag blobs or trees?\n\n-- \nMatthias Andree\n"},{"id":"114254","messageId":"20090519112914.GA21386@coredump.intra.peff.net","threadId":"19341","inReplyTo":"op.ut6ciwjl1e62zd@balu.cs.uni-paderborn.de","subject":"Re: git-tag bug? confusing git fast-export with double tag objects","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-05-19T11:29:14Z","receivedAt":"2009-05-19T11:29:14Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, May 19, 2009 at 01:21:58PM +0200, Matthias Andree wrote:\n\n> What else can we tag in Git? Commits and Tags.  Is it sensible and does it \n> work to tag blobs or trees?\n\nA tagged blob:\n\n  $ cd git && git show junio-gpg-pub | sed '/^--/Q'\n  tag junio-gpg-pub\n  Tagger: Junio C Hamano <junkio@cox.net>\n  Date:   Tue Dec 13 16:33:29 2005 -0800\n\n\n  GPG key to sign git.git archive.\n\n  This blob object contains the GPG public key I use to sign git.git\n  archive.\n\n  To use it to verify tags I signed, do:\n\n    $ git-cat-file blob junio-gpg-pub | gpg --import\n\n  to import it into your keyring, and then\n\n    $ git-verify-tag $tag_to_be_verified\n\nA tagged tree:\n\n  $ cd linux-2.6 && git show v2.6.11 | sed '/^--/Q'\n  tag v2.6.11-tree\n\n  This is the 2.6.11 tree object.\n\n  NOTE! There's no commit for this, since it happened before I started with git.\n  Eventually we'll import some sort of history, and that should tie this tree\n  object up to a real commit. In the meantime, this acts as an anchor point for\n  doing diffs etc under git.\n\n-Peff\n"}]}