{"thread":{"id":"48353","subject":"Fetching tags overwrites existing tags","startedAt":"2018-04-24T19:57:42Z","lastAt":"2018-08-31T22:02:31Z","messageCount":101,"participants":["Wink Saville","Jacob Keller","Junio C Hamano","Bryan Turner","Ævar Arnfjörð Bjarmason","Kaartic Sivaraam","SZEDER Gábor"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"345673","messageId":"CAKk8isrAmWOdioJe2CdY+2F_AzKxcTGwd+kBsrAmGx-U=ZHGgA@mail.gmail.com","threadId":"48353","inReplyTo":null,"subject":"Fetching tags overwrites existing tags","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-04-24T19:57:16Z","receivedAt":"2018-04-24T19:57:42Z","isPatch":false,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"If have a repository with a tag \"v1.0.0\" and I add a remote repository\nwhich also has a tag \"v1.0.0\" tag is overwritten.\n\nGoogle found [1] from 2011 and option 3 is what I'd like to see. Has it been\nimplemented and I just don't see it?\n\n[1]: https://groups.google.com/forum/#!topic/git-version-control/0l_rJFyTE60\n\n\nHere is an example demonstrating what I see:\n\n$ echo abc > abc.txt\n$ git init .\nInitialized empty Git repository in\n/home/wink/prgs/git/investigate-fetch-tags/.git/\n$ git add *\n$ git commit -m \"Initial commit\"\n[master (root-commit) 1116fdc] Initial commit\n 1 file changed, 1 insertion(+)\n create mode 100644 abc.txt\n$ git tag v1.0.0\n$ git remote add gbenchmark git@github.com:google/benchmark\n$ git log --graph --format=\"%h %s %d\"\n* 1116fdc Initial commit  (HEAD -> master, tag: v1.0.0)\n$ git fetch --tags gbenchmark\nwarning: no common commits\nremote: Counting objects: 4400, done.\nremote: Compressing objects: 100% (15/15), done.\nremote: Total 4400 (delta 5), reused 5 (delta 3), pack-reused 4382\nReceiving objects: 100% (4400/4400), 1.33 MiB | 2.81 MiB/s, done.\nResolving deltas: 100% (2863/2863), done.\nFrom github.com:google/benchmark\n * [new branch]      clangtidy       -> gbenchmark/clangtidy\n * [new branch]      iter_report     -> gbenchmark/iter_report\n * [new branch]      master          -> gbenchmark/master\n * [new branch]      releasing       -> gbenchmark/releasing\n * [new branch]      reportercleanup -> gbenchmark/reportercleanup\n * [new branch]      rmheaders       -> gbenchmark/rmheaders\n * [new branch]      v2              -> gbenchmark/v2\n * [new tag]         v0.0.9          -> v0.0.9\n * [new tag]         v0.1.0          -> v0.1.0\n t [tag update]      v1.0.0          -> v1.0.0\n * [new tag]         v1.1.0          -> v1.1.0\n * [new tag]         v1.2.0          -> v1.2.0\n * [new tag]         v1.3.0          -> v1.3.0\n * [new tag]         v1.4.0          -> v1.4.0\n$ git log --graph --format=\"%h %s %d\"\n* 1116fdc Initial commit  (HEAD -> master)\n\nAs you can see the tag on 1116fdc is gone, v1.0.0 tag has been updated\nand now its pointing to the tag in gbenchmark:\n\n$ git log -5 --graph --format=\"%h %s %d\" v1.0.0\n*   cd525ae Merge pull request #171 from eliben/update-doc-userealtime\n (tag: v1.0.0)\n|\\\n| * c7ab1b9 Update README to mention UseRealTime for wallclock time\nmeasurements.\n|/\n* f662e8b Rename OS_MACOSX macro to new name BENCHMARK_OS_MACOSX. Fix #169\n*   0a1f484 Merge pull request #166 from disconnect3d/master\n|\\\n| * d2917bc Fixes #165: CustomArguments ret type in README\n|/\n\nIdeally I would have liked the tags fetched from gbenchmark to have a prefix\nof gbenchmark/, like the branches have, maybe something like:\n\n$ git fetch --tags gbenchmark\n...\n * [new branch]      v2              -> gbenchmark/v2\n * [new tag]         v0.0.9          -> gbenchmark/v0.0.9\n * [new tag]         v0.1.0          -> gbenchmark/v0.1.0\n * [new tag]         v1.0.0          -> gbenchmark/v1.0.0\n * [new tag]         v1.1.0          -> gbenchmark/v1.1.0\n * [new tag]         v1.2.0          -> gbenchmark/v1.2.0\n * [new tag]         v1.3.0          -> gbenchmark/v1.3.0\n * [new tag]         v1.4.0          -> gbenchmark/v1.4.0\n\n\n-- Wink\n"},{"id":"345715","messageId":"CA+P7+xrciFXjufQM5KfjQjWnMsvs_9KoAFPPHSSU7VjLPuTDqg@mail.gmail.com","threadId":"48353","inReplyTo":"CAKk8isrAmWOdioJe2CdY+2F_AzKxcTGwd+kBsrAmGx-U=ZHGgA@mail.gmail.com","subject":"Re: Fetching tags overwrites existing tags","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2018-04-24T23:48:30Z","receivedAt":"2018-04-24T23:48:55Z","isPatch":false,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Apr 24, 2018 at 12:57 PM, Wink Saville <wink@saville.com> wrote:\n> If have a repository with a tag \"v1.0.0\" and I add a remote repository\n> which also has a tag \"v1.0.0\" tag is overwritten.\n>\n> Google found [1] from 2011 and option 3 is what I'd like to see. Has it been\n> implemented and I just don't see it?\n>\n> [1]: https://groups.google.com/forum/#!topic/git-version-control/0l_rJFyTE60\n>\n>\n> Here is an example demonstrating what I see:\n>\n> $ echo abc > abc.txt\n> $ git init .\n> Initialized empty Git repository in\n> /home/wink/prgs/git/investigate-fetch-tags/.git/\n> $ git add *\n> $ git commit -m \"Initial commit\"\n> [master (root-commit) 1116fdc] Initial commit\n>  1 file changed, 1 insertion(+)\n>  create mode 100644 abc.txt\n> $ git tag v1.0.0\n> $ git remote add gbenchmark git@github.com:google/benchmark\n> $ git log --graph --format=\"%h %s %d\"\n> * 1116fdc Initial commit  (HEAD -> master, tag: v1.0.0)\n> $ git fetch --tags gbenchmark\n> warning: no common commits\n> remote: Counting objects: 4400, done.\n> remote: Compressing objects: 100% (15/15), done.\n> remote: Total 4400 (delta 5), reused 5 (delta 3), pack-reused 4382\n> Receiving objects: 100% (4400/4400), 1.33 MiB | 2.81 MiB/s, done.\n> Resolving deltas: 100% (2863/2863), done.\n> From github.com:google/benchmark\n>  * [new branch]      clangtidy       -> gbenchmark/clangtidy\n>  * [new branch]      iter_report     -> gbenchmark/iter_report\n>  * [new branch]      master          -> gbenchmark/master\n>  * [new branch]      releasing       -> gbenchmark/releasing\n>  * [new branch]      reportercleanup -> gbenchmark/reportercleanup\n>  * [new branch]      rmheaders       -> gbenchmark/rmheaders\n>  * [new branch]      v2              -> gbenchmark/v2\n>  * [new tag]         v0.0.9          -> v0.0.9\n>  * [new tag]         v0.1.0          -> v0.1.0\n>  t [tag update]      v1.0.0          -> v1.0.0\n>  * [new tag]         v1.1.0          -> v1.1.0\n>  * [new tag]         v1.2.0          -> v1.2.0\n>  * [new tag]         v1.3.0          -> v1.3.0\n>  * [new tag]         v1.4.0          -> v1.4.0\n> $ git log --graph --format=\"%h %s %d\"\n> * 1116fdc Initial commit  (HEAD -> master)\n>\n> As you can see the tag on 1116fdc is gone, v1.0.0 tag has been updated\n> and now its pointing to the tag in gbenchmark:\n>\n> $ git log -5 --graph --format=\"%h %s %d\" v1.0.0\n> *   cd525ae Merge pull request #171 from eliben/update-doc-userealtime\n>  (tag: v1.0.0)\n> |\\\n> | * c7ab1b9 Update README to mention UseRealTime for wallclock time\n> measurements.\n> |/\n> * f662e8b Rename OS_MACOSX macro to new name BENCHMARK_OS_MACOSX. Fix #169\n> *   0a1f484 Merge pull request #166 from disconnect3d/master\n> |\\\n> | * d2917bc Fixes #165: CustomArguments ret type in README\n> |/\n>\n> Ideally I would have liked the tags fetched from gbenchmark to have a prefix\n> of gbenchmark/, like the branches have, maybe something like:\n>\n\nThat would require a complete redesign of how we handle remotes. I've\nproposed ideas in the past but never had time and they didn't gain\nmuch traction.\n\nIt's a known limitation that the tags namespace can only hold a single\ntag name (even if remotes differ in what that tag is). I *thought*\nthat the tags should not be updated after you fetch it once, but it\nseems this is not the behavior we get now?\n\nMy basic idea was to fetch *all* remote refs into a\nrefs/<remotes-bikeshed>/<remote-name>/* such that *every* ref in a\nremote can be determined by something like\n\"refs/tracking/origin/tags/name\" instead of\n\"refs/remotes/origin/name\", and then tags would have to be updated to\ncheck for tags in each remote as well as locally. Additionally, you\ncould update the tool to warn when two remotes have the same tag at\ndifferent refs, and allow disambiguation.\n\nIdeally, \"origin/branch\" should still DWIM, same for \"tag\" should work\nunless there are conflicts.\n\nUnfortunately, it's a pretty big change in how remotes are handled,\nand I never had time to actually work towards a POC or implementation.\nMostly, we ended up on bikeshedding what the name should be now that\nwe can't use \"refs/remotes\" due to backwards compatibility. I don't\nreally like \"tracking\" as a name, but it was the best I could come up\nwith.\n\n(Note, the impetus for this proposal was actually to allow easy\nsharing of notes and other specialized refs).\n\nThanks,\nJake\n\n> $ git fetch --tags gbenchmark\n> ...\n>  * [new branch]      v2              -> gbenchmark/v2\n>  * [new tag]         v0.0.9          -> gbenchmark/v0.0.9\n>  * [new tag]         v0.1.0          -> gbenchmark/v0.1.0\n>  * [new tag]         v1.0.0          -> gbenchmark/v1.0.0\n>  * [new tag]         v1.1.0          -> gbenchmark/v1.1.0\n>  * [new tag]         v1.2.0          -> gbenchmark/v1.2.0\n>  * [new tag]         v1.3.0          -> gbenchmark/v1.3.0\n>  * [new tag]         v1.4.0          -> gbenchmark/v1.4.0\n>\n>\n> -- Wink\n"},{"id":"345724","messageId":"xmqqzi1s3y5h.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"CAKk8isrAmWOdioJe2CdY+2F_AzKxcTGwd+kBsrAmGx-U=ZHGgA@mail.gmail.com","subject":"Re: Fetching tags overwrites existing tags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-04-25T00:52:42Z","receivedAt":"2018-04-25T00:52:48Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Wink Saville <wink@saville.com> writes:\n\n> Ideally I would have liked the tags fetched from gbenchmark to have a prefix\n> of gbenchmark/, like the branches have, maybe something like:\n>\n> $ git fetch --tags gbenchmark\n> ...\n>  * [new branch]      v2              -> gbenchmark/v2\n>  * [new tag]         v0.0.9          -> gbenchmark/v0.0.9\n>  * [new tag]         v0.1.0          -> gbenchmark/v0.1.0\n>  * [new tag]         v1.0.0          -> gbenchmark/v1.0.0\n>  * [new tag]         v1.1.0          -> gbenchmark/v1.1.0\n>  * [new tag]         v1.2.0          -> gbenchmark/v1.2.0\n>  * [new tag]         v1.3.0          -> gbenchmark/v1.3.0\n>  * [new tag]         v1.4.0          -> gbenchmark/v1.4.0\n\nThe tag namespace (refs/tags/) is considered a shared resource (I am\nnot saying that that is the only valid world model---I am merely\nexplaining why things are like they are), hence the auto-following\ntags will bring them to refs/tags/ (and I do not think there is no\nway to configure auto-following to place them elsewhere).\n\nBut you could configure things yourself.\n\n    $ git init victim && cd victim\n    $ git remote add origin ../git.git\n    $ git config --add remote.origin.fetch \\\n      \"+refs/tags/*:refs/remote-tags/origin/*\"\n    $ tail -n 4 .git/config\n    [remote \"origin\"]\n\turl = ../git.git/\n\tfetch = +refs/heads/*:refs/remotes/origin/*\n\tfetch = +refs/tags/*:refs/remote-tags/origin/*\n    $ git fetch --no-tags\n\nThe \"--no-tags\" option serves to decline the auto-following tags to\nrefs/tags/ hierarchy; once your repository is configured this way,\nyour initial and subsequent \"git fetch\" will copy refs it finds in\nrefs/tags/ hierarchy over there to your refs/remote-tags/origin/\nhierarchy locally.\n"},{"id":"345727","messageId":"CA+P7+xr3cdLB+s+6A81Y9WzZUYupSECjQ1bAOKTHsUJjQQi90A@mail.gmail.com","threadId":"48353","inReplyTo":"xmqqzi1s3y5h.fsf@gitster-ct.c.googlers.com","subject":"Re: Fetching tags overwrites existing tags","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2018-04-25T01:29:50Z","receivedAt":"2018-04-25T01:30:14Z","isPatch":false,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Apr 24, 2018 at 5:52 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Wink Saville <wink@saville.com> writes:\n>\n>> Ideally I would have liked the tags fetched from gbenchmark to have a prefix\n>> of gbenchmark/, like the branches have, maybe something like:\n>>\n>> $ git fetch --tags gbenchmark\n>> ...\n>>  * [new branch]      v2              -> gbenchmark/v2\n>>  * [new tag]         v0.0.9          -> gbenchmark/v0.0.9\n>>  * [new tag]         v0.1.0          -> gbenchmark/v0.1.0\n>>  * [new tag]         v1.0.0          -> gbenchmark/v1.0.0\n>>  * [new tag]         v1.1.0          -> gbenchmark/v1.1.0\n>>  * [new tag]         v1.2.0          -> gbenchmark/v1.2.0\n>>  * [new tag]         v1.3.0          -> gbenchmark/v1.3.0\n>>  * [new tag]         v1.4.0          -> gbenchmark/v1.4.0\n>\n> The tag namespace (refs/tags/) is considered a shared resource (I am\n> not saying that that is the only valid world model---I am merely\n> explaining why things are like they are), hence the auto-following\n> tags will bring them to refs/tags/ (and I do not think there is no\n> way to configure auto-following to place them elsewhere).\n>\n> But you could configure things yourself.\n>\n>     $ git init victim && cd victim\n>     $ git remote add origin ../git.git\n>     $ git config --add remote.origin.fetch \\\n>       \"+refs/tags/*:refs/remote-tags/origin/*\"\n>     $ tail -n 4 .git/config\n>     [remote \"origin\"]\n>         url = ../git.git/\n>         fetch = +refs/heads/*:refs/remotes/origin/*\n>         fetch = +refs/tags/*:refs/remote-tags/origin/*\n>     $ git fetch --no-tags\n>\n> The \"--no-tags\" option serves to decline the auto-following tags to\n> refs/tags/ hierarchy; once your repository is configured this way,\n> your initial and subsequent \"git fetch\" will copy refs it finds in\n> refs/tags/ hierarchy over there to your refs/remote-tags/origin/\n> hierarchy locally.\n\nIt should be noted, that remote-tags would not be integrated into \"git\ntag\" or many other places in git commands, so it may be significantly\nless visible.\n\nThanks,\nJake\n"},{"id":"345728","messageId":"CAKk8isr-7bSVqEv8EjF2UgFfw75D2oVZPJFui30pbY6kWfeL_Q@mail.gmail.com","threadId":"48353","inReplyTo":"xmqqzi1s3y5h.fsf@gitster-ct.c.googlers.com","subject":"Re: Fetching tags overwrites existing tags","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-04-25T01:31:28Z","receivedAt":"2018-04-25T01:31:53Z","isPatch":false,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"On Tue, Apr 24, 2018 at 5:52 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Wink Saville <wink@saville.com> writes:\n>\n>> Ideally I would have liked the tags fetched from gbenchmark to have a prefix\n>> of gbenchmark/, like the branches have, maybe something like:\n>>\n>> $ git fetch --tags gbenchmark\n>> ...\n>>  * [new branch]      v2              -> gbenchmark/v2\n>>  * [new tag]         v0.0.9          -> gbenchmark/v0.0.9\n>>  * [new tag]         v0.1.0          -> gbenchmark/v0.1.0\n>>  * [new tag]         v1.0.0          -> gbenchmark/v1.0.0\n>>  * [new tag]         v1.1.0          -> gbenchmark/v1.1.0\n>>  * [new tag]         v1.2.0          -> gbenchmark/v1.2.0\n>>  * [new tag]         v1.3.0          -> gbenchmark/v1.3.0\n>>  * [new tag]         v1.4.0          -> gbenchmark/v1.4.0\n>\n> The tag namespace (refs/tags/) is considered a shared resource (I am\n> not saying that that is the only valid world model---I am merely\n> explaining why things are like they are), hence the auto-following\n> tags will bring them to refs/tags/ (and I do not think there is no\n> way to configure auto-following to place them elsewhere).\n>\n> But you could configure things yourself.\n>\n>     $ git init victim && cd victim\n>     $ git remote add origin ../git.git\n>     $ git config --add remote.origin.fetch \\\n>       \"+refs/tags/*:refs/remote-tags/origin/*\"\n>     $ tail -n 4 .git/config\n>     [remote \"origin\"]\n>         url = ../git.git/\n>         fetch = +refs/heads/*:refs/remotes/origin/*\n>         fetch = +refs/tags/*:refs/remote-tags/origin/*\n>     $ git fetch --no-tags\n>\n> The \"--no-tags\" option serves to decline the auto-following tags to\n> refs/tags/ hierarchy; once your repository is configured this way,\n> your initial and subsequent \"git fetch\" will copy refs it finds in\n> refs/tags/ hierarchy over there to your refs/remote-tags/origin/\n> hierarchy locally.\n\nInteresting that kinda works, what about teaching git-remote to\nunderstand a \"--prefix-tags\" option which would create the\n\"fetch = refs/tags/*:refs/remote-tags/origin\" entry. And teach\ngit-fetch to use that entry if it exists and not require the \"--no-tags\"?\n\nOf course I'm sure there are \"lots\" of other things to change, doing\na search for \"remotes/\" gives the following:\n\n$ find . -type f -name '*.c' | xargs grep '\\bremotes/' | sort -uk1,1\n./builtin/branch.c: fmt = \"refs/remotes/%s\";\n./builtin/checkout.c: skip_prefix(argv0, \"remotes/\", &argv0);\n./builtin/clone.c: strbuf_addf(&branch_top, \"refs/remotes/%s/\", option_origin);\n./builtin/describe.c:     !skip_prefix(path, \"refs/remotes/\", &path_to_match)) {\n./builtin/fast-export.c: \"refs/remotes/\",\n./builtin/fetch.c: else if (starts_with(rm->name, \"refs/remotes/\")) {\n./builtin/merge.c: if (starts_with(found_ref, \"refs/remotes/\")) {\n./builtin/pull.c: * refs/heads/<branch_name> to\nrefs/remotes/<remote_name>/<branch_name>.\n./builtin/remote.c: strbuf_addf(tmp, \"refs/heads/%s:refs/remotes/%s/%s\",\n./builtin/rev-parse.c: handle_ref_opt(arg, \"refs/remotes/\");\n./builtin/show-branch.c: if (!starts_with(refname, \"refs/remotes/\"))\n./contrib/examples/builtin-fetch--tool.c: else if\n(!strncmp(remote_name, \"refs/remotes/\", 13)) {\n./help.c: if (skip_prefix(refname, \"refs/remotes/\", &remote) &&\n./log-tree.c: else if (starts_with(refname, \"refs/remotes/\"))\n./ref-filter.c:        skip_prefix(refname, \"refs/remotes/\", &refname) ||\n./refs.c: return refs_for_each_ref_in(refs, \"refs/remotes/\", fn, cb_data);\n./remote.c: FILE *f = fopen_or_warn(git_path(\"remotes/%s\", remote->name), \"r\");\n./revision.c: for_each_glob_ref_in(handle_one_ref, optarg,\n\"refs/remotes/\", &cb);\n./sha1_name.c:     starts_with(refname, \"refs/remotes/\"))\n./wt-status.c: skip_prefix(from, \"refs/remotes/\", &from);\n\n-- wink\n"},{"id":"345912","messageId":"CAKk8ispWBoG7D+u8x+s+iB+wzXUN=7vpxgmz5wjL67P6XUmw9g@mail.gmail.com","threadId":"48353","inReplyTo":"CAKk8isr-7bSVqEv8EjF2UgFfw75D2oVZPJFui30pbY6kWfeL_Q@mail.gmail.com","subject":"Re: Fetching tags overwrites existing tags","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-04-26T19:39:06Z","receivedAt":"2018-04-26T19:39:33Z","isPatch":false,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"I've tried to teach 'git remote add' the --prefix-tags option using the\ntechnique Junio provided. At moment it is PR #486 on github [1]\nand I'd love some comments on whether or not this the right direction\nfor fetching tags and putting them in the branches namespace.\n\n-- Wink\n\n[1] https://github.com/git/git/pull/486\n"},{"id":"345920","messageId":"xmqqfu3h1t22.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"CAKk8ispWBoG7D+u8x+s+iB+wzXUN=7vpxgmz5wjL67P6XUmw9g@mail.gmail.com","subject":"Re: Fetching tags overwrites existing tags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-04-26T22:50:13Z","receivedAt":"2018-04-26T22:50:21Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Wink Saville <wink@saville.com> writes:\n\n> I've tried to teach 'git remote add' the --prefix-tags option using the\n> technique Junio provided. At moment it is PR #486 on github [1]\n> and I'd love some comments on whether or not this the right direction\n> for fetching tags and putting them in the branches namespace.\n>\n> -- Wink\n>\n> [1] https://github.com/git/git/pull/486\n\nFWIW, here is how that pull/486/head looks like.\n\n-- >8 --\n\nFrom: Wink Saville <wink@saville.com>\nDate: Thu, 26 Apr 2018 09:56:11 -0700\nSubject: [PATCH] Teach remote add the --prefix-tags option\n\nWhen --prefix-tags is passed to `git remote add` the tagopt is set to\n--prefix-tags and a second fetch line is added so tags are placed in\nthe branches namespace.\n\nFor example:\n  $ git remote add -f --prefix-tags gbenchmark git@github.com:google/benchmark\n  Updating gbenchmark\n  warning: no common commits\n  remote: Counting objects: 4406, done.\n  remote: Compressing objects: 100% (18/18), done.\n  remote: Total 4406 (delta 7), reused 13 (delta 6), pack-reused 4382\n  Receiving objects: 100% (4406/4406), 1.34 MiB | 7.46 MiB/s, done.\n  Resolving deltas: 100% (2865/2865), done.\n  From github.com:google/benchmark\n   * [new branch]      clangtidy       -> gbenchmark/clangtidy\n   * [new branch]      iter_report     -> gbenchmark/iter_report\n   * [new branch]      master          -> gbenchmark/master\n   * [new branch]      releasing       -> gbenchmark/releasing\n   * [new branch]      reportercleanup -> gbenchmark/reportercleanup\n   * [new branch]      rmheaders       -> gbenchmark/rmheaders\n   * [new branch]      v2              -> gbenchmark/v2\n   * [new tag]         v0.0.9          -> refs/remote-tags/gbenchmark/v0.0.9\n   * [new tag]         v0.1.0          -> refs/remote-tags/gbenchmark/v0.1.0\n   * [new tag]         v1.0.0          -> refs/remote-tags/gbenchmark/v1.0.0\n   * [new tag]         v1.1.0          -> refs/remote-tags/gbenchmark/v1.1.0\n   * [new tag]         v1.2.0          -> refs/remote-tags/gbenchmark/v1.2.0\n   * [new tag]         v1.3.0          -> refs/remote-tags/gbenchmark/v1.3.0\n   * [new tag]         v1.4.0          -> refs/remote-tags/gbenchmark/v1.4.0\n\nAnd the .git/config remote \"gbenchmark\" section looks like:\n  [remote \"gbenchmark\"]\n    url = git@github.com:google/benchmark\n    fetch = +refs/heads/*:refs/remotes/gbenchmark/*\n    fetch = +refs/tags/*:refs/remote-tags/gbenchmark/*\n    tagopt = --prefix-tags\n---\n Documentation/git-remote.txt |  8 ++++--\n builtin/remote.c             | 47 +++++++++++++++++++++++++++++++++---\n remote.c                     |  2 ++\n 3 files changed, 51 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 4feddc0293..cdfd24e2ea 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git remote' [-v | --verbose]\n-'git remote add' [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=<fetch|push>] <name> <url>\n+'git remote add' [-t <branch>] [-m <master>] [-f] [--prefix-tags | --tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\n 'git remote rename' <old> <new>\n 'git remote remove' <name>\n 'git remote set-head' <name> (-a | --auto | -d | --delete | <branch>)\n@@ -54,7 +54,11 @@ With `-f` option, `git fetch <name>` is run immediately after\n the remote information is set up.\n +\n With `--tags` option, `git fetch <name>` imports every tag from the\n-remote repository.\n+remote repository to refs/tags, use --prefix-tags to import them\n+to refs/remote-tags/<name>/<tag>.\n++\n+With `--prefix-tags` option, `git fetch <name>` imports every tag from the\n+remote repository to refs/remote-tags/<name>/<tag>.\n +\n With `--no-tags` option, `git fetch <name>` does not import tags from\n the remote repository.\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 805ffc05cd..75813eeaa3 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -11,7 +11,7 @@\n \n static const char * const builtin_remote_usage[] = {\n \tN_(\"git remote [-v | --verbose]\"),\n-\tN_(\"git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\"),\n+\tN_(\"git remote add [-t <branch>] [-m <master>] [-f] [--prefix-tags | --tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\"),\n \tN_(\"git remote rename <old> <new>\"),\n \tN_(\"git remote remove <name>\"),\n \tN_(\"git remote set-head <name> (-a | --auto | -d | --delete | <branch>)\"),\n@@ -101,7 +101,8 @@ static int fetch_remote(const char *name)\n enum {\n \tTAGS_UNSET = 0,\n \tTAGS_DEFAULT = 1,\n-\tTAGS_SET = 2\n+\tTAGS_SET = 2,\n+\tTAGS_SET_PREFIX = 3\n };\n \n #define MIRROR_NONE 0\n@@ -123,6 +124,16 @@ static void add_branch(const char *key, const char *branchname,\n \tgit_config_set_multivar(key, tmp->buf, \"^$\", 0);\n }\n \n+static void add_remote_tags(const char *key, const char *branchname,\n+\t\t       const char *remotename, struct strbuf *tmp)\n+{\n+\tstrbuf_reset(tmp);\n+\tstrbuf_addch(tmp, '+');\n+\tstrbuf_addf(tmp, \"refs/tags/%s:refs/remote-tags/%s/%s\",\n+\t\t\t\tbranchname, remotename, branchname);\n+\tgit_config_set_multivar(key, tmp->buf, \"^$\", 0);\n+}\n+\n static const char mirror_advice[] =\n N_(\"--mirror is dangerous and deprecated; please\\n\"\n    \"\\t use --mirror=fetch or --mirror=push instead\");\n@@ -161,6 +172,9 @@ static int add(int argc, const char **argv)\n \t\tOPT_SET_INT(0, \"tags\", &fetch_tags,\n \t\t\t    N_(\"import all tags and associated objects when fetching\"),\n \t\t\t    TAGS_SET),\n+\t\tOPT_SET_INT(0, \"prefix-tags\", &fetch_tags,\n+\t\t\t    N_(\"import all tags and associated objects when fetching and prefix with <name>\"),\n+          TAGS_SET_PREFIX),\n \t\tOPT_SET_INT(0, NULL, &fetch_tags,\n \t\t\t    N_(\"or do not fetch any tag at all (--no-tags)\"), TAGS_UNSET),\n \t\tOPT_STRING_LIST('t', \"track\", &track, N_(\"branch\"),\n@@ -215,10 +229,35 @@ static int add(int argc, const char **argv)\n \t}\n \n \tif (fetch_tags != TAGS_DEFAULT) {\n+\t\tif (fetch_tags == TAGS_SET_PREFIX) {\n+\t\t\tstrbuf_reset(&buf);\n+\t\t\tstrbuf_addf(&buf, \"remote.%s.fetch\", name);\n+\t\t\tif (track.nr == 0)\n+\t\t\t\tstring_list_append(&track, \"*\");\n+\t\t\tfor (i = 0; i < track.nr; i++) {\n+\t\t\t\tadd_remote_tags(buf.buf, track.items[i].string,\n+\t\t\t\t\t\tname, &buf2);\n+\t\t\t}\n+\t\t}\n+\n \t\tstrbuf_reset(&buf);\n \t\tstrbuf_addf(&buf, \"remote.%s.tagopt\", name);\n-\t\tgit_config_set(buf.buf,\n-\t\t\t       fetch_tags == TAGS_SET ? \"--tags\" : \"--no-tags\");\n+\t\tchar* config_val = NULL;\n+\t\tswitch (fetch_tags) {\n+\t\tcase TAGS_UNSET:\n+\t\t\tconfig_val = \"--no-tags\";\n+\t\t\tbreak;\n+\t\tcase TAGS_SET:\n+\t\t\tconfig_val = \"--tags\";\n+\t\t\tbreak;\n+\t\tcase TAGS_SET_PREFIX:\n+\t\t\tconfig_val = \"--prefix-tags\";\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tdie(_(\"Unexpected TAGS enum %d\"), fetch_tags);\n+\t\t\tbreak;\n+\t\t}\n+\t\tgit_config_set(buf.buf, config_val);\n \t}\n \n \tif (fetch && fetch_remote(name))\ndiff --git a/remote.c b/remote.c\nindex 91eb010ca9..f383ce3cdf 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -447,6 +447,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\t\tremote->fetch_tags = -1;\n \t\telse if (!strcmp(value, \"--tags\"))\n \t\t\tremote->fetch_tags = 2;\n+\t\telse if (!strcmp(value, \"--prefix-tags\"))\n+\t\t\tremote->fetch_tags = -1; // A fetch for refs/tags is present so tags are retrieved\n \t} else if (!strcmp(subkey, \"proxy\")) {\n \t\treturn git_config_string((const char **)&remote->http_proxy,\n \t\t\t\t\t key, value);\n-- \n2.17.0-391-g1f1cddd558\n"},{"id":"345925","messageId":"xmqqbme51rgn.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"xmqqfu3h1t22.fsf@gitster-ct.c.googlers.com","subject":"Re: Fetching tags overwrites existing tags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-04-26T23:24:40Z","receivedAt":"2018-04-26T23:24:46Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Wink Saville <wink@saville.com> writes:\n>\n>> I've tried to teach 'git remote add' the --prefix-tags option using the\n>> technique Junio provided. At moment it is PR #486 on github [1]\n>> and I'd love some comments on whether or not this the right direction\n>> for fetching tags and putting them in the branches namespace.\n>>\n>> -- Wink\n>>\n>> [1] https://github.com/git/git/pull/486\n>\n> FWIW, here is how that pull/486/head looks like.\n>\n> -- >8 --\n>\n> From: Wink Saville <wink@saville.com>\n> Date: Thu, 26 Apr 2018 09:56:11 -0700\n> Subject: [PATCH] Teach remote add the --prefix-tags option\n>\n> When --prefix-tags is passed to `git remote add` the tagopt is set to\n> --prefix-tags and a second fetch line is added so tags are placed in\n> the branches namespace.\n\nWhen I hear \"branches namespace\", what comes to my mind is refs/heads/\nor perhaps refs/remotes/*/.  \"... are placed in a separate hierarchy\nper remote\" or something, perhaps?\n\n>\n> ...\n> And the .git/config remote \"gbenchmark\" section looks like:\n>   [remote \"gbenchmark\"]\n>     url = git@github.com:google/benchmark\n>     fetch = +refs/heads/*:refs/remotes/gbenchmark/*\n>     fetch = +refs/tags/*:refs/remote-tags/gbenchmark/*\n>     tagopt = --prefix-tags\n> ---\n\nMissing sign-off ;-)\n\n> +static void add_remote_tags(const char *key, const char *branchname,\n> +\t\t       const char *remotename, struct strbuf *tmp)\n> +{\n> +\tstrbuf_reset(tmp);\n> +\tstrbuf_addch(tmp, '+');\n> +\tstrbuf_addf(tmp, \"refs/tags/%s:refs/remote-tags/%s/%s\",\n> +\t\t\t\tbranchname, remotename, branchname);\n\nWith \"+refs/tags/%s:refs/remote-tags/%s/%s\", combine addch/addf into\none, perhaps?\n\n> +\tgit_config_set_multivar(key, tmp->buf, \"^$\", 0);\n> +}\n\nCalling the second parameter \"branchname\" makes little sense, I\nwould think.  Practically, you would call this at most once with its\nsecond parameter set to '*', and even if the second parameter is not\na wildcard/asterisk, it would be a tagname.\n\n\n>  static const char mirror_advice[] =\n>  N_(\"--mirror is dangerous and deprecated; please\\n\"\n>     \"\\t use --mirror=fetch or --mirror=push instead\");\n> @@ -161,6 +172,9 @@ static int add(int argc, const char **argv)\n>  \t\tOPT_SET_INT(0, \"tags\", &fetch_tags,\n>  \t\t\t    N_(\"import all tags and associated objects when fetching\"),\n>  \t\t\t    TAGS_SET),\n> +\t\tOPT_SET_INT(0, \"prefix-tags\", &fetch_tags,\n> +\t\t\t    N_(\"import all tags and associated objects when fetching and prefix with <name>\"),\n> +          TAGS_SET_PREFIX),\n\nFunny indent.  Use monospaced font in your editor, set tab width to\n8 and align, imitating how the above OPT_SET_INT() item does for\nTAGS_SET.\n\n> @@ -215,10 +229,35 @@ static int add(int argc, const char **argv)\n>  \t}\n>  \n>  \tif (fetch_tags != TAGS_DEFAULT) {\n> +\t\tif (fetch_tags == TAGS_SET_PREFIX) {\n> +\t\t\tstrbuf_reset(&buf);\n> +\t\t\tstrbuf_addf(&buf, \"remote.%s.fetch\", name);\n> +\t\t\tif (track.nr == 0)\n> +\t\t\t\tstring_list_append(&track, \"*\");\n> +\t\t\tfor (i = 0; i < track.nr; i++) {\n> +\t\t\t\tadd_remote_tags(buf.buf, track.items[i].string,\n> +\t\t\t\t\t\tname, &buf2);\n> +\t\t\t}\n\nThe \"track\" thing is made incompatible with anything but mirror in\nearly part of this function (outside the precontext).  I highly\nsuspect that --prefix-tags does *not* make sense when mirroring.\n\nHence (1) we should detect and error out when --prefix-tags is used\nwith mirror fetch near where we do the same for track used without\nmirror fetch already, (2) detect and error out when --prefix-tags is\nused with track, and (3) add \"+refs/tags/*:refs/remote-tags/$name/*\"\njust once without paying attention to track here.  We may not even\nwant add_remote_tags() helper function if we go that route.\n\n> +\t\t}\n> +\n>  \t\tstrbuf_reset(&buf);\n>  \t\tstrbuf_addf(&buf, \"remote.%s.tagopt\", name);\n> -\t\tgit_config_set(buf.buf,\n> -\t\t\t       fetch_tags == TAGS_SET ? \"--tags\" : \"--no-tags\");\n> +\t\tchar* config_val = NULL;\n\ndecl-after-statement.  Also \"char *var\", not \"char* var\".\n\n> +\t\tswitch (fetch_tags) {\n> +\t\tcase TAGS_UNSET:\n> +\t\t\tconfig_val = \"--no-tags\";\n> +\t\t\tbreak;\n> +\t\tcase TAGS_SET:\n> +\t\t\tconfig_val = \"--tags\";\n> +\t\t\tbreak;\n> +\t\tcase TAGS_SET_PREFIX:\n> +\t\t\tconfig_val = \"--prefix-tags\";\n> +\t\t\tbreak;\n> +\t\tdefault:\n> +\t\t\tdie(_(\"Unexpected TAGS enum %d\"), fetch_tags);\n> +\t\t\tbreak;\n> +\t\t}\n> +\t\tgit_config_set(buf.buf, config_val);\n>  \t}\n>  \n>  \tif (fetch && fetch_remote(name))\n> diff --git a/remote.c b/remote.c\n> index 91eb010ca9..f383ce3cdf 100644\n> --- a/remote.c\n> +++ b/remote.c\n> @@ -447,6 +447,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n>  \t\t\tremote->fetch_tags = -1;\n>  \t\telse if (!strcmp(value, \"--tags\"))\n>  \t\t\tremote->fetch_tags = 2;\n> +\t\telse if (!strcmp(value, \"--prefix-tags\"))\n> +\t\t\tremote->fetch_tags = -1; // A fetch for refs/tags is present so tags are retrieved\n\nWe are old fashioned and do not use // comments, but more\nimportantly it is not clear what this comment is trying to\nsay, at least to me.\n\n>  \t} else if (!strcmp(subkey, \"proxy\")) {\n>  \t\treturn git_config_string((const char **)&remote->http_proxy,\n>  \t\t\t\t\t key, value);\n"},{"id":"345977","messageId":"20180427185024.7245-1-wink@saville.com","threadId":"48353","inReplyTo":"xmqqbme51rgn.fsf@gitster-ct.c.googlers.com","subject":"[RFC PATCH v2] Teach remote add the --prefix-tags option","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-04-27T18:50:24Z","receivedAt":"2018-04-27T18:50:42Z","isPatch":true,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"When --prefix-tags is passed to `git remote add` the tagopt is set to\n--prefix-tags and a second fetch line is added so tags are placed in\na separate hierarchy per remote.\n\nFor example:\n  $ git remote add -f --prefix-tags gbenchmark git@github.com:google/benchmark\n  Updating gbenchmark\n  warning: no common commits\n  remote: Counting objects: 4406, done.\n  remote: Compressing objects: 100% (18/18), done.\n  remote: Total 4406 (delta 7), reused 13 (delta 6), pack-reused 4382\n  Receiving objects: 100% (4406/4406), 1.34 MiB | 7.58 MiB/s, done.\n  Resolving deltas: 100% (2865/2865), done.\n  From github.com:google/benchmark\n   * [new branch]      clangtidy       -> gbenchmark/clangtidy\n   * [new branch]      iter_report     -> gbenchmark/iter_report\n   * [new branch]      master          -> gbenchmark/master\n   * [new branch]      releasing       -> gbenchmark/releasing\n   * [new branch]      reportercleanup -> gbenchmark/reportercleanup\n   * [new branch]      rmheaders       -> gbenchmark/rmheaders\n   * [new branch]      v2              -> gbenchmark/v2\n   * [new tag]         v0.0.9          -> tags/gbenchmark/v0.0.9\n   * [new tag]         v0.1.0          -> tags/gbenchmark/v0.1.0\n   * [new tag]         v1.0.0          -> tags/gbenchmark/v1.0.0\n   * [new tag]         v1.1.0          -> tags/gbenchmark/v1.1.0\n   * [new tag]         v1.2.0          -> tags/gbenchmark/v1.2.0\n   * [new tag]         v1.3.0          -> tags/gbenchmark/v1.3.0\n   * [new tag]         v1.4.0          -> tags/gbenchmark/v1.4.0\n\nAnd the .git/config remote \"gbenchmark\" section looks like:\n  [remote \"gbenchmark\"]\n    url = git@github.com:google/benchmark\n    fetch = +refs/heads/*:refs/remotes/gbenchmark/*\n    fetch = +refs/tags/*:refs/remotes/tags/gbenchmark/*\n    tagopt = --prefix-tags\n\nBased on a solution proposed by Junio on the email list [1]\n\n[1]: https://public-inbox.org/git/xmqqbme51rgn.fsf@gitster-ct.c.googlers.com/T/#me7f7f153b8ba742c0dc48d8ec79c280c9682d32e\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Jacob Keller <jacob.keller@gmail.com>\nSigned-off-by: Wink Saville <wink@saville.com>\n---\n Documentation/git-remote.txt |  8 +++++--\n builtin/remote.c             | 42 ++++++++++++++++++++++++++++++++----\n remote.c                     |  2 ++\n 3 files changed, 46 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 4feddc0293..c97bf29d46 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git remote' [-v | --verbose]\n-'git remote add' [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=<fetch|push>] <name> <url>\n+'git remote add' [-t <branch>] [-m <master>] [-f] [--prefix-tags | --tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\n 'git remote rename' <old> <new>\n 'git remote remove' <name>\n 'git remote set-head' <name> (-a | --auto | -d | --delete | <branch>)\n@@ -54,7 +54,11 @@ With `-f` option, `git fetch <name>` is run immediately after\n the remote information is set up.\n +\n With `--tags` option, `git fetch <name>` imports every tag from the\n-remote repository.\n+remote repository to refs/tags, use --prefix-tags to import them\n+to refs/remotes/tags/<name>/<tag>.\n++\n+With `--prefix-tags` option, `git fetch <name>` imports every tag from the\n+remote repository to refs/remotes/tags/<name>/<tag>.\n +\n With `--no-tags` option, `git fetch <name>` does not import tags from\n the remote repository.\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 805ffc05cd..39de50bdd6 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -11,7 +11,7 @@\n \n static const char * const builtin_remote_usage[] = {\n \tN_(\"git remote [-v | --verbose]\"),\n-\tN_(\"git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\"),\n+\tN_(\"git remote add [-t <branch>] [-m <master>] [-f] [--prefix-tags | --tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\"),\n \tN_(\"git remote rename <old> <new>\"),\n \tN_(\"git remote remove <name>\"),\n \tN_(\"git remote set-head <name> (-a | --auto | -d | --delete | <branch>)\"),\n@@ -101,7 +101,8 @@ static int fetch_remote(const char *name)\n enum {\n \tTAGS_UNSET = 0,\n \tTAGS_DEFAULT = 1,\n-\tTAGS_SET = 2\n+\tTAGS_SET = 2,\n+\tTAGS_SET_PREFIX = 3\n };\n \n #define MIRROR_NONE 0\n@@ -123,6 +124,14 @@ static void add_branch(const char *key, const char *branchname,\n \tgit_config_set_multivar(key, tmp->buf, \"^$\", 0);\n }\n \n+static void add_remote_tags(const char *key, const char *remotename,\n+\t\t\t    struct strbuf *tmp)\n+{\n+\tstrbuf_reset(tmp);\n+\tstrbuf_addf(tmp, \"+refs/tags/*:refs/remotes/tags/%s/*\", remotename);\n+\tgit_config_set_multivar(key, tmp->buf, \"^$\", 0);\n+}\n+\n static const char mirror_advice[] =\n N_(\"--mirror is dangerous and deprecated; please\\n\"\n    \"\\t use --mirror=fetch or --mirror=push instead\");\n@@ -161,6 +170,9 @@ static int add(int argc, const char **argv)\n \t\tOPT_SET_INT(0, \"tags\", &fetch_tags,\n \t\t\t    N_(\"import all tags and associated objects when fetching\"),\n \t\t\t    TAGS_SET),\n+\t\tOPT_SET_INT(0, \"prefix-tags\", &fetch_tags,\n+\t\t\t    N_(\"import all tags and associated objects when fetching and prefix with <name>\"),\n+\t\t\t    TAGS_SET_PREFIX),\n \t\tOPT_SET_INT(0, NULL, &fetch_tags,\n \t\t\t    N_(\"or do not fetch any tag at all (--no-tags)\"), TAGS_UNSET),\n \t\tOPT_STRING_LIST('t', \"track\", &track, N_(\"branch\"),\n@@ -182,6 +194,8 @@ static int add(int argc, const char **argv)\n \t\tdie(_(\"specifying a master branch makes no sense with --mirror\"));\n \tif (mirror && !(mirror & MIRROR_FETCH) && track.nr)\n \t\tdie(_(\"specifying branches to track makes sense only with fetch mirrors\"));\n+\tif (mirror && (fetch_tags == TAGS_SET_PREFIX))\n+\t\tdie(_(\"specifying a --prefix-tags makes no sense with --mirror\"));\n \n \tname = argv[0];\n \turl = argv[1];\n@@ -215,10 +229,30 @@ static int add(int argc, const char **argv)\n \t}\n \n \tif (fetch_tags != TAGS_DEFAULT) {\n+\t\tif (fetch_tags == TAGS_SET_PREFIX) {\n+\t\t\tstrbuf_reset(&buf);\n+\t\t\tstrbuf_addf(&buf, \"remote.%s.fetch\", name);\n+\t\t\tadd_remote_tags(buf.buf, name, &buf2);\n+\t\t}\n+\n \t\tstrbuf_reset(&buf);\n \t\tstrbuf_addf(&buf, \"remote.%s.tagopt\", name);\n-\t\tgit_config_set(buf.buf,\n-\t\t\t       fetch_tags == TAGS_SET ? \"--tags\" : \"--no-tags\");\n+\t\tchar *config_val = NULL;\n+\t\tswitch (fetch_tags) {\n+\t\tcase TAGS_UNSET:\n+\t\t\tconfig_val = \"--no-tags\";\n+\t\t\tbreak;\n+\t\tcase TAGS_SET:\n+\t\t\tconfig_val = \"--tags\";\n+\t\t\tbreak;\n+\t\tcase TAGS_SET_PREFIX:\n+\t\t\tconfig_val = \"--prefix-tags\";\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tdie(_(\"Unexpected TAGS enum %d\"), fetch_tags);\n+\t\t\tbreak;\n+\t\t}\n+\t\tgit_config_set(buf.buf, config_val);\n \t}\n \n \tif (fetch && fetch_remote(name))\ndiff --git a/remote.c b/remote.c\nindex 91eb010ca9..65dd974868 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -447,6 +447,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\t\tremote->fetch_tags = -1;\n \t\telse if (!strcmp(value, \"--tags\"))\n \t\t\tremote->fetch_tags = 2;\n+\t\telse if (!strcmp(value, \"--prefix-tags\"))\n+\t\t\tremote->fetch_tags = -1;\n \t} else if (!strcmp(subkey, \"proxy\")) {\n \t\treturn git_config_string((const char **)&remote->http_proxy,\n \t\t\t\t\t key, value);\n-- \n2.17.0.392.g28baa87d09.dirty\n\n"},{"id":"345979","messageId":"CAKk8isqAd11W4DpjNkd9AeMDGi=ESyhVSFXcvwteasTfHMPGLA@mail.gmail.com","threadId":"48353","inReplyTo":"xmqqbme51rgn.fsf@gitster-ct.c.googlers.com","subject":"Re: Fetching tags overwrites existing tags","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-04-27T19:08:27Z","receivedAt":"2018-04-27T19:08:54Z","isPatch":false,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"On Thu, Apr 26, 2018 at 4:24 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>\n> Hence (1) we should detect and error out when --prefix-tags is used\n> with mirror fetch near where we do the same for track used without\n> mirror fetch already, (2) detect and error out when --prefix-tags is\n> used with track, and (3) add \"+refs/tags/*:refs/remote-tags/$name/*\"\n> just once without paying attention to track here.  We may not even\n> want add_remote_tags() helper function if we go that route.\n>\n\nI've replied to the thread using format-email/send-email with the\nsubject: \"[RFC PATCH v2] Teach remote add the --prefix-tags option\",\nbut I misspelled Junio's email address :(\n\nI've tried to address the issues pointed out by Junio. But I've choosen\nnot to do \"(2) detect and error out when --prefix-tags is used with track\".\nMy thinking is tags are independent of tracking and it seems reasonable\nthat they sould be included if requested. If I'm wrong I'll certainly fix it.\n\nThe other change was rather than using \"\"+refs/tags/*:refs/remote-tags/$name/*\"\nI've changed it to \"+refs/tags/*:refs/remote/tags/$name/*\" which seems cleaner.\nAgain, if remote-tags is preferred I'll change it back.\n\nOne other question, I'm not sure \"--prefix-tags\" is the best name for\nthe option,\nmaybe \"--sub-tags\" or \"--nested-tags\" or ...\n\n-- Wink\n"},{"id":"345981","messageId":"CAGyf7-GhK_wAZn8zO35rsgQ=meLc=Lr+Q=S8omHg1c413dMsFw@mail.gmail.com","threadId":"48353","inReplyTo":"CAKk8isqAd11W4DpjNkd9AeMDGi=ESyhVSFXcvwteasTfHMPGLA@mail.gmail.com","subject":"Re: Fetching tags overwrites existing tags","fromName":"Bryan Turner","fromEmail":"bturner@atlassian.com","sentAt":"2018-04-27T19:13:28Z","receivedAt":"2018-04-27T19:13:32Z","isPatch":false,"sender":{"key":"bturner@atlassian.com","avatar":"https://gravatar.com/avatar/16bcf3167981c1ef7c804e502642366d888a35b0d0b0a4ca01fdc442aa1acb1e?d=mp&s=160"},"body":"On Fri, Apr 27, 2018 at 12:08 PM, Wink Saville <wink@saville.com> wrote:\n>\n> The other change was rather than using \"\"+refs/tags/*:refs/remote-tags/$name/*\"\n> I've changed it to \"+refs/tags/*:refs/remote/tags/$name/*\" which seems cleaner.\n> Again, if remote-tags is preferred I'll change it back.\n\n\nFrom looking at the code, it looks like you mean\n\"+refs/tags/*:refs/remotes/tags/$name/*\".\n\nThe issue with that approach is that it collides with a remote named\n\"tags\". \"refs/remote-tags\", on the other hand, represents a new-to-Git\npath, one that won't already be in use by any other standard\nfunctionality. That seems like a better approach than hoping no one\nout there will call one of their remotes \"tags\".\n\nBryan\n"},{"id":"345983","messageId":"CACBZZX7HFnEMD8PKWj0c7VWqv=yeYCXYJjy-5YPZnF_pJAy0jw@mail.gmail.com","threadId":"48353","inReplyTo":"CAKk8isrAmWOdioJe2CdY+2F_AzKxcTGwd+kBsrAmGx-U=ZHGgA@mail.gmail.com","subject":"Re: Fetching tags overwrites existing tags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-04-27T19:46:25Z","receivedAt":"2018-04-27T19:46:49Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Tue, Apr 24, 2018 at 9:57 PM, Wink Saville <wink@saville.com> wrote:\n> If have a repository with a tag \"v1.0.0\" and I add a remote repository\n> which also has a tag \"v1.0.0\" tag is overwritten.\n\nI feel like this thread has gotten somewhat side-tracked by the valid\ndiscussion about whether we should have remote tracking tags, but the\nmuch easier thing to fix is that the \"+\" prefix for refs/tags/* means\nnothing.\n\nI noticed this when working on fetch.pruneTags in the last release,\nbut didn't dig further.\n\nI.e. if you clone git.git and update \"master\" and a tag:\n\n$ git fetch origin 'refs/heads/*:refs/heads/*' --dry-run\nFrom github.com:git/git\n ! [rejected]              master     -> master  (non-fast-forward)\n$ git fetch origin '+refs/heads/*:refs/heads/*' --dry-run\nFrom github.com:git/git\n + 969e05fae2...1f1cddd558 master     -> master  (forced update)\n\nHere \"+\" does the right thing, but then:\n\n$ git fetch origin 'refs/tags/*:refs/tags/*' --dry-run\nFrom github.com:git/git\n t [tag update]            v2.17.0    -> v2.17.0\n$ git fetch origin '+refs/tags/*:refs/tags/*' --dry-run\nFrom github.com:git/git\n t [tag update]            v2.17.0    -> v2.17.0\n\nHere the former shouldn't be clobbering the existing tag.\n"},{"id":"346025","messageId":"CA+P7+xrs1Dz7evfofrExbg1Pw0C2uR17yNa=yS8XtYBvnK_k4g@mail.gmail.com","threadId":"48353","inReplyTo":"CAKk8isqAd11W4DpjNkd9AeMDGi=ESyhVSFXcvwteasTfHMPGLA@mail.gmail.com","subject":"Re: Fetching tags overwrites existing tags","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2018-04-28T07:26:20Z","receivedAt":"2018-04-28T07:26:58Z","isPatch":false,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Fri, Apr 27, 2018 at 12:08 PM, Wink Saville <wink@saville.com> wrote:\n> On Thu, Apr 26, 2018 at 4:24 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Junio C Hamano <gitster@pobox.com> writes:\n>>\n>>\n>> Hence (1) we should detect and error out when --prefix-tags is used\n>> with mirror fetch near where we do the same for track used without\n>> mirror fetch already, (2) detect and error out when --prefix-tags is\n>> used with track, and (3) add \"+refs/tags/*:refs/remote-tags/$name/*\"\n>> just once without paying attention to track here.  We may not even\n>> want add_remote_tags() helper function if we go that route.\n>>\n>\n> I've replied to the thread using format-email/send-email with the\n> subject: \"[RFC PATCH v2] Teach remote add the --prefix-tags option\",\n> but I misspelled Junio's email address :(\n>\n> I've tried to address the issues pointed out by Junio. But I've choosen\n> not to do \"(2) detect and error out when --prefix-tags is used with track\".\n> My thinking is tags are independent of tracking and it seems reasonable\n> that they sould be included if requested. If I'm wrong I'll certainly fix it.\n>\n> The other change was rather than using \"\"+refs/tags/*:refs/remote-tags/$name/*\"\n> I've changed it to \"+refs/tags/*:refs/remote/tags/$name/*\" which seems cleaner.\n> Again, if remote-tags is preferred I'll change it back.\n\nThe only main concern I have with \"remote\" is that it is very similar\nbut not exactly the same as \"remotes\". Unfortunately, it is not\npossible in *every* circumstance to use remotes.\n\nPersonally, I'd prefer we used \"refs/remote/<remote-name>/tags\" rather\nthan \"refs/remote/tags/<remote-name>\", and possibly plan to migrate\nfrom refs/remotes/<remote-name>/<branches> to\nrefs/remote/<remote-name>/heads/<branches>\n\nThis is mostly so that future additions of things like notes,\nreplaces, or really *any* refs would automatically drop into\n\"refs/remotes/<name>/<full remote ref name with refs prefix removed>\",\nwhich is a longer term goal I've had for a while (though i haven't\nbeen able to put much time to it at present). Historically, I proposed\nusing \"tracking\" instead of \"remote\", but I am ok with any name we\nchoose as long as it doesn't create conflicts.\n\nThanks,\nJake\n\n>\n> One other question, I'm not sure \"--prefix-tags\" is the best name for\n> the option,\n> maybe \"--sub-tags\" or \"--nested-tags\" or ...\n>\n> -- Wink\n"},{"id":"346048","messageId":"20180428182728.21696-1-wink@saville.com","threadId":"48353","inReplyTo":"xmqqbme51rgn.fsf@gitster-ct.c.googlers.com","subject":"[RFC PATCH v3] Teach remote add the --remote-tags option","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-04-28T18:27:28Z","receivedAt":"2018-04-28T18:27:38Z","isPatch":true,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"When --remote-tags is passed to `git remote add` the tagopt is set to\n--remote-tags and a second fetch line is added so tags are placed in\na separate hierarchy per remote.\n\nFor example:\n  $ git remote add -f --remote-tags gbenchmark git@github.com:google/benchmark\n  Updating gbenchmark\n  warning: no common commits\n  remote: Counting objects: 4406, done.\n  remote: Compressing objects: 100% (18/18), done.\n  remote: Total 4406 (delta 7), reused 13 (delta 6), pack-reused 4382\n  Receiving objects: 100% (4406/4406), 1.34 MiB | 7.58 MiB/s, done.\n  Resolving deltas: 100% (2865/2865), done.\n  From github.com:google/benchmark\n   * [new branch]      clangtidy       -> gbenchmark/clangtidy\n   * [new branch]      iter_report     -> gbenchmark/iter_report\n   * [new branch]      master          -> gbenchmark/master\n   * [new branch]      releasing       -> gbenchmark/releasing\n   * [new branch]      reportercleanup -> gbenchmark/reportercleanup\n   * [new branch]      rmheaders       -> gbenchmark/rmheaders\n   * [new branch]      v2              -> gbenchmark/v2\n   * [new tag]         v0.0.9          -> refs/remote-tags/gbenchmark/v0.0.9\n   * [new tag]         v0.1.0          -> refs/remote-tags/gbenchmark/v0.1.0\n   * [new tag]         v1.0.0          -> refs/remote-tags/gbenchmark/v1.0.0\n   * [new tag]         v1.1.0          -> refs/remote-tags/gbenchmark/v1.1.0\n   * [new tag]         v1.2.0          -> refs/remote-tags/gbenchmark/v1.2.0\n   * [new tag]         v1.3.0          -> refs/remote-tags/gbenchmark/v1.3.0\n   * [new tag]         v1.4.0          -> refs/remote-tags/gbenchmark/v1.4.0\n\nAnd the .git/config remote \"gbenchmark\" section looks like:\n  [remote \"gbenchmark\"]\n    url = git@github.com:google/benchmark\n    fetch = +refs/heads/*:refs/remotes/gbenchmark/*\n    fetch = +refs/tags/*:refs/remote-tags/gbenchmark/*\n    tagopt = --remote-tags\n\nBased on a solution proposed by Junio on the email list [1]\n\n[1]: https://public-inbox.org/git/xmqqbme51rgn.fsf@gitster-ct.c.googlers.com/T/#me7f7f153b8ba742c0dc48d8ec79c280c9682d32e\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Jacob Keller <jacob.keller@gmail.com>\nSigned-off-by: Wink Saville <wink@saville.com>\n---\n Documentation/git-remote.txt |  8 +++++--\n builtin/remote.c             | 44 ++++++++++++++++++++++++++++++++----\n remote.c                     |  2 ++\n 3 files changed, 48 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 4feddc0293..fc983c2ff1 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git remote' [-v | --verbose]\n-'git remote add' [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=<fetch|push>] <name> <url>\n+'git remote add' [-t <branch>] [-m <master>] [-f] [--tags | --remote-tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\n 'git remote rename' <old> <new>\n 'git remote remove' <name>\n 'git remote set-head' <name> (-a | --auto | -d | --delete | <branch>)\n@@ -54,7 +54,11 @@ With `-f` option, `git fetch <name>` is run immediately after\n the remote information is set up.\n +\n With `--tags` option, `git fetch <name>` imports every tag from the\n-remote repository.\n+remote repository to refs/tags, use --remote-tags to import them\n+to refs/remote-tags/<name>/<tag>.\n++\n+With `--remote-tags` option, `git fetch <name>` imports every tag from the\n+remote repository to refs/remote-tags/<name>/<tag>.\n +\n With `--no-tags` option, `git fetch <name>` does not import tags from\n the remote repository.\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 805ffc05cd..07832113e9 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -11,7 +11,7 @@\n \n static const char * const builtin_remote_usage[] = {\n \tN_(\"git remote [-v | --verbose]\"),\n-\tN_(\"git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\"),\n+\tN_(\"git remote add [-t <branch>] [-m <master>] [-f] [--tags | --remote-tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\"),\n \tN_(\"git remote rename <old> <new>\"),\n \tN_(\"git remote remove <name>\"),\n \tN_(\"git remote set-head <name> (-a | --auto | -d | --delete | <branch>)\"),\n@@ -101,7 +101,8 @@ static int fetch_remote(const char *name)\n enum {\n \tTAGS_UNSET = 0,\n \tTAGS_DEFAULT = 1,\n-\tTAGS_SET = 2\n+\tTAGS_SET = 2,\n+\tTAGS_SET_REMOTE = 3\n };\n \n #define MIRROR_NONE 0\n@@ -123,6 +124,14 @@ static void add_branch(const char *key, const char *branchname,\n \tgit_config_set_multivar(key, tmp->buf, \"^$\", 0);\n }\n \n+static void add_remote_tags(const char *key, const char *remotename,\n+\t\t\t    struct strbuf *tmp)\n+{\n+\tstrbuf_reset(tmp);\n+\tstrbuf_addf(tmp, \"+refs/tags/*:refs/remote-tags/%s/*\", remotename);\n+\tgit_config_set_multivar(key, tmp->buf, \"^$\", 0);\n+}\n+\n static const char mirror_advice[] =\n N_(\"--mirror is dangerous and deprecated; please\\n\"\n    \"\\t use --mirror=fetch or --mirror=push instead\");\n@@ -161,6 +170,9 @@ static int add(int argc, const char **argv)\n \t\tOPT_SET_INT(0, \"tags\", &fetch_tags,\n \t\t\t    N_(\"import all tags and associated objects when fetching\"),\n \t\t\t    TAGS_SET),\n+\t\tOPT_SET_INT(0, \"remote-tags\", &fetch_tags,\n+\t\t\t    N_(\"import all tags and associated objects when fetching to refs/remote-tags/<name>/<tag>\"),\n+\t\t\t    TAGS_SET_REMOTE),\n \t\tOPT_SET_INT(0, NULL, &fetch_tags,\n \t\t\t    N_(\"or do not fetch any tag at all (--no-tags)\"), TAGS_UNSET),\n \t\tOPT_STRING_LIST('t', \"track\", &track, N_(\"branch\"),\n@@ -182,6 +194,10 @@ static int add(int argc, const char **argv)\n \t\tdie(_(\"specifying a master branch makes no sense with --mirror\"));\n \tif (mirror && !(mirror & MIRROR_FETCH) && track.nr)\n \t\tdie(_(\"specifying branches to track makes sense only with fetch mirrors\"));\n+\tif ((fetch_tags == TAGS_SET_REMOTE) && mirror)\n+\t\tdie(_(\"specifying --remote-tags makes no sense with --mirror\"));\n+\tif ((fetch_tags == TAGS_SET_REMOTE) && track.nr)\n+\t\tdie(_(\"specifying --remote-tags makes no sense with -t or --track\"));\n \n \tname = argv[0];\n \turl = argv[1];\n@@ -215,10 +231,30 @@ static int add(int argc, const char **argv)\n \t}\n \n \tif (fetch_tags != TAGS_DEFAULT) {\n+\t\tif (fetch_tags == TAGS_SET_REMOTE) {\n+\t\t\tstrbuf_reset(&buf);\n+\t\t\tstrbuf_addf(&buf, \"remote.%s.fetch\", name);\n+\t\t\tadd_remote_tags(buf.buf, name, &buf2);\n+\t\t}\n+\n \t\tstrbuf_reset(&buf);\n \t\tstrbuf_addf(&buf, \"remote.%s.tagopt\", name);\n-\t\tgit_config_set(buf.buf,\n-\t\t\t       fetch_tags == TAGS_SET ? \"--tags\" : \"--no-tags\");\n+\t\tchar *config_val = NULL;\n+\t\tswitch (fetch_tags) {\n+\t\tcase TAGS_UNSET:\n+\t\t\tconfig_val = \"--no-tags\";\n+\t\t\tbreak;\n+\t\tcase TAGS_SET:\n+\t\t\tconfig_val = \"--tags\";\n+\t\t\tbreak;\n+\t\tcase TAGS_SET_REMOTE:\n+\t\t\tconfig_val = \"--remote-tags\";\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tdie(_(\"Unexpected TAGS enum %d\"), fetch_tags);\n+\t\t\tbreak;\n+\t\t}\n+\t\tgit_config_set(buf.buf, config_val);\n \t}\n \n \tif (fetch && fetch_remote(name))\ndiff --git a/remote.c b/remote.c\nindex 91eb010ca9..86cccc0b6e 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -447,6 +447,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\t\tremote->fetch_tags = -1;\n \t\telse if (!strcmp(value, \"--tags\"))\n \t\t\tremote->fetch_tags = 2;\n+\t\telse if (!strcmp(value, \"--remote-tags\"))\n+\t\t\tremote->fetch_tags = -1;\n \t} else if (!strcmp(subkey, \"proxy\")) {\n \t\treturn git_config_string((const char **)&remote->http_proxy,\n \t\t\t\t\t key, value);\n-- \n2.17.0.392.ga7de9262c6\n\n"},{"id":"346049","messageId":"CAKk8isq9wXLpMJxT4SHzAtj6abm6phH4G-XjfUY4=0V6g3XxdQ@mail.gmail.com","threadId":"48353","inReplyTo":"20180428182728.21696-1-wink@saville.com","subject":"Re: [RFC PATCH v3] Teach remote add the --remote-tags option","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-04-28T19:00:32Z","receivedAt":"2018-04-28T19:00:58Z","isPatch":true,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"In this version I changed \"+refs/tags/*:refs/remotes/tags/%s/*\"\nback to \"+refs/tags/*:refs/remote-tags/%s/*\" as Junio originally\nsuggested. This way don't have remotename collisions as Bryan\npointed out.\n\nSince I don't like \"--prefix-tags\" I changed the option to\n\"--remote-tags\" but obviously open to anything else.\n\nI think jacob.keller (+ demerphq) suggestion is probably good,\nbut for the moment just using remote-tags as seems simplest.\n"},{"id":"346052","messageId":"CAKk8ispsPehLkVwWqtUq7rhxeBvn0+Xhn9VWhbYJjjZgy8WLPw@mail.gmail.com","threadId":"48353","inReplyTo":"CAKk8isq9wXLpMJxT4SHzAtj6abm6phH4G-XjfUY4=0V6g3XxdQ@mail.gmail.com","subject":"Re: [RFC PATCH v3] Teach remote add the --remote-tags option","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-04-28T21:27:26Z","receivedAt":"2018-04-28T21:27:52Z","isPatch":true,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"I forgot to mention that I added the code to error out\nif --remote-tags is used with \"tracking\" (-t | --track) or\nmirror:\n  +       if ((fetch_tags == TAGS_SET_REMOTE) && mirror)\n  +               die(_(\"specifying --remote-tags makes no sense with\n--mirror\"));\n  +       if ((fetch_tags == TAGS_SET_REMOTE) && track.nr)\n  +               die(_(\"specifying --remote-tags makes no sense with\n-t or --track\"));\n"},{"id":"346094","messageId":"20180429202100.32353-1-avarab@gmail.com","threadId":"48353","inReplyTo":"CACBZZX7HFnEMD8PKWj0c7VWqv=yeYCXYJjy-5YPZnF_pJAy0jw@mail.gmail.com","subject":"[PATCH 0/8] \"git fetch\" should not clobber existing tags without --force","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-04-29T20:20:52Z","receivedAt":"2018-04-29T20:21:22Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Fri, Apr 27 2018, Ævar Arnfjörð Bjarmason wrote:\n\n> On Tue, Apr 24, 2018 at 9:57 PM, Wink Saville <wink@saville.com> wrote:\n>> If have a repository with a tag \"v1.0.0\" and I add a remote repository\n>> which also has a tag \"v1.0.0\" tag is overwritten.\n>\n> I feel like this thread has gotten somewhat side-tracked by the valid\n> discussion about whether we should have remote tracking tags, but the\n> much easier thing to fix is that the \"+\" prefix for refs/tags/* means\n> nothing.\n> [...]\n\nThis patch series implements that simpler way of digging ourselves out\nof the immediate hole that we're clobbering tags by default without\nthe --force option.\n\nI'm not 100% happy about this, but I think sans stuff that comes up in\nreview it's in principle ready for inclusion, stuff I wished I'd done\nbut have left for later:\n\n * Write a gitrefspec(5) man page, now we have how they work, and how\n   they work on push/pull scattered over two docs, which before this\n   are in conflict with one another.\n\n * Have much more exhaustive tests, I started trying to integrate this\n   with the much more exhaustive tag pruning tests in my\n   https://github.com/avar/git/tree/refspec-support-+-in-tags but gave\n   up because the various interaction with those tests is messy,\n   e.g. if we fail a tag update we don't prune as the existing tests\n   assert, because the whole ref transaction fails.\n\n * 05/08 notes how the semantics of whether something needs a --force\n   are really confusing because the rules are different depending on\n   the ref namespace you're pushing into. We should probably build on\n   top of this and e.g. refuse to clobber tags outside of refs/tags/*.\n\n * Should we do better to mitigate this breaking stuff for existing\n   users who really are expecting their tags to be clobbered? Maybe by\n   adding a --force-tags option (which wouldn't clobber branches), or\n   have some config option either enable the old behavior, or make\n   this opt-in?\n\nÆvar Arnfjörð Bjarmason (8):\n  push tests: remove redundant 'git push' invocation\n  push tests: fix logic error in \"push\" test assertion\n  push tests: add more testing for forced tag pushing\n  push tests: assert re-pushing annotated tags\n  push doc: correct lies about how push refspecs work\n  fetch tests: correct a comment \"remove it\" -> \"remove them\"\n  fetch tests: add a test clobbering tag behavior\n  fetch: stop clobbering existing tags without --force\n\n Documentation/fetch-options.txt    | 15 ++++--\n Documentation/git-push.txt         | 30 ++++++++---\n Documentation/gitrevisions.txt     |  7 +--\n Documentation/pull-fetch-param.txt | 22 +++++---\n builtin/fetch.c                    | 20 +++++---\n t/t5510-fetch.sh                   |  2 +-\n t/t5516-fetch-push.sh              | 82 ++++++++++++++++++++++--------\n t/t5612-clone-refspec.sh           |  4 +-\n 8 files changed, 130 insertions(+), 52 deletions(-)\n\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"346095","messageId":"20180429202100.32353-2-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH 1/8] push tests: remove redundant 'git push' invocation","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-04-29T20:20:53Z","receivedAt":"2018-04-29T20:21:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove an invocation of 'git push' that's exactly the same as the one\non the preceding line. This was seemingly added by mistake in\ndbfeddb12e (\"push: require force for refs under refs/tags/\",\n2012-11-29) and doesn't affect the result of the test, the second\n\"push\" was a no-op as there was nothing new to push.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 82239138d5..7b5a553398 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -973,7 +973,6 @@ test_expect_success 'push requires --force to update lightweight tag' '\n \t\tcd child1 &&\n \t\tgit tag Tag &&\n \t\tgit push ../child2 Tag &&\n-\t\tgit push ../child2 Tag &&\n \t\t>file1 &&\n \t\tgit add file1 &&\n \t\tgit commit -m \"file1\" &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"346096","messageId":"20180429202100.32353-3-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH 2/8] push tests: fix logic error in \"push\" test assertion","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-04-29T20:20:54Z","receivedAt":"2018-04-29T20:21:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a logic error that's been here since this test was added in\ndbfeddb12e (\"push: require force for refs under refs/tags/\",\n2012-11-29).\n\nThe intent of this test is to force-create a new tag pointing to\nHEAD~, and then assert that pushing it doesn't work without --force.\n\nInstead, the code was not creating a new tag at all, and then failing\nto push the previous tag for the unrelated reason of providing a\nrefspec that doesn't make any sense.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 7b5a553398..15c8d5a734 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -979,8 +979,8 @@ test_expect_success 'push requires --force to update lightweight tag' '\n \t\tgit tag -f Tag &&\n \t\ttest_must_fail git push ../child2 Tag &&\n \t\tgit push --force ../child2 Tag &&\n-\t\tgit tag -f Tag &&\n-\t\ttest_must_fail git push ../child2 Tag HEAD~ &&\n+\t\tgit tag -f Tag HEAD~ &&\n+\t\ttest_must_fail git push ../child2 Tag &&\n \t\tgit push --force ../child2 Tag\n \t)\n '\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"346097","messageId":"20180429202100.32353-4-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH 3/8] push tests: add more testing for forced tag pushing","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-04-29T20:20:55Z","receivedAt":"2018-04-29T20:21:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Improve the tests added in dbfeddb12e (\"push: require force for refs\nunder refs/tags/\", 2012-11-29) to assert that the same behavior\napplies various forms other refspecs, and that \"+\" in a refspec will\noverride the \"--no-force\" option (but not the other way around).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 12 +++++++++++-\n 1 file changed, 11 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 15c8d5a734..c9a2011915 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -981,7 +981,17 @@ test_expect_success 'push requires --force to update lightweight tag' '\n \t\tgit push --force ../child2 Tag &&\n \t\tgit tag -f Tag HEAD~ &&\n \t\ttest_must_fail git push ../child2 Tag &&\n-\t\tgit push --force ../child2 Tag\n+\t\tgit push --force ../child2 Tag &&\n+\t\tgit tag -f Tag &&\n+\t\ttest_must_fail git push ../child2 \"refs/tags/*:refs/tags/*\" &&\n+\t\tgit push --force ../child2 \"refs/tags/*:refs/tags/*\" &&\n+\t\tgit tag -f Tag HEAD~ &&\n+\t\tgit push ../child2 \"+refs/tags/*:refs/tags/*\" &&\n+\t\tgit tag -f Tag &&\n+\t\tgit push --no-force ../child2 \"+refs/tags/*:refs/tags/*\" &&\n+\t\tgit tag -f Tag HEAD~ &&\n+\t\ttest_must_fail git push ../child2 tag Tag &&\n+\t\tgit push --force ../child2 tag Tag\n \t)\n '\n \n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"346098","messageId":"20180429202100.32353-5-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH 4/8] push tests: assert re-pushing annotated tags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-04-29T20:20:56Z","receivedAt":"2018-04-29T20:21:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the test that asserts that lightweight tags can only be\nclobbered by a force-push to check do the same tests for annotated\ntags.\n\nThere used to be less exhaustive tests for this with the code added in\n40eff17999 (\"push: require force for annotated tags\", 2012-11-29), but\nJunio removed them in 256b9d70a4 (\"push: fix \"refs/tags/ hierarchy\ncannot be updated without --force\"\", 2013-01-16) while fixing some of\nthe behavior around tag pushing.\n\nThat change left us without any coverage asserting that pushing and\nclobbering annotated tags worked as intended.  There was no reason to\nsuspect that the receive machinery wouldn't behave the same way with\nannotated tags, but now we know for sure.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 66 ++++++++++++++++++++++++-------------------\n 1 file changed, 37 insertions(+), 29 deletions(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex c9a2011915..71fc902062 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -965,35 +965,43 @@ test_expect_success 'push into aliased refs (inconsistent)' '\n \t)\n '\n \n-test_expect_success 'push requires --force to update lightweight tag' '\n-\tmk_test testrepo heads/master &&\n-\tmk_child testrepo child1 &&\n-\tmk_child testrepo child2 &&\n-\t(\n-\t\tcd child1 &&\n-\t\tgit tag Tag &&\n-\t\tgit push ../child2 Tag &&\n-\t\t>file1 &&\n-\t\tgit add file1 &&\n-\t\tgit commit -m \"file1\" &&\n-\t\tgit tag -f Tag &&\n-\t\ttest_must_fail git push ../child2 Tag &&\n-\t\tgit push --force ../child2 Tag &&\n-\t\tgit tag -f Tag HEAD~ &&\n-\t\ttest_must_fail git push ../child2 Tag &&\n-\t\tgit push --force ../child2 Tag &&\n-\t\tgit tag -f Tag &&\n-\t\ttest_must_fail git push ../child2 \"refs/tags/*:refs/tags/*\" &&\n-\t\tgit push --force ../child2 \"refs/tags/*:refs/tags/*\" &&\n-\t\tgit tag -f Tag HEAD~ &&\n-\t\tgit push ../child2 \"+refs/tags/*:refs/tags/*\" &&\n-\t\tgit tag -f Tag &&\n-\t\tgit push --no-force ../child2 \"+refs/tags/*:refs/tags/*\" &&\n-\t\tgit tag -f Tag HEAD~ &&\n-\t\ttest_must_fail git push ../child2 tag Tag &&\n-\t\tgit push --force ../child2 tag Tag\n-\t)\n-'\n+test_force_push_tag () {\n+\ttag_type_description=$1\n+\ttag_args=$2\n+\n+\ttest_expect_success \"push requires --force to update $tag_type_description\" \"\n+\t\tmk_test testrepo heads/master &&\n+\t\tmk_child testrepo child1 &&\n+\t\tmk_child testrepo child2 &&\n+\t\t(\n+\t\t\tcd child1 &&\n+\t\t\tgit tag Tag &&\n+\t\t\tgit push ../child2 Tag &&\n+\t\t\t>file1 &&\n+\t\t\tgit add file1 &&\n+\t\t\tgit commit -m 'file1' &&\n+\t\t\tgit tag $tag_args Tag &&\n+\t\t\ttest_must_fail git push ../child2 Tag &&\n+\t\t\tgit push --force ../child2 Tag &&\n+\t\t\tgit tag $tag_args Tag HEAD~ &&\n+\t\t\ttest_must_fail git push ../child2 Tag &&\n+\t\t\tgit push --force ../child2 Tag &&\n+\t\t\tgit tag $tag_args Tag &&\n+\t\t\ttest_must_fail git push ../child2 'refs/tags/*:refs/tags/*' &&\n+\t\t\tgit push --force ../child2 'refs/tags/*:refs/tags/*' &&\n+\t\t\tgit tag $tag_args Tag HEAD~ &&\n+\t\t\tgit push ../child2 '+refs/tags/*:refs/tags/*' &&\n+\t\t\tgit tag $tag_args Tag &&function\n+\t\t\tgit push --no-force ../child2 '+refs/tags/*:refs/tags/*' &&\n+\t\t\tgit tag $tag_args Tag HEAD~ &&\n+\t\t\ttest_must_fail git push ../child2 tag Tag &&\n+\t\t\tgit push --force ../child2 tag Tag\n+\t\t)\n+\t\"\n+}\n+\n+test_force_push_tag \"lightweight tag\" \"-f\"\n+test_force_push_tag \"annotated tag\" \"-f -a -m'msg'\"\n \n test_expect_success 'push --porcelain' '\n \tmk_empty testrepo &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"346099","messageId":"20180429202100.32353-7-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH 6/8] fetch tests: correct a comment \"remove it\" -> \"remove them\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-04-29T20:20:58Z","receivedAt":"2018-04-29T20:21:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Correct a comment referring to the removal of just the branch to also\nrefer to the tag. This should have been changed in my\nca3065e7e7 (\"fetch tests: add a tag to be deleted to the pruning\ntests\", 2018-02-09) when the tag deletion was added, but I missed it\nat the time.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex ae5a530a2d..9bd2783521 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -613,7 +613,7 @@ test_configured_prune_type () {\n \t\t\tgit rev-parse --verify refs/tags/newtag\n \t\t) &&\n \n-\t\t# now remove it\n+\t\t# now remove them\n \t\tgit branch -d newbranch &&\n \t\tgit tag -d newtag &&\n \n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"346100","messageId":"20180429202100.32353-6-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH 5/8] push doc: correct lies about how push refspecs work","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-04-29T20:20:57Z","receivedAt":"2018-04-29T20:21:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"There's complex rules governing whether a push is allowed to take\nplace depending on whether we're pushing to refs/heads/*, refs/tags/*\nor refs/not-that/*. See is_branch() in refs.c, and the various\nassertions in refs/files-backend.c. (e.g. \"trying to write non-commit\nobject %s to branch '%s'\").\n\nThis documentation has never been quite correct, but went downhill\nafter dbfeddb12e (\"push: require force for refs under refs/tags/\",\n2012-11-29) when we started claiming that <dst> couldn't be a tag\nobject, which is incorrect. After some of the logic in that patch was\nchanged in 256b9d70a4 (\"push: fix \"refs/tags/ hierarchy cannot be\nupdated without --force\"\", 2013-01-16) the docs weren't updated, and\nwe've had some version of documentation that confused whether <src>\nwas a tag or not with whether <dst> would accept either an annotated\ntag object or the commit it points to.\n\nThis makes the intro somewhat more verbose & complex, perhaps we\nshould have a shorter description here and split the full complexity\ninto a dedicated section. Very few users will find themselves needing\nto e.g. push blobs or trees to refs/custom-namespace/* (or blobs or\ntrees at all), and that could be covered separately as an advanced\ntopic.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-push.txt     | 30 ++++++++++++++++++++++--------\n Documentation/gitrevisions.txt |  7 ++++---\n 2 files changed, 26 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 5b08302fc2..806c3d8c65 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -60,8 +60,10 @@ OPTIONS[[OPTIONS]]\n \tby a colon `:`, followed by the destination ref <dst>.\n +\n The <src> is often the name of the branch you would want to push, but\n-it can be any arbitrary \"SHA-1 expression\", such as `master~4` or\n-`HEAD` (see linkgit:gitrevisions[7]).\n+it can be any arbitrary \"SHA-1 expression\" referring to a branch, such\n+as `master~4` or `HEAD` (see linkgit:gitrevisions[7]). It can also\n+refer to tag objects, trees or blobs if the <dst> is outside of\n+`refs/heads/*`.\n +\n The <dst> tells which ref on the remote side is updated with this\n push. Arbitrary expressions cannot be used here, an actual ref must\n@@ -74,12 +76,24 @@ without any `<refspec>` on the command line.  Otherwise, missing\n `:<dst>` means to update the same ref as the `<src>`.\n +\n The object referenced by <src> is used to update the <dst> reference\n-on the remote side.  By default this is only allowed if <dst> is not\n-a tag (annotated or lightweight), and then only if it can fast-forward\n-<dst>.  By having the optional leading `+`, you can tell Git to update\n-the <dst> ref even if it is not allowed by default (e.g., it is not a\n-fast-forward.)  This does *not* attempt to merge <src> into <dst>.  See\n-EXAMPLES below for details.\n+on the remote side. Whether this is allowed depends on what where in\n+`refs/*` the <dst> reference lives. The `refs/heads/*` namespace will\n+only accept commit objects, and then only they can be\n+fast-forwarded. The `refs/tags/*` namespace will accept any kind of\n+object, but there commit objects are known as lightweight tags, and\n+any changes to them and others types of objects will be\n+rejected. Finally and most confusingly, it's possible to push any type\n+of object to any namespace outside of `refs/{tags,heads}/*`, but these\n+will be treated as branches, even in the case where a tag object is\n+pushed. That tag object will be overwritten by another tag object (or\n+commit!) without `--force` if the new tag happens to point to a commit\n+that's a fast-forward of the commit it replaces.\n++\n+By having the optional leading `+`, you can tell Git to update the\n+<dst> ref even if it is not allowed by its respective namespace\n+clobbering rules (e.g., it is not a fast-forward. in the case of\n+`refs/heads/*` updates) This does *not* attempt to merge <src> into\n+<dst>.  See EXAMPLES below for details.\n +\n `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.\n +\ndiff --git a/Documentation/gitrevisions.txt b/Documentation/gitrevisions.txt\nindex 27dec5b91d..1b79cf1634 100644\n--- a/Documentation/gitrevisions.txt\n+++ b/Documentation/gitrevisions.txt\n@@ -19,9 +19,10 @@ walk the revision graph (such as linkgit:git-log[1]), all commits which are\n reachable from that commit. For commands that walk the revision graph one can\n also specify a range of revisions explicitly.\n \n-In addition, some Git commands (such as linkgit:git-show[1]) also take\n-revision parameters which denote other objects than commits, e.g. blobs\n-(\"files\") or trees (\"directories of files\").\n+In addition, some Git commands (such as linkgit:git-show[1] and\n+linkgit:git-push[1]) can also take revision parameters which denote\n+other objects than commits, e.g. blobs (\"files\") or trees\n+(\"directories of files\").\n \n include::revisions.txt[]\n \n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"346101","messageId":"20180429202100.32353-9-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH 8/8] fetch: stop clobbering existing tags without --force","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-04-29T20:21:00Z","receivedAt":"2018-04-29T20:21:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change \"fetch\" to treat \"+\" in refspecs (aka --force) to mean we\nshould clobber a local tag of the same name.\n\nThis changes the long-standing behavior of \"fetch\" added in\n853a3697dc (\"[PATCH] Multi-head fetch.\", 2005-08-20), before this\nchange all tag fetches effectively had --force enabled. The original\nrationale in that change was:\n\n    > Tags need not be pointing at commits so there is no way to\n    > guarantee \"fast-forward\" anyway.\n\nThat comment and the rest of the history of \"fetch\" shows that the\n\"+\" (--force) part of refpecs was only conceived for branch updates,\nwhile tags have accepted any changes from upstream unconditionally and\nclobbered the local tag object. Changing this behavior has been\ndiscussed as early as 2011[1].\n\nI the current behavior doesn't make sense, it easily results in local\ntags accidentally being clobbered. Ideally we'd namespace our tags\nper-remote, but as with my 97716d217c (\"fetch: add a --prune-tags\noption and fetch.pruneTags config\", 2018-02-09) it's easier to work\naround the current implementation than to fix the root cause, so this\nimplements suggestion #1 from [1], \"fetch\" now only clobbers the tag\nif either \"+\" is provided as part of the refspec, or if \"--force\" is\nprovided on the command-line.\n\nThis also makes it nicely symmetrical with how \"tag\" itself\nworks. We'll now refuse to clobber any existing tags unless \"--force\"\nis supplied, whether that clobbering would happen by clobbering a\nlocal tag with \"tag\", or by fetching it from the remote with \"fetch\".\n\nIt's still not at all nicely symmetrical with how \"git push\" works, as\ndiscussed in the updated pull-fetch-param.txt documentation, but this\nchange brings them more into line with one another. I don't think\nthere's any reason \"fetch\" couldn't fully converge with the behavior\nused by \"push\", but that's a topic for another change.\n\nOne of the tests added in 31b808a032 (\"clone --single: limit the fetch\nrefspec to fetched branch\", 2012-09-20) is being changed to use\n--force where a clone would clobber a tag. This changes nothing about\nthe existing behavior of the test.\n\n1. https://public-inbox.org/git/20111123221658.GA22313@sigill.intra.peff.net/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/fetch-options.txt    | 15 ++++++++++-----\n Documentation/pull-fetch-param.txt | 22 ++++++++++++++++------\n builtin/fetch.c                    | 20 +++++++++++++-------\n t/t5516-fetch-push.sh              |  5 +++--\n t/t5612-clone-refspec.sh           |  4 ++--\n 5 files changed, 44 insertions(+), 22 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 8631e365f4..5b4fc36866 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -49,11 +49,16 @@ endif::git-pull[]\n \n -f::\n --force::\n-\tWhen 'git fetch' is used with `<rbranch>:<lbranch>`\n-\trefspec, it refuses to update the local branch\n-\t`<lbranch>` unless the remote branch `<rbranch>` it\n-\tfetches is a descendant of `<lbranch>`.  This option\n-\toverrides that check.\n+\tWhen 'git fetch' is used with `<src>:<dst>` refspec it might\n+\trefuse to update the local branch as discussed\n+ifdef::git-pull[]\n+\tin the `<refspec>` part of the linkgit:git-fetch[1]\n+\tdocumentation.\n+endif::git-pull[]\n+ifndef::git-pull[]\n+\tin the `<refspec>` part below.\n+endif::git-pull[]\n+\tThis option overrides that check.\n \n -k::\n --keep::\ndiff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt\nindex c579793af5..672e8bc1c0 100644\n--- a/Documentation/pull-fetch-param.txt\n+++ b/Documentation/pull-fetch-param.txt\n@@ -32,12 +32,22 @@ name.\n `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;\n it requests fetching everything up to the given tag.\n +\n-The remote ref that matches <src>\n-is fetched, and if <dst> is not empty string, the local\n-ref that matches it is fast-forwarded using <src>.\n-If the optional plus `+` is used, the local ref\n-is updated even if it does not result in a fast-forward\n-update.\n+The remote ref that matches <src> is fetched, and if <dst> is not\n+empty string, an attempt is made to update the local ref that matches\n+it.\n++\n+Whether that update is allowed is confusingly not the inverse of\n+whether a server will accept a push as described in the `<refspec>...`\n+section of linkgit:git-push[1]. If it's a commit under `refs/heads/*`\n+only fast-forwards are allowed, but unlike what linkgit:git-push[1]\n+will accept clobbering any ref pointing to blobs, trees etc. in any\n+other namespace will be accepted, but commits in any ref\n+namespace. Those apply the same fast-forward rule. An exception to\n+this is that as of Git version 2.18 any object under `refs/tags/*` is\n+protected from updates.\n++\n+If the optional plus `+` is used, the local ref is updated if the\n+update would have otherwise been rejected.\n +\n [NOTE]\n When the remote branch you want to fetch is known to\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex dcdfc66f09..e3a44b582a 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -126,7 +126,7 @@ static struct option builtin_fetch_options[] = {\n \t\t N_(\"append to .git/FETCH_HEAD instead of overwriting\")),\n \tOPT_STRING(0, \"upload-pack\", &upload_pack, N_(\"path\"),\n \t\t   N_(\"path to upload pack on remote end\")),\n-\tOPT__FORCE(&force, N_(\"force overwrite of local branch\"), 0),\n+\tOPT__FORCE(&force, N_(\"force overwrite of local reference\"), 0),\n \tOPT_BOOL('m', \"multiple\", &multiple,\n \t\t N_(\"fetch from multiple remotes\")),\n \tOPT_SET_INT('t', \"tags\", &tags,\n@@ -664,12 +664,18 @@ static int update_local_ref(struct ref *ref,\n \n \tif (!is_null_oid(&ref->old_oid) &&\n \t    starts_with(ref->name, \"refs/tags/\")) {\n-\t\tint r;\n-\t\tr = s_update_ref(\"updating tag\", ref, 0);\n-\t\tformat_display(display, r ? '!' : 't', _(\"[tag update]\"),\n-\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n-\t\t\t       remote, pretty_ref, summary_width);\n-\t\treturn r;\n+\t\tif (force || ref->force) {\n+\t\t\tint r;\n+\t\t\tr = s_update_ref(\"updating tag\", ref, 0);\n+\t\t\tformat_display(display, r ? '!' : 't', _(\"[tag update]\"),\n+\t\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t\t       remote, pretty_ref, summary_width);\n+\t\t\treturn r;\n+\t\t} else {\n+\t\t\tformat_display(display, '!', _(\"[rejected]\"), _(\"would clobber existing tag\"),\n+\t\t\t\t       remote, pretty_ref, summary_width);\n+\t\t\treturn 1;\n+\t\t}\n \t}\n \n \tcurrent = lookup_commit_reference_gently(&ref->old_oid, 1);\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 9cf14c5cc1..327737ecaf 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1007,7 +1007,7 @@ test_force_fetch_tag () {\n \ttag_type_description=$1\n \ttag_args=$2\n \n-\ttest_expect_success \"fetch will clobber an existing $tag_type_description\" \"\n+\ttest_expect_success \"fetch will not clobber an existing $tag_type_description without --force\" \"\n \t\tmk_test testrepo heads/master &&\n \t\tmk_child testrepo child1 &&\n \t\tmk_child testrepo child2 &&\n@@ -1019,7 +1019,8 @@ test_force_fetch_tag () {\n \t\t\tgit add file1 &&\n \t\t\tgit commit -m 'file1' &&\n \t\t\tgit tag $tag_args Tag &&\n-\t\t\tgit -C ../child1 fetch origin tag Tag\n+\t\t\ttest_must_fail git -C ../child1 fetch origin tag Tag &&\n+\t\t\tgit -C ../child1 fetch origin '+refs/tags/*:refs/tags/*'\n \t\t)\n \t\"\n }\ndiff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh\nindex fac5a73851..6ea8f50dae 100755\n--- a/t/t5612-clone-refspec.sh\n+++ b/t/t5612-clone-refspec.sh\n@@ -104,7 +104,7 @@ test_expect_success 'clone with --no-tags' '\n test_expect_success '--single-branch while HEAD pointing at master' '\n \t(\n \t\tcd dir_master &&\n-\t\tgit fetch &&\n+\t\tgit fetch --force &&\n \t\tgit for-each-ref refs/remotes/origin |\n \t\tsed -e \"/HEAD$/d\" \\\n \t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n@@ -115,7 +115,7 @@ test_expect_success '--single-branch while HEAD pointing at master' '\n \ttest_cmp expect actual &&\n \t(\n \t\tcd dir_master &&\n-\t\tgit fetch --tags &&\n+\t\tgit fetch --tags --force &&\n \t\tgit for-each-ref refs/tags >../actual\n \t) &&\n \tgit for-each-ref refs/tags >expect &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"346102","messageId":"20180429202100.32353-8-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH 7/8] fetch tests: add a test clobbering tag behavior","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-04-29T20:20:59Z","receivedAt":"2018-04-29T20:21:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The test suite only incidentally (and unintentionally) tested for the\ncurrent behavior of eager tag clobbering on \"fetch\". This follow-up to\nthe previous \"push tests: assert re-pushing annotated tags\" change\ntests for it explicitly.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 24 ++++++++++++++++++++++++\n 1 file changed, 24 insertions(+)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 71fc902062..9cf14c5cc1 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1003,6 +1003,30 @@ test_force_push_tag () {\n test_force_push_tag \"lightweight tag\" \"-f\"\n test_force_push_tag \"annotated tag\" \"-f -a -m'msg'\"\n \n+test_force_fetch_tag () {\n+\ttag_type_description=$1\n+\ttag_args=$2\n+\n+\ttest_expect_success \"fetch will clobber an existing $tag_type_description\" \"\n+\t\tmk_test testrepo heads/master &&\n+\t\tmk_child testrepo child1 &&\n+\t\tmk_child testrepo child2 &&\n+\t\t(\n+\t\t\tcd testrepo &&\n+\t\t\tgit tag Tag &&\n+\t\t\tgit -C ../child1 fetch origin tag Tag &&\n+\t\t\t>file1 &&\n+\t\t\tgit add file1 &&\n+\t\t\tgit commit -m 'file1' &&\n+\t\t\tgit tag $tag_args Tag &&\n+\t\t\tgit -C ../child1 fetch origin tag Tag\n+\t\t)\n+\t\"\n+}\n+\n+test_force_fetch_tag \"lightweight tag\" \"-f\"\n+test_force_fetch_tag \"annotated tag\" \"-f -a -m'msg'\"\n+\n test_expect_success 'push --porcelain' '\n \tmk_empty testrepo &&\n \techo >.git/foo  \"To testrepo\" &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"346297","messageId":"20180501165931.25515-1-wink@saville.com","threadId":"48353","inReplyTo":"xmqqbme51rgn.fsf@gitster-ct.c.googlers.com","subject":"[RFC PATCH v4 0/3] Optional sub hierarchy for remote tags","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-05-01T16:59:28Z","receivedAt":"2018-05-01T16:59:41Z","isPatch":true,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"As discussed on the git email list [1] with the subject\n\"Fetching tags overwrites existing tags\" there is a bug\nwhere fetching tags can overwrite existing tags.\n\nÆvar Arnfjörð Bjarmasono, has created a patch series [2]\nwhich requires a '--force' flag when fetching if the user\nactually wants to overwrite any existing flags.\n\nThis patch series allows tags in remotes to be placed in\na sub hierarchy, refs/remote-tags when \"git remote add\" is\npassed the --remote-tags option.\n\nThe patch series also modifies tag so \"git tag -l\" will list\ntags in refs/remote-tags properly. It also test for\n\"git remote add\" which uses \"git tag -l\".\n\n[1]: https://public-inbox.org/git/xmqqbme51rgn.fsf@gitster-ct.c.googlers.com/T/#m4928340a9f49c6ffa9db5b94243e46df6958eaa5\n[2]: https://public-inbox.org/git/xmqqbme51rgn.fsf@gitster-ct.c.googlers.com/T/#mb68d54667a9ad8eee60a22ebdf2264f8022d07a8\n\nWink Saville (3):\n  Teach remote add the --remote-tags option\n  Teach tag to list remote-tags\n  Test git remote add -f --remote-tags\n\n Documentation/git-remote.txt |  8 +++++--\n builtin/remote.c             | 44 ++++++++++++++++++++++++++++++++----\n builtin/tag.c                |  2 +-\n ref-filter.c                 |  9 ++++++--\n ref-filter.h                 | 11 +++++----\n refs.c                       |  1 +\n remote.c                     |  2 ++\n t/t5505-remote.sh            | 20 ++++++++++++++++\n 8 files changed, 83 insertions(+), 14 deletions(-)\n\n-- \n2.17.0.393.g4573f5e134\n\n"},{"id":"346298","messageId":"20180501165931.25515-2-wink@saville.com","threadId":"48353","inReplyTo":"20180501165931.25515-1-wink@saville.com","subject":"[RFC PATCH v4 1/3] Teach remote add the --remote-tags option","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-05-01T16:59:29Z","receivedAt":"2018-05-01T16:59:44Z","isPatch":true,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"When --remote-tags is passed to `git remote add` the tagopt is set to\n--remote-tags and a second fetch line is added so tags are placed in\na separate hierarchy per remote.\n\nFor example:\n  $ git remote add -f --remote-tags gbenchmark git@github.com:google/benchmark\n  Updating gbenchmark\n  warning: no common commits\n  remote: Counting objects: 4406, done.\n  remote: Compressing objects: 100% (18/18), done.\n  remote: Total 4406 (delta 7), reused 13 (delta 6), pack-reused 4382\n  Receiving objects: 100% (4406/4406), 1.34 MiB | 7.58 MiB/s, done.\n  Resolving deltas: 100% (2865/2865), done.\n  From github.com:google/benchmark\n   * [new branch]      clangtidy       -> gbenchmark/clangtidy\n   * [new branch]      iter_report     -> gbenchmark/iter_report\n   * [new branch]      master          -> gbenchmark/master\n   * [new branch]      releasing       -> gbenchmark/releasing\n   * [new branch]      reportercleanup -> gbenchmark/reportercleanup\n   * [new branch]      rmheaders       -> gbenchmark/rmheaders\n   * [new branch]      v2              -> gbenchmark/v2\n   * [new tag]         v0.0.9          -> gbenchmark/v0.0.9\n   * [new tag]         v0.1.0          -> gbenchmark/v0.1.0\n   * [new tag]         v1.0.0          -> gbenchmark/v1.0.0\n   * [new tag]         v1.1.0          -> gbenchmark/v1.1.0\n   * [new tag]         v1.2.0          -> gbenchmark/v1.2.0\n   * [new tag]         v1.3.0          -> gbenchmark/v1.3.0\n   * [new tag]         v1.4.0          -> gbenchmark/v1.4.0\n\nAnd the .git/config remote \"gbenchmark\" section looks like:\n  [remote \"gbenchmark\"]\n    url = git@github.com:google/benchmark\n    fetch = +refs/heads/*:refs/remotes/gbenchmark/*\n    fetch = +refs/tags/*:refs/remote-tags/gbenchmark/*\n    tagopt = --remote-tags\n\nBased on a solution proposed by Junio on the email list [1]\n\n[1]: https://public-inbox.org/git/xmqqbme51rgn.fsf@gitster-ct.c.googlers.com/T/#me7f7f153b8ba742c0dc48d8ec79c280c9682d32e\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Jacob Keller <jacob.keller@gmail.com>\nHelped-by: Bryan Turner <bturner@atlassian.com>\nHelped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Wink Saville <wink@saville.com>\n---\n Documentation/git-remote.txt |  8 +++++--\n builtin/remote.c             | 44 ++++++++++++++++++++++++++++++++----\n refs.c                       |  1 +\n remote.c                     |  2 ++\n 4 files changed, 49 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 4feddc0293..fc983c2ff1 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git remote' [-v | --verbose]\n-'git remote add' [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=<fetch|push>] <name> <url>\n+'git remote add' [-t <branch>] [-m <master>] [-f] [--tags | --remote-tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\n 'git remote rename' <old> <new>\n 'git remote remove' <name>\n 'git remote set-head' <name> (-a | --auto | -d | --delete | <branch>)\n@@ -54,7 +54,11 @@ With `-f` option, `git fetch <name>` is run immediately after\n the remote information is set up.\n +\n With `--tags` option, `git fetch <name>` imports every tag from the\n-remote repository.\n+remote repository to refs/tags, use --remote-tags to import them\n+to refs/remote-tags/<name>/<tag>.\n++\n+With `--remote-tags` option, `git fetch <name>` imports every tag from the\n+remote repository to refs/remote-tags/<name>/<tag>.\n +\n With `--no-tags` option, `git fetch <name>` does not import tags from\n the remote repository.\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 805ffc05cd..07832113e9 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -11,7 +11,7 @@\n \n static const char * const builtin_remote_usage[] = {\n \tN_(\"git remote [-v | --verbose]\"),\n-\tN_(\"git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\"),\n+\tN_(\"git remote add [-t <branch>] [-m <master>] [-f] [--tags | --remote-tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\"),\n \tN_(\"git remote rename <old> <new>\"),\n \tN_(\"git remote remove <name>\"),\n \tN_(\"git remote set-head <name> (-a | --auto | -d | --delete | <branch>)\"),\n@@ -101,7 +101,8 @@ static int fetch_remote(const char *name)\n enum {\n \tTAGS_UNSET = 0,\n \tTAGS_DEFAULT = 1,\n-\tTAGS_SET = 2\n+\tTAGS_SET = 2,\n+\tTAGS_SET_REMOTE = 3\n };\n \n #define MIRROR_NONE 0\n@@ -123,6 +124,14 @@ static void add_branch(const char *key, const char *branchname,\n \tgit_config_set_multivar(key, tmp->buf, \"^$\", 0);\n }\n \n+static void add_remote_tags(const char *key, const char *remotename,\n+\t\t\t    struct strbuf *tmp)\n+{\n+\tstrbuf_reset(tmp);\n+\tstrbuf_addf(tmp, \"+refs/tags/*:refs/remote-tags/%s/*\", remotename);\n+\tgit_config_set_multivar(key, tmp->buf, \"^$\", 0);\n+}\n+\n static const char mirror_advice[] =\n N_(\"--mirror is dangerous and deprecated; please\\n\"\n    \"\\t use --mirror=fetch or --mirror=push instead\");\n@@ -161,6 +170,9 @@ static int add(int argc, const char **argv)\n \t\tOPT_SET_INT(0, \"tags\", &fetch_tags,\n \t\t\t    N_(\"import all tags and associated objects when fetching\"),\n \t\t\t    TAGS_SET),\n+\t\tOPT_SET_INT(0, \"remote-tags\", &fetch_tags,\n+\t\t\t    N_(\"import all tags and associated objects when fetching to refs/remote-tags/<name>/<tag>\"),\n+\t\t\t    TAGS_SET_REMOTE),\n \t\tOPT_SET_INT(0, NULL, &fetch_tags,\n \t\t\t    N_(\"or do not fetch any tag at all (--no-tags)\"), TAGS_UNSET),\n \t\tOPT_STRING_LIST('t', \"track\", &track, N_(\"branch\"),\n@@ -182,6 +194,10 @@ static int add(int argc, const char **argv)\n \t\tdie(_(\"specifying a master branch makes no sense with --mirror\"));\n \tif (mirror && !(mirror & MIRROR_FETCH) && track.nr)\n \t\tdie(_(\"specifying branches to track makes sense only with fetch mirrors\"));\n+\tif ((fetch_tags == TAGS_SET_REMOTE) && mirror)\n+\t\tdie(_(\"specifying --remote-tags makes no sense with --mirror\"));\n+\tif ((fetch_tags == TAGS_SET_REMOTE) && track.nr)\n+\t\tdie(_(\"specifying --remote-tags makes no sense with -t or --track\"));\n \n \tname = argv[0];\n \turl = argv[1];\n@@ -215,10 +231,30 @@ static int add(int argc, const char **argv)\n \t}\n \n \tif (fetch_tags != TAGS_DEFAULT) {\n+\t\tif (fetch_tags == TAGS_SET_REMOTE) {\n+\t\t\tstrbuf_reset(&buf);\n+\t\t\tstrbuf_addf(&buf, \"remote.%s.fetch\", name);\n+\t\t\tadd_remote_tags(buf.buf, name, &buf2);\n+\t\t}\n+\n \t\tstrbuf_reset(&buf);\n \t\tstrbuf_addf(&buf, \"remote.%s.tagopt\", name);\n-\t\tgit_config_set(buf.buf,\n-\t\t\t       fetch_tags == TAGS_SET ? \"--tags\" : \"--no-tags\");\n+\t\tchar *config_val = NULL;\n+\t\tswitch (fetch_tags) {\n+\t\tcase TAGS_UNSET:\n+\t\t\tconfig_val = \"--no-tags\";\n+\t\t\tbreak;\n+\t\tcase TAGS_SET:\n+\t\t\tconfig_val = \"--tags\";\n+\t\t\tbreak;\n+\t\tcase TAGS_SET_REMOTE:\n+\t\t\tconfig_val = \"--remote-tags\";\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tdie(_(\"Unexpected TAGS enum %d\"), fetch_tags);\n+\t\t\tbreak;\n+\t\t}\n+\t\tgit_config_set(buf.buf, config_val);\n \t}\n \n \tif (fetch && fetch_remote(name))\ndiff --git a/refs.c b/refs.c\nindex 8b7a77fe5e..4075150e26 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -472,6 +472,7 @@ const char *prettify_refname(const char *name)\n {\n \tif (skip_prefix(name, \"refs/heads/\", &name) ||\n \t    skip_prefix(name, \"refs/tags/\", &name) ||\n+\t    skip_prefix(name, \"refs/remote-tags/\", &name) ||\n \t    skip_prefix(name, \"refs/remotes/\", &name))\n \t\t; /* nothing */\n \treturn name;\ndiff --git a/remote.c b/remote.c\nindex 91eb010ca9..86cccc0b6e 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -447,6 +447,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\t\tremote->fetch_tags = -1;\n \t\telse if (!strcmp(value, \"--tags\"))\n \t\t\tremote->fetch_tags = 2;\n+\t\telse if (!strcmp(value, \"--remote-tags\"))\n+\t\t\tremote->fetch_tags = -1;\n \t} else if (!strcmp(subkey, \"proxy\")) {\n \t\treturn git_config_string((const char **)&remote->http_proxy,\n \t\t\t\t\t key, value);\n-- \n2.17.0.393.g4573f5e134\n\n"},{"id":"346299","messageId":"20180501165931.25515-3-wink@saville.com","threadId":"48353","inReplyTo":"20180501165931.25515-1-wink@saville.com","subject":"[RFC PATCH v4 2/3] Teach tag to list remote-tags","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-05-01T16:59:30Z","receivedAt":"2018-05-01T16:59:47Z","isPatch":true,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"Add FILTER_REFS_REMOTE_TAGS to allow tags to be to identified as a\nremote-tags and then list them without displaying refs/remote-tags.\n\nSigned-off-by: Wink Saville <wink@saville.com>\n---\n builtin/tag.c |  2 +-\n ref-filter.c  |  9 +++++++--\n ref-filter.h  | 11 ++++++-----\n 3 files changed, 14 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 46a5c6a1da..9ac85f0ff8 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -57,7 +57,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,\n \tif (verify_ref_format(format))\n \t\tdie(_(\"unable to parse format string\"));\n \tfilter->with_commit_tag_algo = 1;\n-\tfilter_refs(&array, filter, FILTER_REFS_TAGS);\n+\tfilter_refs(&array, filter, (FILTER_REFS_TAGS | FILTER_REFS_REMOTE_TAGS));\n \tref_array_sort(sorting, &array);\n \n \tfor (i = 0; i < array.nr; i++)\ndiff --git a/ref-filter.c b/ref-filter.c\nindex ac82f9f21e..1fcaa2ea33 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1682,6 +1682,7 @@ static int match_pattern(const struct ref_filter *filter, const char *refname)\n \t * for matching refs of tags and branches.\n \t */\n \t(void)(skip_prefix(refname, \"refs/tags/\", &refname) ||\n+\t       skip_prefix(refname, \"refs/remote-tags/\", &refname) ||\n \t       skip_prefix(refname, \"refs/heads/\", &refname) ||\n \t       skip_prefix(refname, \"refs/remotes/\", &refname) ||\n \t       skip_prefix(refname, \"refs/\", &refname));\n@@ -1866,7 +1867,8 @@ static int ref_kind_from_refname(const char *refname)\n \t} ref_kind[] = {\n \t\t{ \"refs/heads/\" , FILTER_REFS_BRANCHES },\n \t\t{ \"refs/remotes/\" , FILTER_REFS_REMOTES },\n-\t\t{ \"refs/tags/\", FILTER_REFS_TAGS}\n+\t\t{ \"refs/tags/\", FILTER_REFS_TAGS},\n+\t\t{ \"refs/remote-tags/\", FILTER_REFS_REMOTE_TAGS}\n \t};\n \n \tif (!strcmp(refname, \"HEAD\"))\n@@ -1884,7 +1886,8 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)\n {\n \tif (filter->kind == FILTER_REFS_BRANCHES ||\n \t    filter->kind == FILTER_REFS_REMOTES ||\n-\t    filter->kind == FILTER_REFS_TAGS)\n+\t    filter->kind == FILTER_REFS_TAGS ||\n+\t    filter->kind == FILTER_REFS_REMOTE_TAGS)\n \t\treturn filter->kind;\n \treturn ref_kind_from_refname(refname);\n }\n@@ -2053,6 +2056,8 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int\n \t\t\tret = for_each_fullref_in(\"refs/remotes/\", ref_filter_handler, &ref_cbdata, broken);\n \t\telse if (filter->kind == FILTER_REFS_TAGS)\n \t\t\tret = for_each_fullref_in(\"refs/tags/\", ref_filter_handler, &ref_cbdata, broken);\n+\t\telse if (filter->kind == FILTER_REFS_REMOTE_TAGS)\n+\t\t\tret = for_each_fullref_in(\"refs/remote-tags/\", ref_filter_handler, &ref_cbdata, broken);\n \t\telse if (filter->kind & FILTER_REFS_ALL)\n \t\t\tret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata, broken);\n \t\tif (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 76cf87cb6c..8b8d725b37 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -15,12 +15,13 @@\n \n #define FILTER_REFS_INCLUDE_BROKEN 0x0001\n #define FILTER_REFS_TAGS           0x0002\n-#define FILTER_REFS_BRANCHES       0x0004\n-#define FILTER_REFS_REMOTES        0x0008\n-#define FILTER_REFS_OTHERS         0x0010\n+#define FILTER_REFS_REMOTE_TAGS    0x0004\n+#define FILTER_REFS_BRANCHES       0x0008\n+#define FILTER_REFS_REMOTES        0x0010\n+#define FILTER_REFS_OTHERS         0x0020\n #define FILTER_REFS_ALL            (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \\\n-\t\t\t\t    FILTER_REFS_REMOTES | FILTER_REFS_OTHERS)\n-#define FILTER_REFS_DETACHED_HEAD  0x0020\n+\t\t\t\t    FILTER_REFS_REMOTES | FILTER_REFS_REMOTE_TAGS | FILTER_REFS_OTHERS)\n+#define FILTER_REFS_DETACHED_HEAD  0x0040\n #define FILTER_REFS_KIND_MASK      (FILTER_REFS_ALL | FILTER_REFS_DETACHED_HEAD)\n \n struct atom_value;\n-- \n2.17.0.393.g4573f5e134\n\n"},{"id":"346300","messageId":"20180501165931.25515-4-wink@saville.com","threadId":"48353","inReplyTo":"20180501165931.25515-1-wink@saville.com","subject":"[RFC PATCH v4 3/3] Test git remote add -f --remote-tags","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-05-01T16:59:31Z","receivedAt":"2018-05-01T16:59:49Z","isPatch":true,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"The test adds and fetches a remote repository and then lists the\nimported remote-tags verifying the correct values.\n\nSigned-off-by: Wink Saville <wink@saville.com>\n---\n t/t5505-remote.sh | 20 ++++++++++++++++++++\n 1 file changed, 20 insertions(+)\n\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex a6c0178f3a..cb30ed5cf2 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -573,6 +573,26 @@ test_expect_success 'add --tags' '\n \ttest_cmp test/expect test/output\n '\n \n+cat >test/expect <<\\EOF\n+origin/some-tag\n+origin/foobar-tag\n+--remote-tags\n+EOF\n+\n+test_expect_success 'add --remote-tags' '\n+\trm -rf add-tags &&\n+\t(\n+\t\tmkdir add-tags &&\n+\t\tcd add-tags &&\n+\t\tgit init &&\n+\t\tgit remote add -f --remote-tags origin ../one &&\n+\t\tgit tag -l origin/some-tag >../test/output &&\n+\t\tgit tag -l origin/foobar-tag >>../test/output &&\n+\t\tgit config remote.origin.tagopt >>../test/output\n+\t) &&\n+\ttest_cmp test/expect test/output\n+'\n+\n cat >test/expect <<\\EOF\n --no-tags\n EOF\n-- \n2.17.0.393.g4573f5e134\n\n"},{"id":"346301","messageId":"CAKk8isoEcgFo=z+_w8Ps_zF_5sPnb2zhO3CvFVS9UAt9MvpoXw@mail.gmail.com","threadId":"48353","inReplyTo":"CAKk8isrAmWOdioJe2CdY+2F_AzKxcTGwd+kBsrAmGx-U=ZHGgA@mail.gmail.com","subject":"Re: Fetching tags overwrites existing tags","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-05-01T17:11:37Z","receivedAt":"2018-05-01T17:12:03Z","isPatch":false,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"I'm going on vacation until May 17 and will have limited connectivity.\nI've created a new patch series, \"Optional sub hierarchy for remote tags\" [1]\nthat I'd appreciate comments and if someone wants to take this over\nin my absence that would be great. I'd sure like to see this continue to move\nforward.\n\nIn v4 I've tweaked git tag -l and added a test, so for me it has the minimal\nfunctionality I'd need to make use of it.\n\n-- Wink\n\nhttps://public-inbox.org/git/xmqqbme51rgn.fsf@gitster-ct.c.googlers.com/T/#m89df9b973ab55d85eae72dd749e038354ea3a250\n"},{"id":"346322","messageId":"871sevyzu4.fsf@evledraar.gmail.com","threadId":"48353","inReplyTo":"20180501165931.25515-2-wink@saville.com","subject":"Re: [RFC PATCH v4 1/3] Teach remote add the --remote-tags option","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-01T18:50:59Z","receivedAt":"2018-05-01T18:51:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, May 01 2018, Wink Saville wrote:\n\n> [1]: https://public-inbox.org/git/xmqqbme51rgn.fsf@gitster-ct.c.googlers.com/T/#me7f7f153b8ba742c0dc48d8ec79c280c9682d32e\n\nBetter to link to https://public-inbox.org/git/xmqqzi1s3y5h.fsf@gitster-ct.c.googlers.com/\n"},{"id":"346323","messageId":"87zi1jxjqn.fsf@evledraar.gmail.com","threadId":"48353","inReplyTo":"20180501165931.25515-1-wink@saville.com","subject":"Re: [RFC PATCH v4 0/3] Optional sub hierarchy for remote tags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-01T19:24:00Z","receivedAt":"2018-05-01T19:24:07Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, May 01 2018, Wink Saville wrote:\n\n> As discussed on the git email list [1] with the subject\n> \"Fetching tags overwrites existing tags\" there is a bug\n> where fetching tags can overwrite existing tags.\n>\n> Ævar Arnfjörð Bjarmasono, has created a patch series [2]\n> which requires a '--force' flag when fetching if the user\n> actually wants to overwrite any existing flags.\n>\n> This patch series allows tags in remotes to be placed in\n> a sub hierarchy, refs/remote-tags when \"git remote add\" is\n> passed the --remote-tags option.\n>\n> The patch series also modifies tag so \"git tag -l\" will list\n> tags in refs/remote-tags properly. It also test for\n> \"git remote add\" which uses \"git tag -l\".\n\nThanks a lot for working on this, it's great to have something the\ndirection of getting rid of this discrepancy between the 1=1 mapping for\nbranches, but the 1=many mapping for tags. My recent patch series and\nthe pruneTags feature I added in 2.17.0 are really just hacks around not\nhaving that.\n\nMy concern with your patches is not that they're not implementing some\nmythical future where we're mapping each <remote>'s refs to\nremotes/<fully qualified ref> and local refs to local/<fully qualified\nref>, which is what we should really be aiming for and would forever get\nus out of this rut of 1=many and local & remote refs existing in the\nsame namespace, but that they might make it harder to get there.\n\nSo specifically, your 1/3 writes this to the config:\n\n  [remote \"gbenchmark\"]\n    url = git@github.com:google/benchmark\n    fetch = +refs/heads/*:refs/remotes/gbenchmark/*\n    fetch = +refs/tags/*:refs/remote-tags/gbenchmark/*\n    tagopt = --remote-tags\n\nIf the user clones with your patch, and then uses an older git version\non the same repo (a use case we need to support) that older version\ndoesn't know about --remote-tags, and will fetch them all.\n\nAs a workaround for that maybe we'll need something like:\n\n  [remote \"gbenchmark\"]\n    url = git@github.com:google/benchmark\n    fetch = +refs/heads/*:refs/remotes/gbenchmark/*\n    fetch = +refs/tags/*:refs/remote-tags/gbenchmark/*\n\ttagStyle = remote\n    tagopt = --no-tags\n\nOr whatever, i.e. something where only the new version will fetch the\ntags and ignore the tagopt option (which I never liked anyway). It's a\nhack, but at least you don't end up with crap it your ref namespace by\nflip-flopping between versions.\n\nThen as I alluded to in my\nhttps://public-inbox.org/git/20180429202100.32353-6-avarab@gmail.com/ we\nhave a lot of stuff that hardcodes special behaviors for\nrefs/{tags,heads}/, including but not limited to:\n\n    git grep -C2 -e TAG_REFSPEC -e tag_refspec -e '\"refs/tags/' -- '*.[ch]'\n\nSo maybe we need to start this series with some set of patches where we\nmake the currently hardcoded behavior for refs/{heads,tags}/\nconfigurable.\n\nSorry about this \"nice shed you built, how about you make a cathedral\ninstead?\" E-Mail. I really don't think we should make perfect the enemy\nof the good, but at the same time it would be unfortunate if we can't\nget perfect because we settled for good.\n"},{"id":"346324","messageId":"CA+P7+xry-dE0-TMtRzNjiJpR1ZVmGXDy5ObfMxKyWA+eR8XMsQ@mail.gmail.com","threadId":"48353","inReplyTo":"87zi1jxjqn.fsf@evledraar.gmail.com","subject":"Re: [RFC PATCH v4 0/3] Optional sub hierarchy for remote tags","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2018-05-01T19:45:56Z","receivedAt":"2018-05-01T19:46:21Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, May 1, 2018 at 12:24 PM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Thanks a lot for working on this, it's great to have something the\n> direction of getting rid of this discrepancy between the 1=1 mapping for\n> branches, but the 1=many mapping for tags. My recent patch series and\n> the pruneTags feature I added in 2.17.0 are really just hacks around not\n> having that.\n>\n> My concern with your patches is not that they're not implementing some\n> mythical future where we're mapping each <remote>'s refs to\n> remotes/<fully qualified ref> and local refs to local/<fully qualified\n> ref>, which is what we should really be aiming for and would forever get\n> us out of this rut of 1=many and local & remote refs existing in the\n> same namespace, but that they might make it harder to get there.\n>\n\nI also agree, I'd prefer if we aim for the mapping to be something\nwhich works for all refs in the future, even if such support isn't\nadded now, which is why i've proposed using \"refs/remote/<name>/\" so\nthat a tag would go from\n\nrefs/tags/v1.7\n\nto\n\nrefs/remote/<name>/tags/v1.7\n\nIdeally, we could work to update \"refs/remotes/<name>\" to go to\n\"refs/remote/<name>/heads\" as well. This allows obtaining any ref and\nmapping it safely per remote. I don't really like the fact that we\ncan't simply re-use \"remotes\", nor do I like the fact that \"remote\" is\nvery close to \"remotes\" increasing the chance of typos... historically\nI suggested using \"tracking\" but I don't really like that either..\nbecause honestly there *is* no good name left for this purpose once\n\"remotes\" became only about branches.\n\nThe reason I care about this is that I do want to be able to share the\nnotes refs in a way that allows easy merging, which currently is not\nreally doable without a lot of work from all users of notes knowing\nexactly how you plan to work on them.\n\nThanks,\nJake\n\n> So specifically, your 1/3 writes this to the config:\n>\n>   [remote \"gbenchmark\"]\n>     url = git@github.com:google/benchmark\n>     fetch = +refs/heads/*:refs/remotes/gbenchmark/*\n>     fetch = +refs/tags/*:refs/remote-tags/gbenchmark/*\n>     tagopt = --remote-tags\n>\n> If the user clones with your patch, and then uses an older git version\n> on the same repo (a use case we need to support) that older version\n> doesn't know about --remote-tags, and will fetch them all.\n>\n> As a workaround for that maybe we'll need something like:\n>\n>   [remote \"gbenchmark\"]\n>     url = git@github.com:google/benchmark\n>     fetch = +refs/heads/*:refs/remotes/gbenchmark/*\n>     fetch = +refs/tags/*:refs/remote-tags/gbenchmark/*\n>         tagStyle = remote\n>     tagopt = --no-tags\n>\n> Or whatever, i.e. something where only the new version will fetch the\n> tags and ignore the tagopt option (which I never liked anyway). It's a\n> hack, but at least you don't end up with crap it your ref namespace by\n> flip-flopping between versions.\n>\n> Then as I alluded to in my\n> https://public-inbox.org/git/20180429202100.32353-6-avarab@gmail.com/ we\n> have a lot of stuff that hardcodes special behaviors for\n> refs/{tags,heads}/, including but not limited to:\n>\n>     git grep -C2 -e TAG_REFSPEC -e tag_refspec -e '\"refs/tags/' -- '*.[ch]'\n>\n> So maybe we need to start this series with some set of patches where we\n> make the currently hardcoded behavior for refs/{heads,tags}/\n> configurable.\n>\n> Sorry about this \"nice shed you built, how about you make a cathedral\n> instead?\" E-Mail. I really don't think we should make perfect the enemy\n> of the good, but at the same time it would be unfortunate if we can't\n> get perfect because we settled for good.\n"},{"id":"346327","messageId":"CAKk8ispQD-HNDTq4J0hNiDro=eRrJSM18t6_A=Ej6LNn+xxHyA@mail.gmail.com","threadId":"48353","inReplyTo":"CA+P7+xry-dE0-TMtRzNjiJpR1ZVmGXDy5ObfMxKyWA+eR8XMsQ@mail.gmail.com","subject":"Re: [RFC PATCH v4 0/3] Optional sub hierarchy for remote tags","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-05-01T20:34:13Z","receivedAt":"2018-05-01T20:34:39Z","isPatch":true,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"Jake & Ævar,\n\nThanks for the great feed back. As I mentioned previously,\nI'm going to be on vacation until May 17th, but will be following\nalong on email as connedtivity allows.\n\nI'm all for moving in the \"perfect\" direction so hopefully you guys can\ndecide on a solution and start moving in that direction. When I\nget back I'll do whatever I can to help.\n\nMy only criteria is that when a \"remote\" is added and fetched its tags\nare put in a different namespace and can be referenced easily.\n\nHere are some suggestions for naming:\n\n  refs/subs\n  refs/sub-repos\n  refs/repos\n  refs/externals\n  refs/imports\n\n\nArrivederci,\n\nWink\n"},{"id":"346351","messageId":"xmqqh8nrx8m1.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"CA+P7+xry-dE0-TMtRzNjiJpR1ZVmGXDy5ObfMxKyWA+eR8XMsQ@mail.gmail.com","subject":"Re: [RFC PATCH v4 0/3] Optional sub hierarchy for remote tags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-01T23:24:22Z","receivedAt":"2018-05-01T23:28:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.keller@gmail.com> writes:\n\n> I also agree, I'd prefer if we aim for the mapping to be something\n> which works for all refs in the future, even if such support isn't\n> added now, which is why i've proposed using \"refs/remote/<name>/\" so\n> that a tag would go from\n>\n> refs/tags/v1.7\n>\n> to\n>\n> refs/remote/<name>/tags/v1.7\n>\n> Ideally, we could work to update \"refs/remotes/<name>\" to go to\n> \"refs/remote/<name>/heads\" as well.\n\nThis is *not* imcompatible with having refs/remote-tags/* as an\ninterim solution.  \n\nWe'll have to support refs/remotes/<name>/<branch> anyway long after\nwe start using refs/remote/<name>/heads/<branch> by (1) switching\nthe fetch refspecs newer \"git clone\" writes to the latter format,\nand (2) extending the dwim table to try both formats.  Having Wink's\nsolution as an interim step adds one more entry to (2) but the\nmachinery is already there.  And it does not change (1), either.\n\n"},{"id":"346352","messageId":"xmqqd0yfx8fp.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"87zi1jxjqn.fsf@evledraar.gmail.com","subject":"Re: [RFC PATCH v4 0/3] Optional sub hierarchy for remote tags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-01T23:28:10Z","receivedAt":"2018-05-01T23:28:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> As a workaround for that maybe we'll need something like:\n>\n>   [remote \"gbenchmark\"]\n>     url = git@github.com:google/benchmark\n>     fetch = +refs/heads/*:refs/remotes/gbenchmark/*\n>     fetch = +refs/tags/*:refs/remote-tags/gbenchmark/*\n> \ttagStyle = remote\n>     tagopt = --no-tags\n\nGood thinking.  In the longer term we would probably want to\ndeprecate tagopt that was invented in a very lazy way (it was\noriginally meant to hold any random string that we can insert on the\nshell command that invokes \"git fetch\", which obviously is not a\ngood idea in the production code) and replace it with something more\n\"controlled\", and the above looks like a good improvement to Wink's\nproposed change.\n\n> Or whatever, i.e. something where only the new version will fetch the\n> tags and ignore the tagopt option (which I never liked anyway). It's a\n> hack, but at least you don't end up with crap it your ref namespace by\n> flip-flopping between versions.\n"},{"id":"346354","messageId":"CA+P7+xrPkMkt4zULuhEY-H9QHq8QE3gbkY-i+_HFk04W=vS29w@mail.gmail.com","threadId":"48353","inReplyTo":"xmqqh8nrx8m1.fsf@gitster-ct.c.googlers.com","subject":"Re: [RFC PATCH v4 0/3] Optional sub hierarchy for remote tags","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2018-05-02T00:08:06Z","receivedAt":"2018-05-02T00:08:30Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, May 1, 2018 at 4:24 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jacob Keller <jacob.keller@gmail.com> writes:\n>\n>> I also agree, I'd prefer if we aim for the mapping to be something\n>> which works for all refs in the future, even if such support isn't\n>> added now, which is why i've proposed using \"refs/remote/<name>/\" so\n>> that a tag would go from\n>>\n>> refs/tags/v1.7\n>>\n>> to\n>>\n>> refs/remote/<name>/tags/v1.7\n>>\n>> Ideally, we could work to update \"refs/remotes/<name>\" to go to\n>> \"refs/remote/<name>/heads\" as well.\n>\n> This is *not* imcompatible with having refs/remote-tags/* as an\n> interim solution.\n\nSure. I'm just proposing that we pick a name that all the refs can move to now.\n\n>\n> We'll have to support refs/remotes/<name>/<branch> anyway long after\n> we start using refs/remote/<name>/heads/<branch> by (1) switching\n> the fetch refspecs newer \"git clone\" writes to the latter format,\n\nOfcourse we'll have to support this, and i didn't mean to imply we wouldn't.\n\nI was just hoping to avoid having even more places to check in the future.\n\n> and (2) extending the dwim table to try both formats.  Having Wink's\n> solution as an interim step adds one more entry to (2) but the\n> machinery is already there.  And it does not change (1), either.\n>\n\nSure, we could. And yes, we have to do (1), which means we have to do\n(2) anyways. But we can still pick something which is more easily\nexpandable than refs/remotes/<name> was.\n\nThanks,\nJake\n"},{"id":"346677","messageId":"CA+P7+xp_FWrvY8M+K_1ogK27Rm0pDvBDL73n7nFu34tv557vKg@mail.gmail.com","threadId":"48353","inReplyTo":"CAGyf7-GhK_wAZn8zO35rsgQ=meLc=Lr+Q=S8omHg1c413dMsFw@mail.gmail.com","subject":"Re: Fetching tags overwrites existing tags","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2018-05-04T15:56:13Z","receivedAt":"2018-05-04T15:56:38Z","isPatch":false,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Fri, Apr 27, 2018 at 12:13 PM, Bryan Turner <bturner@atlassian.com> wrote:\n> On Fri, Apr 27, 2018 at 12:08 PM, Wink Saville <wink@saville.com> wrote:\n>>\n>> The other change was rather than using \"\"+refs/tags/*:refs/remote-tags/$name/*\"\n>> I've changed it to \"+refs/tags/*:refs/remote/tags/$name/*\" which seems cleaner.\n>> Again, if remote-tags is preferred I'll change it back.\n>\n>\n> From looking at the code, it looks like you mean\n> \"+refs/tags/*:refs/remotes/tags/$name/*\".\n>\n> The issue with that approach is that it collides with a remote named\n> \"tags\". \"refs/remote-tags\", on the other hand, represents a new-to-Git\n> path, one that won't already be in use by any other standard\n> functionality. That seems like a better approach than hoping no one\n> out there will call one of their remotes \"tags\".\n>\n> Bryan\n\nNote that my suggestion was very specific \"remote\" not pluralized,\nwhich is obviously a bit confusing, since there's remote and\n\"remotes\".\n\nThe goal being that you put \"remote/<name>/\" followed by the full\nremote ref minus the refs prefix.\n\nIt specifically is attempting to avoid the problem of expanding\n\"remotes\". Unfortunately, I don't have a better alternative format,\nand i very much want to avoid having to do \"remote-tags\",\n\"remote-notes\", \"remote-replaces\", \"remote-meta\" etc...\n\nIn that spirit, I'm working to hopefully propose something today.\n\nThanks,\nJake\n"},{"id":"346857","messageId":"a5125d8f-62df-feab-4154-99da7e84af9b@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-4-avarab@gmail.com","subject":"Re: [PATCH 3/8] push tests: add more testing for forced tag pushing","fromName":"Kaartic Sivaraam","fromEmail":"kaartic.sivaraam@gmail.com","sentAt":"2018-05-07T10:09:38Z","receivedAt":"2018-05-07T10:09:52Z","isPatch":true,"sender":{"key":"kaartic.sivaraam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12448084?v=4"},"body":"Hi,\n\nOn Monday 30 April 2018 01:50 AM, Ævar Arnfjörð Bjarmason wrote:\n\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index 15c8d5a734..c9a2011915 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -981,7 +981,17 @@ test_expect_success 'push requires --force to update lightweight tag' '\n\nI think the test description has become misleading now that it's testing\nfor 'force pushing' in general and not just the '--force' option. So, a\nbetter description is needed. Probably, \"force pushing required to\nupdate lightweight tag\".\n\n\n>  \t\tgit push --force ../child2 Tag &&\n>  \t\tgit tag -f Tag HEAD~ &&\n>  \t\ttest_must_fail git push ../child2 Tag &&\n> -\t\tgit push --force ../child2 Tag\n> +\t\tgit push --force ../child2 Tag &&\n> +\t\tgit tag -f Tag &&\n> +\t\ttest_must_fail git push ../child2 \"refs/tags/*:refs/tags/*\" &&\n> +\t\tgit push --force ../child2 \"refs/tags/*:refs/tags/*\" &&\n> +\t\tgit tag -f Tag HEAD~ &&\n> +\t\tgit push ../child2 \"+refs/tags/*:refs/tags/*\" &&\n\n> +\t\tgit tag -f Tag &&\n> +\t\tgit push --no-force ../child2 \"+refs/tags/*:refs/tags/*\" &&\n\nThis test shouldn't hiding within the tests about force pushing. It\nseems to warrant a separate test case to clearly note the behavior that\nthe \"+\" in refspec overrides \"--no-force\". This would help in easily\nidentifying if this particular behavior is broken or not.\n\n\n-- \nSivaraam\n\nQUOTE:\n\n“The most valuable person on any team is the person who makes everyone\nelse on the team more valuable, not the person who knows the most.”\n\n      - Joel Spolsky\n\n\nSivaraam?\n\nYou possibly might have noticed that my signature recently changed from\n'Kaartic' to 'Sivaraam' both of which are parts of my name. I find the\nnew signature to be better for several reasons one of which is that the\nformer signature has a lot of ambiguities in the place I live as it is a\ncommon name (NOTE: it's not a common spelling, just a common name). So,\nI switched signatures before it's too late.\n\nThat said, I won't mind you calling me 'Kaartic' if you like it [of\ncourse ;-)]. You can always call me using either of the names.\n\n\nKIND NOTE TO THE NATIVE ENGLISH SPEAKER:\n\nAs I'm not a native English speaker myself, there might be mistaeks in\nmy usage of English. I apologise for any mistakes that I make.\n\nIt would be \"helpful\" if you take the time to point out the mistakes.\n\nIt would be \"super helpful\" if you could provide suggestions about how\nto correct those mistakes.\n\nThanks in advance!\n\n"},{"id":"346951","messageId":"xmqq8t8uoovn.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"20180429202100.32353-4-avarab@gmail.com","subject":"Re: [PATCH 3/8] push tests: add more testing for forced tag pushing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-08T02:35:56Z","receivedAt":"2018-05-08T02:36:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Improve the tests added in dbfeddb12e (\"push: require force for refs\n> under refs/tags/\", 2012-11-29) to assert that the same behavior\n> applies various forms other refspecs, and that \"+\" in a refspec will\n> override the \"--no-force\" option (but not the other way around).\n\nFor some reason this fell out of my radar; sorry about that.\n\nI like the general idea to ensure non-ff pushes are rejected, unless\nforced, to update a light-weight tag with another.  I am unsure what\nshould happen when trying to update a light-weight tag with an\nobject with different type (or vice versa), and haven't read in this\nseries what your opinion is yet.  Let's read on and see how it goes.\n\nI have a moderately strong preference that\n\n\t$ git push --no-force child2\n\nwith a configured refspec\n\n\t[remote \"child2\"]\n\t\turl = ../child2\n\t\tpush = +refs/tags/*:refs/tags/*\n\nshould behave as a non-forced push (regardless of the refs hierarchy\ninvolved, not limited to tags/).  I have a mild preference against\n\n\t$ git push --no-force ../child2 +refs/tags/*:refs/tags/*\n\nthat forces, just because command line options look a lot more\nexplicit than the prefix '+', and choosing it not to force would\nmake it consistent with the desired behaviour for configured forcing\nrefspec.\n\nI couldn't quite get what you meant by \"(but not the other way\naround)\".  Did you mean\n\n\t$ git push --force ../child2 refs/tags/*:refs/tags/*\n\nshould not become non-forcing version because of the (lack of)\nprefix on the refspec does not trump the --force command line\noption?  If so, making\n\n\t$ git push --no-force ../child2 +refs/tags/*:refs/tags/*\n\nnot to force would make things more consistent, I suspect, i.e. we\ncan simply declare that presence or absense of '+' prefix in the\nrefspec determines the forced-ness of the push/fetch when there is\nno command line option to decide it, but an explicit command line\noption will always override it.  \n\nAm I missing something obvious?\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t5516-fetch-push.sh | 12 +++++++++++-\n>  1 file changed, 11 insertions(+), 1 deletion(-)\n>\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index 15c8d5a734..c9a2011915 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -981,7 +981,17 @@ test_expect_success 'push requires --force to update lightweight tag' '\n>  \t\tgit push --force ../child2 Tag &&\n>  \t\tgit tag -f Tag HEAD~ &&\n>  \t\ttest_must_fail git push ../child2 Tag &&\n> -\t\tgit push --force ../child2 Tag\n> +\t\tgit push --force ../child2 Tag &&\n> +\t\tgit tag -f Tag &&\n> +\t\ttest_must_fail git push ../child2 \"refs/tags/*:refs/tags/*\" &&\n> +\t\tgit push --force ../child2 \"refs/tags/*:refs/tags/*\" &&\n> +\t\tgit tag -f Tag HEAD~ &&\n> +\t\tgit push ../child2 \"+refs/tags/*:refs/tags/*\" &&\n> +\t\tgit tag -f Tag &&\n> +\t\tgit push --no-force ../child2 \"+refs/tags/*:refs/tags/*\" &&\n> +\t\tgit tag -f Tag HEAD~ &&\n> +\t\ttest_must_fail git push ../child2 tag Tag &&\n> +\t\tgit push --force ../child2 tag Tag\n>  \t)\n>  '\n"},{"id":"346953","messageId":"xmqq4ljiomvv.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"xmqq8t8uoovn.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 3/8] push tests: add more testing for forced tag pushing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-08T03:19:00Z","receivedAt":"2018-05-08T03:19:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I couldn't quite get what you meant by \"(but not the other way\n> around)\".  Did you mean\n>\n> \t$ git push --force ../child2 refs/tags/*:refs/tags/*\n>\n> should not become non-forcing version because of the (lack of)\n> prefix on the refspec does not trump the --force command line\n> option?  If so, making\n>\n> \t$ git push --no-force ../child2 +refs/tags/*:refs/tags/*\n>\n> not to force would make things more consistent, I suspect, i.e. we\n> can simply declare that presence or absense of '+' prefix in the\n> refspec determines the forced-ness of the push/fetch when there is\n> no command line option to decide it, but an explicit command line\n> option will always override it.  \n>\n> Am I missing something obvious?\n\nAnd of course I am missing the fact that --force and --no-force\ncontrols a single boolean.  If it controled a tristate (unspecified,\nfalse, true), then what I wrote above makes tons of sense, but that\nis not the reality.  \"git push --no-force\" is saying the the same as\n\"git push\", and its primarily reason for being there is to countermand\na \"--force\" that appears earlier on the command line for whatever\nreason, e.g.\n\n\t$ alias push='git push --force'\n\t$ push --no-force ../child2 ...\n\nSo what you said in this patch 100%  makes sense.\n\n"},{"id":"346964","messageId":"xmqq603yn50l.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"20180429202100.32353-5-avarab@gmail.com","subject":"Re: [PATCH 4/8] push tests: assert re-pushing annotated tags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-08T04:30:18Z","receivedAt":"2018-05-08T04:30:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Change the test that asserts that lightweight tags can only be\n> clobbered by a force-push to check do the same tests for annotated\n> tags.\n>\n> There used to be less exhaustive tests for this with the code added in\n> 40eff17999 (\"push: require force for annotated tags\", 2012-11-29), but\n> Junio removed them in 256b9d70a4 (\"push: fix \"refs/tags/ hierarchy\n> cannot be updated without --force\"\", 2013-01-16) while fixing some of\n> the behavior around tag pushing.\n>\n> That change left us without any coverage asserting that pushing and\n> clobbering annotated tags worked as intended.  There was no reason to\n> suspect that the receive machinery wouldn't behave the same way with\n> annotated tags, but now we know for sure.\n\nHmm, I am not sure if annotated tag T1 on commit C1 should be called\nto \"fast-forward to annotated tag T2 on commit C2\" when C2 is a\ndescendant of C1.  Tag is meant to be a non-moving anchor point, so\nit may make sense not to allow \"fast-forwarding hence it is OK to\nreplace\" that is typical for branch heads, which are meant to move\n\"forward\".\n\nBut let's move on and keep reading, at least temporarily assuming\nthat \"fast-forwarding\" annotated tags makes sense.  Under that\nassumption, this patch makes perfect sense to ensure lightweight and\nannotated tags behave the same.\n\nThanks.\n\n\n"},{"id":"346966","messageId":"xmqqzi1alodz.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"20180429202100.32353-6-avarab@gmail.com","subject":"Re: [PATCH 5/8] push doc: correct lies about how push refspecs work","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-08T05:14:48Z","receivedAt":"2018-05-08T05:14:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n>  +\n>  The <src> is often the name of the branch you would want to push, but\n> -it can be any arbitrary \"SHA-1 expression\", such as `master~4` or\n> -`HEAD` (see linkgit:gitrevisions[7]).\n> +it can be any arbitrary \"SHA-1 expression\" referring to a branch, such\n> +as `master~4` or `HEAD` (see linkgit:gitrevisions[7]). It can also\n> +refer to tag objects, trees or blobs if the <dst> is outside of\n> +`refs/heads/*`.\n\nI think the addition of \"referring to a branch\" here is an opposite\nof an improvement.  The fact <src> can name any object (if outside\nthe refs/heads/) or any commit (otherwise) is stressed with the\nadded \"It can also ...\", which is great, but neither \"master~4\" nor\n\"HEAD\" refer to a branch (they refer to a commit in terms relative\nto a branch and a (psuedo)ref, respectively).  And a \"SHA-1 expression\"\nthat uses branch tips as a starting point (e.g. master~4 is \"start\nat the tip of master and go backwards by 4 steps) is *not* special\nhere.  You can spell your <src> side as \"v2.17.0^0\" for example, and\nit does not refer to any branch.\n\n> @@ -74,12 +76,24 @@ without any `<refspec>` on the command line.  Otherwise, missing\n>  `:<dst>` means to update the same ref as the `<src>`.\n>  +\n>  The object referenced by <src> is used to update the <dst> reference\n> -on the remote side.  By default this is only allowed if <dst> is not\n> -a tag (annotated or lightweight), and then only if it can fast-forward\n> -<dst>.  By having the optional leading `+`, you can tell Git to update\n> -the <dst> ref even if it is not allowed by default (e.g., it is not a\n> -fast-forward.)  This does *not* attempt to merge <src> into <dst>.  See\n> -EXAMPLES below for details.\n> +on the remote side. Whether this is allowed depends on what where in\n\ns/what where/where/, I think.\n\n> +`refs/*` the <dst> reference lives. The `refs/heads/*` namespace will\n> +only accept commit objects, and then only they can be\n> +fast-forwarded. ...\n\nNicely clarified.  Excellent.\n\n> +.... The `refs/tags/*` namespace will accept any kind of\n> +object, but there commit objects are known as lightweight tags, and\n> +any changes to them and others types of objects will be\n> +rejected. ...\n\nwith s/, but there commit objects are known as lightweght tags/ the\nsentence does not change any meaning?  An early part of the paragraph\nmade readers anticipate that they hear rules for what can go where,\nand \"refs/tags/ ref that point at a commit is called lightweight tag\",\nwhile it is not an incorrect statement per-se, does not belong to\nthese \"rules\".  Unless the discussing of the rules immediately\nfollows involves (or becomes easier to read if we use the term)\n\"lightweight tags\", it probably is better to drop it.\n\n> +... Finally and most confusingly, it's possible to push any type\n> +of object to any namespace outside of `refs/{tags,heads}/*`, but these\n> +will be treated as branches, even in the case where a tag object is\n> +pushed.\n\nI sense a confused writer, not a confusing behaviour being described\nhere.  If refs/poo/* is \"treated as branches\", because of what you\nearlier said, you shouldn't be able to push a tag object in the\nfirst place.\n\nIf refs/poo/* is meant to be lawless land where anything goes, then\nsaying \"will be treated as branches\" does not help readers.\n\n> +... That tag object will be overwritten by another tag object (or\n> +commit!) without `--force` if the new tag happens to point to a commit\n> +that's a fast-forward of the commit it replaces.\n\nIf I pretend that I didn't see the \"treated as branches\", I fully\nagree with the above description and refs/poo/* being a world\ngoverned by random rules, and I do not think I'd be too opposed to\nchange it to \"anything goes\".  I do not think I'd be too opposed to\nchange it to \"nothing is allowed unless forced\", either, though.\n\n> +By having the optional leading `+`, you can tell Git to update the\n> +<dst> ref even if it is not allowed by its respective namespace\n> +clobbering rules (e.g., it is not a fast-forward. in the case of\n> +`refs/heads/*` updates) This does *not* attempt to merge <src> into\n> +<dst>.  See EXAMPLES below for details.\n\nExcellent.  \n\ns/leading `+`/& to a refspec (or using \"--force\" command line option)/\n\nThanks.\n\n"},{"id":"346967","messageId":"xmqqvabylnbi.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"20180429202100.32353-9-avarab@gmail.com","subject":"Re: [PATCH 8/8] fetch: stop clobbering existing tags without --force","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-08T05:37:53Z","receivedAt":"2018-05-08T05:37:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n>     > Tags need not be pointing at commits so there is no way to\n>     > guarantee \"fast-forward\" anyway.\n\nThe observation the above statement makes is not incorrect per-se,\nbut it does not justify \"anything goes\".  \"nothing is allowed unless\nforced\" is equally a logical consequence of the observation.\n\n> That comment and the rest of the history of \"fetch\" shows that the\n> \"+\" (--force) part of refpecs was only conceived for branch updates,\n> while tags have accepted any changes from upstream unconditionally and\n> clobbered the local tag object. Changing this behavior has been\n> discussed as early as 2011[1].\n\nThanks for a pointer.  We didn't keep reflog on tags as we wanted\ntags to be fixed points and made --tags a refspec without leading\n'+' because we didn't want this local clobbering.  I'd say it is\njust a buggy implementation, and we should just implement a simple\nrule \"refs/tags/* is never updated unless forced\".\n\n> I the current behavior doesn't make sense, it easily results in local\n\ns/I the/To me, the/, or s/I the/The/.\n\n> tags accidentally being clobbered. Ideally we'd namespace our tags\n> per-remote, but as with my 97716d217c (\"fetch: add a --prune-tags\n> option and fetch.pruneTags config\", 2018-02-09) it's easier to work\n> around the current implementation than to fix the root cause,\n\nI do not think they are the same problem.\n\nYou can have refs/remote/$name/v1.0 and have look-up rules to peek\nat various places in refs/* hierarchy for v1.0, and you may have\n*solved* the \"oops I overwrote and the meaning of v1.0 suddenly\nchanged\" issue, but if you fetched to a location in refs/* that has\nhigher precedence, then \"oops, the meaning of v1.0 suddenly changed\"\nissue itself is *not* solved at all.\n\n> so this\n> implements suggestion #1 from [1], \"fetch\" now only clobbers the tag\n> if either \"+\" is provided as part of the refspec, or if \"--force\" is\n> provided on the command-line.\n\nGood.  Regardless of the issue of separate namespace that is\noverlayed at the look-up time, this makes tons of sense.\n\n> diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\n> index 8631e365f4..5b4fc36866 100644\n> --- a/Documentation/fetch-options.txt\n> +++ b/Documentation/fetch-options.txt\n> @@ -49,11 +49,16 @@ endif::git-pull[]\n>  \n>  -f::\n>  --force::\n> -\tWhen 'git fetch' is used with `<rbranch>:<lbranch>`\n> -\trefspec, it refuses to update the local branch\n> -\t`<lbranch>` unless the remote branch `<rbranch>` it\n> -\tfetches is a descendant of `<lbranch>`.  This option\n> -\toverrides that check.\n> +\tWhen 'git fetch' is used with `<src>:<dst>` refspec it might\n\nNice to see attention to the detail here.  s/might/may/, I would\nsay, though.\n\n> +\trefuse to update the local branch as discussed\n\n> diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt\n> index c579793af5..672e8bc1c0 100644\n> --- a/Documentation/pull-fetch-param.txt\n> +++ b/Documentation/pull-fetch-param.txt\n> @@ -32,12 +32,22 @@ name.\n>  `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;\n>  it requests fetching everything up to the given tag.\n>  +\n> -The remote ref that matches <src>\n> -is fetched, and if <dst> is not empty string, the local\n> -ref that matches it is fast-forwarded using <src>.\n> -If the optional plus `+` is used, the local ref\n> -is updated even if it does not result in a fast-forward\n> -update.\n> +The remote ref that matches <src> is fetched, and if <dst> is not\n> +empty string, an attempt is made to update the local ref that matches\n> +it.\n> ++\n> +Whether that update is allowed is confusingly not the inverse of\n> +whether a server will accept a push as described in the `<refspec>...`\n> +section of linkgit:git-push[1]. If it's a commit under `refs/heads/*`\n> +only fast-forwards are allowed,\n\nPerhaps correct.  It is unclear what happens when it is fetching\nnon-commit to refs/heads/* in the above sentence.\n\n> but unlike what linkgit:git-push[1]\n> +will accept clobbering any ref pointing to blobs, trees etc. in any\n> +other namespace will be accepted, but commits in any ref\n> +namespace. ...\n\nI cannot quite parse this.\n\n> +... Those apply the same fast-forward rule.\n\nWho are \"Those\"?  refs/poo/*?\n\n> +... An exception to\n> +this is that as of Git version 2.18 any object under `refs/tags/*` is\n> +protected from updates.\n\nOK.\n\n> +If the optional plus `+` is used, the local ref is updated if the\n\nTighten \"is used\" to claify that you are talking about the '+'\nprefix that signals a forced push/fetch.  We do not want to hear\nfrom people who complain their \"git fetch origin master+\" does not\nwork.\n\n> -\tOPT__FORCE(&force, N_(\"force overwrite of local branch\"), 0),\n> +\tOPT__FORCE(&force, N_(\"force overwrite of local reference\"), 0),\n\nGood.  This is long overdue.\n\n"},{"id":"346972","messageId":"f3b891c3-381f-de42-51d8-24fdfbca91d2@gmail.com","threadId":"48353","inReplyTo":"xmqq4ljiomvv.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 3/8] push tests: add more testing for forced tag pushing","fromName":"Kaartic Sivaraam","fromEmail":"kaartic.sivaraam@gmail.com","sentAt":"2018-05-08T09:52:52Z","receivedAt":"2018-05-08T09:53:11Z","isPatch":true,"sender":{"key":"kaartic.sivaraam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12448084?v=4"},"body":"On Tuesday 08 May 2018 08:49 AM, Junio C Hamano wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n>> I couldn't quite get what you meant by \"(but not the other way\n>> around)\".  Did you mean\n>>\n>> \t$ git push --force ../child2 refs/tags/*:refs/tags/*\n>>\n>> should not become non-forcing version because of the (lack of)\n>> prefix on the refspec does not trump the --force command line\n>> option?\n\nWhen I was reading the commit message, I had the same doubt about what\n\"(but not the other way around)\" actually meant but I assumed it meant\nthat \"the `--no-force` in the command line does not override the '+' in\nthe refspec\". Maybe the commit message could be updated to clarify this?\n\n\n-- \nSivaraam\n\nQUOTE:\n\n“The most valuable person on any team is the person who makes everyone\nelse on the team more valuable, not the person who knows the most.”\n\n      - Joel Spolsky\n\n\nSivaraam?\n\nYou possibly might have noticed that my signature recently changed from\n'Kaartic' to 'Sivaraam' both of which are parts of my name. I find the\nnew signature to be better for several reasons one of which is that the\nformer signature has a lot of ambiguities in the place I live as it is a\ncommon name (NOTE: it's not a common spelling, just a common name). So,\nI switched signatures before it's too late.\n\nThat said, I won't mind you calling me 'Kaartic' if you like it [of\ncourse ;-)]. You can always call me using either of the names.\n\n\nKIND NOTE TO THE NATIVE ENGLISH SPEAKER:\n\nAs I'm not a native English speaker myself, there might be mistaeks in\nmy usage of English. I apologise for any mistakes that I make.\n\nIt would be \"helpful\" if you take the time to point out the mistakes.\n\nIt would be \"super helpful\" if you could provide suggestions about how\nto correct those mistakes.\n\nThanks in advance!\n\n"},{"id":"346974","messageId":"589400fa-be87-ddf4-f974-5bd9ff43b298@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-4-avarab@gmail.com","subject":"Re: [PATCH 3/8] push tests: add more testing for forced tag pushing","fromName":"Kaartic Sivaraam","fromEmail":"kaartic.sivaraam@gmail.com","sentAt":"2018-05-08T10:19:39Z","receivedAt":"2018-05-08T10:19:52Z","isPatch":true,"sender":{"key":"kaartic.sivaraam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12448084?v=4"},"body":"On Monday 30 April 2018 01:50 AM, Ævar Arnfjörð Bjarmason wrote:\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index 15c8d5a734..c9a2011915 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -981,7 +981,17 @@ test_expect_success 'push requires --force to update lightweight tag' '\n>  \t\tgit push --force ../child2 Tag &&\n>  \t\tgit tag -f Tag HEAD~ &&\n>  \t\ttest_must_fail git push ../child2 Tag &&\n> -\t\tgit push --force ../child2 Tag\n> +\t\tgit push --force ../child2 Tag &&\n> +\t\tgit tag -f Tag &&\n> +\t\ttest_must_fail git push ../child2 \"refs/tags/*:refs/tags/*\" &&\n> +\t\tgit push --force ../child2 \"refs/tags/*:refs/tags/*\" &&\n> +\t\tgit tag -f Tag HEAD~ &&\n> +\t\tgit push ../child2 \"+refs/tags/*:refs/tags/*\" &&\n> +\t\tgit tag -f Tag &&\n> +\t\tgit push --no-force ../child2 \"+refs/tags/*:refs/tags/*\" &&\n> +\t\tgit tag -f Tag HEAD~ &&\n> +\t\ttest_must_fail git push ../child2 tag Tag &&\n> +\t\tgit push --force ../child2 tag Tag\n\nAs a person who came to know about the \"tag <tag_name>\" refspec for the\nfirst time while seeing this patch, I found it a little hard to parse\nthe following two lines of the test:\n\n\t\ttest_must_fail git push ../child2 tag Tag &&\n\t\tgit push --force ../child2 tag Tag\n\nMaybe some other name than \"Tag\" for the example would have made it\neasier for the person reading it. Something like \"foo\"/\"bar\" etc.\n\n\n-- \nSivaraam\n\nQUOTE:\n\n“The most valuable person on any team is the person who makes everyone\nelse on the team more valuable, not the person who knows the most.”\n\n      - Joel Spolsky\n\n\nSivaraam?\n\nYou possibly might have noticed that my signature recently changed from\n'Kaartic' to 'Sivaraam' both of which are parts of my name. I find the\nnew signature to be better for several reasons one of which is that the\nformer signature has a lot of ambiguities in the place I live as it is a\ncommon name (NOTE: it's not a common spelling, just a common name). So,\nI switched signatures before it's too late.\n\nThat said, I won't mind you calling me 'Kaartic' if you like it [of\ncourse ;-)]. You can always call me using either of the names.\n\n\nKIND NOTE TO THE NATIVE ENGLISH SPEAKER:\n\nAs I'm not a native English speaker myself, there might be mistaeks in\nmy usage of English. I apologise for any mistakes that I make.\n\nIt would be \"helpful\" if you take the time to point out the mistakes.\n\nIt would be \"super helpful\" if you could provide suggestions about how\nto correct those mistakes.\n\nThanks in advance!\n\n"},{"id":"346975","messageId":"5d61da6d-fa90-e25d-2bde-6f7f93f39c2f@gmail.com","threadId":"48353","inReplyTo":"20180501165931.25515-2-wink@saville.com","subject":"Re: [RFC PATCH v4 1/3] Teach remote add the --remote-tags option","fromName":"Kaartic Sivaraam","fromEmail":"kaartic.sivaraam@gmail.com","sentAt":"2018-05-08T10:26:10Z","receivedAt":"2018-05-08T10:26:21Z","isPatch":true,"sender":{"key":"kaartic.sivaraam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12448084?v=4"},"body":"On Tuesday 01 May 2018 10:29 PM, Wink Saville wrote:\n> When --remote-tags is passed to `git remote add` the tagopt is set to\n> --remote-tags and a second fetch line is added so tags are placed in\n> a separate hierarchy per remote.\n> \n\nI find '--remote' in the option name to be redundant given that it is an\noption to `git remote add`. I guess '--namespace-tags' would be a better\nalternative as it seems to convey the meaning more directly to the user.\n\n\n-- \nSivaraam\n\nQUOTE:\n\n“The most valuable person on any team is the person who makes everyone\nelse on the team more valuable, not the person who knows the most.”\n\n      - Joel Spolsky\n\n\nSivaraam?\n\nYou possibly might have noticed that my signature recently changed from\n'Kaartic' to 'Sivaraam' both of which are parts of my name. I find the\nnew signature to be better for several reasons one of which is that the\nformer signature has a lot of ambiguities in the place I live as it is a\ncommon name (NOTE: it's not a common spelling, just a common name). So,\nI switched signatures before it's too late.\n\nThat said, I won't mind you calling me 'Kaartic' if you like it [of\ncourse ;-)]. You can always call me using either of the names.\n\n\nKIND NOTE TO THE NATIVE ENGLISH SPEAKER:\n\nAs I'm not a native English speaker myself, there might be mistaeks in\nmy usage of English. I apologise for any mistakes that I make.\n\nIt would be \"helpful\" if you take the time to point out the mistakes.\n\nIt would be \"super helpful\" if you could provide suggestions about how\nto correct those mistakes.\n\nThanks in advance!\n\n"},{"id":"346985","messageId":"20180508140553.7869-1-szeder.dev@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-5-avarab@gmail.com","subject":"Re: [PATCH 4/8] push tests: assert re-pushing annotated tags","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2018-05-08T14:05:53Z","receivedAt":"2018-05-08T14:06:12Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index c9a2011915..71fc902062 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -965,35 +965,43 @@ test_expect_success 'push into aliased refs (inconsistent)' '\n>  \t)\n>  '\n>  \n> -test_expect_success 'push requires --force to update lightweight tag' '\n> -\tmk_test testrepo heads/master &&\n> -\tmk_child testrepo child1 &&\n> -\tmk_child testrepo child2 &&\n> -\t(\n> -\t\tcd child1 &&\n> -\t\tgit tag Tag &&\n> -\t\tgit push ../child2 Tag &&\n> -\t\t>file1 &&\n> -\t\tgit add file1 &&\n> -\t\tgit commit -m \"file1\" &&\n> -\t\tgit tag -f Tag &&\n> -\t\ttest_must_fail git push ../child2 Tag &&\n> -\t\tgit push --force ../child2 Tag &&\n> -\t\tgit tag -f Tag HEAD~ &&\n> -\t\ttest_must_fail git push ../child2 Tag &&\n> -\t\tgit push --force ../child2 Tag &&\n> -\t\tgit tag -f Tag &&\n> -\t\ttest_must_fail git push ../child2 \"refs/tags/*:refs/tags/*\" &&\n> -\t\tgit push --force ../child2 \"refs/tags/*:refs/tags/*\" &&\n> -\t\tgit tag -f Tag HEAD~ &&\n> -\t\tgit push ../child2 \"+refs/tags/*:refs/tags/*\" &&\n> -\t\tgit tag -f Tag &&\n> -\t\tgit push --no-force ../child2 \"+refs/tags/*:refs/tags/*\" &&\n> -\t\tgit tag -f Tag HEAD~ &&\n> -\t\ttest_must_fail git push ../child2 tag Tag &&\n> -\t\tgit push --force ../child2 tag Tag\n> -\t)\n> -'\n> +test_force_push_tag () {\n> +\ttag_type_description=$1\n> +\ttag_args=$2\n> +\n> +\ttest_expect_success \"push requires --force to update $tag_type_description\" \"\n> +\t\tmk_test testrepo heads/master &&\n> +\t\tmk_child testrepo child1 &&\n> +\t\tmk_child testrepo child2 &&\n> +\t\t(\n> +\t\t\tcd child1 &&\n> +\t\t\tgit tag Tag &&\n> +\t\t\tgit push ../child2 Tag &&\n> +\t\t\t>file1 &&\n> +\t\t\tgit add file1 &&\n> +\t\t\tgit commit -m 'file1' &&\n> +\t\t\tgit tag $tag_args Tag &&\n> +\t\t\ttest_must_fail git push ../child2 Tag &&\n> +\t\t\tgit push --force ../child2 Tag &&\n> +\t\t\tgit tag $tag_args Tag HEAD~ &&\n> +\t\t\ttest_must_fail git push ../child2 Tag &&\n> +\t\t\tgit push --force ../child2 Tag &&\n> +\t\t\tgit tag $tag_args Tag &&\n> +\t\t\ttest_must_fail git push ../child2 'refs/tags/*:refs/tags/*' &&\n> +\t\t\tgit push --force ../child2 'refs/tags/*:refs/tags/*' &&\n> +\t\t\tgit tag $tag_args Tag HEAD~ &&\n> +\t\t\tgit push ../child2 '+refs/tags/*:refs/tags/*' &&\n> +\t\t\tgit tag $tag_args Tag &&function\n\nThere is that unwanted \"function\" at the end of the line.\n\nInterstingly, the test does pass when run with dash, but fails the\nchain-lint tests when run with Bash, even though it's in a subshell.\n\n> +\t\t\tgit push --no-force ../child2 '+refs/tags/*:refs/tags/*' &&\n> +\t\t\tgit tag $tag_args Tag HEAD~ &&\n> +\t\t\ttest_must_fail git push ../child2 tag Tag &&\n> +\t\t\tgit push --force ../child2 tag Tag\n> +\t\t)\n> +\t\"\n> +}\n> +\n> +test_force_push_tag \"lightweight tag\" \"-f\"\n> +test_force_push_tag \"annotated tag\" \"-f -a -m'msg'\"\n>  \n>  test_expect_success 'push --porcelain' '\n>  \tmk_empty testrepo &&\n"},{"id":"354022","messageId":"20180731130718.25222-1-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH v2 00/10] \"git fetch\" should not clobber existing tags without --force","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-07-31T13:07:08Z","receivedAt":"2018-07-31T13:07:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"It took me a long time to submit a re-roll for this, but this should\nsolve all issues noted with v1, see\nhttps://public-inbox.org/git/20180429202100.32353-1-avarab@gmail.com/\nfor the notes on that.\n\nA range-diff with v1 follows below.\n\n 2:  a47d861704 !  1:  77a612e89c push tests: fix logic error in \"push\" test assertion\n    @@ -1,17 +1,13 @@\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    -    push tests: fix logic error in \"push\" test assertion\n    +    fetch tests: change \"Tag\" test tag to \"testTag\"\n     \n    -    Fix a logic error that's been here since this test was added in\n    -    dbfeddb12e (\"push: require force for refs under refs/tags/\",\n    -    2012-11-29).\n    +    Calling the test tag \"Tag\" will make for confusing reading later in\n    +    this series when making use of the \"git push tag <name>\"\n    +    feature. Let's call the tag testTag instead.\n     \n    -    The intent of this test is to force-create a new tag pointing to\n    -    HEAD~, and then assert that pushing it doesn't work without --force.\n    -\n    -    Instead, the code was not creating a new tag at all, and then failing\n    -    to push the previous tag for the unrelated reason of providing a\n    -    refspec that doesn't make any sense.\n    +    Changes code initially added in dbfeddb12e (\"push: require force for\n    +    refs under refs/tags/\", 2012-11-29).\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ -19,13 +15,30 @@\n     --- a/t/t5516-fetch-push.sh\n     +++ b/t/t5516-fetch-push.sh\n     @@\n    - \t\tgit tag -f Tag &&\n    - \t\ttest_must_fail git push ../child2 Tag &&\n    - \t\tgit push --force ../child2 Tag &&\n    + \tmk_child testrepo child2 &&\n    + \t(\n    + \t\tcd child1 &&\n    +-\t\tgit tag Tag &&\n    +-\t\tgit push ../child2 Tag &&\n    +-\t\tgit push ../child2 Tag &&\n    ++\t\tgit tag testTag &&\n    ++\t\tgit push ../child2 testTag &&\n    ++\t\tgit push ../child2 testTag &&\n    + \t\t>file1 &&\n    + \t\tgit add file1 &&\n    + \t\tgit commit -m \"file1\" &&\n    +-\t\tgit tag -f Tag &&\n    +-\t\ttest_must_fail git push ../child2 Tag &&\n    +-\t\tgit push --force ../child2 Tag &&\n     -\t\tgit tag -f Tag &&\n     -\t\ttest_must_fail git push ../child2 Tag HEAD~ &&\n    -+\t\tgit tag -f Tag HEAD~ &&\n    -+\t\ttest_must_fail git push ../child2 Tag &&\n    - \t\tgit push --force ../child2 Tag\n    +-\t\tgit push --force ../child2 Tag\n    ++\t\tgit tag -f testTag &&\n    ++\t\ttest_must_fail git push ../child2 testTag &&\n    ++\t\tgit push --force ../child2 testTag &&\n    ++\t\tgit tag -f testTag &&\n    ++\t\ttest_must_fail git push ../child2 testTag HEAD~ &&\n    ++\t\tgit push --force ../child2 testTag\n      \t)\n      '\n    + \n 1:  4a3c29b593 !  2:  2386f0c6c6 push tests: remove redundant 'git push' invocation\n    @@ -15,9 +15,9 @@\n     +++ b/t/t5516-fetch-push.sh\n     @@\n      \t\tcd child1 &&\n    - \t\tgit tag Tag &&\n    - \t\tgit push ../child2 Tag &&\n    --\t\tgit push ../child2 Tag &&\n    + \t\tgit tag testTag &&\n    + \t\tgit push ../child2 testTag &&\n    +-\t\tgit push ../child2 testTag &&\n      \t\t>file1 &&\n      \t\tgit add file1 &&\n      \t\tgit commit -m \"file1\" &&\n -:  ---------- >  3:  3eaea7c262 push tests: fix logic error in \"push\" test assertion\n 3:  6c54d51a0e !  4:  9dbfb0c058 push tests: add more testing for forced tag pushing\n    @@ -4,8 +4,17 @@\n     \n         Improve the tests added in dbfeddb12e (\"push: require force for refs\n         under refs/tags/\", 2012-11-29) to assert that the same behavior\n    -    applies various forms other refspecs, and that \"+\" in a refspec will\n    -    override the \"--no-force\" option (but not the other way around).\n    +    applies various other combinations of command-line option and\n    +    refspecs.\n    +\n    +    Supplying either \"+\" in refspec or \"--force\" is sufficient to clobber\n    +    the reference. With --no-force we still pay attention to \"+\" in the\n    +    refspec, and vice-versa with clobbering kicking in if there's no \"+\"\n    +    in the refspec but \"+\" is given.\n    +\n    +    This is consistent with how refspecs work for branches, where either\n    +    \"+\" or \"--force\" will enable clobbering, with neither taking priority\n    +    over the other.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ -13,21 +22,38 @@\n     --- a/t/t5516-fetch-push.sh\n     +++ b/t/t5516-fetch-push.sh\n     @@\n    - \t\tgit push --force ../child2 Tag &&\n    - \t\tgit tag -f Tag HEAD~ &&\n    - \t\ttest_must_fail git push ../child2 Tag &&\n    --\t\tgit push --force ../child2 Tag\n    -+\t\tgit push --force ../child2 Tag &&\n    -+\t\tgit tag -f Tag &&\n    + \t)\n    + '\n    + \n    +-test_expect_success 'push requires --force to update lightweight tag' '\n    ++test_expect_success 'force pushing required to update lightweight tag' '\n    + \tmk_test testrepo heads/master &&\n    + \tmk_child testrepo child1 &&\n    + \tmk_child testrepo child2 &&\n    +@@\n    + \t\tgit push --force ../child2 testTag &&\n    + \t\tgit tag -f testTag HEAD~ &&\n    + \t\ttest_must_fail git push ../child2 testTag &&\n    +-\t\tgit push --force ../child2 testTag\n    ++\t\tgit push --force ../child2 testTag &&\n    ++\n    ++\t\t# Clobbering without + in refspec needs --force\n    ++\t\tgit tag -f testTag &&\n     +\t\ttest_must_fail git push ../child2 \"refs/tags/*:refs/tags/*\" &&\n     +\t\tgit push --force ../child2 \"refs/tags/*:refs/tags/*\" &&\n    -+\t\tgit tag -f Tag HEAD~ &&\n    ++\n    ++\t\t# Clobbering with + in refspec does not need --force\n    ++\t\tgit tag -f testTag HEAD~ &&\n     +\t\tgit push ../child2 \"+refs/tags/*:refs/tags/*\" &&\n    -+\t\tgit tag -f Tag &&\n    ++\n    ++\t\t# Clobbering with --no-force still obeys + in refspec\n    ++\t\tgit tag -f testTag &&\n     +\t\tgit push --no-force ../child2 \"+refs/tags/*:refs/tags/*\" &&\n    -+\t\tgit tag -f Tag HEAD~ &&\n    -+\t\ttest_must_fail git push ../child2 tag Tag &&\n    -+\t\tgit push --force ../child2 tag Tag\n    ++\n    ++\t\t# Clobbering with/without --force and \"tag <name>\" format\n    ++\t\tgit tag -f testTag HEAD~ &&\n    ++\t\ttest_must_fail git push ../child2 tag testTag &&\n    ++\t\tgit push --force ../child2 tag testTag\n      \t)\n      '\n      \n 4:  0d6b780cb3 !  5:  64bae445e5 push tests: assert re-pushing annotated tags\n    @@ -26,66 +26,82 @@\n      \t)\n      '\n      \n    --test_expect_success 'push requires --force to update lightweight tag' '\n    +-test_expect_success 'force pushing required to update lightweight tag' '\n     -\tmk_test testrepo heads/master &&\n     -\tmk_child testrepo child1 &&\n     -\tmk_child testrepo child2 &&\n     -\t(\n     -\t\tcd child1 &&\n    --\t\tgit tag Tag &&\n    --\t\tgit push ../child2 Tag &&\n    +-\t\tgit tag testTag &&\n    +-\t\tgit push ../child2 testTag &&\n     -\t\t>file1 &&\n     -\t\tgit add file1 &&\n     -\t\tgit commit -m \"file1\" &&\n    --\t\tgit tag -f Tag &&\n    --\t\ttest_must_fail git push ../child2 Tag &&\n    --\t\tgit push --force ../child2 Tag &&\n    --\t\tgit tag -f Tag HEAD~ &&\n    --\t\ttest_must_fail git push ../child2 Tag &&\n    --\t\tgit push --force ../child2 Tag &&\n    --\t\tgit tag -f Tag &&\n    +-\t\tgit tag -f testTag &&\n    +-\t\ttest_must_fail git push ../child2 testTag &&\n    +-\t\tgit push --force ../child2 testTag &&\n    +-\t\tgit tag -f testTag HEAD~ &&\n    +-\t\ttest_must_fail git push ../child2 testTag &&\n    +-\t\tgit push --force ../child2 testTag &&\n    +-\n    +-\t\t# Clobbering without + in refspec needs --force\n    +-\t\tgit tag -f testTag &&\n     -\t\ttest_must_fail git push ../child2 \"refs/tags/*:refs/tags/*\" &&\n     -\t\tgit push --force ../child2 \"refs/tags/*:refs/tags/*\" &&\n    --\t\tgit tag -f Tag HEAD~ &&\n    +-\n    +-\t\t# Clobbering with + in refspec does not need --force\n    +-\t\tgit tag -f testTag HEAD~ &&\n     -\t\tgit push ../child2 \"+refs/tags/*:refs/tags/*\" &&\n    --\t\tgit tag -f Tag &&\n    +-\n    +-\t\t# Clobbering with --no-force still obeys + in refspec\n    +-\t\tgit tag -f testTag &&\n     -\t\tgit push --no-force ../child2 \"+refs/tags/*:refs/tags/*\" &&\n    --\t\tgit tag -f Tag HEAD~ &&\n    --\t\ttest_must_fail git push ../child2 tag Tag &&\n    --\t\tgit push --force ../child2 tag Tag\n    +-\n    +-\t\t# Clobbering with/without --force and \"tag <name>\" format\n    +-\t\tgit tag -f testTag HEAD~ &&\n    +-\t\ttest_must_fail git push ../child2 tag testTag &&\n    +-\t\tgit push --force ../child2 tag testTag\n     -\t)\n     -'\n     +test_force_push_tag () {\n     +\ttag_type_description=$1\n     +\ttag_args=$2\n     +\n    -+\ttest_expect_success \"push requires --force to update $tag_type_description\" \"\n    ++\ttest_expect_success 'force pushing required to update lightweight tag' \"\n     +\t\tmk_test testrepo heads/master &&\n     +\t\tmk_child testrepo child1 &&\n     +\t\tmk_child testrepo child2 &&\n     +\t\t(\n     +\t\t\tcd child1 &&\n    -+\t\t\tgit tag Tag &&\n    -+\t\t\tgit push ../child2 Tag &&\n    ++\t\t\tgit tag testTag &&\n    ++\t\t\tgit push ../child2 testTag &&\n     +\t\t\t>file1 &&\n     +\t\t\tgit add file1 &&\n     +\t\t\tgit commit -m 'file1' &&\n    -+\t\t\tgit tag $tag_args Tag &&\n    -+\t\t\ttest_must_fail git push ../child2 Tag &&\n    -+\t\t\tgit push --force ../child2 Tag &&\n    -+\t\t\tgit tag $tag_args Tag HEAD~ &&\n    -+\t\t\ttest_must_fail git push ../child2 Tag &&\n    -+\t\t\tgit push --force ../child2 Tag &&\n    -+\t\t\tgit tag $tag_args Tag &&\n    ++\t\t\tgit tag $tag_args testTag &&\n    ++\t\t\ttest_must_fail git push ../child2 testTag &&\n    ++\t\t\tgit push --force ../child2 testTag &&\n    ++\t\t\tgit tag $tag_args testTag HEAD~ &&\n    ++\t\t\ttest_must_fail git push ../child2 testTag &&\n    ++\t\t\tgit push --force ../child2 testTag &&\n    ++\n    ++\t\t\t# Clobbering without + in refspec needs --force\n    ++\t\t\tgit tag -f testTag &&\n     +\t\t\ttest_must_fail git push ../child2 'refs/tags/*:refs/tags/*' &&\n     +\t\t\tgit push --force ../child2 'refs/tags/*:refs/tags/*' &&\n    -+\t\t\tgit tag $tag_args Tag HEAD~ &&\n    ++\n    ++\t\t\t# Clobbering with + in refspec does not need --force\n    ++\t\t\tgit tag -f testTag HEAD~ &&\n     +\t\t\tgit push ../child2 '+refs/tags/*:refs/tags/*' &&\n    -+\t\t\tgit tag $tag_args Tag &&function\n    ++\n    ++\t\t\t# Clobbering with --no-force still obeys + in refspec\n    ++\t\t\tgit tag -f testTag &&\n     +\t\t\tgit push --no-force ../child2 '+refs/tags/*:refs/tags/*' &&\n    -+\t\t\tgit tag $tag_args Tag HEAD~ &&\n    -+\t\t\ttest_must_fail git push ../child2 tag Tag &&\n    -+\t\t\tgit push --force ../child2 tag Tag\n    ++\n    ++\t\t\t# Clobbering with/without --force and 'tag <name>' format\n    ++\t\t\tgit tag -f testTag HEAD~ &&\n    ++\t\t\ttest_must_fail git push ../child2 tag testTag &&\n    ++\t\t\tgit push --force ../child2 tag testTag\n     +\t\t)\n     +\t\"\n     +}\n 5:  277fa440a7 !  6:  2209f03463 push doc: correct lies about how push refspecs work\n    @@ -36,10 +36,9 @@\n      The <src> is often the name of the branch you would want to push, but\n     -it can be any arbitrary \"SHA-1 expression\", such as `master~4` or\n     -`HEAD` (see linkgit:gitrevisions[7]).\n    -+it can be any arbitrary \"SHA-1 expression\" referring to a branch, such\n    -+as `master~4` or `HEAD` (see linkgit:gitrevisions[7]). It can also\n    -+refer to tag objects, trees or blobs if the <dst> is outside of\n    -+`refs/heads/*`.\n    ++it can be any arbitrary expression to a commit, such as `master~4` or\n    ++`HEAD` (see linkgit:gitrevisions[7]). It can also refer to tag\n    ++objects, trees or blobs if the <dst> is outside of `refs/heads/*`.\n      +\n      The <dst> tells which ref on the remote side is updated with this\n      push. Arbitrary expressions cannot be used here, an actual ref must\n    @@ -53,24 +52,25 @@\n     -the <dst> ref even if it is not allowed by default (e.g., it is not a\n     -fast-forward.)  This does *not* attempt to merge <src> into <dst>.  See\n     -EXAMPLES below for details.\n    -+on the remote side. Whether this is allowed depends on what where in\n    ++on the remote side. Whether this is allowed depends on where in\n     +`refs/*` the <dst> reference lives. The `refs/heads/*` namespace will\n     +only accept commit objects, and then only they can be\n     +fast-forwarded. The `refs/tags/*` namespace will accept any kind of\n    -+object, but there commit objects are known as lightweight tags, and\n    -+any changes to them and others types of objects will be\n    -+rejected. Finally and most confusingly, it's possible to push any type\n    -+of object to any namespace outside of `refs/{tags,heads}/*`, but these\n    -+will be treated as branches, even in the case where a tag object is\n    -+pushed. That tag object will be overwritten by another tag object (or\n    -+commit!) without `--force` if the new tag happens to point to a commit\n    -+that's a fast-forward of the commit it replaces.\n    ++object, and any changes to them and others types of objects will be\n    ++rejected. Finally, it's possible to push any type of object to any\n    ++namespace outside of `refs/{tags,heads}/*`, but these will be treated\n    ++as branches for the purposes of whether `--force` is required, even in\n    ++the case where a tag object is pushed. That tag object will be\n    ++overwritten by another tag object (or commit!) without `--force` if\n    ++the new tag happens to point to a commit that's a fast-forward of the\n    ++commit it replaces.\n     ++\n    -+By having the optional leading `+`, you can tell Git to update the\n    -+<dst> ref even if it is not allowed by its respective namespace\n    -+clobbering rules (e.g., it is not a fast-forward. in the case of\n    -+`refs/heads/*` updates) This does *not* attempt to merge <src> into\n    -+<dst>.  See EXAMPLES below for details.\n    ++By having the optional leading `+` to a refspec (or using `--force`\n    ++command line option) you can tell Git to update the <dst> ref even if\n    ++it is not allowed by its respective namespace clobbering rules (e.g.,\n    ++it is not a fast-forward. in the case of `refs/heads/*` updates) This\n    ++does *not* attempt to merge <src> into <dst>.  See EXAMPLES below for\n    ++details.\n      +\n      `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.\n      +\n 6:  158fcc08d2 =  7:  844f4fb491 fetch tests: correct a comment \"remove it\" -> \"remove them\"\n 7:  0134b9353f =  8:  cf9abd7c69 fetch tests: add a test clobbering tag behavior\n -:  ---------- >  9:  af1e1482a2 pull doc: fix a long-standing grammar error\n 8:  57b7bc325d ! 10:  9c508fb926 fetch: stop clobbering existing tags without --force\n    @@ -7,31 +7,34 @@\n     \n         This changes the long-standing behavior of \"fetch\" added in\n         853a3697dc (\"[PATCH] Multi-head fetch.\", 2005-08-20), before this\n    -    change all tag fetches effectively had --force enabled. The original\n    -    rationale in that change was:\n    +    change all tag fetches effectively had --force enabled. See the\n    +    git-fetch-script code in fast_forward_local() with the comment:\n     \n             > Tags need not be pointing at commits so there is no way to\n             > guarantee \"fast-forward\" anyway.\n     \n    -    That comment and the rest of the history of \"fetch\" shows that the\n    +    That commit and the rest of the history of \"fetch\" shows that the\n         \"+\" (--force) part of refpecs was only conceived for branch updates,\n         while tags have accepted any changes from upstream unconditionally and\n         clobbered the local tag object. Changing this behavior has been\n         discussed as early as 2011[1].\n     \n    -    I the current behavior doesn't make sense, it easily results in local\n    -    tags accidentally being clobbered. Ideally we'd namespace our tags\n    -    per-remote, but as with my 97716d217c (\"fetch: add a --prune-tags\n    -    option and fetch.pruneTags config\", 2018-02-09) it's easier to work\n    -    around the current implementation than to fix the root cause, so this\n    -    implements suggestion #1 from [1], \"fetch\" now only clobbers the tag\n    -    if either \"+\" is provided as part of the refspec, or if \"--force\" is\n    -    provided on the command-line.\n    +    The current behavior doesn't make sense to me, it easily results in\n    +    local tags accidentally being clobbered.  We could namespace our tags\n    +    per-remote and not locally populate refs/tags/*, but as with my\n    +    97716d217c (\"fetch: add a --prune-tags option and fetch.pruneTags\n    +    config\", 2018-02-09) it's easier to work around the current\n    +    implementation than to fix the root cause.\n     \n    -    This also makes it nicely symmetrical with how \"tag\" itself\n    -    works. We'll now refuse to clobber any existing tags unless \"--force\"\n    -    is supplied, whether that clobbering would happen by clobbering a\n    -    local tag with \"tag\", or by fetching it from the remote with \"fetch\".\n    +    So this change implements suggestion #1 from Jeff's 2011 E-Mail[1],\n    +    \"fetch\" now only clobbers the tag if either \"+\" is provided as part of\n    +    the refspec, or if \"--force\" is provided on the command-line.\n    +\n    +    This also makes it nicely symmetrical with how \"tag\" itself works when\n    +    creating tags. I.e. we refuse to clobber any existing tags unless\n    +    \"--force\" is supplied. Now we can refuse all such clobbering, whether\n    +    it would happen by clobbering a local tag with \"tag\", or by fetching\n    +    it from the remote with \"fetch\".\n     \n         It's still not at all nicely symmetrical with how \"git push\" works, as\n         discussed in the updated pull-fetch-param.txt documentation, but this\n    @@ -60,7 +63,7 @@\n     -\t`<lbranch>` unless the remote branch `<rbranch>` it\n     -\tfetches is a descendant of `<lbranch>`.  This option\n     -\toverrides that check.\n    -+\tWhen 'git fetch' is used with `<src>:<dst>` refspec it might\n    ++\tWhen 'git fetch' is used with `<src>:<dst>` refspec it may\n     +\trefuse to update the local branch as discussed\n     +ifdef::git-pull[]\n     +\tin the `<refspec>` part of the linkgit:git-fetch[1]\n    @@ -78,31 +81,29 @@\n     --- a/Documentation/pull-fetch-param.txt\n     +++ b/Documentation/pull-fetch-param.txt\n     @@\n    - `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;\n      it requests fetching everything up to the given tag.\n      +\n    --The remote ref that matches <src>\n    --is fetched, and if <dst> is not empty string, the local\n    + The remote ref that matches <src>\n    +-is fetched, and if <dst> is not an empty string, the local\n     -ref that matches it is fast-forwarded using <src>.\n     -If the optional plus `+` is used, the local ref\n     -is updated even if it does not result in a fast-forward\n     -update.\n    -+The remote ref that matches <src> is fetched, and if <dst> is not\n    -+empty string, an attempt is made to update the local ref that matches\n    -+it.\n    ++is fetched, and if <dst> is not an empty string, an attempt\n    ++is made to update the local ref that matches it.\n    +++\n    ++Whether that update is allowed without `--force` depends on the ref\n    ++namespace it's being fetched to, and the type of object being\n    ++fetched. If it's a commit under `refs/heads/*` only fast-forwards are\n    ++allowed.\n     ++\n    -+Whether that update is allowed is confusingly not the inverse of\n    -+whether a server will accept a push as described in the `<refspec>...`\n    -+section of linkgit:git-push[1]. If it's a commit under `refs/heads/*`\n    -+only fast-forwards are allowed, but unlike what linkgit:git-push[1]\n    -+will accept clobbering any ref pointing to blobs, trees etc. in any\n    -+other namespace will be accepted, but commits in any ref\n    -+namespace. Those apply the same fast-forward rule. An exception to\n    -+this is that as of Git version 2.18 any object under `refs/tags/*` is\n    -+protected from updates.\n    ++By having the optional leading `+` to a refspec (or using `--force`\n    ++command line option) you can tell Git to update the local ref even if\n    ++it is not allowed by its respective namespace clobbering rules.\n     ++\n    -+If the optional plus `+` is used, the local ref is updated if the\n    -+update would have otherwise been rejected.\n    ++Before Git version 2.19 tag objects under `refs/tags/*` would not be\n    ++protected from updates, but since then the `+` (or `--force`) syntax\n    ++is required to clobber them.\n      +\n      [NOTE]\n      When the remote branch you want to fetch is known to\n\nÆvar Arnfjörð Bjarmason (10):\n  fetch tests: change \"Tag\" test tag to \"testTag\"\n  push tests: remove redundant 'git push' invocation\n  push tests: fix logic error in \"push\" test assertion\n  push tests: add more testing for forced tag pushing\n  push tests: assert re-pushing annotated tags\n  push doc: correct lies about how push refspecs work\n  fetch tests: correct a comment \"remove it\" -> \"remove them\"\n  fetch tests: add a test clobbering tag behavior\n  pull doc: fix a long-standing grammar error\n  fetch: stop clobbering existing tags without --force\n\n Documentation/fetch-options.txt    | 15 +++--\n Documentation/git-push.txt         | 30 +++++++---\n Documentation/gitrevisions.txt     |  7 ++-\n Documentation/pull-fetch-param.txt | 20 +++++--\n builtin/fetch.c                    | 20 ++++---\n t/t5510-fetch.sh                   |  2 +-\n t/t5516-fetch-push.sh              | 90 +++++++++++++++++++++++-------\n t/t5612-clone-refspec.sh           |  4 +-\n 8 files changed, 137 insertions(+), 51 deletions(-)\n\n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"354023","messageId":"20180731130718.25222-2-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH v2 01/10] fetch tests: change \"Tag\" test tag to \"testTag\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-07-31T13:07:09Z","receivedAt":"2018-07-31T13:07:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Calling the test tag \"Tag\" will make for confusing reading later in\nthis series when making use of the \"git push tag <name>\"\nfeature. Let's call the tag testTag instead.\n\nChanges code initially added in dbfeddb12e (\"push: require force for\nrefs under refs/tags/\", 2012-11-29).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 18 +++++++++---------\n 1 file changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex a5077d8b7c..08b9cf581d 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -971,18 +971,18 @@ test_expect_success 'push requires --force to update lightweight tag' '\n \tmk_child testrepo child2 &&\n \t(\n \t\tcd child1 &&\n-\t\tgit tag Tag &&\n-\t\tgit push ../child2 Tag &&\n-\t\tgit push ../child2 Tag &&\n+\t\tgit tag testTag &&\n+\t\tgit push ../child2 testTag &&\n+\t\tgit push ../child2 testTag &&\n \t\t>file1 &&\n \t\tgit add file1 &&\n \t\tgit commit -m \"file1\" &&\n-\t\tgit tag -f Tag &&\n-\t\ttest_must_fail git push ../child2 Tag &&\n-\t\tgit push --force ../child2 Tag &&\n-\t\tgit tag -f Tag &&\n-\t\ttest_must_fail git push ../child2 Tag HEAD~ &&\n-\t\tgit push --force ../child2 Tag\n+\t\tgit tag -f testTag &&\n+\t\ttest_must_fail git push ../child2 testTag &&\n+\t\tgit push --force ../child2 testTag &&\n+\t\tgit tag -f testTag &&\n+\t\ttest_must_fail git push ../child2 testTag HEAD~ &&\n+\t\tgit push --force ../child2 testTag\n \t)\n '\n \n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"354024","messageId":"20180731130718.25222-3-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH v2 02/10] push tests: remove redundant 'git push' invocation","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-07-31T13:07:10Z","receivedAt":"2018-07-31T13:07:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove an invocation of 'git push' that's exactly the same as the one\non the preceding line. This was seemingly added by mistake in\ndbfeddb12e (\"push: require force for refs under refs/tags/\",\n2012-11-29) and doesn't affect the result of the test, the second\n\"push\" was a no-op as there was nothing new to push.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 08b9cf581d..4d487d6875 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -973,7 +973,6 @@ test_expect_success 'push requires --force to update lightweight tag' '\n \t\tcd child1 &&\n \t\tgit tag testTag &&\n \t\tgit push ../child2 testTag &&\n-\t\tgit push ../child2 testTag &&\n \t\t>file1 &&\n \t\tgit add file1 &&\n \t\tgit commit -m \"file1\" &&\n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"354025","messageId":"20180731130718.25222-4-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH v2 03/10] push tests: fix logic error in \"push\" test assertion","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-07-31T13:07:11Z","receivedAt":"2018-07-31T13:07:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a logic error that's been here since this test was added in\ndbfeddb12e (\"push: require force for refs under refs/tags/\",\n2012-11-29).\n\nThe intent of this test is to force-create a new tag pointing to\nHEAD~, and then assert that pushing it doesn't work without --force.\n\nInstead, the code was not creating a new tag at all, and then failing\nto push the previous tag for the unrelated reason of providing a\nrefspec that doesn't make any sense.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 4d487d6875..82af990ab3 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -979,8 +979,8 @@ test_expect_success 'push requires --force to update lightweight tag' '\n \t\tgit tag -f testTag &&\n \t\ttest_must_fail git push ../child2 testTag &&\n \t\tgit push --force ../child2 testTag &&\n-\t\tgit tag -f testTag &&\n-\t\ttest_must_fail git push ../child2 testTag HEAD~ &&\n+\t\tgit tag -f testTag HEAD~ &&\n+\t\ttest_must_fail git push ../child2 testTag &&\n \t\tgit push --force ../child2 testTag\n \t)\n '\n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"354026","messageId":"20180731130718.25222-5-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH v2 04/10] push tests: add more testing for forced tag pushing","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-07-31T13:07:12Z","receivedAt":"2018-07-31T13:07:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Improve the tests added in dbfeddb12e (\"push: require force for refs\nunder refs/tags/\", 2012-11-29) to assert that the same behavior\napplies various other combinations of command-line option and\nrefspecs.\n\nSupplying either \"+\" in refspec or \"--force\" is sufficient to clobber\nthe reference. With --no-force we still pay attention to \"+\" in the\nrefspec, and vice-versa with clobbering kicking in if there's no \"+\"\nin the refspec but \"+\" is given.\n\nThis is consistent with how refspecs work for branches, where either\n\"+\" or \"--force\" will enable clobbering, with neither taking priority\nover the other.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 22 ++++++++++++++++++++--\n 1 file changed, 20 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 82af990ab3..4bd533dd48 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -965,7 +965,7 @@ test_expect_success 'push into aliased refs (inconsistent)' '\n \t)\n '\n \n-test_expect_success 'push requires --force to update lightweight tag' '\n+test_expect_success 'force pushing required to update lightweight tag' '\n \tmk_test testrepo heads/master &&\n \tmk_child testrepo child1 &&\n \tmk_child testrepo child2 &&\n@@ -981,7 +981,25 @@ test_expect_success 'push requires --force to update lightweight tag' '\n \t\tgit push --force ../child2 testTag &&\n \t\tgit tag -f testTag HEAD~ &&\n \t\ttest_must_fail git push ../child2 testTag &&\n-\t\tgit push --force ../child2 testTag\n+\t\tgit push --force ../child2 testTag &&\n+\n+\t\t# Clobbering without + in refspec needs --force\n+\t\tgit tag -f testTag &&\n+\t\ttest_must_fail git push ../child2 \"refs/tags/*:refs/tags/*\" &&\n+\t\tgit push --force ../child2 \"refs/tags/*:refs/tags/*\" &&\n+\n+\t\t# Clobbering with + in refspec does not need --force\n+\t\tgit tag -f testTag HEAD~ &&\n+\t\tgit push ../child2 \"+refs/tags/*:refs/tags/*\" &&\n+\n+\t\t# Clobbering with --no-force still obeys + in refspec\n+\t\tgit tag -f testTag &&\n+\t\tgit push --no-force ../child2 \"+refs/tags/*:refs/tags/*\" &&\n+\n+\t\t# Clobbering with/without --force and \"tag <name>\" format\n+\t\tgit tag -f testTag HEAD~ &&\n+\t\ttest_must_fail git push ../child2 tag testTag &&\n+\t\tgit push --force ../child2 tag testTag\n \t)\n '\n \n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"354027","messageId":"20180731130718.25222-6-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH v2 05/10] push tests: assert re-pushing annotated tags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-07-31T13:07:13Z","receivedAt":"2018-07-31T13:07:47Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the test that asserts that lightweight tags can only be\nclobbered by a force-push to check do the same tests for annotated\ntags.\n\nThere used to be less exhaustive tests for this with the code added in\n40eff17999 (\"push: require force for annotated tags\", 2012-11-29), but\nJunio removed them in 256b9d70a4 (\"push: fix \"refs/tags/ hierarchy\ncannot be updated without --force\"\", 2013-01-16) while fixing some of\nthe behavior around tag pushing.\n\nThat change left us without any coverage asserting that pushing and\nclobbering annotated tags worked as intended.  There was no reason to\nsuspect that the receive machinery wouldn't behave the same way with\nannotated tags, but now we know for sure.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 82 ++++++++++++++++++++++++-------------------\n 1 file changed, 45 insertions(+), 37 deletions(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 4bd533dd48..1331a8de08 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -965,43 +965,51 @@ test_expect_success 'push into aliased refs (inconsistent)' '\n \t)\n '\n \n-test_expect_success 'force pushing required to update lightweight tag' '\n-\tmk_test testrepo heads/master &&\n-\tmk_child testrepo child1 &&\n-\tmk_child testrepo child2 &&\n-\t(\n-\t\tcd child1 &&\n-\t\tgit tag testTag &&\n-\t\tgit push ../child2 testTag &&\n-\t\t>file1 &&\n-\t\tgit add file1 &&\n-\t\tgit commit -m \"file1\" &&\n-\t\tgit tag -f testTag &&\n-\t\ttest_must_fail git push ../child2 testTag &&\n-\t\tgit push --force ../child2 testTag &&\n-\t\tgit tag -f testTag HEAD~ &&\n-\t\ttest_must_fail git push ../child2 testTag &&\n-\t\tgit push --force ../child2 testTag &&\n-\n-\t\t# Clobbering without + in refspec needs --force\n-\t\tgit tag -f testTag &&\n-\t\ttest_must_fail git push ../child2 \"refs/tags/*:refs/tags/*\" &&\n-\t\tgit push --force ../child2 \"refs/tags/*:refs/tags/*\" &&\n-\n-\t\t# Clobbering with + in refspec does not need --force\n-\t\tgit tag -f testTag HEAD~ &&\n-\t\tgit push ../child2 \"+refs/tags/*:refs/tags/*\" &&\n-\n-\t\t# Clobbering with --no-force still obeys + in refspec\n-\t\tgit tag -f testTag &&\n-\t\tgit push --no-force ../child2 \"+refs/tags/*:refs/tags/*\" &&\n-\n-\t\t# Clobbering with/without --force and \"tag <name>\" format\n-\t\tgit tag -f testTag HEAD~ &&\n-\t\ttest_must_fail git push ../child2 tag testTag &&\n-\t\tgit push --force ../child2 tag testTag\n-\t)\n-'\n+test_force_push_tag () {\n+\ttag_type_description=$1\n+\ttag_args=$2\n+\n+\ttest_expect_success 'force pushing required to update lightweight tag' \"\n+\t\tmk_test testrepo heads/master &&\n+\t\tmk_child testrepo child1 &&\n+\t\tmk_child testrepo child2 &&\n+\t\t(\n+\t\t\tcd child1 &&\n+\t\t\tgit tag testTag &&\n+\t\t\tgit push ../child2 testTag &&\n+\t\t\t>file1 &&\n+\t\t\tgit add file1 &&\n+\t\t\tgit commit -m 'file1' &&\n+\t\t\tgit tag $tag_args testTag &&\n+\t\t\ttest_must_fail git push ../child2 testTag &&\n+\t\t\tgit push --force ../child2 testTag &&\n+\t\t\tgit tag $tag_args testTag HEAD~ &&\n+\t\t\ttest_must_fail git push ../child2 testTag &&\n+\t\t\tgit push --force ../child2 testTag &&\n+\n+\t\t\t# Clobbering without + in refspec needs --force\n+\t\t\tgit tag -f testTag &&\n+\t\t\ttest_must_fail git push ../child2 'refs/tags/*:refs/tags/*' &&\n+\t\t\tgit push --force ../child2 'refs/tags/*:refs/tags/*' &&\n+\n+\t\t\t# Clobbering with + in refspec does not need --force\n+\t\t\tgit tag -f testTag HEAD~ &&\n+\t\t\tgit push ../child2 '+refs/tags/*:refs/tags/*' &&\n+\n+\t\t\t# Clobbering with --no-force still obeys + in refspec\n+\t\t\tgit tag -f testTag &&\n+\t\t\tgit push --no-force ../child2 '+refs/tags/*:refs/tags/*' &&\n+\n+\t\t\t# Clobbering with/without --force and 'tag <name>' format\n+\t\t\tgit tag -f testTag HEAD~ &&\n+\t\t\ttest_must_fail git push ../child2 tag testTag &&\n+\t\t\tgit push --force ../child2 tag testTag\n+\t\t)\n+\t\"\n+}\n+\n+test_force_push_tag \"lightweight tag\" \"-f\"\n+test_force_push_tag \"annotated tag\" \"-f -a -m'msg'\"\n \n test_expect_success 'push --porcelain' '\n \tmk_empty testrepo &&\n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"354028","messageId":"20180731130718.25222-7-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH v2 06/10] push doc: correct lies about how push refspecs work","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-07-31T13:07:14Z","receivedAt":"2018-07-31T13:07:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"There's complex rules governing whether a push is allowed to take\nplace depending on whether we're pushing to refs/heads/*, refs/tags/*\nor refs/not-that/*. See is_branch() in refs.c, and the various\nassertions in refs/files-backend.c. (e.g. \"trying to write non-commit\nobject %s to branch '%s'\").\n\nThis documentation has never been quite correct, but went downhill\nafter dbfeddb12e (\"push: require force for refs under refs/tags/\",\n2012-11-29) when we started claiming that <dst> couldn't be a tag\nobject, which is incorrect. After some of the logic in that patch was\nchanged in 256b9d70a4 (\"push: fix \"refs/tags/ hierarchy cannot be\nupdated without --force\"\", 2013-01-16) the docs weren't updated, and\nwe've had some version of documentation that confused whether <src>\nwas a tag or not with whether <dst> would accept either an annotated\ntag object or the commit it points to.\n\nThis makes the intro somewhat more verbose & complex, perhaps we\nshould have a shorter description here and split the full complexity\ninto a dedicated section. Very few users will find themselves needing\nto e.g. push blobs or trees to refs/custom-namespace/* (or blobs or\ntrees at all), and that could be covered separately as an advanced\ntopic.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-push.txt     | 30 ++++++++++++++++++++++--------\n Documentation/gitrevisions.txt |  7 ++++---\n 2 files changed, 26 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 55277a9781..fe654482dc 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -60,8 +60,9 @@ OPTIONS[[OPTIONS]]\n \tby a colon `:`, followed by the destination ref <dst>.\n +\n The <src> is often the name of the branch you would want to push, but\n-it can be any arbitrary \"SHA-1 expression\", such as `master~4` or\n-`HEAD` (see linkgit:gitrevisions[7]).\n+it can be any arbitrary expression to a commit, such as `master~4` or\n+`HEAD` (see linkgit:gitrevisions[7]). It can also refer to tag\n+objects, trees or blobs if the <dst> is outside of `refs/heads/*`.\n +\n The <dst> tells which ref on the remote side is updated with this\n push. Arbitrary expressions cannot be used here, an actual ref must\n@@ -74,12 +75,25 @@ without any `<refspec>` on the command line.  Otherwise, missing\n `:<dst>` means to update the same ref as the `<src>`.\n +\n The object referenced by <src> is used to update the <dst> reference\n-on the remote side.  By default this is only allowed if <dst> is not\n-a tag (annotated or lightweight), and then only if it can fast-forward\n-<dst>.  By having the optional leading `+`, you can tell Git to update\n-the <dst> ref even if it is not allowed by default (e.g., it is not a\n-fast-forward.)  This does *not* attempt to merge <src> into <dst>.  See\n-EXAMPLES below for details.\n+on the remote side. Whether this is allowed depends on where in\n+`refs/*` the <dst> reference lives. The `refs/heads/*` namespace will\n+only accept commit objects, and then only they can be\n+fast-forwarded. The `refs/tags/*` namespace will accept any kind of\n+object, and any changes to them and others types of objects will be\n+rejected. Finally, it's possible to push any type of object to any\n+namespace outside of `refs/{tags,heads}/*`, but these will be treated\n+as branches for the purposes of whether `--force` is required, even in\n+the case where a tag object is pushed. That tag object will be\n+overwritten by another tag object (or commit!) without `--force` if\n+the new tag happens to point to a commit that's a fast-forward of the\n+commit it replaces.\n++\n+By having the optional leading `+` to a refspec (or using `--force`\n+command line option) you can tell Git to update the <dst> ref even if\n+it is not allowed by its respective namespace clobbering rules (e.g.,\n+it is not a fast-forward. in the case of `refs/heads/*` updates) This\n+does *not* attempt to merge <src> into <dst>.  See EXAMPLES below for\n+details.\n +\n `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.\n +\ndiff --git a/Documentation/gitrevisions.txt b/Documentation/gitrevisions.txt\nindex 1f6cceaefb..d407b7dee1 100644\n--- a/Documentation/gitrevisions.txt\n+++ b/Documentation/gitrevisions.txt\n@@ -19,9 +19,10 @@ walk the revision graph (such as linkgit:git-log[1]), all commits which are\n reachable from that commit. For commands that walk the revision graph one can\n also specify a range of revisions explicitly.\n \n-In addition, some Git commands (such as linkgit:git-show[1]) also take\n-revision parameters which denote other objects than commits, e.g. blobs\n-(\"files\") or trees (\"directories of files\").\n+In addition, some Git commands (such as linkgit:git-show[1] and\n+linkgit:git-push[1]) can also take revision parameters which denote\n+other objects than commits, e.g. blobs (\"files\") or trees\n+(\"directories of files\").\n \n include::revisions.txt[]\n \n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"354029","messageId":"20180731130718.25222-9-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH v2 08/10] fetch tests: add a test clobbering tag behavior","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-07-31T13:07:16Z","receivedAt":"2018-07-31T13:07:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The test suite only incidentally (and unintentionally) tested for the\ncurrent behavior of eager tag clobbering on \"fetch\". This follow-up to\nthe previous \"push tests: assert re-pushing annotated tags\" change\ntests for it explicitly.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 24 ++++++++++++++++++++++++\n 1 file changed, 24 insertions(+)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 1331a8de08..8912312be7 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1011,6 +1011,30 @@ test_force_push_tag () {\n test_force_push_tag \"lightweight tag\" \"-f\"\n test_force_push_tag \"annotated tag\" \"-f -a -m'msg'\"\n \n+test_force_fetch_tag () {\n+\ttag_type_description=$1\n+\ttag_args=$2\n+\n+\ttest_expect_success \"fetch will clobber an existing $tag_type_description\" \"\n+\t\tmk_test testrepo heads/master &&\n+\t\tmk_child testrepo child1 &&\n+\t\tmk_child testrepo child2 &&\n+\t\t(\n+\t\t\tcd testrepo &&\n+\t\t\tgit tag Tag &&\n+\t\t\tgit -C ../child1 fetch origin tag Tag &&\n+\t\t\t>file1 &&\n+\t\t\tgit add file1 &&\n+\t\t\tgit commit -m 'file1' &&\n+\t\t\tgit tag $tag_args Tag &&\n+\t\t\tgit -C ../child1 fetch origin tag Tag\n+\t\t)\n+\t\"\n+}\n+\n+test_force_fetch_tag \"lightweight tag\" \"-f\"\n+test_force_fetch_tag \"annotated tag\" \"-f -a -m'msg'\"\n+\n test_expect_success 'push --porcelain' '\n \tmk_empty testrepo &&\n \techo >.git/foo  \"To testrepo\" &&\n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"354030","messageId":"20180731130718.25222-8-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH v2 07/10] fetch tests: correct a comment \"remove it\" -> \"remove them\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-07-31T13:07:15Z","receivedAt":"2018-07-31T13:07:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Correct a comment referring to the removal of just the branch to also\nrefer to the tag. This should have been changed in my\nca3065e7e7 (\"fetch tests: add a tag to be deleted to the pruning\ntests\", 2018-02-09) when the tag deletion was added, but I missed it\nat the time.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex e402aee6a2..6ab093207a 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -613,7 +613,7 @@ test_configured_prune_type () {\n \t\t\tgit rev-parse --verify refs/tags/newtag\n \t\t) &&\n \n-\t\t# now remove it\n+\t\t# now remove them\n \t\tgit branch -d newbranch &&\n \t\tgit tag -d newtag &&\n \n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"354031","messageId":"20180731130718.25222-10-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH v2 09/10] pull doc: fix a long-standing grammar error","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-07-31T13:07:17Z","receivedAt":"2018-07-31T13:07:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"It should be \"is not an empty string\" not \"is not empty string\". This\nfixes wording originally introduced in ab9b31386b (\"Documentation:\nmulti-head fetch.\", 2005-08-24).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/pull-fetch-param.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt\nindex c579793af5..f1fb08dc68 100644\n--- a/Documentation/pull-fetch-param.txt\n+++ b/Documentation/pull-fetch-param.txt\n@@ -33,7 +33,7 @@ name.\n it requests fetching everything up to the given tag.\n +\n The remote ref that matches <src>\n-is fetched, and if <dst> is not empty string, the local\n+is fetched, and if <dst> is not an empty string, the local\n ref that matches it is fast-forwarded using <src>.\n If the optional plus `+` is used, the local ref\n is updated even if it does not result in a fast-forward\n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"354032","messageId":"20180731130718.25222-11-avarab@gmail.com","threadId":"48353","inReplyTo":"20180429202100.32353-1-avarab@gmail.com","subject":"[PATCH v2 10/10] fetch: stop clobbering existing tags without --force","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-07-31T13:07:18Z","receivedAt":"2018-07-31T13:07:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change \"fetch\" to treat \"+\" in refspecs (aka --force) to mean we\nshould clobber a local tag of the same name.\n\nThis changes the long-standing behavior of \"fetch\" added in\n853a3697dc (\"[PATCH] Multi-head fetch.\", 2005-08-20), before this\nchange all tag fetches effectively had --force enabled. See the\ngit-fetch-script code in fast_forward_local() with the comment:\n\n    > Tags need not be pointing at commits so there is no way to\n    > guarantee \"fast-forward\" anyway.\n\nThat commit and the rest of the history of \"fetch\" shows that the\n\"+\" (--force) part of refpecs was only conceived for branch updates,\nwhile tags have accepted any changes from upstream unconditionally and\nclobbered the local tag object. Changing this behavior has been\ndiscussed as early as 2011[1].\n\nThe current behavior doesn't make sense to me, it easily results in\nlocal tags accidentally being clobbered.  We could namespace our tags\nper-remote and not locally populate refs/tags/*, but as with my\n97716d217c (\"fetch: add a --prune-tags option and fetch.pruneTags\nconfig\", 2018-02-09) it's easier to work around the current\nimplementation than to fix the root cause.\n\nSo this change implements suggestion #1 from Jeff's 2011 E-Mail[1],\n\"fetch\" now only clobbers the tag if either \"+\" is provided as part of\nthe refspec, or if \"--force\" is provided on the command-line.\n\nThis also makes it nicely symmetrical with how \"tag\" itself works when\ncreating tags. I.e. we refuse to clobber any existing tags unless\n\"--force\" is supplied. Now we can refuse all such clobbering, whether\nit would happen by clobbering a local tag with \"tag\", or by fetching\nit from the remote with \"fetch\".\n\nIt's still not at all nicely symmetrical with how \"git push\" works, as\ndiscussed in the updated pull-fetch-param.txt documentation, but this\nchange brings them more into line with one another. I don't think\nthere's any reason \"fetch\" couldn't fully converge with the behavior\nused by \"push\", but that's a topic for another change.\n\nOne of the tests added in 31b808a032 (\"clone --single: limit the fetch\nrefspec to fetched branch\", 2012-09-20) is being changed to use\n--force where a clone would clobber a tag. This changes nothing about\nthe existing behavior of the test.\n\n1. https://public-inbox.org/git/20111123221658.GA22313@sigill.intra.peff.net/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/fetch-options.txt    | 15 ++++++++++-----\n Documentation/pull-fetch-param.txt | 20 +++++++++++++++-----\n builtin/fetch.c                    | 20 +++++++++++++-------\n t/t5516-fetch-push.sh              |  5 +++--\n t/t5612-clone-refspec.sh           |  4 ++--\n 5 files changed, 43 insertions(+), 21 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 97d3217df9..5b624caf58 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -49,11 +49,16 @@ endif::git-pull[]\n \n -f::\n --force::\n-\tWhen 'git fetch' is used with `<rbranch>:<lbranch>`\n-\trefspec, it refuses to update the local branch\n-\t`<lbranch>` unless the remote branch `<rbranch>` it\n-\tfetches is a descendant of `<lbranch>`.  This option\n-\toverrides that check.\n+\tWhen 'git fetch' is used with `<src>:<dst>` refspec it may\n+\trefuse to update the local branch as discussed\n+ifdef::git-pull[]\n+\tin the `<refspec>` part of the linkgit:git-fetch[1]\n+\tdocumentation.\n+endif::git-pull[]\n+ifndef::git-pull[]\n+\tin the `<refspec>` part below.\n+endif::git-pull[]\n+\tThis option overrides that check.\n \n -k::\n --keep::\ndiff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt\nindex f1fb08dc68..acb8e1a4f0 100644\n--- a/Documentation/pull-fetch-param.txt\n+++ b/Documentation/pull-fetch-param.txt\n@@ -33,11 +33,21 @@ name.\n it requests fetching everything up to the given tag.\n +\n The remote ref that matches <src>\n-is fetched, and if <dst> is not an empty string, the local\n-ref that matches it is fast-forwarded using <src>.\n-If the optional plus `+` is used, the local ref\n-is updated even if it does not result in a fast-forward\n-update.\n+is fetched, and if <dst> is not an empty string, an attempt\n+is made to update the local ref that matches it.\n++\n+Whether that update is allowed without `--force` depends on the ref\n+namespace it's being fetched to, and the type of object being\n+fetched. If it's a commit under `refs/heads/*` only fast-forwards are\n+allowed.\n++\n+By having the optional leading `+` to a refspec (or using `--force`\n+command line option) you can tell Git to update the local ref even if\n+it is not allowed by its respective namespace clobbering rules.\n++\n+Before Git version 2.19 tag objects under `refs/tags/*` would not be\n+protected from updates, but since then the `+` (or `--force`) syntax\n+is required to clobber them.\n +\n [NOTE]\n When the remote branch you want to fetch is known to\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex ac06f6a576..683f70d71e 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -113,7 +113,7 @@ static struct option builtin_fetch_options[] = {\n \t\t N_(\"append to .git/FETCH_HEAD instead of overwriting\")),\n \tOPT_STRING(0, \"upload-pack\", &upload_pack, N_(\"path\"),\n \t\t   N_(\"path to upload pack on remote end\")),\n-\tOPT__FORCE(&force, N_(\"force overwrite of local branch\"), 0),\n+\tOPT__FORCE(&force, N_(\"force overwrite of local reference\"), 0),\n \tOPT_BOOL('m', \"multiple\", &multiple,\n \t\t N_(\"fetch from multiple remotes\")),\n \tOPT_SET_INT('t', \"tags\", &tags,\n@@ -664,12 +664,18 @@ static int update_local_ref(struct ref *ref,\n \n \tif (!is_null_oid(&ref->old_oid) &&\n \t    starts_with(ref->name, \"refs/tags/\")) {\n-\t\tint r;\n-\t\tr = s_update_ref(\"updating tag\", ref, 0);\n-\t\tformat_display(display, r ? '!' : 't', _(\"[tag update]\"),\n-\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n-\t\t\t       remote, pretty_ref, summary_width);\n-\t\treturn r;\n+\t\tif (force || ref->force) {\n+\t\t\tint r;\n+\t\t\tr = s_update_ref(\"updating tag\", ref, 0);\n+\t\t\tformat_display(display, r ? '!' : 't', _(\"[tag update]\"),\n+\t\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t\t       remote, pretty_ref, summary_width);\n+\t\t\treturn r;\n+\t\t} else {\n+\t\t\tformat_display(display, '!', _(\"[rejected]\"), _(\"would clobber existing tag\"),\n+\t\t\t\t       remote, pretty_ref, summary_width);\n+\t\t\treturn 1;\n+\t\t}\n \t}\n \n \tcurrent = lookup_commit_reference_gently(&ref->old_oid, 1);\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 8912312be7..8647f9cc9e 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1015,7 +1015,7 @@ test_force_fetch_tag () {\n \ttag_type_description=$1\n \ttag_args=$2\n \n-\ttest_expect_success \"fetch will clobber an existing $tag_type_description\" \"\n+\ttest_expect_success \"fetch will not clobber an existing $tag_type_description without --force\" \"\n \t\tmk_test testrepo heads/master &&\n \t\tmk_child testrepo child1 &&\n \t\tmk_child testrepo child2 &&\n@@ -1027,7 +1027,8 @@ test_force_fetch_tag () {\n \t\t\tgit add file1 &&\n \t\t\tgit commit -m 'file1' &&\n \t\t\tgit tag $tag_args Tag &&\n-\t\t\tgit -C ../child1 fetch origin tag Tag\n+\t\t\ttest_must_fail git -C ../child1 fetch origin tag Tag &&\n+\t\t\tgit -C ../child1 fetch origin '+refs/tags/*:refs/tags/*'\n \t\t)\n \t\"\n }\ndiff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh\nindex fac5a73851..6ea8f50dae 100755\n--- a/t/t5612-clone-refspec.sh\n+++ b/t/t5612-clone-refspec.sh\n@@ -104,7 +104,7 @@ test_expect_success 'clone with --no-tags' '\n test_expect_success '--single-branch while HEAD pointing at master' '\n \t(\n \t\tcd dir_master &&\n-\t\tgit fetch &&\n+\t\tgit fetch --force &&\n \t\tgit for-each-ref refs/remotes/origin |\n \t\tsed -e \"/HEAD$/d\" \\\n \t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n@@ -115,7 +115,7 @@ test_expect_success '--single-branch while HEAD pointing at master' '\n \ttest_cmp expect actual &&\n \t(\n \t\tcd dir_master &&\n-\t\tgit fetch --tags &&\n+\t\tgit fetch --tags --force &&\n \t\tgit for-each-ref refs/tags >../actual\n \t) &&\n \tgit for-each-ref refs/tags >expect &&\n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"354073","messageId":"xmqq8t5rcnhg.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"20180731130718.25222-7-avarab@gmail.com","subject":"Re: [PATCH v2 06/10] push doc: correct lies about how push refspecs work","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-07-31T17:40:43Z","receivedAt":"2018-07-31T17:40:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n>  The <src> is often the name of the branch you would want to push, but\n> -it can be any arbitrary \"SHA-1 expression\", such as `master~4` or\n> -`HEAD` (see linkgit:gitrevisions[7]).\n> +it can be any arbitrary expression to a commit, such as `master~4` or\n> +`HEAD` (see linkgit:gitrevisions[7]). It can also refer to tag\n> +objects, trees or blobs if the <dst> is outside of `refs/heads/*`.\n\n\"It can also refer to...\" is a good addition, but do you really want\nto make it part of this series to change/deprecate \"SHA-1 expression\"\n(which would certainly involve discussion on \"then what to call them\ninstead, now we are trying to refrain from saying SHA-1?\")?\n\n> +on the remote side. Whether this is allowed depends on where in\n> +`refs/*` the <dst> reference lives. The `refs/heads/*` namespace will\n> +only accept commit objects, and then only they can be\n> +fast-forwarded. The `refs/tags/*` namespace will accept any kind of\n> +object, and any changes to them and others types of objects will be\n> +rejected. Finally, it's possible to push any type of object to any\n> +namespace outside of `refs/{tags,heads}/*`,\n\nAll sound correct.\n\n> but these will be treated\n> +as branches for the purposes of whether `--force` is required, even in\n> +the case where a tag object is pushed.\n\nI am not sure what \"will be treated as branches\" exactly means.\nDoes it mean \"as if they were in refs/heads/* hierarchy?\"  Or\nsomething else?\n\n> That tag object will be\n> +overwritten by another tag object (or commit!) without `--force` if\n> +the new tag happens to point to a commit that's a fast-forward of the\n> +commit it replaces.\n\nYup, and that is something we want to fix with a later part of this\nseries.\n\n> +By having the optional leading `+` to a refspec (or using `--force`\n> +command line option) you can tell Git to update the <dst> ref even if\n> +it is not allowed by its respective namespace clobbering rules (e.g.,\n> +it is not a fast-forward. in the case of `refs/heads/*` updates).\n\nThis gives an impression that with \"--force\" you can put non-commit\ninside refs/heads/* hierarchy.  Is that correct (if so we probably\nwould want to fix that behaviour)?\n\n> +This\n> +does *not* attempt to merge <src> into <dst>.  See EXAMPLES below for\n> +details.\n\nThat is not wrong per-se, but would normal people expect a merge to\nhappen upon pushing on the other side, I wonder?\n\nThanks for cleaning up our longstanding mess.\n\n"},{"id":"354074","messageId":"xmqq4lgfcn5a.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"20180731130718.25222-9-avarab@gmail.com","subject":"Re: [PATCH v2 08/10] fetch tests: add a test clobbering tag behavior","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-07-31T17:48:01Z","receivedAt":"2018-07-31T17:48:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> The test suite only incidentally (and unintentionally) tested for the\n> current behavior of eager tag clobbering on \"fetch\". This follow-up to\n> the previous \"push tests: assert re-pushing annotated tags\" change\n> tests for it explicitly.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t5516-fetch-push.sh | 24 ++++++++++++++++++++++++\n>  1 file changed, 24 insertions(+)\n>\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index 1331a8de08..8912312be7 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -1011,6 +1011,30 @@ test_force_push_tag () {\n>  test_force_push_tag \"lightweight tag\" \"-f\"\n>  test_force_push_tag \"annotated tag\" \"-f -a -m'msg'\"\n>  \n> +test_force_fetch_tag () {\n> +\ttag_type_description=$1\n> +\ttag_args=$2\n> +\n> +\ttest_expect_success \"fetch will clobber an existing $tag_type_description\" \"\n> +\t\tmk_test testrepo heads/master &&\n> +\t\tmk_child testrepo child1 &&\n> +\t\tmk_child testrepo child2 &&\n> +\t\t(\n> +\t\t\tcd testrepo &&\n> +\t\t\tgit tag Tag &&\n> +\t\t\tgit -C ../child1 fetch origin tag Tag &&\n> +\t\t\t>file1 &&\n> +\t\t\tgit add file1 &&\n> +\t\t\tgit commit -m 'file1' &&\n> +\t\t\tgit tag $tag_args Tag &&\n> +\t\t\tgit -C ../child1 fetch origin tag Tag\n> +\t\t)\n> +\t\"\n> +}\n> +\n> +test_force_fetch_tag \"lightweight tag\" \"-f\"\n> +test_force_fetch_tag \"annotated tag\" \"-f -a -m'msg'\"\n\nI do not think that the single quotes around msg on the second one\ndoes what you want them to.  In \"git tag $tag_args Tag\" there is no\neval.  You have the same for the push side, which can be seen in the\nprecontext of this hunk.\n\nI somehow thought that you switched to using testTag for some\nreason in an earlier step.  Shouldn't we be calling this Tag\nalso testTag?\n\n\n> +\n>  test_expect_success 'push --porcelain' '\n>  \tmk_empty testrepo &&\n>  \techo >.git/foo  \"To testrepo\" &&\n"},{"id":"354097","messageId":"xmqqzhy7b7v9.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"20180731130718.25222-11-avarab@gmail.com","subject":"Re: [PATCH v2 10/10] fetch: stop clobbering existing tags without --force","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-07-31T18:03:22Z","receivedAt":"2018-07-31T18:03:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\n> index 97d3217df9..5b624caf58 100644\n> --- a/Documentation/fetch-options.txt\n> +++ b/Documentation/fetch-options.txt\n> @@ -49,11 +49,16 @@ endif::git-pull[]\n>  \n>  -f::\n>  --force::\n> -\tWhen 'git fetch' is used with `<rbranch>:<lbranch>`\n> -\trefspec, it refuses to update the local branch\n> -\t`<lbranch>` unless the remote branch `<rbranch>` it\n> -\tfetches is a descendant of `<lbranch>`.  This option\n> -\toverrides that check.\n> +\tWhen 'git fetch' is used with `<src>:<dst>` refspec it may\n> +\trefuse to update the local branch as discussed\n> +ifdef::git-pull[]\n> +\tin the `<refspec>` part of the linkgit:git-fetch[1]\n> +\tdocumentation.\n> +endif::git-pull[]\n> +ifndef::git-pull[]\n> +\tin the `<refspec>` part below.\n> +endif::git-pull[]\n> +\tThis option overrides that check.\n\nAh, that's tricky.  I could not locate \"the `<refspec>` part\" in\nDocumentation/git-fetch.txt and was scratching my head, but it\ncomes from pull-fetch-param.txt by inclusion ;-)\n\n> diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt\n> index f1fb08dc68..acb8e1a4f0 100644\n> --- a/Documentation/pull-fetch-param.txt\n> +++ b/Documentation/pull-fetch-param.txt\n> @@ -33,11 +33,21 @@ name.\n>  it requests fetching everything up to the given tag.\n>  +\n>  The remote ref that matches <src>\n> -is fetched, and if <dst> is not an empty string, the local\n> -ref that matches it is fast-forwarded using <src>.\n> -If the optional plus `+` is used, the local ref\n> -is updated even if it does not result in a fast-forward\n> -update.\n> +is fetched, and if <dst> is not an empty string, an attempt\n> +is made to update the local ref that matches it.\n> ++\n> +Whether that update is allowed without `--force` depends on the ref\n> +namespace it's being fetched to, and the type of object being\n> +fetched. If it's a commit under `refs/heads/*` only fast-forwards are\n> +allowed.\n> ++\n> +By having the optional leading `+` to a refspec (or using `--force`\n> +command line option) you can tell Git to update the local ref even if\n> +it is not allowed by its respective namespace clobbering rules.\n\nThe above two paragraphs imply that I can \"fetch +blob:refs/heads/master\"\nto cause havoc locally?\n\n> +Before Git version 2.19 tag objects under `refs/tags/*` would not be\n> +protected from updates, but since then the `+` (or `--force`) syntax\n> +is required to clobber them.\n\nI think that is a good change; it belongs more to the b/c notes in\nthe release notes; while I do not think it is wrong describe \"it\nused to be that way\" just after a drastic change in the immediate\npast, we shouldn't carry that forever, so perhaps we can leave a\n\"NEEDSWORK: remove the 'it used to be this way' in 2020\" comment\naround here?\n\n"},{"id":"355455","messageId":"20180813192249.27585-1-avarab@gmail.com","threadId":"48353","inReplyTo":"20180731130718.25222-1-avarab@gmail.com","subject":"[PATCH v3 0/7] Prep for \"git fetch\" should not clobber existing tags without --force","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-13T19:22:42Z","receivedAt":"2018-08-13T19:23:07Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"I have not had time to re-submit a v2 of my patch series to make \"+\"\nmeaningful in refspecs when it comes to tags, see v2 here:\nhttps://public-inbox.org/git/20180731130718.25222-1-avarab@gmail.com/\n\nGiven where we're at with the 2.19 release I'd like to propose this\nshortened version for inclusion in 2.19 for now. It's 7/10 patches in\nthat series, that purely deal with fixing some test issues and a\ntrivial grammar error in the tests.\n\nThis is unchanged from what's been cooking in pu for months now, so\nhopefully it can be merged down faster than most, and then I can later\nsubmit the actual meat of this series once I fix the (mostly doc)\nissues with it.\n\nÆvar Arnfjörð Bjarmason (7):\n  fetch tests: change \"Tag\" test tag to \"testTag\"\n  push tests: remove redundant 'git push' invocation\n  push tests: fix logic error in \"push\" test assertion\n  push tests: add more testing for forced tag pushing\n  push tests: assert re-pushing annotated tags\n  fetch tests: correct a comment \"remove it\" -> \"remove them\"\n  pull doc: fix a long-standing grammar error\n\n Documentation/pull-fetch-param.txt |  2 +-\n t/t5510-fetch.sh                   |  2 +-\n t/t5516-fetch-push.sh              | 65 +++++++++++++++++++++---------\n 3 files changed, 47 insertions(+), 22 deletions(-)\n\n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"355456","messageId":"20180813192249.27585-3-avarab@gmail.com","threadId":"48353","inReplyTo":"20180731130718.25222-1-avarab@gmail.com","subject":"[PATCH v3 2/7] push tests: remove redundant 'git push' invocation","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-13T19:22:44Z","receivedAt":"2018-08-13T19:23:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove an invocation of 'git push' that's exactly the same as the one\non the preceding line. This was seemingly added by mistake in\ndbfeddb12e (\"push: require force for refs under refs/tags/\",\n2012-11-29) and doesn't affect the result of the test, the second\n\"push\" was a no-op as there was nothing new to push.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 2cbe459ee6..59d7ea689a 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -973,7 +973,6 @@ test_expect_success 'push requires --force to update lightweight tag' '\n \t\tcd child1 &&\n \t\tgit tag testTag &&\n \t\tgit push ../child2 testTag &&\n-\t\tgit push ../child2 testTag &&\n \t\t>file1 &&\n \t\tgit add file1 &&\n \t\tgit commit -m \"file1\" &&\n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"355457","messageId":"20180813192249.27585-4-avarab@gmail.com","threadId":"48353","inReplyTo":"20180731130718.25222-1-avarab@gmail.com","subject":"[PATCH v3 3/7] push tests: fix logic error in \"push\" test assertion","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-13T19:22:45Z","receivedAt":"2018-08-13T19:23:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a logic error that's been here since this test was added in\ndbfeddb12e (\"push: require force for refs under refs/tags/\",\n2012-11-29).\n\nThe intent of this test is to force-create a new tag pointing to\nHEAD~, and then assert that pushing it doesn't work without --force.\n\nInstead, the code was not creating a new tag at all, and then failing\nto push the previous tag for the unrelated reason of providing a\nrefspec that doesn't make any sense.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 59d7ea689a..fbc44273d9 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -979,8 +979,8 @@ test_expect_success 'push requires --force to update lightweight tag' '\n \t\tgit tag -f testTag &&\n \t\ttest_must_fail git push ../child2 testTag &&\n \t\tgit push --force ../child2 testTag &&\n-\t\tgit tag -f testTag &&\n-\t\ttest_must_fail git push ../child2 testTag HEAD~ &&\n+\t\tgit tag -f testTag HEAD~ &&\n+\t\ttest_must_fail git push ../child2 testTag &&\n \t\tgit push --force ../child2 testTag\n \t)\n '\n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"355458","messageId":"20180813192249.27585-5-avarab@gmail.com","threadId":"48353","inReplyTo":"20180731130718.25222-1-avarab@gmail.com","subject":"[PATCH v3 4/7] push tests: add more testing for forced tag pushing","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-13T19:22:46Z","receivedAt":"2018-08-13T19:23:14Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Improve the tests added in dbfeddb12e (\"push: require force for refs\nunder refs/tags/\", 2012-11-29) to assert that the same behavior\napplies various other combinations of command-line option and\nrefspecs.\n\nSupplying either \"+\" in refspec or \"--force\" is sufficient to clobber\nthe reference. With --no-force we still pay attention to \"+\" in the\nrefspec, and vice-versa with clobbering kicking in if there's no \"+\"\nin the refspec but \"+\" is given.\n\nThis is consistent with how refspecs work for branches, where either\n\"+\" or \"--force\" will enable clobbering, with neither taking priority\nover the other.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 22 ++++++++++++++++++++--\n 1 file changed, 20 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex fbc44273d9..c7b0d2ba00 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -965,7 +965,7 @@ test_expect_success 'push into aliased refs (inconsistent)' '\n \t)\n '\n \n-test_expect_success 'push requires --force to update lightweight tag' '\n+test_expect_success 'force pushing required to update lightweight tag' '\n \tmk_test testrepo heads/master &&\n \tmk_child testrepo child1 &&\n \tmk_child testrepo child2 &&\n@@ -981,7 +981,25 @@ test_expect_success 'push requires --force to update lightweight tag' '\n \t\tgit push --force ../child2 testTag &&\n \t\tgit tag -f testTag HEAD~ &&\n \t\ttest_must_fail git push ../child2 testTag &&\n-\t\tgit push --force ../child2 testTag\n+\t\tgit push --force ../child2 testTag &&\n+\n+\t\t# Clobbering without + in refspec needs --force\n+\t\tgit tag -f testTag &&\n+\t\ttest_must_fail git push ../child2 \"refs/tags/*:refs/tags/*\" &&\n+\t\tgit push --force ../child2 \"refs/tags/*:refs/tags/*\" &&\n+\n+\t\t# Clobbering with + in refspec does not need --force\n+\t\tgit tag -f testTag HEAD~ &&\n+\t\tgit push ../child2 \"+refs/tags/*:refs/tags/*\" &&\n+\n+\t\t# Clobbering with --no-force still obeys + in refspec\n+\t\tgit tag -f testTag &&\n+\t\tgit push --no-force ../child2 \"+refs/tags/*:refs/tags/*\" &&\n+\n+\t\t# Clobbering with/without --force and \"tag <name>\" format\n+\t\tgit tag -f testTag HEAD~ &&\n+\t\ttest_must_fail git push ../child2 tag testTag &&\n+\t\tgit push --force ../child2 tag testTag\n \t)\n '\n \n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"355459","messageId":"20180813192249.27585-6-avarab@gmail.com","threadId":"48353","inReplyTo":"20180731130718.25222-1-avarab@gmail.com","subject":"[PATCH v3 5/7] push tests: assert re-pushing annotated tags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-13T19:22:47Z","receivedAt":"2018-08-13T19:23:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the test that asserts that lightweight tags can only be\nclobbered by a force-push to check do the same tests for annotated\ntags.\n\nThere used to be less exhaustive tests for this with the code added in\n40eff17999 (\"push: require force for annotated tags\", 2012-11-29), but\nJunio removed them in 256b9d70a4 (\"push: fix \"refs/tags/ hierarchy\ncannot be updated without --force\"\", 2013-01-16) while fixing some of\nthe behavior around tag pushing.\n\nThat change left us without any coverage asserting that pushing and\nclobbering annotated tags worked as intended.  There was no reason to\nsuspect that the receive machinery wouldn't behave the same way with\nannotated tags, but now we know for sure.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 82 ++++++++++++++++++++++++-------------------\n 1 file changed, 45 insertions(+), 37 deletions(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex c7b0d2ba00..539c25aada 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -965,43 +965,51 @@ test_expect_success 'push into aliased refs (inconsistent)' '\n \t)\n '\n \n-test_expect_success 'force pushing required to update lightweight tag' '\n-\tmk_test testrepo heads/master &&\n-\tmk_child testrepo child1 &&\n-\tmk_child testrepo child2 &&\n-\t(\n-\t\tcd child1 &&\n-\t\tgit tag testTag &&\n-\t\tgit push ../child2 testTag &&\n-\t\t>file1 &&\n-\t\tgit add file1 &&\n-\t\tgit commit -m \"file1\" &&\n-\t\tgit tag -f testTag &&\n-\t\ttest_must_fail git push ../child2 testTag &&\n-\t\tgit push --force ../child2 testTag &&\n-\t\tgit tag -f testTag HEAD~ &&\n-\t\ttest_must_fail git push ../child2 testTag &&\n-\t\tgit push --force ../child2 testTag &&\n-\n-\t\t# Clobbering without + in refspec needs --force\n-\t\tgit tag -f testTag &&\n-\t\ttest_must_fail git push ../child2 \"refs/tags/*:refs/tags/*\" &&\n-\t\tgit push --force ../child2 \"refs/tags/*:refs/tags/*\" &&\n-\n-\t\t# Clobbering with + in refspec does not need --force\n-\t\tgit tag -f testTag HEAD~ &&\n-\t\tgit push ../child2 \"+refs/tags/*:refs/tags/*\" &&\n-\n-\t\t# Clobbering with --no-force still obeys + in refspec\n-\t\tgit tag -f testTag &&\n-\t\tgit push --no-force ../child2 \"+refs/tags/*:refs/tags/*\" &&\n-\n-\t\t# Clobbering with/without --force and \"tag <name>\" format\n-\t\tgit tag -f testTag HEAD~ &&\n-\t\ttest_must_fail git push ../child2 tag testTag &&\n-\t\tgit push --force ../child2 tag testTag\n-\t)\n-'\n+test_force_push_tag () {\n+\ttag_type_description=$1\n+\ttag_args=$2\n+\n+\ttest_expect_success 'force pushing required to update lightweight tag' \"\n+\t\tmk_test testrepo heads/master &&\n+\t\tmk_child testrepo child1 &&\n+\t\tmk_child testrepo child2 &&\n+\t\t(\n+\t\t\tcd child1 &&\n+\t\t\tgit tag testTag &&\n+\t\t\tgit push ../child2 testTag &&\n+\t\t\t>file1 &&\n+\t\t\tgit add file1 &&\n+\t\t\tgit commit -m 'file1' &&\n+\t\t\tgit tag $tag_args testTag &&\n+\t\t\ttest_must_fail git push ../child2 testTag &&\n+\t\t\tgit push --force ../child2 testTag &&\n+\t\t\tgit tag $tag_args testTag HEAD~ &&\n+\t\t\ttest_must_fail git push ../child2 testTag &&\n+\t\t\tgit push --force ../child2 testTag &&\n+\n+\t\t\t# Clobbering without + in refspec needs --force\n+\t\t\tgit tag -f testTag &&\n+\t\t\ttest_must_fail git push ../child2 'refs/tags/*:refs/tags/*' &&\n+\t\t\tgit push --force ../child2 'refs/tags/*:refs/tags/*' &&\n+\n+\t\t\t# Clobbering with + in refspec does not need --force\n+\t\t\tgit tag -f testTag HEAD~ &&\n+\t\t\tgit push ../child2 '+refs/tags/*:refs/tags/*' &&\n+\n+\t\t\t# Clobbering with --no-force still obeys + in refspec\n+\t\t\tgit tag -f testTag &&\n+\t\t\tgit push --no-force ../child2 '+refs/tags/*:refs/tags/*' &&\n+\n+\t\t\t# Clobbering with/without --force and 'tag <name>' format\n+\t\t\tgit tag -f testTag HEAD~ &&\n+\t\t\ttest_must_fail git push ../child2 tag testTag &&\n+\t\t\tgit push --force ../child2 tag testTag\n+\t\t)\n+\t\"\n+}\n+\n+test_force_push_tag \"lightweight tag\" \"-f\"\n+test_force_push_tag \"annotated tag\" \"-f -a -m'msg'\"\n \n test_expect_success 'push --porcelain' '\n \tmk_empty testrepo &&\n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"355460","messageId":"20180813192249.27585-7-avarab@gmail.com","threadId":"48353","inReplyTo":"20180731130718.25222-1-avarab@gmail.com","subject":"[PATCH v3 6/7] fetch tests: correct a comment \"remove it\" -> \"remove them\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-13T19:22:48Z","receivedAt":"2018-08-13T19:23:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Correct a comment referring to the removal of just the branch to also\nrefer to the tag. This should have been changed in my\nca3065e7e7 (\"fetch tests: add a tag to be deleted to the pruning\ntests\", 2018-02-09) when the tag deletion was added, but I missed it\nat the time.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 62308be499..fdb73b3971 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -613,7 +613,7 @@ test_configured_prune_type () {\n \t\t\tgit rev-parse --verify refs/tags/newtag\n \t\t) &&\n \n-\t\t# now remove it\n+\t\t# now remove them\n \t\tgit branch -d newbranch &&\n \t\tgit tag -d newtag &&\n \n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"355461","messageId":"20180813192249.27585-8-avarab@gmail.com","threadId":"48353","inReplyTo":"20180731130718.25222-1-avarab@gmail.com","subject":"[PATCH v3 7/7] pull doc: fix a long-standing grammar error","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-13T19:22:49Z","receivedAt":"2018-08-13T19:23:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"It should be \"is not an empty string\" not \"is not empty string\". This\nfixes wording originally introduced in ab9b31386b (\"Documentation:\nmulti-head fetch.\", 2005-08-24).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/pull-fetch-param.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt\nindex c579793af5..f1fb08dc68 100644\n--- a/Documentation/pull-fetch-param.txt\n+++ b/Documentation/pull-fetch-param.txt\n@@ -33,7 +33,7 @@ name.\n it requests fetching everything up to the given tag.\n +\n The remote ref that matches <src>\n-is fetched, and if <dst> is not empty string, the local\n+is fetched, and if <dst> is not an empty string, the local\n ref that matches it is fast-forwarded using <src>.\n If the optional plus `+` is used, the local ref\n is updated even if it does not result in a fast-forward\n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"355462","messageId":"20180813192249.27585-2-avarab@gmail.com","threadId":"48353","inReplyTo":"20180731130718.25222-1-avarab@gmail.com","subject":"[PATCH v3 1/7] fetch tests: change \"Tag\" test tag to \"testTag\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-13T19:22:43Z","receivedAt":"2018-08-13T19:23:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Calling the test tag \"Tag\" will make for confusing reading later in\nthis series when making use of the \"git push tag <name>\"\nfeature. Let's call the tag testTag instead.\n\nChanges code initially added in dbfeddb12e (\"push: require force for\nrefs under refs/tags/\", 2012-11-29).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 18 +++++++++---------\n 1 file changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex bd8f23e430..2cbe459ee6 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -971,18 +971,18 @@ test_expect_success 'push requires --force to update lightweight tag' '\n \tmk_child testrepo child2 &&\n \t(\n \t\tcd child1 &&\n-\t\tgit tag Tag &&\n-\t\tgit push ../child2 Tag &&\n-\t\tgit push ../child2 Tag &&\n+\t\tgit tag testTag &&\n+\t\tgit push ../child2 testTag &&\n+\t\tgit push ../child2 testTag &&\n \t\t>file1 &&\n \t\tgit add file1 &&\n \t\tgit commit -m \"file1\" &&\n-\t\tgit tag -f Tag &&\n-\t\ttest_must_fail git push ../child2 Tag &&\n-\t\tgit push --force ../child2 Tag &&\n-\t\tgit tag -f Tag &&\n-\t\ttest_must_fail git push ../child2 Tag HEAD~ &&\n-\t\tgit push --force ../child2 Tag\n+\t\tgit tag -f testTag &&\n+\t\ttest_must_fail git push ../child2 testTag &&\n+\t\tgit push --force ../child2 testTag &&\n+\t\tgit tag -f testTag &&\n+\t\ttest_must_fail git push ../child2 testTag HEAD~ &&\n+\t\tgit push --force ../child2 testTag\n \t)\n '\n \n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"355473","messageId":"xmqqlg9a3t8b.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"20180813192249.27585-1-avarab@gmail.com","subject":"Re: [PATCH v3 0/7] Prep for \"git fetch\" should not clobber existing tags without --force","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-08-13T20:29:56Z","receivedAt":"2018-08-13T20:30:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> This is unchanged from what's been cooking in pu for months now, so\n> hopefully it can be merged down faster than most, and then I can later\n> submit the actual meat of this series once I fix the (mostly doc)\n> issues with it.\n\nThey have been held in 'pu' only because you said they were not\nready, I think ;-)\n\nI can confirm that the first 5 do look the same, and you dropped the\nold 6, 8 and 10.  The remainder look the same.\n\nI quickly re-scanned them and all of them looked obviously good.\nWill discard the remainder and requeue.\n\n\n>\n> Ævar Arnfjörð Bjarmason (7):\n>   fetch tests: change \"Tag\" test tag to \"testTag\"\n>   push tests: remove redundant 'git push' invocation\n>   push tests: fix logic error in \"push\" test assertion\n>   push tests: add more testing for forced tag pushing\n>   push tests: assert re-pushing annotated tags\n>   fetch tests: correct a comment \"remove it\" -> \"remove them\"\n>   pull doc: fix a long-standing grammar error\n>\n>  Documentation/pull-fetch-param.txt |  2 +-\n>  t/t5510-fetch.sh                   |  2 +-\n>  t/t5516-fetch-push.sh              | 65 +++++++++++++++++++++---------\n>  3 files changed, 47 insertions(+), 22 deletions(-)\n"},{"id":"355475","messageId":"87ftziroj5.fsf@evledraar.gmail.com","threadId":"48353","inReplyTo":"xmqqlg9a3t8b.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v3 0/7] Prep for \"git fetch\" should not clobber existing tags without --force","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-13T20:37:34Z","receivedAt":"2018-08-13T20:37:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Aug 13 2018, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> This is unchanged from what's been cooking in pu for months now, so\n>> hopefully it can be merged down faster than most, and then I can later\n>> submit the actual meat of this series once I fix the (mostly doc)\n>> issues with it.\n>\n> They have been held in 'pu' only because you said they were not\n> ready, I think ;-)\n\nYou had some feedback to 6, 8 & 10 in the last round which I haven't\naddressed yet.\n\nI think the \"not ready\" comment you're remembering is this for v1:\nhttps://public-inbox.org/git/CACBZZX4yG5h5kk4NFQz_NzAweMa+Nh3H-39OHtcH4XWsA6FGpg@mail.gmail.com/\n\n> I can confirm that the first 5 do look the same, and you dropped the\n> old 6, 8 and 10.  The remainder look the same.\n\nYup!\n\n> I quickly re-scanned them and all of them looked obviously good.\n> Will discard the remainder and requeue.\n\nThanks!\n"},{"id":"356979","messageId":"87o9djdi0c.fsf@evledraar.gmail.com","threadId":"48353","inReplyTo":"xmqq8t5rcnhg.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 06/10] push doc: correct lies about how push refspecs work","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-30T14:52:03Z","receivedAt":"2018-08-30T14:52:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Jul 31 2018, Junio C Hamano wrote:\n\nI'm finally getting to re-rolling this. Just some inline comments.\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>>  The <src> is often the name of the branch you would want to push, but\n>> -it can be any arbitrary \"SHA-1 expression\", such as `master~4` or\n>> -`HEAD` (see linkgit:gitrevisions[7]).\n>> +it can be any arbitrary expression to a commit, such as `master~4` or\n>> +`HEAD` (see linkgit:gitrevisions[7]). It can also refer to tag\n>> +objects, trees or blobs if the <dst> is outside of `refs/heads/*`.\n>\n> \"It can also refer to...\" is a good addition, but do you really want\n> to make it part of this series to change/deprecate \"SHA-1 expression\"\n> (which would certainly involve discussion on \"then what to call them\n> instead, now we are trying to refrain from saying SHA-1?\")?\n\nI won't change that.\n\n>> +on the remote side. Whether this is allowed depends on where in\n>> +`refs/*` the <dst> reference lives. The `refs/heads/*` namespace will\n>> +only accept commit objects, and then only they can be\n>> +fast-forwarded. The `refs/tags/*` namespace will accept any kind of\n>> +object, and any changes to them and others types of objects will be\n>> +rejected. Finally, it's possible to push any type of object to any\n>> +namespace outside of `refs/{tags,heads}/*`,\n>\n> All sound correct.\n>\n>> but these will be treated\n>> +as branches for the purposes of whether `--force` is required, even in\n>> +the case where a tag object is pushed.\n>\n> I am not sure what \"will be treated as branches\" exactly means.\n> Does it mean \"as if they were in refs/heads/* hierarchy?\"  Or\n> something else?\n\nI'll clarify this. Have rewritten most of this.\n\n>> That tag object will be\n>> +overwritten by another tag object (or commit!) without `--force` if\n>> +the new tag happens to point to a commit that's a fast-forward of the\n>> +commit it replaces.\n>\n> Yup, and that is something we want to fix with a later part of this\n> series.\n>\n\nFor what it's worth this is not at all what I'm fixing. The new docs\ndescribe this better, but what I'm talking about here is that you can\npush a tag like git.git's v2.18.0 to refs/blah/my-tag, then you can push\nv2.19.0-rc0^{} to refs/blah/my-tag and it'll be allowed as a\nfast-forward, and then v2.19.0-rc1 etc.\n\nI.e. the non-refs/{tags,heads}/* update logic treats all updates to\ntags/commits as branch updates. We just look at the tag v2.18.0, see you\nwant to replace it with the commit v2.19.0-rc0^{} and see \"oh, that's a\nfast-forward\".\n\nArguably that should be changed, but I won't do that in this series.\n\n>> +By having the optional leading `+` to a refspec (or using `--force`\n>> +command line option) you can tell Git to update the <dst> ref even if\n>> +it is not allowed by its respective namespace clobbering rules (e.g.,\n>> +it is not a fast-forward. in the case of `refs/heads/*` updates).\n>\n> This gives an impression that with \"--force\" you can put non-commit\n> inside refs/heads/* hierarchy.  Is that correct (if so we probably\n> would want to fix that behaviour)?\n\nI'll fix the wording, but nope, luckily you can't do that.\n\n>> +This\n>> +does *not* attempt to merge <src> into <dst>.  See EXAMPLES below for\n>> +details.\n>\n> That is not wrong per-se, but would normal people expect a merge to\n> happen upon pushing on the other side, I wonder?\n>\n> Thanks for cleaning up our longstanding mess.\n\nWill fix/reword.\n"},{"id":"356982","messageId":"xmqqh8jb7uax.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"87o9djdi0c.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 06/10] push doc: correct lies about how push refspecs work","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-08-30T15:23:02Z","receivedAt":"2018-08-30T15:23:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> I.e. the non-refs/{tags,heads}/* update logic treats all updates to\n> tags/commits as branch updates. We just look at the tag v2.18.0, see you\n> want to replace it with the commit v2.19.0-rc0^{} and see \"oh, that's a\n> fast-forward\".\n\nIn my old message you are responding to, I asked what you meant by\n\"will be treated as branches\", and after seeing \"as branch updates\"\nabove, I think I know what you want the phrase to mean, namely, that\nold-to-new transition requires new to be a descendant of old.  But I\nthink that is weaker than what other people (including me) thinks of\nrules to update refs/heads/* hierarchy (i.e. \"branch update\").\n\nYou are allowing to store an object that is not a commit in\nrefs/blah/my-tag in your example, so it clearly does not protect the\nref with an extra rule that applies to \"branches\", namely, \"it has\nto be a commit.\"\n\n> Arguably that should be changed, but I won't do that in this series.\n\nOK.\n"},{"id":"356991","messageId":"CACBZZX5FmuctKaE6Z3O-Mt6X40=RpCGUaae3AGhrSrTwH9AMhA@mail.gmail.com","threadId":"48353","inReplyTo":"xmqqh8jb7uax.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 06/10] push doc: correct lies about how push refspecs work","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-30T16:59:34Z","receivedAt":"2018-08-30T16:59:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Thu, Aug 30, 2018 at 5:23 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n> > I.e. the non-refs/{tags,heads}/* update logic treats all updates to\n> > tags/commits as branch updates. We just look at the tag v2.18.0, see you\n> > want to replace it with the commit v2.19.0-rc0^{} and see \"oh, that's a\n> > fast-forward\".\n>\n> In my old message you are responding to, I asked what you meant by\n> \"will be treated as branches\", and after seeing \"as branch updates\"\n> above, I think I know what you want the phrase to mean, namely, that\n> old-to-new transition requires new to be a descendant of old.  But I\n> think that is weaker than what other people (including me) thinks of\n> rules to update refs/heads/* hierarchy (i.e. \"branch update\").\n>\n> You are allowing to store an object that is not a commit in\n> refs/blah/my-tag in your example, so it clearly does not protect the\n> ref with an extra rule that applies to \"branches\", namely, \"it has\n> to be a commit.\"\n\nIndeed. This was all confusing. I've reworded in something I'll send\nshortly, which should address this confusion.\n\n> > Arguably that should be changed, but I won't do that in this series.\n>\n> OK.\n"},{"id":"357014","messageId":"20180830201244.25759-1-avarab@gmail.com","threadId":"48353","inReplyTo":"20180813192249.27585-1-avarab@gmail.com","subject":"[PATCH v4 0/6] \"git fetch\" should not clobber existing tags without --force","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-30T20:12:38Z","receivedAt":"2018-08-30T20:12:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that the tests for this have landed in master (in v3), and because\nI needed to rebase these for rolling out my own version based on\nv2.19.0-rc1, here's a re-roll which should address the (mostly doc)\ncomments on the previous (v2) round.\n\nÆvar Arnfjörð Bjarmason (6):\n  fetch: change \"branch\" to \"reference\" in --force -h output\n  push tests: correct quoting in interpolated string\n  fetch tests: add a test for clobbering tag behavior\n  push doc: correct lies about how push refspecs work\n  fetch: document local ref updates with/without --force\n  fetch: stop clobbering existing tags without --force\n\n Documentation/fetch-options.txt    | 15 +++++++----\n Documentation/git-push.txt         | 41 +++++++++++++++++++++++++-----\n Documentation/gitrevisions.txt     |  7 ++---\n Documentation/pull-fetch-param.txt | 35 +++++++++++++++++++++----\n builtin/fetch.c                    | 20 ++++++++++-----\n t/t5516-fetch-push.sh              | 27 +++++++++++++++++++-\n t/t5612-clone-refspec.sh           |  4 +--\n 7 files changed, 120 insertions(+), 29 deletions(-)\n\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357015","messageId":"20180830201244.25759-2-avarab@gmail.com","threadId":"48353","inReplyTo":"20180813192249.27585-1-avarab@gmail.com","subject":"[PATCH v4 1/6] fetch: change \"branch\" to \"reference\" in --force -h output","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-30T20:12:39Z","receivedAt":"2018-08-30T20:12:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The -h output has been referring to the --force command as forcing the\noverwriting of local branches, but since \"fetch\" more generally\nfetches all sorts of references in all refs/ namespaces, let's talk\nabout forcing the update of a a \"reference\" instead.\n\nThis wording was initially introduced in 8320199873 (\"Rewrite\nbuiltin-fetch option parsing to use parse_options().\", 2007-12-04).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fetch.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 61bec5d213..b0706b3803 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -114,7 +114,7 @@ static struct option builtin_fetch_options[] = {\n \t\t N_(\"append to .git/FETCH_HEAD instead of overwriting\")),\n \tOPT_STRING(0, \"upload-pack\", &upload_pack, N_(\"path\"),\n \t\t   N_(\"path to upload pack on remote end\")),\n-\tOPT__FORCE(&force, N_(\"force overwrite of local branch\"), 0),\n+\tOPT__FORCE(&force, N_(\"force overwrite of local reference\"), 0),\n \tOPT_BOOL('m', \"multiple\", &multiple,\n \t\t N_(\"fetch from multiple remotes\")),\n \tOPT_SET_INT('t', \"tags\", &tags,\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357016","messageId":"20180830201244.25759-3-avarab@gmail.com","threadId":"48353","inReplyTo":"20180813192249.27585-1-avarab@gmail.com","subject":"[PATCH v4 2/6] push tests: correct quoting in interpolated string","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-30T20:12:40Z","receivedAt":"2018-08-30T20:13:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The quoted -m'msg' option is passed as a string to another function,\nwhere due to interpolation it'll end up meaning the same as if we did\njust did -m'msg' here.\n\nIn [1] this was pointed out to me, but in submitting [2] the patches I\nmissed this (since it was feedback on another patch I was holding\noff), so this logic error landed in 380efb65df (\"push tests: assert\nre-pushing annotated tags\", 2018-07-31).\n\nLet's just remove the quotes, and use a string that doesn't need to be\nquoted (-mtag.message is a bit less confusing than -mmsg). I could try\nto chase after getting the quoting right here with multiple\nbackslashes, but I don't think it's worth it, and it makes things much\nless readable.\n\n1. https://public-inbox.org/git/xmqq4lgfcn5a.fsf@gitster-ct.c.googlers.com/\n2. https://public-inbox.org/git/20180813192249.27585-1-avarab@gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 539c25aada..69f7c9bfe6 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1009,7 +1009,7 @@ test_force_push_tag () {\n }\n \n test_force_push_tag \"lightweight tag\" \"-f\"\n-test_force_push_tag \"annotated tag\" \"-f -a -m'msg'\"\n+test_force_push_tag \"annotated tag\" \"-f -a -mtag.message\"\n \n test_expect_success 'push --porcelain' '\n \tmk_empty testrepo &&\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357017","messageId":"20180830201244.25759-4-avarab@gmail.com","threadId":"48353","inReplyTo":"20180813192249.27585-1-avarab@gmail.com","subject":"[PATCH v4 3/6] fetch tests: add a test for clobbering tag behavior","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-30T20:12:41Z","receivedAt":"2018-08-30T20:13:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The test suite only incidentally (and unintentionally) tested for the\ncurrent behavior of eager tag clobbering on \"fetch\". This is a\nfollowup to 380efb65df (\"push tests: assert re-pushing annotated\ntags\", 2018-07-31) which tests for it explicitly.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 24 ++++++++++++++++++++++++\n 1 file changed, 24 insertions(+)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 69f7c9bfe6..3cde72ae47 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1011,6 +1011,30 @@ test_force_push_tag () {\n test_force_push_tag \"lightweight tag\" \"-f\"\n test_force_push_tag \"annotated tag\" \"-f -a -mtag.message\"\n \n+test_force_fetch_tag () {\n+\ttag_type_description=$1\n+\ttag_args=$2\n+\n+\ttest_expect_success \"fetch will clobber an existing $tag_type_description\" \"\n+\t\tmk_test testrepo heads/master &&\n+\t\tmk_child testrepo child1 &&\n+\t\tmk_child testrepo child2 &&\n+\t\t(\n+\t\t\tcd testrepo &&\n+\t\t\tgit tag testTag &&\n+\t\t\tgit -C ../child1 fetch origin tag testTag &&\n+\t\t\t>file1 &&\n+\t\t\tgit add file1 &&\n+\t\t\tgit commit -m 'file1' &&\n+\t\t\tgit tag $tag_args testTag &&\n+\t\t\tgit -C ../child1 fetch origin tag testTag\n+\t\t)\n+\t\"\n+}\n+\n+test_force_fetch_tag \"lightweight tag\" \"-f\"\n+test_force_fetch_tag \"annotated tag\" \"-f -a -mtag.message\"\n+\n test_expect_success 'push --porcelain' '\n \tmk_empty testrepo &&\n \techo >.git/foo  \"To testrepo\" &&\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357018","messageId":"20180830201244.25759-5-avarab@gmail.com","threadId":"48353","inReplyTo":"20180813192249.27585-1-avarab@gmail.com","subject":"[PATCH v4 4/6] push doc: correct lies about how push refspecs work","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-30T20:12:42Z","receivedAt":"2018-08-30T20:13:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"There's complex rules governing whether a push is allowed to take\nplace depending on whether we're pushing to refs/heads/*, refs/tags/*\nor refs/not-that/*. See is_branch() in refs.c, and the various\nassertions in refs/files-backend.c. (e.g. \"trying to write non-commit\nobject %s to branch '%s'\").\n\nThis documentation has never been quite correct, but went downhill\nafter dbfeddb12e (\"push: require force for refs under refs/tags/\",\n2012-11-29) when we started claiming that <dst> couldn't be a tag\nobject, which is incorrect. After some of the logic in that patch was\nchanged in 256b9d70a4 (\"push: fix \"refs/tags/ hierarchy cannot be\nupdated without --force\"\", 2013-01-16) the docs weren't updated, and\nwe've had some version of documentation that confused whether <src>\nwas a tag or not with whether <dst> would accept either an annotated\ntag object or the commit it points to.\n\nThis makes the intro somewhat more verbose & complex, perhaps we\nshould have a shorter description here and split the full complexity\ninto a dedicated section. Very few users will find themselves needing\nto e.g. push blobs or trees to refs/custom-namespace/* (or blobs or\ntrees at all), and that could be covered separately as an advanced\ntopic.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-push.txt     | 41 +++++++++++++++++++++++++++++-----\n Documentation/gitrevisions.txt |  7 +++---\n 2 files changed, 39 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 55277a9781..0f03d36f1e 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -74,12 +74,41 @@ without any `<refspec>` on the command line.  Otherwise, missing\n `:<dst>` means to update the same ref as the `<src>`.\n +\n The object referenced by <src> is used to update the <dst> reference\n-on the remote side.  By default this is only allowed if <dst> is not\n-a tag (annotated or lightweight), and then only if it can fast-forward\n-<dst>.  By having the optional leading `+`, you can tell Git to update\n-the <dst> ref even if it is not allowed by default (e.g., it is not a\n-fast-forward.)  This does *not* attempt to merge <src> into <dst>.  See\n-EXAMPLES below for details.\n+on the remote side. Whether this is allowed depends on where in\n+`refs/*` the <dst> reference lives as described in detail below. Any\n+such update does *not* attempt to merge <src> into <dst>. See EXAMPLES\n+below for details.\n++\n+The `refs/heads/*` namespace will only accept commit objects, and only\n+if they can be fast-forwarded.\n++\n+The `refs/tags/*` namespace will accept any kind of object (as\n+commits, trees and blobs can be tagged), and any changes to them will\n+be rejected.\n++\n+It's possible to push any type of object to any namespace outside of\n+`refs/{tags,heads}/*`. In the case of tags and commits, these will be\n+treated as if they were the commits inside `refs/heads/*` for the\n+purposes of whether the update is allowed.\n++\n+I.e. a fast-forward of commits and tags outside `refs/{tags,heads}/*`\n+is allowed, even in cases where what's being fast-forwarded is not a\n+commit, but a tag object which happens to point to a new commit which\n+is a fast-forward of the commit the last tag (or commit) it's\n+replacing. Replacing a tag with an entirely different tag is also\n+allowed, if it points to the same commit, as well as pushing a peeled\n+tag, i.e. pushing the commit that existing tag object points to, or a\n+new tag object which an existing commit points to.\n++\n+Tree and blob objects outside of `refs/{tags,heads}/*` will be treated\n+the same way as if they were inside `refs/tags/*`, any modification of\n+them will be rejected.\n++\n+All of the rules described above about what's not allowed as an update\n+can be overridden by adding an the optional leading `+` to a refspec\n+(or using `--force` command line option). The only exception to this\n+is that no amount of forcing will make the `refs/heads/*` namespace\n+accept a non-commit object.\n +\n `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.\n +\ndiff --git a/Documentation/gitrevisions.txt b/Documentation/gitrevisions.txt\nindex 1f6cceaefb..d407b7dee1 100644\n--- a/Documentation/gitrevisions.txt\n+++ b/Documentation/gitrevisions.txt\n@@ -19,9 +19,10 @@ walk the revision graph (such as linkgit:git-log[1]), all commits which are\n reachable from that commit. For commands that walk the revision graph one can\n also specify a range of revisions explicitly.\n \n-In addition, some Git commands (such as linkgit:git-show[1]) also take\n-revision parameters which denote other objects than commits, e.g. blobs\n-(\"files\") or trees (\"directories of files\").\n+In addition, some Git commands (such as linkgit:git-show[1] and\n+linkgit:git-push[1]) can also take revision parameters which denote\n+other objects than commits, e.g. blobs (\"files\") or trees\n+(\"directories of files\").\n \n include::revisions.txt[]\n \n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357019","messageId":"20180830201244.25759-6-avarab@gmail.com","threadId":"48353","inReplyTo":"20180813192249.27585-1-avarab@gmail.com","subject":"[PATCH v4 5/6] fetch: document local ref updates with/without --force","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-30T20:12:43Z","receivedAt":"2018-08-30T20:13:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refer to the new git-push(1) documentation about when ref updates are\nand aren't allowed with and without --force, noting how \"git-fetch\"\ndiffers from the behavior of \"git-push\".\n\nPerhaps it would be better to split this all out into a new\ngitrefspecs(7) man page, or present this information using tables.\n\nIn lieu of that, this is accurate, and fixes a big omission in the\nexisting refspec docs.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/fetch-options.txt    | 15 +++++++++-----\n Documentation/pull-fetch-param.txt | 32 +++++++++++++++++++++++++-----\n 2 files changed, 37 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 8bc36af4b1..fa0a3151b3 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -68,11 +68,16 @@ endif::git-pull[]\n \n -f::\n --force::\n-\tWhen 'git fetch' is used with `<rbranch>:<lbranch>`\n-\trefspec, it refuses to update the local branch\n-\t`<lbranch>` unless the remote branch `<rbranch>` it\n-\tfetches is a descendant of `<lbranch>`.  This option\n-\toverrides that check.\n+\tWhen 'git fetch' is used with `<src>:<dst>` refspec it may\n+\trefuse to update the local branch as discussed\n+ifdef::git-pull[]\n+\tin the `<refspec>` part of the linkgit:git-fetch[1]\n+\tdocumentation.\n+endif::git-pull[]\n+ifndef::git-pull[]\n+\tin the `<refspec>` part below.\n+endif::git-pull[]\n+\tThis option overrides that check.\n \n -k::\n --keep::\ndiff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt\nindex f1fb08dc68..ab9617ad01 100644\n--- a/Documentation/pull-fetch-param.txt\n+++ b/Documentation/pull-fetch-param.txt\n@@ -33,11 +33,33 @@ name.\n it requests fetching everything up to the given tag.\n +\n The remote ref that matches <src>\n-is fetched, and if <dst> is not an empty string, the local\n-ref that matches it is fast-forwarded using <src>.\n-If the optional plus `+` is used, the local ref\n-is updated even if it does not result in a fast-forward\n-update.\n+is fetched, and if <dst> is not an empty string, an attempt\n+is made to update the local ref that matches it.\n++\n+Whether that update is allowed without `--force` depends on the ref\n+namespace it's being fetched to, the type of object being fetched, and\n+whether the update is considered to be a fast-forward. Generally, the\n+same rules apply for fetching as when pushing, see the `<refspec>...`\n+section of linkgit:git-push[1] for what those are. Exceptions to those\n+rules particular to 'git fetch' are noted below.\n++\n+Unlike when pushing with linkgit:git-push[1], any updates to\n+`refs/tags/*` will be accepted without `+` in the refspec (or\n+`--force`). The receiving promiscuously considers all tag updates from\n+a remote to be forced fetches.\n++\n+Unlike when pushing with linkgit:git-push[1], any updates outside of\n+`refs/{tags,heads}/*` will be accepted without `+` in the refspec (or\n+`--force`), whether that's swapping e.g. a tree object for a blob, or\n+a commit for another commit that's doesn't have the previous commit as\n+an ancestor etc.\n++\n+As with pushing with linkgit:git-push[1], all of the rules described\n+above about what's not allowed as an update can be overridden by\n+adding an the optional leading `+` to a refspec (or using `--force`\n+command line option). The only exception to this is that no amount of\n+forcing will make the `refs/heads/*` namespace accept a non-commit\n+object.\n +\n [NOTE]\n When the remote branch you want to fetch is known to\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357020","messageId":"20180830201244.25759-7-avarab@gmail.com","threadId":"48353","inReplyTo":"20180813192249.27585-1-avarab@gmail.com","subject":"[PATCH v4 6/6] fetch: stop clobbering existing tags without --force","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-30T20:12:44Z","receivedAt":"2018-08-30T20:13:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change \"fetch\" to treat \"+\" in refspecs (aka --force) to mean we\nshould clobber a local tag of the same name.\n\nThis changes the long-standing behavior of \"fetch\" added in\n853a3697dc (\"[PATCH] Multi-head fetch.\", 2005-08-20). Before this\nchange, all tag fetches effectively had --force enabled. See the\ngit-fetch-script code in fast_forward_local() with the comment:\n\n    > Tags need not be pointing at commits so there is no way to\n    > guarantee \"fast-forward\" anyway.\n\nThat commit and the rest of the history of \"fetch\" shows that the\n\"+\" (--force) part of refpecs was only conceived for branch updates,\nwhile tags have accepted any changes from upstream unconditionally and\nclobbered the local tag object. Changing this behavior has been\ndiscussed as early as 2011[1].\n\nThe current behavior doesn't make sense to me, it easily results in\nlocal tags accidentally being clobbered. We could namespace our tags\nper-remote and not locally populate refs/tags/*, but as with my\n97716d217c (\"fetch: add a --prune-tags option and fetch.pruneTags\nconfig\", 2018-02-09) it's easier to work around the current\nimplementation than to fix the root cause.\n\nSo this change implements suggestion #1 from Jeff's 2011 E-Mail[1],\n\"fetch\" now only clobbers the tag if either \"+\" is provided as part of\nthe refspec, or if \"--force\" is provided on the command-line.\n\nThis also makes it nicely symmetrical with how \"tag\" itself works when\ncreating tags. I.e. we refuse to clobber any existing tags unless\n\"--force\" is supplied. Now we can refuse all such clobbering, whether\nit would happen by clobbering a local tag with \"tag\", or by fetching\nit from the remote with \"fetch\".\n\nRef updates outside refs/{tags,heads/* are still still not symmetrical\nwith how \"git push\" works, as discussed in the recently changed\npull-fetch-param.txt documentation. This change brings the two\ndivergent behaviors more into line with one another. I don't think\nthere's any reason \"fetch\" couldn't fully converge with the behavior\nused by \"push\", but that's a topic for another change.\n\nOne of the tests added in 31b808a032 (\"clone --single: limit the fetch\nrefspec to fetched branch\", 2012-09-20) is being changed to use\n--force where a clone would clobber a tag. This changes nothing about\nthe existing behavior of the test.\n\n1. https://public-inbox.org/git/20111123221658.GA22313@sigill.intra.peff.net/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/pull-fetch-param.txt | 11 +++++++----\n builtin/fetch.c                    | 18 ++++++++++++------\n t/t5516-fetch-push.sh              |  5 +++--\n t/t5612-clone-refspec.sh           |  4 ++--\n 4 files changed, 24 insertions(+), 14 deletions(-)\n\ndiff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt\nindex ab9617ad01..47c832b17c 100644\n--- a/Documentation/pull-fetch-param.txt\n+++ b/Documentation/pull-fetch-param.txt\n@@ -43,10 +43,13 @@ same rules apply for fetching as when pushing, see the `<refspec>...`\n section of linkgit:git-push[1] for what those are. Exceptions to those\n rules particular to 'git fetch' are noted below.\n +\n-Unlike when pushing with linkgit:git-push[1], any updates to\n-`refs/tags/*` will be accepted without `+` in the refspec (or\n-`--force`). The receiving promiscuously considers all tag updates from\n-a remote to be forced fetches.\n+Until Git version 2.20, and unlike when pushing with\n+linkgit:git-push[1], any updates to `refs/tags/*` would be accepted\n+without `+` in the refspec (or `--force`). The receiving promiscuously\n+considered all tag updates from a remote to be forced fetches. Since\n+Git version 2.20 updates to `refs/tags/*` work the same way as when\n+pushing. I.e. any updates will be rejected without `+` in the refspec\n+(or `--force`).\n +\n Unlike when pushing with linkgit:git-push[1], any updates outside of\n `refs/{tags,heads}/*` will be accepted without `+` in the refspec (or\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex b0706b3803..ed4ed9d8c4 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -667,12 +667,18 @@ static int update_local_ref(struct ref *ref,\n \n \tif (!is_null_oid(&ref->old_oid) &&\n \t    starts_with(ref->name, \"refs/tags/\")) {\n-\t\tint r;\n-\t\tr = s_update_ref(\"updating tag\", ref, 0);\n-\t\tformat_display(display, r ? '!' : 't', _(\"[tag update]\"),\n-\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n-\t\t\t       remote, pretty_ref, summary_width);\n-\t\treturn r;\n+\t\tif (force || ref->force) {\n+\t\t\tint r;\n+\t\t\tr = s_update_ref(\"updating tag\", ref, 0);\n+\t\t\tformat_display(display, r ? '!' : 't', _(\"[tag update]\"),\n+\t\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t\t       remote, pretty_ref, summary_width);\n+\t\t\treturn r;\n+\t\t} else {\n+\t\t\tformat_display(display, '!', _(\"[rejected]\"), _(\"would clobber existing tag\"),\n+\t\t\t\t       remote, pretty_ref, summary_width);\n+\t\t\treturn 1;\n+\t\t}\n \t}\n \n \tcurrent = lookup_commit_reference_gently(the_repository,\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 3cde72ae47..6c5aa967ee 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1015,7 +1015,7 @@ test_force_fetch_tag () {\n \ttag_type_description=$1\n \ttag_args=$2\n \n-\ttest_expect_success \"fetch will clobber an existing $tag_type_description\" \"\n+\ttest_expect_success \"fetch will not clobber an existing $tag_type_description without --force\" \"\n \t\tmk_test testrepo heads/master &&\n \t\tmk_child testrepo child1 &&\n \t\tmk_child testrepo child2 &&\n@@ -1027,7 +1027,8 @@ test_force_fetch_tag () {\n \t\t\tgit add file1 &&\n \t\t\tgit commit -m 'file1' &&\n \t\t\tgit tag $tag_args testTag &&\n-\t\t\tgit -C ../child1 fetch origin tag testTag\n+\t\t\ttest_must_fail git -C ../child1 fetch origin tag testTag &&\n+\t\t\tgit -C ../child1 fetch origin '+refs/tags/*:refs/tags/*'\n \t\t)\n \t\"\n }\ndiff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh\nindex 5582b3d5fd..e36ac01661 100755\n--- a/t/t5612-clone-refspec.sh\n+++ b/t/t5612-clone-refspec.sh\n@@ -103,7 +103,7 @@ test_expect_success 'clone with --no-tags' '\n test_expect_success '--single-branch while HEAD pointing at master' '\n \t(\n \t\tcd dir_master &&\n-\t\tgit fetch &&\n+\t\tgit fetch --force &&\n \t\tgit for-each-ref refs/remotes/origin |\n \t\tsed -e \"/HEAD$/d\" \\\n \t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n@@ -114,7 +114,7 @@ test_expect_success '--single-branch while HEAD pointing at master' '\n \ttest_cmp expect actual &&\n \t(\n \t\tcd dir_master &&\n-\t\tgit fetch --tags &&\n+\t\tgit fetch --tags --force &&\n \t\tgit for-each-ref refs/tags >../actual\n \t) &&\n \tgit for-each-ref refs/tags >expect &&\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357028","messageId":"xmqqefef4kli.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"20180830201244.25759-3-avarab@gmail.com","subject":"Re: [PATCH v4 2/6] push tests: correct quoting in interpolated string","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-08-30T21:20:57Z","receivedAt":"2018-08-30T21:21:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> The quoted -m'msg' option is passed as a string to another function,\n> where due to interpolation it'll end up meaning the same as if we did\n> just did -m'msg' here.\n\n\"as if we did just did\"?  Also the sentence says -m'msg' is treated\nas if we gave -m'msg' that is tautology.  Perhaps\n\n\t... as if we just did -mmsg here.\n\nis what you meant?\n\nBut I think the pointing out I did in the old thread is wrong.  If\nyou change your \"-mtag.message\" to \"-m'tag  message'\" (notice that\nI have two spaces between the words) and then insert an invocation\nof \"git show -s testTag\" immediately after \"git tag\" is run to\ncreate testTag with $tag_args in test_force_push_tag, I can\nobserve in \"cd t && sh t5516-fetch-push.sh -v\" output that\nthe single quote is taking effect just fine.  IOW, I do not see\nanything wrong in the original \"-m'msg'\", which probably anticipated\nthat we may want to change it to -m'tag message' or something to\nclarify.\n\nHowever,...\n\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index 539c25aada..69f7c9bfe6 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -1009,7 +1009,7 @@ test_force_push_tag () {\n>  }\n>  \n>  test_force_push_tag \"lightweight tag\" \"-f\"\n> -test_force_push_tag \"annotated tag\" \"-f -a -m'msg'\"\n> +test_force_push_tag \"annotated tag\" \"-f -a -mtag.message\"\n\nComparing test_force_push_tag and test_force_fetch_tag which is\nadded in the next step, I notice that the former ignores $1 so\npassing \"annotated tag\" here has no effect.  That may be worth\nfixing in a follow-up patch like this.\n"},{"id":"357029","messageId":"xmqqa7p34kje.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"20180830201244.25759-4-avarab@gmail.com","subject":"Re: [PATCH v4 3/6] fetch tests: add a test for clobbering tag behavior","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-08-30T21:22:13Z","receivedAt":"2018-08-30T21:22:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> The test suite only incidentally (and unintentionally) tested for the\n> current behavior of eager tag clobbering on \"fetch\". This is a\n> followup to 380efb65df (\"push tests: assert re-pushing annotated\n> tags\", 2018-07-31) which tests for it explicitly.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n\nGood addition, and correctly uses $tag_type_description unlike the\none that is left unfixed in 2/6 for the push side.\n\n>  t/t5516-fetch-push.sh | 24 ++++++++++++++++++++++++\n>  1 file changed, 24 insertions(+)\n>\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index 69f7c9bfe6..3cde72ae47 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -1011,6 +1011,30 @@ test_force_push_tag () {\n>  test_force_push_tag \"lightweight tag\" \"-f\"\n>  test_force_push_tag \"annotated tag\" \"-f -a -mtag.message\"\n>  \n> +test_force_fetch_tag () {\n> +\ttag_type_description=$1\n> +\ttag_args=$2\n> +\n> +\ttest_expect_success \"fetch will clobber an existing $tag_type_description\" \"\n> +\t\tmk_test testrepo heads/master &&\n> +\t\tmk_child testrepo child1 &&\n> +\t\tmk_child testrepo child2 &&\n> +\t\t(\n> +\t\t\tcd testrepo &&\n> +\t\t\tgit tag testTag &&\n> +\t\t\tgit -C ../child1 fetch origin tag testTag &&\n> +\t\t\t>file1 &&\n> +\t\t\tgit add file1 &&\n> +\t\t\tgit commit -m 'file1' &&\n> +\t\t\tgit tag $tag_args testTag &&\n> +\t\t\tgit -C ../child1 fetch origin tag testTag\n> +\t\t)\n> +\t\"\n> +}\n> +\n> +test_force_fetch_tag \"lightweight tag\" \"-f\"\n> +test_force_fetch_tag \"annotated tag\" \"-f -a -mtag.message\"\n> +\n>  test_expect_success 'push --porcelain' '\n>  \tmk_empty testrepo &&\n>  \techo >.git/foo  \"To testrepo\" &&\n"},{"id":"357030","messageId":"xmqq4lfb4k3z.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"20180830201244.25759-5-avarab@gmail.com","subject":"Re: [PATCH v4 4/6] push doc: correct lies about how push refspecs work","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-08-30T21:31:28Z","receivedAt":"2018-08-30T21:31:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> +on the remote side. Whether this is allowed depends on where in\n> +`refs/*` the <dst> reference lives as described in detail below. Any\n> +such update does *not* attempt to merge <src> into <dst>. See EXAMPLES\n> +below for details.\n> ++\n> +The `refs/heads/*` namespace will only accept commit objects, and only\n> +if they can be fast-forwarded.\n> ++\n> +The `refs/tags/*` namespace will accept any kind of object (as\n> +commits, trees and blobs can be tagged), and any changes to them will\n> +be rejected.\n> ++\n> +It's possible to push any type of object to any namespace outside of\n> +`refs/{tags,heads}/*`. In the case of tags and commits, these will be\n> +treated as if they were the commits inside `refs/heads/*` for the\n> +purposes of whether the update is allowed.\n> ++\n> +I.e. a fast-forward of commits and tags outside `refs/{tags,heads}/*`\n> +is allowed, even in cases where what's being fast-forwarded is not a\n> +commit, but a tag object which happens to point to a new commit which\n> +is a fast-forward of the commit the last tag (or commit) it's\n> +replacing. Replacing a tag with an entirely different tag is also\n> +allowed, if it points to the same commit, as well as pushing a peeled\n> +tag, i.e. pushing the commit that existing tag object points to, or a\n> +new tag object which an existing commit points to.\n> ++\n> +Tree and blob objects outside of `refs/{tags,heads}/*` will be treated\n> +the same way as if they were inside `refs/tags/*`, any modification of\n> +them will be rejected.\n> ++\n> +All of the rules described above about what's not allowed as an update\n> +can be overridden by adding an the optional leading `+` to a refspec\n> +(or using `--force` command line option). The only exception to this\n> +is that no amount of forcing will make the `refs/heads/*` namespace\n> +accept a non-commit object.\n\nThis, while some may find it overly long, is quite clear, compared\nto the current text and to the previous rounds of this patch, and I\nfound it very much readable.\n\n"},{"id":"357052","messageId":"xmqqwos73503.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"20180830201244.25759-7-avarab@gmail.com","subject":"Re: [PATCH v4 6/6] fetch: stop clobbering existing tags without --force","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-08-30T21:43:08Z","receivedAt":"2018-08-30T21:43:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n>  +\n> -Unlike when pushing with linkgit:git-push[1], any updates to\n> -`refs/tags/*` will be accepted without `+` in the refspec (or\n> -`--force`). The receiving promiscuously considers all tag updates from\n> -a remote to be forced fetches.\n> +Until Git version 2.20, and unlike when pushing with\n> +linkgit:git-push[1], any updates to `refs/tags/*` would be accepted\n> +without `+` in the refspec (or `--force`). The receiving promiscuously\n> +considered all tag updates from a remote to be forced fetches. Since\n> +Git version 2.20 updates to `refs/tags/*` work the same way as when\n> +pushing. I.e. any updates will be rejected without `+` in the refspec\n> +(or `--force`).\n\nHave a comma after 2.20; otherwise it was unreadable, at least to\nme, who took three attempts before realizing that the \"updates\" is\nnot a verb whose subject is \"Git version 2.20\".  Or\n\n\tSince Git version 2.20, fetching to update `refs/tags/*`\n\twork the same way as pushing into it\n\nperhaps.\n\n> diff --git a/builtin/fetch.c b/builtin/fetch.c\n> index b0706b3803..ed4ed9d8c4 100644\n> --- a/builtin/fetch.c\n> +++ b/builtin/fetch.c\n> @@ -667,12 +667,18 @@ static int update_local_ref(struct ref *ref,\n>  \n>  \tif (!is_null_oid(&ref->old_oid) &&\n>  \t    starts_with(ref->name, \"refs/tags/\")) {\n> -\t\tint r;\n> -\t\tr = s_update_ref(\"updating tag\", ref, 0);\n> -\t\tformat_display(display, r ? '!' : 't', _(\"[tag update]\"),\n> -\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n> -\t\t\t       remote, pretty_ref, summary_width);\n> -\t\treturn r;\n> +\t\tif (force || ref->force) {\n> +\t\t\tint r;\n> +\t\t\tr = s_update_ref(\"updating tag\", ref, 0);\n> +\t\t\tformat_display(display, r ? '!' : 't', _(\"[tag update]\"),\n> +\t\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n> +\t\t\t\t       remote, pretty_ref, summary_width);\n> +\t\t\treturn r;\n> +\t\t} else {\n> +\t\t\tformat_display(display, '!', _(\"[rejected]\"), _(\"would clobber existing tag\"),\n> +\t\t\t\t       remote, pretty_ref, summary_width);\n> +\t\t\treturn 1;\n> +\t\t}\n>  \t}\n\nA straight-forward change to turn an unconditional update to either\nan unconditonal rejection (when force is not given) or an\nunconditional acceptance (when forced), which makes sense and has\nnear-zero chance of being wrong ;-)\n\nIt is a huge change in behaviour, but in a very good way.  I'd\nimagine that users will welcome it very much.\n\n"},{"id":"357056","messageId":"87efefcwme.fsf@evledraar.gmail.com","threadId":"48353","inReplyTo":"20180830201244.25759-5-avarab@gmail.com","subject":"Re: [PATCH v4 4/6] push doc: correct lies about how push refspecs work","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-30T22:34:01Z","receivedAt":"2018-08-30T22:34:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Aug 30 2018, Ævar Arnfjörð Bjarmason wrote:\n\n[Notes to self]\n\n> diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\n> index 55277a9781..0f03d36f1e 100644\n> --- a/Documentation/git-push.txt\n> +++ b/Documentation/git-push.txt\n> @@ -74,12 +74,41 @@ without any `<refspec>` on the command line.  Otherwise, missing\n>  `:<dst>` means to update the same ref as the `<src>`.\n>  +\n>  The object referenced by <src> is used to update the <dst> reference\n> -on the remote side.  By default this is only allowed if <dst> is not\n> -a tag (annotated or lightweight), and then only if it can fast-forward\n> -<dst>.  By having the optional leading `+`, you can tell Git to update\n> -the <dst> ref even if it is not allowed by default (e.g., it is not a\n> -fast-forward.)  This does *not* attempt to merge <src> into <dst>.  See\n> -EXAMPLES below for details.\n> +on the remote side. Whether this is allowed depends on where in\n> +`refs/*` the <dst> reference lives as described in detail below. Any\n> +such update does *not* attempt to merge <src> into <dst>. See EXAMPLES\n> +below for details.\n> ++\n> +The `refs/heads/*` namespace will only accept commit objects, and only\n> +if they can be fast-forwarded.\n> ++\n> +The `refs/tags/*` namespace will accept any kind of object (as\n> +commits, trees and blobs can be tagged), and any changes to them will\n> +be rejected.\n> ++\n\nBoth of these should carve out some mention for the \"deletion\" aspect of\n\"updates\". I.e. you don't need --force to delete.\n\n> +It's possible to push any type of object to any namespace outside of\n> +`refs/{tags,heads}/*`. In the case of tags and commits, these will be\n> +treated as if they were the commits inside `refs/heads/*` for the\n> +purposes of whether the update is allowed.\n> ++\n> +I.e. a fast-forward of commits and tags outside `refs/{tags,heads}/*`\n> +is allowed, even in cases where what's being fast-forwarded is not a\n> +commit, but a tag object which happens to point to a new commit which\n> +is a fast-forward of the commit the last tag (or commit) it's\n> +replacing. Replacing a tag with an entirely different tag is also\n> +allowed, if it points to the same commit, as well as pushing a peeled\n> +tag, i.e. pushing the commit that existing tag object points to, or a\n> +new tag object which an existing commit points to.\n> ++\n> +Tree and blob objects outside of `refs/{tags,heads}/*` will be treated\n> +the same way as if they were inside `refs/tags/*`, any modification of\n> +them will be rejected.\n> ++\n> +All of the rules described above about what's not allowed as an update\n> +can be overridden by adding an the optional leading `+` to a refspec\n> +(or using `--force` command line option). The only exception to this\n> +is that no amount of forcing will make the `refs/heads/*` namespace\n> +accept a non-commit object.\n>  +\n>  `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.\n>  +\n\nLater below this we say:\n\n    Pushing an empty <src> allows you to delete the <dst> ref from the\n    remote repository.\n\nWhich, perhaps given the discussion of deletions as updates, should be\nmentioned earlier in some way, i.e. should we just say above all these\nrules that by \"update\" we mean non-deletions?\n"},{"id":"357089","messageId":"xmqqd0ty33nz.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"87efefcwme.fsf@evledraar.gmail.com","subject":"Re: [PATCH v4 4/6] push doc: correct lies about how push refspecs work","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-08-31T16:24:16Z","receivedAt":"2018-08-31T16:24:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> On Thu, Aug 30 2018, Ævar Arnfjörð Bjarmason wrote:\n>\n> [Notes to self]\n> ...\n>\n> Later below this we say:\n>\n>     Pushing an empty <src> allows you to delete the <dst> ref from the\n>     remote repository.\n>\n> Which, perhaps given the discussion of deletions as updates, should be\n> mentioned earlier in some way, i.e. should we just say above all these\n> rules that by \"update\" we mean non-deletions?\n\nYou raised good points.  The rule that applies to deletion is quite\ndifferent from the one for update, we want to make sure readers know\nupdates and deletions are different.  As the rule for deletion is a\nlot simpler (i.e. you can always delete unless a configuration or\npre-receive says otherwise), perhaps it would be sufficient to give\nthe rules for deletion upfront in one section, and then start the\nsection(s) for update with a phrase like \"rules for accepting\nupdates are follows\" after that.\n"},{"id":"357091","messageId":"CACBZZX7A79KRMprLUaYbSrECBsdc2_4EF0WqhcOmU26cWvoYBA@mail.gmail.com","threadId":"48353","inReplyTo":"xmqqd0ty33nz.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v4 4/6] push doc: correct lies about how push refspecs work","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-31T16:35:04Z","receivedAt":"2018-08-31T16:35:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Fri, Aug 31, 2018 at 6:24 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n> > On Thu, Aug 30 2018, Ævar Arnfjörð Bjarmason wrote:\n> >\n> > [Notes to self]\n> > ...\n> >\n> > Later below this we say:\n> >\n> >     Pushing an empty <src> allows you to delete the <dst> ref from the\n> >     remote repository.\n> >\n> > Which, perhaps given the discussion of deletions as updates, should be\n> > mentioned earlier in some way, i.e. should we just say above all these\n> > rules that by \"update\" we mean non-deletions?\n>\n> You raised good points.  The rule that applies to deletion is quite\n> different from the one for update, we want to make sure readers know\n> updates and deletions are different.  As the rule for deletion is a\n> lot simpler (i.e. you can always delete unless a configuration or\n> pre-receive says otherwise), perhaps it would be sufficient to give\n> the rules for deletion upfront in one section, and then start the\n> section(s) for update with a phrase like \"rules for accepting\n> updates are follows\" after that.\n\nYeah, that was the plan. I'll do that.\n"},{"id":"357119","messageId":"20180831201004.12087-1-avarab@gmail.com","threadId":"48353","inReplyTo":"20180830201244.25759-1-avarab@gmail.com","subject":"[PATCH v5 0/9] git fetch\" should not clobber existing tags without --force","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-31T20:09:55Z","receivedAt":"2018-08-31T20:10:21Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Addresses Junio's comments to v4, and I had a few fixes of my own. I\ndon't know if this range-diff is more or less readble than just\nre-reading it, but here goes:\n\n 1:  d05fd561f3 =  1:  d05fd561f3 fetch: change \"branch\" to \"reference\" in --force -h output\n -:  ---------- >  2:  28275baca2 push tests: make use of unused $1 in test description\n 2:  013ecd83b3 !  3:  834501afdc push tests: correct quoting in interpolated string\n    @@ -1,24 +1,11 @@\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    -    push tests: correct quoting in interpolated string\n    +    push tests: use spaces in interpolated string\n     \n    -    The quoted -m'msg' option is passed as a string to another function,\n    -    where due to interpolation it'll end up meaning the same as if we did\n    -    just did -m'msg' here.\n    -\n    -    In [1] this was pointed out to me, but in submitting [2] the patches I\n    -    missed this (since it was feedback on another patch I was holding\n    -    off), so this logic error landed in 380efb65df (\"push tests: assert\n    -    re-pushing annotated tags\", 2018-07-31).\n    -\n    -    Let's just remove the quotes, and use a string that doesn't need to be\n    -    quoted (-mtag.message is a bit less confusing than -mmsg). I could try\n    -    to chase after getting the quoting right here with multiple\n    -    backslashes, but I don't think it's worth it, and it makes things much\n    -    less readable.\n    -\n    -    1. https://public-inbox.org/git/xmqq4lgfcn5a.fsf@gitster-ct.c.googlers.com/\n    -    2. https://public-inbox.org/git/20180813192249.27585-1-avarab@gmail.com/\n    +    The quoted -m'msg' option would mean the same as -mmsg when passed\n    +    through the test_force_push_tag helper. Let's instead use a string\n    +    with spaces in it, to have a working example in case we need to pass\n    +    other whitespace-delimited arguments to git-tag.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ -30,7 +17,7 @@\n      \n      test_force_push_tag \"lightweight tag\" \"-f\"\n     -test_force_push_tag \"annotated tag\" \"-f -a -m'msg'\"\n    -+test_force_push_tag \"annotated tag\" \"-f -a -mtag.message\"\n    ++test_force_push_tag \"annotated tag\" \"-f -a -m'tag message'\"\n      \n      test_expect_success 'push --porcelain' '\n      \tmk_empty testrepo &&\n 3:  2d216a7ef6 !  4:  5f85542bb2 fetch tests: add a test for clobbering tag behavior\n    @@ -14,7 +14,7 @@\n      +++ b/t/t5516-fetch-push.sh\n     @@\n      test_force_push_tag \"lightweight tag\" \"-f\"\n    - test_force_push_tag \"annotated tag\" \"-f -a -mtag.message\"\n    + test_force_push_tag \"annotated tag\" \"-f -a -m'tag message'\"\n      \n     +test_force_fetch_tag () {\n     +\ttag_type_description=$1\n    @@ -38,7 +38,7 @@\n     +}\n     +\n     +test_force_fetch_tag \"lightweight tag\" \"-f\"\n    -+test_force_fetch_tag \"annotated tag\" \"-f -a -mtag.message\"\n    ++test_force_fetch_tag \"annotated tag\" \"-f -a -m'tag message'\"\n     +\n      test_expect_success 'push --porcelain' '\n      \tmk_empty testrepo &&\n -:  ---------- >  5:  6906d5a84d push doc: remove confusing mention of remote merger\n -:  ---------- >  6:  a16a9c2d7f push doc: move mention of \"tag <tag>\" later in the prose\n 4:  b751e80b00 !  7:  9f8785e01a push doc: correct lies about how push refspecs work\n    @@ -38,18 +38,20 @@\n     -a tag (annotated or lightweight), and then only if it can fast-forward\n     -<dst>.  By having the optional leading `+`, you can tell Git to update\n     -the <dst> ref even if it is not allowed by default (e.g., it is not a\n    --fast-forward.)  This does *not* attempt to merge <src> into <dst>.  See\n    --EXAMPLES below for details.\n    +-fast-forward.).\n    +-+\n    +-Pushing an empty <src> allows you to delete the <dst> ref from\n    +-the remote repository.\n     +on the remote side. Whether this is allowed depends on where in\n    -+`refs/*` the <dst> reference lives as described in detail below. Any\n    -+such update does *not* attempt to merge <src> into <dst>. See EXAMPLES\n    -+below for details.\n    ++`refs/*` the <dst> reference lives as described in detail below, in\n    ++those sections \"update\" means any modifications except deletes, which\n    ++as noted after the next few sections are treated differently.\n     ++\n    -+The `refs/heads/*` namespace will only accept commit objects, and only\n    -+if they can be fast-forwarded.\n    ++The `refs/heads/*` namespace will only accept commit objects, and\n    ++updates only if they can be fast-forwarded.\n     ++\n     +The `refs/tags/*` namespace will accept any kind of object (as\n    -+commits, trees and blobs can be tagged), and any changes to them will\n    ++commits, trees and blobs can be tagged), and any updates to them will\n     +be rejected.\n     ++\n     +It's possible to push any type of object to any namespace outside of\n    @@ -67,17 +69,26 @@\n     +new tag object which an existing commit points to.\n     ++\n     +Tree and blob objects outside of `refs/{tags,heads}/*` will be treated\n    -+the same way as if they were inside `refs/tags/*`, any modification of\n    -+them will be rejected.\n    ++the same way as if they were inside `refs/tags/*`, any update of them\n    ++will be rejected.\n     ++\n     +All of the rules described above about what's not allowed as an update\n     +can be overridden by adding an the optional leading `+` to a refspec\n     +(or using `--force` command line option). The only exception to this\n     +is that no amount of forcing will make the `refs/heads/*` namespace\n    -+accept a non-commit object.\n    - +\n    - `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.\n    ++accept a non-commit object. Hooks and configuration can also override\n    ++or amend these rules, see e.g. `receive.denyNonFastForwards` in\n    ++linkgit:git-config[1] and`pre-receive` and `update` in\n    ++linkgit:githooks[5].\n    +++\n    ++Pushing an empty <src> allows you to delete the <dst> ref from the\n    ++remote repository. Deletions are always accepted without a leading `+`\n    ++in the refspec (or `--force`), except when forbidden by configuration\n    ++or hooks. See `receive.denyDeletes` in linkgit:git-config[1] and\n    ++`pre-receive` and `update` in linkgit:githooks[5].\n      +\n    + The special refspec `:` (or `+:` to allow non-fast-forward updates)\n    + directs Git to push \"matching\" branches: for every branch that exists on\n     \n      diff --git a/Documentation/gitrevisions.txt b/Documentation/gitrevisions.txt\n      --- a/Documentation/gitrevisions.txt\n 5:  b120051957 =  8:  3e90699b9f fetch: document local ref updates with/without --force\n 6:  25df331fce !  9:  0e183b6f23 fetch: stop clobbering existing tags without --force\n    @@ -66,13 +66,24 @@\n     +Until Git version 2.20, and unlike when pushing with\n     +linkgit:git-push[1], any updates to `refs/tags/*` would be accepted\n     +without `+` in the refspec (or `--force`). The receiving promiscuously\n    -+considered all tag updates from a remote to be forced fetches. Since\n    -+Git version 2.20 updates to `refs/tags/*` work the same way as when\n    -+pushing. I.e. any updates will be rejected without `+` in the refspec\n    -+(or `--force`).\n    ++considered all tag updates from a remote to be forced fetches.  Since\n    ++Git version 2.20, fetching to update `refs/tags/*` work the same way\n    ++as when pushing. I.e. any updates will be rejected without `+` in the\n    ++refspec (or `--force`).\n      +\n      Unlike when pushing with linkgit:git-push[1], any updates outside of\n      `refs/{tags,heads}/*` will be accepted without `+` in the refspec (or\n    +@@\n    + a commit for another commit that's doesn't have the previous commit as\n    + an ancestor etc.\n    + +\n    ++Unlike when pushing with linkgit:git-push[1], there is no\n    ++configuration which'll amend these rules, and nothing like a\n    ++`pre-fetch` hook analogous to the `pre-receive` hook.\n    +++\n    + As with pushing with linkgit:git-push[1], all of the rules described\n    + above about what's not allowed as an update can be overridden by\n    + adding an the optional leading `+` to a refspec (or using `--force`\n     \n      diff --git a/builtin/fetch.c b/builtin/fetch.c\n      --- a/builtin/fetch.c\n\nÆvar Arnfjörð Bjarmason (9):\n  fetch: change \"branch\" to \"reference\" in --force -h output\n  push tests: make use of unused $1 in test description\n  push tests: use spaces in interpolated string\n  fetch tests: add a test for clobbering tag behavior\n  push doc: remove confusing mention of remote merger\n  push doc: move mention of \"tag <tag>\" later in the prose\n  push doc: correct lies about how push refspecs work\n  fetch: document local ref updates with/without --force\n  fetch: stop clobbering existing tags without --force\n\n Documentation/fetch-options.txt    | 15 +++++---\n Documentation/git-push.txt         | 57 ++++++++++++++++++++++++------\n Documentation/gitrevisions.txt     |  7 ++--\n Documentation/pull-fetch-param.txt | 39 +++++++++++++++++---\n builtin/fetch.c                    | 20 +++++++----\n t/t5516-fetch-push.sh              | 29 +++++++++++++--\n t/t5612-clone-refspec.sh           |  4 +--\n 7 files changed, 136 insertions(+), 35 deletions(-)\n\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357120","messageId":"20180831201004.12087-2-avarab@gmail.com","threadId":"48353","inReplyTo":"20180830201244.25759-1-avarab@gmail.com","subject":"[PATCH v5 1/9] fetch: change \"branch\" to \"reference\" in --force -h output","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-31T20:09:56Z","receivedAt":"2018-08-31T20:10:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The -h output has been referring to the --force command as forcing the\noverwriting of local branches, but since \"fetch\" more generally\nfetches all sorts of references in all refs/ namespaces, let's talk\nabout forcing the update of a a \"reference\" instead.\n\nThis wording was initially introduced in 8320199873 (\"Rewrite\nbuiltin-fetch option parsing to use parse_options().\", 2007-12-04).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fetch.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 61bec5d213..b0706b3803 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -114,7 +114,7 @@ static struct option builtin_fetch_options[] = {\n \t\t N_(\"append to .git/FETCH_HEAD instead of overwriting\")),\n \tOPT_STRING(0, \"upload-pack\", &upload_pack, N_(\"path\"),\n \t\t   N_(\"path to upload pack on remote end\")),\n-\tOPT__FORCE(&force, N_(\"force overwrite of local branch\"), 0),\n+\tOPT__FORCE(&force, N_(\"force overwrite of local reference\"), 0),\n \tOPT_BOOL('m', \"multiple\", &multiple,\n \t\t N_(\"fetch from multiple remotes\")),\n \tOPT_SET_INT('t', \"tags\", &tags,\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357121","messageId":"20180831201004.12087-3-avarab@gmail.com","threadId":"48353","inReplyTo":"20180830201244.25759-1-avarab@gmail.com","subject":"[PATCH v5 2/9] push tests: make use of unused $1 in test description","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-31T20:09:57Z","receivedAt":"2018-08-31T20:10:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix up a logic error in 380efb65df (\"push tests: assert re-pushing\nannotated tags\", 2018-07-31), where the $tag_type_description variable\nwas assigned to but never used, unlike in the subsequently added\ncompanion test for fetches in 2d216a7ef6 (\"fetch tests: add a test for\nclobbering tag behavior\", 2018-04-29).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 539c25aada..62d5059f92 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -969,7 +969,7 @@ test_force_push_tag () {\n \ttag_type_description=$1\n \ttag_args=$2\n \n-\ttest_expect_success 'force pushing required to update lightweight tag' \"\n+\ttest_expect_success 'force pushing required to update $tag_type_description' \"\n \t\tmk_test testrepo heads/master &&\n \t\tmk_child testrepo child1 &&\n \t\tmk_child testrepo child2 &&\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357122","messageId":"20180831201004.12087-4-avarab@gmail.com","threadId":"48353","inReplyTo":"20180830201244.25759-1-avarab@gmail.com","subject":"[PATCH v5 3/9] push tests: use spaces in interpolated string","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-31T20:09:58Z","receivedAt":"2018-08-31T20:10:25Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The quoted -m'msg' option would mean the same as -mmsg when passed\nthrough the test_force_push_tag helper. Let's instead use a string\nwith spaces in it, to have a working example in case we need to pass\nother whitespace-delimited arguments to git-tag.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 62d5059f92..8b67f08265 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1009,7 +1009,7 @@ test_force_push_tag () {\n }\n \n test_force_push_tag \"lightweight tag\" \"-f\"\n-test_force_push_tag \"annotated tag\" \"-f -a -m'msg'\"\n+test_force_push_tag \"annotated tag\" \"-f -a -m'tag message'\"\n \n test_expect_success 'push --porcelain' '\n \tmk_empty testrepo &&\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357123","messageId":"20180831201004.12087-5-avarab@gmail.com","threadId":"48353","inReplyTo":"20180830201244.25759-1-avarab@gmail.com","subject":"[PATCH v5 4/9] fetch tests: add a test for clobbering tag behavior","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-31T20:09:59Z","receivedAt":"2018-08-31T20:10:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The test suite only incidentally (and unintentionally) tested for the\ncurrent behavior of eager tag clobbering on \"fetch\". This is a\nfollowup to 380efb65df (\"push tests: assert re-pushing annotated\ntags\", 2018-07-31) which tests for it explicitly.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5516-fetch-push.sh | 24 ++++++++++++++++++++++++\n 1 file changed, 24 insertions(+)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 8b67f08265..7f3d4c4965 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1011,6 +1011,30 @@ test_force_push_tag () {\n test_force_push_tag \"lightweight tag\" \"-f\"\n test_force_push_tag \"annotated tag\" \"-f -a -m'tag message'\"\n \n+test_force_fetch_tag () {\n+\ttag_type_description=$1\n+\ttag_args=$2\n+\n+\ttest_expect_success \"fetch will clobber an existing $tag_type_description\" \"\n+\t\tmk_test testrepo heads/master &&\n+\t\tmk_child testrepo child1 &&\n+\t\tmk_child testrepo child2 &&\n+\t\t(\n+\t\t\tcd testrepo &&\n+\t\t\tgit tag testTag &&\n+\t\t\tgit -C ../child1 fetch origin tag testTag &&\n+\t\t\t>file1 &&\n+\t\t\tgit add file1 &&\n+\t\t\tgit commit -m 'file1' &&\n+\t\t\tgit tag $tag_args testTag &&\n+\t\t\tgit -C ../child1 fetch origin tag testTag\n+\t\t)\n+\t\"\n+}\n+\n+test_force_fetch_tag \"lightweight tag\" \"-f\"\n+test_force_fetch_tag \"annotated tag\" \"-f -a -m'tag message'\"\n+\n test_expect_success 'push --porcelain' '\n \tmk_empty testrepo &&\n \techo >.git/foo  \"To testrepo\" &&\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357124","messageId":"20180831201004.12087-6-avarab@gmail.com","threadId":"48353","inReplyTo":"20180830201244.25759-1-avarab@gmail.com","subject":"[PATCH v5 5/9] push doc: remove confusing mention of remote merger","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-31T20:10:00Z","receivedAt":"2018-08-31T20:10:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Saying that \"git push <remote> <src>:<dst>\" won't push a merger of\n<src> and <dst> to <dst> is clear from the rest of the context here,\nso mentioning it is redundant, furthermore the mention of \"EXAMPLES\nbelow\" isn't specific or useful.\n\nThis phrase was originally added in 149f6ddfb3 (\"Docs: Expand\nexplanation of the use of + in git push refspecs.\", 2009-02-19), as\ncan be seen in that change the point of the example being cited was to\nshow that force pushing can leave unreferenced commits on the\nremote. It's enough that we explain that in its own section, it\ndoesn't need to be mentioned here.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-push.txt | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 55277a9781..83e499ee97 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -78,8 +78,7 @@ on the remote side.  By default this is only allowed if <dst> is not\n a tag (annotated or lightweight), and then only if it can fast-forward\n <dst>.  By having the optional leading `+`, you can tell Git to update\n the <dst> ref even if it is not allowed by default (e.g., it is not a\n-fast-forward.)  This does *not* attempt to merge <src> into <dst>.  See\n-EXAMPLES below for details.\n+fast-forward.).\n +\n `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.\n +\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357125","messageId":"20180831201004.12087-7-avarab@gmail.com","threadId":"48353","inReplyTo":"20180830201244.25759-1-avarab@gmail.com","subject":"[PATCH v5 6/9] push doc: move mention of \"tag <tag>\" later in the prose","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-31T20:10:01Z","receivedAt":"2018-08-31T20:10:30Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This change will be followed-up with a subsequent change where I'll\nchange both sides of this mention of \"tag <tag>\" to be something\nthat's best read without interruption.\n\nTo make that change smaller, let's move this mention of \"tag <tag>\" to\nthe end of the \"<refspec>...\" section, it's now somewhere in the\nmiddle.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-push.txt | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 83e499ee97..71c78ac1a4 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -80,8 +80,6 @@ a tag (annotated or lightweight), and then only if it can fast-forward\n the <dst> ref even if it is not allowed by default (e.g., it is not a\n fast-forward.).\n +\n-`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.\n-+\n Pushing an empty <src> allows you to delete the <dst> ref from\n the remote repository.\n +\n@@ -89,6 +87,8 @@ The special refspec `:` (or `+:` to allow non-fast-forward updates)\n directs Git to push \"matching\" branches: for every branch that exists on\n the local side, the remote side is updated if a branch of the same name\n already exists on the remote side.\n++\n+`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.\n \n --all::\n \tPush all branches (i.e. refs under `refs/heads/`); cannot be\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357126","messageId":"20180831201004.12087-8-avarab@gmail.com","threadId":"48353","inReplyTo":"20180830201244.25759-1-avarab@gmail.com","subject":"[PATCH v5 7/9] push doc: correct lies about how push refspecs work","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-31T20:10:02Z","receivedAt":"2018-08-31T20:10:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"There's complex rules governing whether a push is allowed to take\nplace depending on whether we're pushing to refs/heads/*, refs/tags/*\nor refs/not-that/*. See is_branch() in refs.c, and the various\nassertions in refs/files-backend.c. (e.g. \"trying to write non-commit\nobject %s to branch '%s'\").\n\nThis documentation has never been quite correct, but went downhill\nafter dbfeddb12e (\"push: require force for refs under refs/tags/\",\n2012-11-29) when we started claiming that <dst> couldn't be a tag\nobject, which is incorrect. After some of the logic in that patch was\nchanged in 256b9d70a4 (\"push: fix \"refs/tags/ hierarchy cannot be\nupdated without --force\"\", 2013-01-16) the docs weren't updated, and\nwe've had some version of documentation that confused whether <src>\nwas a tag or not with whether <dst> would accept either an annotated\ntag object or the commit it points to.\n\nThis makes the intro somewhat more verbose & complex, perhaps we\nshould have a shorter description here and split the full complexity\ninto a dedicated section. Very few users will find themselves needing\nto e.g. push blobs or trees to refs/custom-namespace/* (or blobs or\ntrees at all), and that could be covered separately as an advanced\ntopic.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-push.txt     | 52 ++++++++++++++++++++++++++++------\n Documentation/gitrevisions.txt |  7 +++--\n 2 files changed, 48 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 71c78ac1a4..f345bd30fc 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -74,14 +74,50 @@ without any `<refspec>` on the command line.  Otherwise, missing\n `:<dst>` means to update the same ref as the `<src>`.\n +\n The object referenced by <src> is used to update the <dst> reference\n-on the remote side.  By default this is only allowed if <dst> is not\n-a tag (annotated or lightweight), and then only if it can fast-forward\n-<dst>.  By having the optional leading `+`, you can tell Git to update\n-the <dst> ref even if it is not allowed by default (e.g., it is not a\n-fast-forward.).\n-+\n-Pushing an empty <src> allows you to delete the <dst> ref from\n-the remote repository.\n+on the remote side. Whether this is allowed depends on where in\n+`refs/*` the <dst> reference lives as described in detail below, in\n+those sections \"update\" means any modifications except deletes, which\n+as noted after the next few sections are treated differently.\n++\n+The `refs/heads/*` namespace will only accept commit objects, and\n+updates only if they can be fast-forwarded.\n++\n+The `refs/tags/*` namespace will accept any kind of object (as\n+commits, trees and blobs can be tagged), and any updates to them will\n+be rejected.\n++\n+It's possible to push any type of object to any namespace outside of\n+`refs/{tags,heads}/*`. In the case of tags and commits, these will be\n+treated as if they were the commits inside `refs/heads/*` for the\n+purposes of whether the update is allowed.\n++\n+I.e. a fast-forward of commits and tags outside `refs/{tags,heads}/*`\n+is allowed, even in cases where what's being fast-forwarded is not a\n+commit, but a tag object which happens to point to a new commit which\n+is a fast-forward of the commit the last tag (or commit) it's\n+replacing. Replacing a tag with an entirely different tag is also\n+allowed, if it points to the same commit, as well as pushing a peeled\n+tag, i.e. pushing the commit that existing tag object points to, or a\n+new tag object which an existing commit points to.\n++\n+Tree and blob objects outside of `refs/{tags,heads}/*` will be treated\n+the same way as if they were inside `refs/tags/*`, any update of them\n+will be rejected.\n++\n+All of the rules described above about what's not allowed as an update\n+can be overridden by adding an the optional leading `+` to a refspec\n+(or using `--force` command line option). The only exception to this\n+is that no amount of forcing will make the `refs/heads/*` namespace\n+accept a non-commit object. Hooks and configuration can also override\n+or amend these rules, see e.g. `receive.denyNonFastForwards` in\n+linkgit:git-config[1] and`pre-receive` and `update` in\n+linkgit:githooks[5].\n++\n+Pushing an empty <src> allows you to delete the <dst> ref from the\n+remote repository. Deletions are always accepted without a leading `+`\n+in the refspec (or `--force`), except when forbidden by configuration\n+or hooks. See `receive.denyDeletes` in linkgit:git-config[1] and\n+`pre-receive` and `update` in linkgit:githooks[5].\n +\n The special refspec `:` (or `+:` to allow non-fast-forward updates)\n directs Git to push \"matching\" branches: for every branch that exists on\ndiff --git a/Documentation/gitrevisions.txt b/Documentation/gitrevisions.txt\nindex 1f6cceaefb..d407b7dee1 100644\n--- a/Documentation/gitrevisions.txt\n+++ b/Documentation/gitrevisions.txt\n@@ -19,9 +19,10 @@ walk the revision graph (such as linkgit:git-log[1]), all commits which are\n reachable from that commit. For commands that walk the revision graph one can\n also specify a range of revisions explicitly.\n \n-In addition, some Git commands (such as linkgit:git-show[1]) also take\n-revision parameters which denote other objects than commits, e.g. blobs\n-(\"files\") or trees (\"directories of files\").\n+In addition, some Git commands (such as linkgit:git-show[1] and\n+linkgit:git-push[1]) can also take revision parameters which denote\n+other objects than commits, e.g. blobs (\"files\") or trees\n+(\"directories of files\").\n \n include::revisions.txt[]\n \n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357127","messageId":"20180831201004.12087-9-avarab@gmail.com","threadId":"48353","inReplyTo":"20180830201244.25759-1-avarab@gmail.com","subject":"[PATCH v5 8/9] fetch: document local ref updates with/without --force","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-31T20:10:03Z","receivedAt":"2018-08-31T20:10:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refer to the new git-push(1) documentation about when ref updates are\nand aren't allowed with and without --force, noting how \"git-fetch\"\ndiffers from the behavior of \"git-push\".\n\nPerhaps it would be better to split this all out into a new\ngitrefspecs(7) man page, or present this information using tables.\n\nIn lieu of that, this is accurate, and fixes a big omission in the\nexisting refspec docs.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/fetch-options.txt    | 15 +++++++++-----\n Documentation/pull-fetch-param.txt | 32 +++++++++++++++++++++++++-----\n 2 files changed, 37 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 8bc36af4b1..fa0a3151b3 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -68,11 +68,16 @@ endif::git-pull[]\n \n -f::\n --force::\n-\tWhen 'git fetch' is used with `<rbranch>:<lbranch>`\n-\trefspec, it refuses to update the local branch\n-\t`<lbranch>` unless the remote branch `<rbranch>` it\n-\tfetches is a descendant of `<lbranch>`.  This option\n-\toverrides that check.\n+\tWhen 'git fetch' is used with `<src>:<dst>` refspec it may\n+\trefuse to update the local branch as discussed\n+ifdef::git-pull[]\n+\tin the `<refspec>` part of the linkgit:git-fetch[1]\n+\tdocumentation.\n+endif::git-pull[]\n+ifndef::git-pull[]\n+\tin the `<refspec>` part below.\n+endif::git-pull[]\n+\tThis option overrides that check.\n \n -k::\n --keep::\ndiff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt\nindex f1fb08dc68..ab9617ad01 100644\n--- a/Documentation/pull-fetch-param.txt\n+++ b/Documentation/pull-fetch-param.txt\n@@ -33,11 +33,33 @@ name.\n it requests fetching everything up to the given tag.\n +\n The remote ref that matches <src>\n-is fetched, and if <dst> is not an empty string, the local\n-ref that matches it is fast-forwarded using <src>.\n-If the optional plus `+` is used, the local ref\n-is updated even if it does not result in a fast-forward\n-update.\n+is fetched, and if <dst> is not an empty string, an attempt\n+is made to update the local ref that matches it.\n++\n+Whether that update is allowed without `--force` depends on the ref\n+namespace it's being fetched to, the type of object being fetched, and\n+whether the update is considered to be a fast-forward. Generally, the\n+same rules apply for fetching as when pushing, see the `<refspec>...`\n+section of linkgit:git-push[1] for what those are. Exceptions to those\n+rules particular to 'git fetch' are noted below.\n++\n+Unlike when pushing with linkgit:git-push[1], any updates to\n+`refs/tags/*` will be accepted without `+` in the refspec (or\n+`--force`). The receiving promiscuously considers all tag updates from\n+a remote to be forced fetches.\n++\n+Unlike when pushing with linkgit:git-push[1], any updates outside of\n+`refs/{tags,heads}/*` will be accepted without `+` in the refspec (or\n+`--force`), whether that's swapping e.g. a tree object for a blob, or\n+a commit for another commit that's doesn't have the previous commit as\n+an ancestor etc.\n++\n+As with pushing with linkgit:git-push[1], all of the rules described\n+above about what's not allowed as an update can be overridden by\n+adding an the optional leading `+` to a refspec (or using `--force`\n+command line option). The only exception to this is that no amount of\n+forcing will make the `refs/heads/*` namespace accept a non-commit\n+object.\n +\n [NOTE]\n When the remote branch you want to fetch is known to\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357128","messageId":"20180831201004.12087-10-avarab@gmail.com","threadId":"48353","inReplyTo":"20180830201244.25759-1-avarab@gmail.com","subject":"[PATCH v5 9/9] fetch: stop clobbering existing tags without --force","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-31T20:10:04Z","receivedAt":"2018-08-31T20:10:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change \"fetch\" to treat \"+\" in refspecs (aka --force) to mean we\nshould clobber a local tag of the same name.\n\nThis changes the long-standing behavior of \"fetch\" added in\n853a3697dc (\"[PATCH] Multi-head fetch.\", 2005-08-20). Before this\nchange, all tag fetches effectively had --force enabled. See the\ngit-fetch-script code in fast_forward_local() with the comment:\n\n    > Tags need not be pointing at commits so there is no way to\n    > guarantee \"fast-forward\" anyway.\n\nThat commit and the rest of the history of \"fetch\" shows that the\n\"+\" (--force) part of refpecs was only conceived for branch updates,\nwhile tags have accepted any changes from upstream unconditionally and\nclobbered the local tag object. Changing this behavior has been\ndiscussed as early as 2011[1].\n\nThe current behavior doesn't make sense to me, it easily results in\nlocal tags accidentally being clobbered. We could namespace our tags\nper-remote and not locally populate refs/tags/*, but as with my\n97716d217c (\"fetch: add a --prune-tags option and fetch.pruneTags\nconfig\", 2018-02-09) it's easier to work around the current\nimplementation than to fix the root cause.\n\nSo this change implements suggestion #1 from Jeff's 2011 E-Mail[1],\n\"fetch\" now only clobbers the tag if either \"+\" is provided as part of\nthe refspec, or if \"--force\" is provided on the command-line.\n\nThis also makes it nicely symmetrical with how \"tag\" itself works when\ncreating tags. I.e. we refuse to clobber any existing tags unless\n\"--force\" is supplied. Now we can refuse all such clobbering, whether\nit would happen by clobbering a local tag with \"tag\", or by fetching\nit from the remote with \"fetch\".\n\nRef updates outside refs/{tags,heads/* are still still not symmetrical\nwith how \"git push\" works, as discussed in the recently changed\npull-fetch-param.txt documentation. This change brings the two\ndivergent behaviors more into line with one another. I don't think\nthere's any reason \"fetch\" couldn't fully converge with the behavior\nused by \"push\", but that's a topic for another change.\n\nOne of the tests added in 31b808a032 (\"clone --single: limit the fetch\nrefspec to fetched branch\", 2012-09-20) is being changed to use\n--force where a clone would clobber a tag. This changes nothing about\nthe existing behavior of the test.\n\n1. https://public-inbox.org/git/20111123221658.GA22313@sigill.intra.peff.net/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/pull-fetch-param.txt | 15 +++++++++++----\n builtin/fetch.c                    | 18 ++++++++++++------\n t/t5516-fetch-push.sh              |  5 +++--\n t/t5612-clone-refspec.sh           |  4 ++--\n 4 files changed, 28 insertions(+), 14 deletions(-)\n\ndiff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt\nindex ab9617ad01..293c6b967d 100644\n--- a/Documentation/pull-fetch-param.txt\n+++ b/Documentation/pull-fetch-param.txt\n@@ -43,10 +43,13 @@ same rules apply for fetching as when pushing, see the `<refspec>...`\n section of linkgit:git-push[1] for what those are. Exceptions to those\n rules particular to 'git fetch' are noted below.\n +\n-Unlike when pushing with linkgit:git-push[1], any updates to\n-`refs/tags/*` will be accepted without `+` in the refspec (or\n-`--force`). The receiving promiscuously considers all tag updates from\n-a remote to be forced fetches.\n+Until Git version 2.20, and unlike when pushing with\n+linkgit:git-push[1], any updates to `refs/tags/*` would be accepted\n+without `+` in the refspec (or `--force`). The receiving promiscuously\n+considered all tag updates from a remote to be forced fetches.  Since\n+Git version 2.20, fetching to update `refs/tags/*` work the same way\n+as when pushing. I.e. any updates will be rejected without `+` in the\n+refspec (or `--force`).\n +\n Unlike when pushing with linkgit:git-push[1], any updates outside of\n `refs/{tags,heads}/*` will be accepted without `+` in the refspec (or\n@@ -54,6 +57,10 @@ Unlike when pushing with linkgit:git-push[1], any updates outside of\n a commit for another commit that's doesn't have the previous commit as\n an ancestor etc.\n +\n+Unlike when pushing with linkgit:git-push[1], there is no\n+configuration which'll amend these rules, and nothing like a\n+`pre-fetch` hook analogous to the `pre-receive` hook.\n++\n As with pushing with linkgit:git-push[1], all of the rules described\n above about what's not allowed as an update can be overridden by\n adding an the optional leading `+` to a refspec (or using `--force`\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex b0706b3803..ed4ed9d8c4 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -667,12 +667,18 @@ static int update_local_ref(struct ref *ref,\n \n \tif (!is_null_oid(&ref->old_oid) &&\n \t    starts_with(ref->name, \"refs/tags/\")) {\n-\t\tint r;\n-\t\tr = s_update_ref(\"updating tag\", ref, 0);\n-\t\tformat_display(display, r ? '!' : 't', _(\"[tag update]\"),\n-\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n-\t\t\t       remote, pretty_ref, summary_width);\n-\t\treturn r;\n+\t\tif (force || ref->force) {\n+\t\t\tint r;\n+\t\t\tr = s_update_ref(\"updating tag\", ref, 0);\n+\t\t\tformat_display(display, r ? '!' : 't', _(\"[tag update]\"),\n+\t\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t\t       remote, pretty_ref, summary_width);\n+\t\t\treturn r;\n+\t\t} else {\n+\t\t\tformat_display(display, '!', _(\"[rejected]\"), _(\"would clobber existing tag\"),\n+\t\t\t\t       remote, pretty_ref, summary_width);\n+\t\t\treturn 1;\n+\t\t}\n \t}\n \n \tcurrent = lookup_commit_reference_gently(the_repository,\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 7f3d4c4965..0e758e2a43 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1015,7 +1015,7 @@ test_force_fetch_tag () {\n \ttag_type_description=$1\n \ttag_args=$2\n \n-\ttest_expect_success \"fetch will clobber an existing $tag_type_description\" \"\n+\ttest_expect_success \"fetch will not clobber an existing $tag_type_description without --force\" \"\n \t\tmk_test testrepo heads/master &&\n \t\tmk_child testrepo child1 &&\n \t\tmk_child testrepo child2 &&\n@@ -1027,7 +1027,8 @@ test_force_fetch_tag () {\n \t\t\tgit add file1 &&\n \t\t\tgit commit -m 'file1' &&\n \t\t\tgit tag $tag_args testTag &&\n-\t\t\tgit -C ../child1 fetch origin tag testTag\n+\t\t\ttest_must_fail git -C ../child1 fetch origin tag testTag &&\n+\t\t\tgit -C ../child1 fetch origin '+refs/tags/*:refs/tags/*'\n \t\t)\n \t\"\n }\ndiff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh\nindex 5582b3d5fd..e36ac01661 100755\n--- a/t/t5612-clone-refspec.sh\n+++ b/t/t5612-clone-refspec.sh\n@@ -103,7 +103,7 @@ test_expect_success 'clone with --no-tags' '\n test_expect_success '--single-branch while HEAD pointing at master' '\n \t(\n \t\tcd dir_master &&\n-\t\tgit fetch &&\n+\t\tgit fetch --force &&\n \t\tgit for-each-ref refs/remotes/origin |\n \t\tsed -e \"/HEAD$/d\" \\\n \t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n@@ -114,7 +114,7 @@ test_expect_success '--single-branch while HEAD pointing at master' '\n \ttest_cmp expect actual &&\n \t(\n \t\tcd dir_master &&\n-\t\tgit fetch --tags &&\n+\t\tgit fetch --tags --force &&\n \t\tgit for-each-ref refs/tags >../actual\n \t) &&\n \tgit for-each-ref refs/tags >expect &&\n-- \n2.19.0.rc1.350.ge57e33dbd1\n\n"},{"id":"357132","messageId":"xmqqftyu1byv.fsf@gitster-ct.c.googlers.com","threadId":"48353","inReplyTo":"20180831201004.12087-3-avarab@gmail.com","subject":"Re: [PATCH v5 2/9] push tests: make use of unused $1 in test description","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-08-31T21:07:52Z","receivedAt":"2018-08-31T21:07:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Fix up a logic error in 380efb65df (\"push tests: assert re-pushing\n> annotated tags\", 2018-07-31), where the $tag_type_description variable\n> was assigned to but never used, unlike in the subsequently added\n> companion test for fetches in 2d216a7ef6 (\"fetch tests: add a test for\n> clobbering tag behavior\", 2018-04-29).\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t5516-fetch-push.sh | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n>\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index 539c25aada..62d5059f92 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -969,7 +969,7 @@ test_force_push_tag () {\n>  \ttag_type_description=$1\n>  \ttag_args=$2\n>  \n> -\ttest_expect_success 'force pushing required to update lightweight tag' \"\n> +\ttest_expect_success 'force pushing required to update $tag_type_description' \"\n\nOf course, $1 needs to be inside \"dq-pair\" for $tag_type_description\nto be substituted ;-)  So I'll tweak it while queuing.\n\nAll the other ones in this series looked sensible to me.  Will\nreplace.\n\nThanks.\n\n>  \t\tmk_test testrepo heads/master &&\n>  \t\tmk_child testrepo child1 &&\n>  \t\tmk_child testrepo child2 &&\n"},{"id":"357141","messageId":"871saechzl.fsf@evledraar.gmail.com","threadId":"48353","inReplyTo":"xmqqftyu1byv.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5 2/9] push tests: make use of unused $1 in test description","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-08-31T22:02:22Z","receivedAt":"2018-08-31T22:02:31Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Aug 31 2018, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Fix up a logic error in 380efb65df (\"push tests: assert re-pushing\n>> annotated tags\", 2018-07-31), where the $tag_type_description variable\n>> was assigned to but never used, unlike in the subsequently added\n>> companion test for fetches in 2d216a7ef6 (\"fetch tests: add a test for\n>> clobbering tag behavior\", 2018-04-29).\n>>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>  t/t5516-fetch-push.sh | 2 +-\n>>  1 file changed, 1 insertion(+), 1 deletion(-)\n>>\n>> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n>> index 539c25aada..62d5059f92 100755\n>> --- a/t/t5516-fetch-push.sh\n>> +++ b/t/t5516-fetch-push.sh\n>> @@ -969,7 +969,7 @@ test_force_push_tag () {\n>>  \ttag_type_description=$1\n>>  \ttag_args=$2\n>>\n>> -\ttest_expect_success 'force pushing required to update lightweight tag' \"\n>> +\ttest_expect_success 'force pushing required to update $tag_type_description' \"\n>\n> Of course, $1 needs to be inside \"dq-pair\" for $tag_type_description\n> to be substituted ;-)  So I'll tweak it while queuing.\n\nD'oh! I knew I'd miss something. Hopefully this was the only thing.\n\n> All the other ones in this series looked sensible to me.  Will\n> replace.\n\nThanks!\n"}]}