{"thread":{"id":"47614","subject":"[BUG] git remote prune removes local tags, depending on fetch config","startedAt":"2018-01-15T21:16:47Z","lastAt":"2018-02-24T21:42:10Z","messageCount":118,"participants":["Michael Giuffrida","Ævar Arnfjörð Bjarmason","Andreas Schwab","Kevin Daudt","Eric Sunshine","Junio C Hamano","Jeff King"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"336628","messageId":"CACi5S_39wNrbfjLfn0xhCY+uewtFN2YmnAcRc86z6pjUTjWPHQ@mail.gmail.com","threadId":"47614","inReplyTo":null,"subject":"[BUG] git remote prune removes local tags, depending on fetch config","fromName":"Michael Giuffrida","fromEmail":"michaelpg@chromium.org","sentAt":"2018-01-15T21:16:18Z","receivedAt":"2018-01-15T21:16:47Z","isPatch":false,"sender":{"key":"michaelpg@chromium.org","avatar":null},"body":"`git remote prune <name>` should \"delete all stale remote-tracking\nbranches under <name>\". I was surprised to discover, after some\ntroubleshooting, that it also deletes *all* local tags that don't\nexist on the remote, if the following refspec is included in the\nremote's fetch config:\n\n    +refs/tags/*:refs/tags/*\n\nSo, if `remote.origin.fetch` is configured to fetch all tags from the\nremote, any tags I create locally will be deleted when running `git\nremote prune origin`. This is not intuitive [1], nor is is it\nexplained in the docs [2]. Is this behavior obvious to someone with a\nbetter understanding of Git internals?\n\nI did find a better way to automatically fetch tags (using tagopt\ninstead of adding the fetch refspec). However, the refspec doesn't\nseem \"wrong\" in itself; in particular, `git fetch --tags` used to be\nconsidered equivalent to specifying the refspec\n\"refs/tags/*:refs/tags/*\" -- implying that this is a sensible refspec\n[3]. So I wouldn't expect it to \"break\" the behavior of another\ncommand.\n\n[1] https://stackoverflow.com/q/34687657/1327867\n[2] https://git-scm.com/docs/git-remote.html#git-remote-empruneem\n[3] https://github.com/git/git/commit/c5a84e92a2fe9e8748e32341c344d7a6c0f52a50\n"},{"id":"336639","messageId":"87tvvmiqn7.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"CACi5S_39wNrbfjLfn0xhCY+uewtFN2YmnAcRc86z6pjUTjWPHQ@mail.gmail.com","subject":"Re: [BUG] git remote prune removes local tags, depending on fetch config","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-16T00:38:52Z","receivedAt":"2018-01-16T00:39:02Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Jan 15 2018, Michael Giuffrida jotted:\n\n> `git remote prune <name>` should \"delete all stale remote-tracking\n> branches under <name>\". I was surprised to discover, after some\n> troubleshooting, that it also deletes *all* local tags that don't\n> exist on the remote, if the following refspec is included in the\n> remote's fetch config:\n>\n>     +refs/tags/*:refs/tags/*\n>\n> So, if `remote.origin.fetch` is configured to fetch all tags from the\n> remote, any tags I create locally will be deleted when running `git\n> remote prune origin`. This is not intuitive [1], nor is is it\n> explained in the docs [2]. Is this behavior obvious to someone with a\n> better understanding of Git internals?\n>\n> I did find a better way to automatically fetch tags (using tagopt\n> instead of adding the fetch refspec). However, the refspec doesn't\n> seem \"wrong\" in itself; in particular, `git fetch --tags` used to be\n> considered equivalent to specifying the refspec\n> \"refs/tags/*:refs/tags/*\" -- implying that this is a sensible refspec\n> [3]. So I wouldn't expect it to \"break\" the behavior of another\n> command.\n>\n> [1] https://stackoverflow.com/q/34687657/1327867\n> [2] https://git-scm.com/docs/git-remote.html#git-remote-empruneem\n> [3] https://github.com/git/git/commit/c5a84e92a2fe9e8748e32341c344d7a6c0f52a50\n\nThese docs are really confusing, but it is working as intended, and\nreally should be re-documented.\n\nThe `git remote prune` subcommand just ends up piggy-backing on\ngit-fetch, whose behavior is explained here:\nhttps://git-scm.com/docs/git-fetch.html#git-fetch---prune\n\nIt's worked this way since at least v1.8.5.6, maybe at some distant\npoint in the past it only did this for branches when invoked via\ngit-remote as the documentation says.\n\nRELATED:\n\nI've actually had the reverse problem with this. I want some way to turn\nthis behavior on without explicitly hacking the refspec, so I can do it\nglobally in /etc/gitconfig or in ~/.gitconfig without screwing with the\nconfig of each checkout on certain machines.\n\nYou can set fetch.prune=true, but that only prunes the branches, you\nneed to inject remote.origin.fetch into each checkout, unless I've\nmissed some way of doing this.\n\nI wanted to add fetch.pruneTags that would make it as if you had\nrefs/tags/*:refs/tags/* in the fetch spec, but I haven't hacked that up\nyet, if anyone can see any inherent issue with that plan I'd like to\nknow about it.\n"},{"id":"336641","messageId":"CACi5S_33F7VVRYiBOQsxgg_ZPOg9ZvhLZ26bStYWiq7n96aeug@mail.gmail.com","threadId":"47614","inReplyTo":"87tvvmiqn7.fsf@evledraar.gmail.com","subject":"Re: [BUG] git remote prune removes local tags, depending on fetch config","fromName":"Michael Giuffrida","fromEmail":"michaelpg@chromium.org","sentAt":"2018-01-16T02:14:30Z","receivedAt":"2018-01-16T02:15:10Z","isPatch":false,"sender":{"key":"michaelpg@chromium.org","avatar":null},"body":"Just to confirm, you're talking about the behavior of removing *all*\ntags that aren't found on the remote? (Not just tags that used to be\non some remote, but have since been removed from that remote.) So,\nwith your proposed workflow, you would never create your own tags\nlocally, without pushing them to the remote before running `git fetch`\n-- otherwise they would simply be deleted.\n\nIt doesn't seem like a useful feature -- you wouldn't expect `git\nfetch --prune` to remove your local branches that you were developing\non, right?\n\nOn Mon, Jan 15, 2018 at 4:38 PM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> On Mon, Jan 15 2018, Michael Giuffrida jotted:\n>\n>> `git remote prune <name>` should \"delete all stale remote-tracking\n>> branches under <name>\". I was surprised to discover, after some\n>> troubleshooting, that it also deletes *all* local tags that don't\n>> exist on the remote, if the following refspec is included in the\n>> remote's fetch config:\n>>\n>>     +refs/tags/*:refs/tags/*\n>>\n>> So, if `remote.origin.fetch` is configured to fetch all tags from the\n>> remote, any tags I create locally will be deleted when running `git\n>> remote prune origin`. This is not intuitive [1], nor is is it\n>> explained in the docs [2]. Is this behavior obvious to someone with a\n>> better understanding of Git internals?\n>>\n>> I did find a better way to automatically fetch tags (using tagopt\n>> instead of adding the fetch refspec). However, the refspec doesn't\n>> seem \"wrong\" in itself; in particular, `git fetch --tags` used to be\n>> considered equivalent to specifying the refspec\n>> \"refs/tags/*:refs/tags/*\" -- implying that this is a sensible refspec\n>> [3]. So I wouldn't expect it to \"break\" the behavior of another\n>> command.\n>>\n>> [1] https://stackoverflow.com/q/34687657/1327867\n>> [2] https://git-scm.com/docs/git-remote.html#git-remote-empruneem\n>> [3] https://github.com/git/git/commit/c5a84e92a2fe9e8748e32341c344d7a6c0f52a50\n>\n> These docs are really confusing, but it is working as intended, and\n> really should be re-documented.\n>\n> The `git remote prune` subcommand just ends up piggy-backing on\n> git-fetch, whose behavior is explained here:\n> https://git-scm.com/docs/git-fetch.html#git-fetch---prune\n>\n> It's worked this way since at least v1.8.5.6, maybe at some distant\n> point in the past it only did this for branches when invoked via\n> git-remote as the documentation says.\n>\n> RELATED:\n>\n> I've actually had the reverse problem with this. I want some way to turn\n> this behavior on without explicitly hacking the refspec, so I can do it\n> globally in /etc/gitconfig or in ~/.gitconfig without screwing with the\n> config of each checkout on certain machines.\n>\n> You can set fetch.prune=true, but that only prunes the branches, you\n> need to inject remote.origin.fetch into each checkout, unless I've\n> missed some way of doing this.\n>\n> I wanted to add fetch.pruneTags that would make it as if you had\n> refs/tags/*:refs/tags/* in the fetch spec, but I haven't hacked that up\n> yet, if anyone can see any inherent issue with that plan I'd like to\n> know about it.\n"},{"id":"336655","messageId":"87po6ahx87.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"CACi5S_33F7VVRYiBOQsxgg_ZPOg9ZvhLZ26bStYWiq7n96aeug@mail.gmail.com","subject":"Re: [BUG] git remote prune removes local tags, depending on fetch config","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-16T11:14:16Z","receivedAt":"2018-01-16T11:14:25Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Jan 16 2018, Michael Giuffrida jotted:\n\n> Just to confirm, you're talking about the behavior of removing *all*\n> tags that aren't found on the remote? (Not just tags that used to be\n> on some remote, but have since been removed from that remote.) So,\n> with your proposed workflow, you would never create your own tags\n> locally, without pushing them to the remote before running `git fetch`\n> -- otherwise they would simply be deleted.\n>\n> It doesn't seem like a useful feature -- you wouldn't expect `git\n> fetch --prune` to remove your local branches that you were developing\n> on, right?\n\nYes, I realize this isn't everyone's cup of tea, but it's very useful in\nour in-house use-case for git.\n\nWe have repos that have tags pushed to them on every deployment, thus\nthe cumulative set of tags is in the hundreds of thousands.\n\nThere's a pruning script that prunes both branches and tags from the\nserver (and archives them to an archive repo that has every ref ever).\n\nThen we need to push these deletions back to client checkouts. For\nbranches that's easy, just set fetch.prune=true. For tags I need to\nchase down checkouts and do:\n\n    git -C {} config --replace-all remote.origin.fetch \"refs/tags/*:refs/tags/*\" \"^refs/tags/*:refs/tags/*$\"\n\nIt would be easier to just set fetch.{prune,pruneTags}=true in\n/etc/gitconfig, this is on e.g. shared development staging boxes where\nyou might have a manually cloned checkout in your ~.\n\nOf course it'll wipe away any manually created tag you have, *ideally*\nwe'd have something like the refs/remotes/ namespace for tags, but that\nship has sailed.\n\nBut meanwhile tag pruning is an OK compromise, 99% of our users will\nnever even think to create a tag (and if they created one manually they\ncouldn't push it), the advanced users that would know what sort of repo\nthey're editing and can out-out of the behavior by editing their local\nconfig.\n\n> On Mon, Jan 15, 2018 at 4:38 PM, Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>>\n>> On Mon, Jan 15 2018, Michael Giuffrida jotted:\n>>\n>>> `git remote prune <name>` should \"delete all stale remote-tracking\n>>> branches under <name>\". I was surprised to discover, after some\n>>> troubleshooting, that it also deletes *all* local tags that don't\n>>> exist on the remote, if the following refspec is included in the\n>>> remote's fetch config:\n>>>\n>>>     +refs/tags/*:refs/tags/*\n>>>\n>>> So, if `remote.origin.fetch` is configured to fetch all tags from the\n>>> remote, any tags I create locally will be deleted when running `git\n>>> remote prune origin`. This is not intuitive [1], nor is is it\n>>> explained in the docs [2]. Is this behavior obvious to someone with a\n>>> better understanding of Git internals?\n>>>\n>>> I did find a better way to automatically fetch tags (using tagopt\n>>> instead of adding the fetch refspec). However, the refspec doesn't\n>>> seem \"wrong\" in itself; in particular, `git fetch --tags` used to be\n>>> considered equivalent to specifying the refspec\n>>> \"refs/tags/*:refs/tags/*\" -- implying that this is a sensible refspec\n>>> [3]. So I wouldn't expect it to \"break\" the behavior of another\n>>> command.\n>>>\n>>> [1] https://stackoverflow.com/q/34687657/1327867\n>>> [2] https://git-scm.com/docs/git-remote.html#git-remote-empruneem\n>>> [3] https://github.com/git/git/commit/c5a84e92a2fe9e8748e32341c344d7a6c0f52a50\n>>\n>> These docs are really confusing, but it is working as intended, and\n>> really should be re-documented.\n>>\n>> The `git remote prune` subcommand just ends up piggy-backing on\n>> git-fetch, whose behavior is explained here:\n>> https://git-scm.com/docs/git-fetch.html#git-fetch---prune\n>>\n>> It's worked this way since at least v1.8.5.6, maybe at some distant\n>> point in the past it only did this for branches when invoked via\n>> git-remote as the documentation says.\n>>\n>> RELATED:\n>>\n>> I've actually had the reverse problem with this. I want some way to turn\n>> this behavior on without explicitly hacking the refspec, so I can do it\n>> globally in /etc/gitconfig or in ~/.gitconfig without screwing with the\n>> config of each checkout on certain machines.\n>>\n>> You can set fetch.prune=true, but that only prunes the branches, you\n>> need to inject remote.origin.fetch into each checkout, unless I've\n>> missed some way of doing this.\n>>\n>> I wanted to add fetch.pruneTags that would make it as if you had\n>> refs/tags/*:refs/tags/* in the fetch spec, but I haven't hacked that up\n>> yet, if anyone can see any inherent issue with that plan I'd like to\n>> know about it.\n"},{"id":"336656","messageId":"874lnmkorz.fsf@linux-m68k.org","threadId":"47614","inReplyTo":"CACi5S_33F7VVRYiBOQsxgg_ZPOg9ZvhLZ26bStYWiq7n96aeug@mail.gmail.com","subject":"Re: [BUG] git remote prune removes local tags, depending on fetch config","fromName":"Andreas Schwab","fromEmail":"schwab@linux-m68k.org","sentAt":"2018-01-16T11:48:32Z","receivedAt":"2018-01-16T11:48:41Z","isPatch":false,"sender":{"key":"schwab@linux-m68k.org","avatar":"https://avatars.githubusercontent.com/u/2175493?v=4"},"body":"On Jan 15 2018, Michael Giuffrida <michaelpg@chromium.org> wrote:\n\n> It doesn't seem like a useful feature -- you wouldn't expect `git\n> fetch --prune` to remove your local branches that you were developing\n> on, right?\n\nDon't mix local and remote refs.  There is a reason why remote tracking\nbranches are put in a separate name space.  If you fetch the remote tags\ninto a separate name space (eg. refs/remote/tags/*:refs/tags/*) then\nthere is no conflict.\n\nAndreas.\n\n-- \nAndreas Schwab, schwab@linux-m68k.org\nGPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5\n\"And now for something completely different.\"\n"},{"id":"336749","messageId":"20180118061814.GC5618@alpha.vpn.ikke.info","threadId":"47614","inReplyTo":"874lnmkorz.fsf@linux-m68k.org","subject":"Re: [BUG] git remote prune removes local tags, depending on fetch config","fromName":"Kevin Daudt","fromEmail":"me@ikke.info","sentAt":"2018-01-18T06:18:14Z","receivedAt":"2018-01-18T06:18:20Z","isPatch":false,"sender":{"key":"me@ikke.info","avatar":"https://avatars.githubusercontent.com/u/135698?v=4"},"body":"On Tue, Jan 16, 2018 at 12:48:32PM +0100, Andreas Schwab wrote:\n> On Jan 15 2018, Michael Giuffrida <michaelpg@chromium.org> wrote:\n> \n> > It doesn't seem like a useful feature -- you wouldn't expect `git\n> > fetch --prune` to remove your local branches that you were developing\n> > on, right?\n> \n> Don't mix local and remote refs.  There is a reason why remote tracking\n> branches are put in a separate name space.  If you fetch the remote tags\n> into a separate name space (eg. refs/remote/tags/*:refs/tags/*) then\n> there is no conflict.\n> \n> Andreas.\n\nBut then they are no longer considered tags, but remote tracking\nbranches. Tools like git tag and git describe won't consider them, and\ngit branch -r would show them as remote tracking branches.\n\n> \n> -- \n> Andreas Schwab, schwab@linux-m68k.org\n> GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5\n> \"And now for something completely different.\"\n"},{"id":"336849","messageId":"20180119000027.28898-1-avarab@gmail.com","threadId":"47614","inReplyTo":"87po6ahx87.fsf@evledraar.gmail.com","subject":"[PATCH 00/11] document & test fetch pruning + WIP fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-19T00:00:16Z","receivedAt":"2018-01-19T00:00:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Michael Giuffrida noted that the git-remote docs were very confusing,\nand upthread I said I wanted this shiny related thing in 11/11.\n\nAlong the way I fixed up fetch tests & documentation to hopefully be a\nlot less confusing.\n\nI think 1-10/11 of this makes sense for inclusion as-is (pending\nreview etc.), 11/11 is broken currently, but review / comments on it\nwelcome, particularly the CLI / config interface / docs etc.\n\nThe bug causing it not to work is less of a \"I can't figure this out\"\nand more of a \"I won't have time again for hacking in the next couple\nof days, and wanted to see what people thought\", but if someone wants\nto see what I'm screwing up there and do my homework for me that's\nalso most welcome.\n\nÆvar Arnfjörð Bjarmason (11):\n  fetch tests: refactor in preparation for testing tag pruning\n  fetch tests: arrange arguments for future readability\n  fetch tests: add a tag to be deleted to the pruning tests\n  fetch tests: double quote a variable for interpolation\n  fetch tests: test --prune and refspec interaction\n  git fetch doc: add a new section to explain the ins & outs of pruning\n  git remote doc: correct dangerous lies about what prune does\n  git-fetch & config doc: link to the new PRUNING section\n  fetch: don't redundantly null something calloc() gave us\n  fetch tests: add scaffolding for the new fetch.pruneTags\n  WIP fetch: add a --fetch-prune option and fetch.pruneTags config\n\n Documentation/config.txt        |  21 ++++++-\n Documentation/fetch-options.txt |  18 +++++-\n Documentation/git-fetch.txt     |  49 ++++++++++++++++\n Documentation/git-remote.txt    |  14 +++--\n builtin/fetch.c                 |  38 ++++++++++--\n remote.c                        |   2 +\n remote.h                        |   1 +\n t/t5510-fetch.sh                | 125 +++++++++++++++++++++++++++-------------\n 8 files changed, 216 insertions(+), 52 deletions(-)\n\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"336850","messageId":"20180119000027.28898-2-avarab@gmail.com","threadId":"47614","inReplyTo":"20180119000027.28898-1-avarab@gmail.com","subject":"[PATCH 01/11] fetch tests: refactor in preparation for testing tag pruning","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-19T00:00:17Z","receivedAt":"2018-01-19T00:01:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In a subsequent commit this function will learn to test for tag\npruning, prepare for that by making space for more variables, and\nmaking it clear that \"expected\" here refers to branches.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 9 ++++++---\n 1 file changed, 6 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 668c54be41..11da97f9b7 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -549,9 +549,12 @@ set_config_tristate () {\n }\n \n test_configured_prune () {\n-\tfetch_prune=$1 remote_origin_prune=$2 cmdline=$3 expected=$4\n+\tfetch_prune=$1\n+\tremote_origin_prune=$2\n+\tcmdline=$3\n+\texpected_branch=$4\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; $4\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; branch:$4\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\t(\n@@ -572,7 +575,7 @@ test_configured_prune () {\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n \n \t\t\tgit fetch $cmdline &&\n-\t\t\tcase \"$expected\" in\n+\t\t\tcase \"$expected_branch\" in\n \t\t\tpruned)\n \t\t\t\ttest_must_fail git rev-parse --verify refs/remotes/origin/newbranch\n \t\t\t\t;;\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"336851","messageId":"20180119000027.28898-3-avarab@gmail.com","threadId":"47614","inReplyTo":"20180119000027.28898-1-avarab@gmail.com","subject":"[PATCH 02/11] fetch tests: arrange arguments for future readability","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-19T00:00:18Z","receivedAt":"2018-01-19T00:01:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Re-arrange the arguments to the test_configured_prune() function used\nin this test to pass the arguments to --fetch last. A subsequent\nchange will test for more elaborate fetch arguments, including long\nrefspecs. It'll be more readable to be able to wrap those on a new\nline of their own.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 82 ++++++++++++++++++++++++++++++--------------------------\n 1 file changed, 44 insertions(+), 38 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 11da97f9b7..ab8b25344d 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -551,10 +551,10 @@ set_config_tristate () {\n test_configured_prune () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n-\tcmdline=$3\n-\texpected_branch=$4\n+\texpected_branch=$3\n+\tcmdline=$4\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; branch:$4\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${4:+ $4}; branch:$3\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\t(\n@@ -587,41 +587,47 @@ test_configured_prune () {\n \t'\n }\n \n-test_configured_prune unset unset \"\"\t\tkept\n-test_configured_prune unset unset \"--no-prune\"\tkept\n-test_configured_prune unset unset \"--prune\"\tpruned\n-\n-test_configured_prune false unset \"\"\t\tkept\n-test_configured_prune false unset \"--no-prune\"\tkept\n-test_configured_prune false unset \"--prune\"\tpruned\n-\n-test_configured_prune true  unset \"\"\t\tpruned\n-test_configured_prune true  unset \"--prune\"\tpruned\n-test_configured_prune true  unset \"--no-prune\"\tkept\n-\n-test_configured_prune unset false \"\"\t\tkept\n-test_configured_prune unset false \"--no-prune\"\tkept\n-test_configured_prune unset false \"--prune\"\tpruned\n-\n-test_configured_prune false false \"\"\t\tkept\n-test_configured_prune false false \"--no-prune\"\tkept\n-test_configured_prune false false \"--prune\"\tpruned\n-\n-test_configured_prune true  false \"\"\t\tkept\n-test_configured_prune true  false \"--prune\"\tpruned\n-test_configured_prune true  false \"--no-prune\"\tkept\n-\n-test_configured_prune unset true  \"\"\t\tpruned\n-test_configured_prune unset true  \"--no-prune\"\tkept\n-test_configured_prune unset true  \"--prune\"\tpruned\n-\n-test_configured_prune false true  \"\"\t\tpruned\n-test_configured_prune false true  \"--no-prune\"\tkept\n-test_configured_prune false true  \"--prune\"\tpruned\n-\n-test_configured_prune true  true  \"\"\t\tpruned\n-test_configured_prune true  true  \"--prune\"\tpruned\n-test_configured_prune true  true  \"--no-prune\"\tkept\n+# $1 config: fetch.prune\n+# $2 config: remote.<name>.prune\n+# $3 expect: branch to be pruned?\n+# $4 git-fetch $cmdline:\n+#\n+#                     $1    $2    $3     $4\n+test_configured_prune unset unset kept   \"\"\n+test_configured_prune unset unset kept   \"--no-prune\"\n+test_configured_prune unset unset pruned \"--prune\"\n+\n+test_configured_prune false unset kept   \"\"\n+test_configured_prune false unset kept   \"--no-prune\"\n+test_configured_prune false unset pruned \"--prune\"\n+\n+test_configured_prune true  unset pruned \"\"\n+test_configured_prune true  unset pruned \"--prune\"\n+test_configured_prune true  unset kept   \"--no-prune\"\n+\n+test_configured_prune unset false kept   \"\"\n+test_configured_prune unset false kept   \"--no-prune\"\n+test_configured_prune unset false pruned \"--prune\"\n+\n+test_configured_prune false false kept   \"\"\n+test_configured_prune false false kept   \"--no-prune\"\n+test_configured_prune false false pruned \"--prune\"\n+\n+test_configured_prune true  false kept   \"\"\n+test_configured_prune true  false pruned \"--prune\"\n+test_configured_prune true  false kept   \"--no-prune\"\n+\n+test_configured_prune unset true  pruned \"\"\n+test_configured_prune unset true  kept   \"--no-prune\"\n+test_configured_prune unset true  pruned \"--prune\"\n+\n+test_configured_prune false true  pruned \"\"\n+test_configured_prune false true  kept   \"--no-prune\"\n+test_configured_prune false true  pruned \"--prune\"\n+\n+test_configured_prune true  true  pruned \"\"\n+test_configured_prune true  true  pruned \"--prune\"\n+test_configured_prune true  true  kept   \"--no-prune\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"336852","messageId":"20180119000027.28898-4-avarab@gmail.com","threadId":"47614","inReplyTo":"20180119000027.28898-1-avarab@gmail.com","subject":"[PATCH 03/11] fetch tests: add a tag to be deleted to the pruning tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-19T00:00:19Z","receivedAt":"2018-01-19T00:01:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a tag to be deleted to the fetch --prune tests. The tag is always\nkept for now, which is the expected behavior, but now I can add a test\nfor tag pruning in a later commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 93 ++++++++++++++++++++++++++++++++------------------------\n 1 file changed, 53 insertions(+), 40 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex ab8b25344d..fad65bd885 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -552,21 +552,25 @@ test_configured_prune () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n \texpected_branch=$3\n-\tcmdline=$4\n+\texpected_tag=$4\n+\tcmdline=$5\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${4:+ $4}; branch:$3\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n+\t\tgit tag -f newtag &&\n \t\t(\n \t\t\tcd one &&\n \t\t\ttest_unconfig fetch.prune &&\n \t\t\ttest_unconfig remote.origin.prune &&\n \t\t\tgit fetch &&\n-\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch\n+\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch &&\n+\t\t\tgit rev-parse --verify refs/tags/newtag\n \t\t) &&\n \n \t\t# now remove it\n \t\tgit branch -d newbranch &&\n+\t\tgit tag -d newtag &&\n \n \t\t# then test\n \t\t(\n@@ -582,6 +586,14 @@ test_configured_prune () {\n \t\t\tkept)\n \t\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch\n \t\t\t\t;;\n+\t\t\tesac &&\n+\t\t\tcase \"$expected_tag\" in\n+\t\t\tpruned)\n+\t\t\t\ttest_must_fail git rev-parse --verify refs/tags/newtag\n+\t\t\t\t;;\n+\t\t\tkept)\n+\t\t\t\tgit rev-parse --verify refs/tags/newtag\n+\t\t\t\t;;\n \t\t\tesac\n \t\t)\n \t'\n@@ -590,44 +602,45 @@ test_configured_prune () {\n # $1 config: fetch.prune\n # $2 config: remote.<name>.prune\n # $3 expect: branch to be pruned?\n-# $4 git-fetch $cmdline:\n+# $4 expect: tag to be pruned?\n+# $5 git-fetch $cmdline:\n #\n-#                     $1    $2    $3     $4\n-test_configured_prune unset unset kept   \"\"\n-test_configured_prune unset unset kept   \"--no-prune\"\n-test_configured_prune unset unset pruned \"--prune\"\n-\n-test_configured_prune false unset kept   \"\"\n-test_configured_prune false unset kept   \"--no-prune\"\n-test_configured_prune false unset pruned \"--prune\"\n-\n-test_configured_prune true  unset pruned \"\"\n-test_configured_prune true  unset pruned \"--prune\"\n-test_configured_prune true  unset kept   \"--no-prune\"\n-\n-test_configured_prune unset false kept   \"\"\n-test_configured_prune unset false kept   \"--no-prune\"\n-test_configured_prune unset false pruned \"--prune\"\n-\n-test_configured_prune false false kept   \"\"\n-test_configured_prune false false kept   \"--no-prune\"\n-test_configured_prune false false pruned \"--prune\"\n-\n-test_configured_prune true  false kept   \"\"\n-test_configured_prune true  false pruned \"--prune\"\n-test_configured_prune true  false kept   \"--no-prune\"\n-\n-test_configured_prune unset true  pruned \"\"\n-test_configured_prune unset true  kept   \"--no-prune\"\n-test_configured_prune unset true  pruned \"--prune\"\n-\n-test_configured_prune false true  pruned \"\"\n-test_configured_prune false true  kept   \"--no-prune\"\n-test_configured_prune false true  pruned \"--prune\"\n-\n-test_configured_prune true  true  pruned \"\"\n-test_configured_prune true  true  pruned \"--prune\"\n-test_configured_prune true  true  kept   \"--no-prune\"\n+#                     $1    $2    $3     $4     $5\n+test_configured_prune unset unset kept   kept   \"\"\n+test_configured_prune unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset unset pruned kept   \"--prune\"\n+\n+test_configured_prune false unset kept   kept   \"\"\n+test_configured_prune false unset kept   kept   \"--no-prune\"\n+test_configured_prune false unset pruned kept   \"--prune\"\n+\n+test_configured_prune true  unset pruned kept   \"\"\n+test_configured_prune true  unset pruned kept   \"--prune\"\n+test_configured_prune true  unset kept   kept   \"--no-prune\"\n+\n+test_configured_prune unset false kept   kept   \"\"\n+test_configured_prune unset false kept   kept   \"--no-prune\"\n+test_configured_prune unset false pruned kept   \"--prune\"\n+\n+test_configured_prune false false kept   kept   \"\"\n+test_configured_prune false false kept   kept   \"--no-prune\"\n+test_configured_prune false false pruned kept   \"--prune\"\n+\n+test_configured_prune true  false kept   kept   \"\"\n+test_configured_prune true  false pruned kept   \"--prune\"\n+test_configured_prune true  false kept   kept   \"--no-prune\"\n+\n+test_configured_prune unset true  pruned kept   \"\"\n+test_configured_prune unset true  kept   kept   \"--no-prune\"\n+test_configured_prune unset true  pruned kept   \"--prune\"\n+\n+test_configured_prune false true  pruned kept   \"\"\n+test_configured_prune false true  kept   kept   \"--no-prune\"\n+test_configured_prune false true  pruned kept   \"--prune\"\n+\n+test_configured_prune true  true  pruned kept   \"\"\n+test_configured_prune true  true  pruned kept   \"--prune\"\n+test_configured_prune true  true  kept   kept   \"--no-prune\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"336853","messageId":"20180119000027.28898-5-avarab@gmail.com","threadId":"47614","inReplyTo":"20180119000027.28898-1-avarab@gmail.com","subject":"[PATCH 04/11] fetch tests: double quote a variable for interpolation","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-19T00:00:20Z","receivedAt":"2018-01-19T00:01:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"If the $cmdline variable contains multiple arguments they won't be\ninterpolated correctly since the body of the test is single quoted. I\ndon't know what part of test-lib.sh is expanding variables within\nsingle-quoted strings, but interpolating this inline is the desired\nbehavior here.\n\nThis will be used in a subsequent commit to pass more than one\nvariable to git-fetch.\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 fad65bd885..542eb53a99 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -578,7 +578,7 @@ test_configured_prune () {\n \t\t\tset_config_tristate fetch.prune $fetch_prune &&\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n \n-\t\t\tgit fetch $cmdline &&\n+\t\t\tgit fetch '\"$cmdline\"' &&\n \t\t\tcase \"$expected_branch\" in\n \t\t\tpruned)\n \t\t\t\ttest_must_fail git rev-parse --verify refs/remotes/origin/newbranch\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"336854","messageId":"20180119000027.28898-6-avarab@gmail.com","threadId":"47614","inReplyTo":"20180119000027.28898-1-avarab@gmail.com","subject":"[PATCH 05/11] fetch tests: test --prune and refspec interaction","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-19T00:00:21Z","receivedAt":"2018-01-19T00:01:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for the interaction between explicitly provided refspecs\nand fetch.prune.\n\nThere's no point in adding this boilerplate to every combination of\nunset/false/true, it's instructive and sufficient to show that no\nmatter if the variable is unset, false or true the refspec on the\ncommand-line overrides any configuration variable.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 12 ++++++++++++\n 1 file changed, 12 insertions(+)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 542eb53a99..576c2598c9 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -609,6 +609,10 @@ test_configured_prune () {\n test_configured_prune unset unset kept   kept   \"\"\n test_configured_prune unset unset kept   kept   \"--no-prune\"\n test_configured_prune unset unset pruned kept   \"--prune\"\n+test_configured_prune unset unset kept   pruned \\\n+\t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n+test_configured_prune unset unset pruned pruned \\\n+\t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n \n test_configured_prune false unset kept   kept   \"\"\n test_configured_prune false unset kept   kept   \"--no-prune\"\n@@ -625,6 +629,10 @@ test_configured_prune unset false pruned kept   \"--prune\"\n test_configured_prune false false kept   kept   \"\"\n test_configured_prune false false kept   kept   \"--no-prune\"\n test_configured_prune false false pruned kept   \"--prune\"\n+test_configured_prune false false kept   pruned \\\n+\t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n+test_configured_prune false false pruned pruned \\\n+\t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n \n test_configured_prune true  false kept   kept   \"\"\n test_configured_prune true  false pruned kept   \"--prune\"\n@@ -641,6 +649,10 @@ test_configured_prune false true  pruned kept   \"--prune\"\n test_configured_prune true  true  pruned kept   \"\"\n test_configured_prune true  true  pruned kept   \"--prune\"\n test_configured_prune true  true  kept   kept   \"--no-prune\"\n+test_configured_prune true  true  kept   pruned \\\n+\t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n+test_configured_prune true  true  pruned pruned \\\n+\t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"336855","messageId":"20180119000027.28898-7-avarab@gmail.com","threadId":"47614","inReplyTo":"20180119000027.28898-1-avarab@gmail.com","subject":"[PATCH 06/11] git fetch doc: add a new section to explain the ins & outs of pruning","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-19T00:00:22Z","receivedAt":"2018-01-19T00:01:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a new section to canonically explain how remote reference pruning\nworks, and how users should be careful about using it in conjunction\nwith tag refspecs in particular.\n\nA subsequent commit will update the git-remote documentation to refer\nto this section, and details the motivation for writing this in the\nfirst place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-fetch.txt | 49 +++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 49 insertions(+)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex b153aefa68..b07b669a1f 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -99,6 +99,55 @@ The latter use of the `remote.<repository>.fetch` values can be\n overridden by giving the `--refmap=<refspec>` parameter(s) on the\n command line.\n \n+PRUNING\n+-------\n+\n+Git has a default disposition to keeping data unless it's explicitly\n+thrown away, this extends to keeping a hold of local references to\n+branches on remotes that have themselves deleted those branches.\n+\n+If left to accumulate these stale references might make performance\n+worse on big and busy repos that have a lot of branch churn, and\n+e.g. make the output of commands like `git branch -a --contains\n+<commit>` needlessly verbose, as well as impacting anything else\n+that'll work with the complete set of known references.\n+\n+These remote tracking references can be deleted as a one-off with\n+either of:\n+\n+------------------------------------------------\n+# While fetching\n+$ git fetch --prune <name>\n+\n+# Only prune, don't fetch\n+$ git remote <name>\n+------------------------------------------------\n+\n+To prune references on a remote as part of your normal workflow\n+without needing to remember to run that set `fetch.prune` globally, or\n+`remote.<name>.prune` per-remote in the config. See\n+linkgit:git-config[1].\n+\n+Here's where things get tricky and more specific. The pruning feature\n+doesn't actually care about branches, instead it'll prune local <->\n+remote references as a function of the refspec of the remote (see\n+`<refspec>` and <<CRTB,CONFIGURED REMOTE-TRACKING BRANCHES>> above).\n+\n+Therefore if the refspec for the remote includes\n+e.g. `refs/tags/*:refs/tags/*`, or you manually run e.g. `git fetch\n+--prune <name> \"refs/tags/*:refs/tags/*\"` it won't be stale remote\n+tracking branches that are deleted, but any local tag that doesn't\n+exist on the remote.\n+\n+This might not be what you expect, i.e. you want to prune remote\n+`<name>`, but also explicitly fetch tags from it, so when you fetch\n+from it you delete all your local tags, most of which may not have\n+come from the `<name>` remote in the first place.\n+\n+So be careful when using this with a refspec like\n+`refs/tags/*:refs/tags/*`, or any other refspec which might map\n+references from multiple remotes to the same local namespace.\n+\n OUTPUT\n ------\n \n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"336856","messageId":"20180119000027.28898-8-avarab@gmail.com","threadId":"47614","inReplyTo":"20180119000027.28898-1-avarab@gmail.com","subject":"[PATCH 07/11] git remote doc: correct dangerous lies about what prune does","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-19T00:00:23Z","receivedAt":"2018-01-19T00:01:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The \"git remote prune <name>\" command uses the same machinery as \"git\nfetch <name> --prune\", and shares all the same caveats, but its\ndocumentation has suggested that it'll just \"delete stale\nremote-tracking branches under <name>\".\n\nThis isn't true, and hasn't been true since at least v1.8.5.6 (the\noldest version I could be bothered to test).\n\nE.g. if \"+refs/tags/*:refs/tags/*\" is explicitly set in the refspec of\nthe remote it'll delete all local tags <name> doesn't know about.\n\nInstead, briefly give the reader just enough of a hint that this\noption might constitute a shotgun aimed at their foot, and point them\nto the new PRUNING section in the git-fetch documentation which\nexplains all the nuances of what this facility does.\n\nSee \"[BUG] git remote prune removes local tags, depending on fetch\nconfig\" (CACi5S_39wNrbfjLfn0xhCY+uewtFN2YmnAcRc86z6pjUTjWPHQ@mail.gmail.com)\nby Michael Giuffrida for the initial report.\n\nReported-by: Michael Giuffrida <michaelpg@chromium.org>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-remote.txt | 14 +++++++++-----\n 1 file changed, 9 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 577b969c1b..7183a72a23 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -172,10 +172,14 @@ With `-n` option, the remote heads are not queried first with\n \n 'prune'::\n \n-Deletes all stale remote-tracking branches under <name>.\n-These stale branches have already been removed from the remote repository\n-referenced by <name>, but are still locally available in\n-\"remotes/<name>\".\n+Deletes stale references associated with <name>. By default stale\n+remote-tracking branches under <name>, but depending on global\n+configuration and the configuration of the remote we might even prune\n+local tags. Equivalent to `git fetch <name> --prune`, except that no\n+new references will be fetched.\n++\n+See the PRUNING section of linkgit:git-fetch[1] for what it'll prune\n+depending on various configuration.\n +\n With `--dry-run` option, report what branches will be pruned, but do not\n actually prune them.\n@@ -189,7 +193,7 @@ remotes.default is not defined, all remotes which do not have the\n configuration parameter remote.<name>.skipDefaultUpdate set to true will\n be updated.  (See linkgit:git-config[1]).\n +\n-With `--prune` option, prune all the remotes that are updated.\n+With `--prune` option, run pruning against all the remotes that are updated.\n \n \n DISCUSSION\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"336857","messageId":"20180119000027.28898-9-avarab@gmail.com","threadId":"47614","inReplyTo":"20180119000027.28898-1-avarab@gmail.com","subject":"[PATCH 08/11] git-fetch & config doc: link to the new PRUNING section","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-19T00:00:24Z","receivedAt":"2018-01-19T00:01:22Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend the documentation for fetch.prune, fetch.<name>.prune and\n--prune to link to the recently added PRUNING section.\n\nI'd have liked to link directly to it with \"<<PRUNING>>\" from\nfetch-options.txt, since it's included in git-fetch.txt (git-pull.txt\nalso includes it, but doesn't include that option). However making a\nreference across files yields this error:\n\n    [...]/Documentation/git-fetch.xml:226: element xref: validity\n    error : IDREF attribute linkend references an unknown ID \"PRUNING\"\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt        | 6 +++++-\n Documentation/fetch-options.txt | 3 +++\n 2 files changed, 8 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 0e25b2c92b..0f27af5760 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1398,7 +1398,8 @@ fetch.unpackLimit::\n \n fetch.prune::\n \tIf true, fetch will automatically behave as if the `--prune`\n-\toption was given on the command line.  See also `remote.<name>.prune`.\n+\toption was given on the command line.  See also `remote.<name>.prune`\n+\tand the PRUNING section of linkgit:git-fetch[1].\n \n fetch.output::\n \tControl how ref update status is printed. Valid values are\n@@ -2944,6 +2945,9 @@ remote.<name>.prune::\n \tremove any remote-tracking references that no longer exist on the\n \tremote (as if the `--prune` option was given on the command line).\n \tOverrides `fetch.prune` settings, if any.\n++\n+See also `remote.<name>.prune` and the PRUNING section of\n+linkgit:git-fetch[1].\n \n remotes.<group>::\n \tThe list of remotes which are fetched by \"git remote update\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex fb6bebbc61..9f5c85ad96 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -74,6 +74,9 @@ ifndef::git-pull[]\n \tline or in the remote configuration, for example if the remote\n \twas cloned with the --mirror option), then they are also\n \tsubject to pruning.\n++\n+See the PRUNING section below for more details.\n+\n endif::git-pull[]\n \n ifndef::git-pull[]\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"336858","messageId":"20180119000027.28898-10-avarab@gmail.com","threadId":"47614","inReplyTo":"20180119000027.28898-1-avarab@gmail.com","subject":"[PATCH 09/11] fetch: don't redundantly null something calloc() gave us","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-19T00:00:25Z","receivedAt":"2018-01-19T00:01:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Stop redundantly NULL-ing the last element of the refs structure,\nwhich was retrieved via calloc() and is thus guaranteed to be\npre-NULL'd.\n\nThis code dates back to b888d61c83 (\"Make fetch a builtin\",\n2007-09-10), where wasn't any reason to do this back then either, it's\njust something left over from when git-fetch was initially introduced.\n\nThe initial motivation for this change was to make a subsequent change\nwhich'll also modify the refs variable smaller, since it won't have to\ncopy this redundant \"NULL the last + 1 item\" pattern.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fetch.c | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 7bbcd26faf..b34665db9e 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1302,7 +1302,6 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \t\t\t} else\n \t\t\t\trefs[j++] = argv[i];\n \t\t}\n-\t\trefs[j] = NULL;\n \t\tref_nr = j;\n \t}\n \n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"336859","messageId":"20180119000027.28898-11-avarab@gmail.com","threadId":"47614","inReplyTo":"20180119000027.28898-1-avarab@gmail.com","subject":"[PATCH 10/11] fetch tests: add scaffolding for the new fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-19T00:00:26Z","receivedAt":"2018-01-19T00:01:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The fetch.pruneTags configuration doesn't exist yet, but will be added\nin a subsequent commit. Since testing for it requires adding new\nparameters to the test_configured_prune function it's easier to review\nthis patch first to assert that no functional changes are introduced\nyet.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 90 ++++++++++++++++++++++++++++++--------------------------\n 1 file changed, 49 insertions(+), 41 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 576c2598c9..18280df4fc 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -551,18 +551,22 @@ set_config_tristate () {\n test_configured_prune () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n-\texpected_branch=$3\n-\texpected_tag=$4\n-\tcmdline=$5\n+\tfetch_prune_tags=$3\n+\tremote_origin_prune_tags=$4\n+\texpected_branch=$5\n+\texpected_tag=$6\n+\tcmdline=$7\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n+\ttest_expect_success \"prune fetch.prune=$1 fetch.pruneTags=$2 remote.origin.prune=$3 remote.origin.prune=$4${7:+ $7}; branch:$5 tag:$6\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\tgit tag -f newtag &&\n \t\t(\n \t\t\tcd one &&\n \t\t\ttest_unconfig fetch.prune &&\n+\t\t\ttest_unconfig fetch.pruneTags &&\n \t\t\ttest_unconfig remote.origin.prune &&\n+\t\t\ttest_unconfig remote.origin.pruneTags &&\n \t\t\tgit fetch &&\n \t\t\tgit rev-parse --verify refs/remotes/origin/newbranch &&\n \t\t\tgit rev-parse --verify refs/tags/newtag\n@@ -576,7 +580,9 @@ test_configured_prune () {\n \t\t(\n \t\t\tcd one &&\n \t\t\tset_config_tristate fetch.prune $fetch_prune &&\n+\t\t\tset_config_tristate fetch.pruneTags $fetch_prune_tags &&\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n+\t\t\tset_config_tristate remote.origin.pruneTags $remote_origin_prune_tags &&\n \n \t\t\tgit fetch '\"$cmdline\"' &&\n \t\t\tcase \"$expected_branch\" in\n@@ -601,57 +607,59 @@ test_configured_prune () {\n \n # $1 config: fetch.prune\n # $2 config: remote.<name>.prune\n-# $3 expect: branch to be pruned?\n-# $4 expect: tag to be pruned?\n-# $5 git-fetch $cmdline:\n+# $3 config: fetch.pruneTags\n+# $4 config: remote.<name>.pruneTags\n+# $5 expect: branch to be pruned?\n+# $6 expect: tag to be pruned?\n+# $7 git-fetch $cmdline:\n #\n-#                     $1    $2    $3     $4     $5\n-test_configured_prune unset unset kept   kept   \"\"\n-test_configured_prune unset unset kept   kept   \"--no-prune\"\n-test_configured_prune unset unset pruned kept   \"--prune\"\n-test_configured_prune unset unset kept   pruned \\\n+#                     $1    $2    $3    $4    $5     $6     $7\n+test_configured_prune unset unset unset unset kept   kept   \"\"\n+test_configured_prune unset unset unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset unset unset unset pruned kept   \"--prune\"\n+test_configured_prune unset unset unset unset kept   pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n-test_configured_prune unset unset pruned pruned \\\n+test_configured_prune unset unset unset unset pruned pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n \n-test_configured_prune false unset kept   kept   \"\"\n-test_configured_prune false unset kept   kept   \"--no-prune\"\n-test_configured_prune false unset pruned kept   \"--prune\"\n+test_configured_prune false unset unset unset kept   kept   \"\"\n+test_configured_prune false unset unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false unset unset unset pruned kept   \"--prune\"\n \n-test_configured_prune true  unset pruned kept   \"\"\n-test_configured_prune true  unset pruned kept   \"--prune\"\n-test_configured_prune true  unset kept   kept   \"--no-prune\"\n+test_configured_prune true  unset unset unset pruned kept   \"\"\n+test_configured_prune true  unset unset unset pruned kept   \"--prune\"\n+test_configured_prune true  unset unset unset kept   kept   \"--no-prune\"\n \n-test_configured_prune unset false kept   kept   \"\"\n-test_configured_prune unset false kept   kept   \"--no-prune\"\n-test_configured_prune unset false pruned kept   \"--prune\"\n+test_configured_prune unset false unset unset kept   kept   \"\"\n+test_configured_prune unset false unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset false unset unset pruned kept   \"--prune\"\n \n-test_configured_prune false false kept   kept   \"\"\n-test_configured_prune false false kept   kept   \"--no-prune\"\n-test_configured_prune false false pruned kept   \"--prune\"\n-test_configured_prune false false kept   pruned \\\n+test_configured_prune false false unset unset kept   kept   \"\"\n+test_configured_prune false false unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false false unset unset pruned kept   \"--prune\"\n+test_configured_prune false false unset unset kept   pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n-test_configured_prune false false pruned pruned \\\n+test_configured_prune false false unset unset pruned pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n \n-test_configured_prune true  false kept   kept   \"\"\n-test_configured_prune true  false pruned kept   \"--prune\"\n-test_configured_prune true  false kept   kept   \"--no-prune\"\n+test_configured_prune true  false unset unset kept   kept   \"\"\n+test_configured_prune true  false unset unset pruned kept   \"--prune\"\n+test_configured_prune true  false unset unset kept   kept   \"--no-prune\"\n \n-test_configured_prune unset true  pruned kept   \"\"\n-test_configured_prune unset true  kept   kept   \"--no-prune\"\n-test_configured_prune unset true  pruned kept   \"--prune\"\n+test_configured_prune unset true  unset unset pruned kept   \"\"\n+test_configured_prune unset true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset true  unset unset pruned kept   \"--prune\"\n \n-test_configured_prune false true  pruned kept   \"\"\n-test_configured_prune false true  kept   kept   \"--no-prune\"\n-test_configured_prune false true  pruned kept   \"--prune\"\n+test_configured_prune false true  unset unset pruned kept   \"\"\n+test_configured_prune false true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false true  unset unset pruned kept   \"--prune\"\n \n-test_configured_prune true  true  pruned kept   \"\"\n-test_configured_prune true  true  pruned kept   \"--prune\"\n-test_configured_prune true  true  kept   kept   \"--no-prune\"\n-test_configured_prune true  true  kept   pruned \\\n+test_configured_prune true  true  unset unset pruned kept   \"\"\n+test_configured_prune true  true  unset unset pruned kept   \"--prune\"\n+test_configured_prune true  true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune true  true  unset unset kept   pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n-test_configured_prune true  true  pruned pruned \\\n+test_configured_prune true  true  unset unset pruned pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n \n test_expect_success 'all boundary commits are excluded' '\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"336860","messageId":"20180119000027.28898-12-avarab@gmail.com","threadId":"47614","inReplyTo":"20180119000027.28898-1-avarab@gmail.com","subject":"[RFC/PATCH 11/11] WIP fetch: add a --fetch-prune option and fetch.pruneTags config","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-19T00:00:27Z","receivedAt":"2018-01-19T00:01:30Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"[WIP: This doesn't (yet) work as advertised, see further WIP note\nbelow]\n\nAdd a --fetch-prune option to git-fetch along with fetch.pruneTags\nconfig option. This allows for doing:\n\n    git fetch origin -p -P\n\nOr simply:\n\n    git config fetch.prune true &&\n    git config fetch.pruneTags true &&\n    git fetch origin\n\nInstead of the much more verbose:\n\n    git fetch --prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\n\nA use-case of git which is a pain to support now is frequent pulling\nfrom a repo which is having both its branches *and* tags deleted\nregularly. At work we create deployment tags in the repo for each\nrollout, and there's *lots* of those, so they're archived within weeks\nfor performance reasons.\n\nWithout this change it's hard to centrally configure such repos in\n/etc/gitconfig (on servers that are only used for working with\nthem). You need to set fetch.prune=true globally, and then for each\nrepo:\n\n    git -C {} config --replace-all remote.origin.fetch \"refs/tags/*:refs/tags/*\" \"^refs/tags/*:refs/tags/*$\"\n\nNow I can simply set fetch.pruneTags=true in /etc/gitconfig as well,\nand users running \"git pull\" will automatically get the pruning\nsemantics we want.\n\nSee my \"Re: [BUG] git remote prune removes local tags, depending on\nfetch config\" (87po6ahx87.fsf@evledraar.gmail.com;\nhttps://public-inbox.org/git/87po6ahx87.fsf@evledraar.gmail.com/) for\nmore details.\n\n[WIP: This doesn't actually work, since the implementation is to just\npretend that 'refs/tags/*:refs/tags/*' was supplied on the\ncommand-line, we'll consequently stop fetching any branches, since we\ndon't use the default refspec found in .git/config anymore\n\nI'm not familiar enough with this code and the refspec machinery to\nknow if this argument injection is crazy, and how to tell it to *also*\ndo whatever it's doing by default]\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt        | 15 +++++++++++++++\n Documentation/fetch-options.txt | 15 ++++++++++++++-\n builtin/fetch.c                 | 37 ++++++++++++++++++++++++++++++++++---\n remote.c                        |  2 ++\n remote.h                        |  1 +\n t/t5510-fetch.sh                |  1 +\n 6 files changed, 67 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 0f27af5760..476175ef8d 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1401,6 +1401,14 @@ fetch.prune::\n \toption was given on the command line.  See also `remote.<name>.prune`\n \tand the PRUNING section of linkgit:git-fetch[1].\n \n+fetch.pruneTags::\n+\tIf true, fetch will automatically behave as if the\n+\t`refs/tags/*:refs/tags/*` refspec was provided when pruning,\n+\tif not set already. This allows for setting both this option\n+\tand `fetch.prune` to maintain a 1=1 mapping to upstrem\n+\trefs. See also `remote.<name>.pruneTags` and the PRUNING\n+\tsection of linkgit:git-fetch[1].\n+\n fetch.output::\n \tControl how ref update status is printed. Valid values are\n \t`full` and `compact`. Default value is `full`. See section\n@@ -2945,6 +2953,13 @@ remote.<name>.prune::\n \tremove any remote-tracking references that no longer exist on the\n \tremote (as if the `--prune` option was given on the command line).\n \tOverrides `fetch.prune` settings, if any.\n+\n+remote.<name>.pruneTags::\n+\tWhen set to true, fetching from this remote by default will also\n+\tremove any local tags that no longer exist on the remote (as\n+\tif the `--prune` option was given on the command line in\n+\tconjunction with the `refs/tags/*:refs/tags/*` refspec).\n+\tOverrides `fetch.prune` settings, if any.\n +\n See also `remote.<name>.prune` and the PRUNING section of\n linkgit:git-fetch[1].\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 9f5c85ad96..dc13bed741 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -73,7 +73,20 @@ ifndef::git-pull[]\n \tare fetched due to an explicit refspec (either on the command\n \tline or in the remote configuration, for example if the remote\n \twas cloned with the --mirror option), then they are also\n-\tsubject to pruning.\n+\tsubject to pruning. Supplying `--prune-tags` is a shorthand for\n+\tproviding the tag refspec.\n++\n+See the PRUNING section below for more details.\n+\n+-P::\n+--prune-tags::\n+\tBefore fetching, remove any local tags that no longer exist on\n+\tthe remote if `--prune` is enabled. This option should be used\n+\tmore carefully, unlike `--prune` it will remove any local\n+\treferences (local tags) that have been created. This option is\n+\tmerely a shorthand for providing the explicit tag refspec\n+\talong with `--prune`, see the discussion about that in its\n+\tdocumentation.\n +\n See the PRUNING section below for more details.\n \ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex b34665db9e..57669a000c 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -38,6 +38,10 @@ static int fetch_prune_config = -1; /* unspecified */\n static int prune = -1; /* unspecified */\n #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */\n \n+static int fetch_prune_tags_config = -1; /* unspecified */\n+static int prune_tags = -1; /* unspecified */\n+#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */\n+\n static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;\n static int progress = -1;\n static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;\n@@ -64,6 +68,11 @@ static int git_fetch_config(const char *k, const char *v, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(k, \"fetch.prunetags\")) {\n+\t\tfetch_prune_tags_config = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(k, \"submodule.recurse\")) {\n \t\tint r = git_config_bool(k, v) ?\n \t\t\tRECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;\n@@ -126,6 +135,8 @@ static struct option builtin_fetch_options[] = {\n \t\t    N_(\"number of submodules fetched in parallel\")),\n \tOPT_BOOL('p', \"prune\", &prune,\n \t\t N_(\"prune remote-tracking branches no longer on remote\")),\n+\tOPT_BOOL('P', \"prune-tags\", &prune_tags,\n+\t\t N_(\"prune local tags not found on remote\")),\n \t{ OPTION_CALLBACK, 0, \"recurse-submodules\", &recurse_submodules, N_(\"on-demand\"),\n \t\t    N_(\"control recursive fetching of submodules\"),\n \t\t    PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },\n@@ -1212,6 +1223,8 @@ static void add_options_to_argv(struct argv_array *argv)\n \t\targv_array_push(argv, \"--dry-run\");\n \tif (prune != -1)\n \t\targv_array_push(argv, prune ? \"--prune\" : \"--no-prune\");\n+\tif (prune_tags != -1)\n+\t\targv_array_push(argv, prune_tags ? \"--prune-tags\" : \"--no-prune-tags\");\n \tif (update_head_ok)\n \t\targv_array_push(argv, \"--update-head-ok\");\n \tif (force)\n@@ -1270,6 +1283,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \tstatic const char **refs = NULL;\n \tstruct refspec *refspec;\n \tint ref_nr = 0;\n+\tint j = 0;\n \tint exit_code;\n \n \tif (!remote)\n@@ -1288,10 +1302,27 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \t\t\tprune = PRUNE_BY_DEFAULT;\n \t}\n \n+\tif (prune_tags < 0) {\n+\t\t/* no command line request */\n+\t\tif (0 <= gtransport->remote->prune_tags)\n+\t\t\tprune_tags = gtransport->remote->prune_tags;\n+\t\telse if (0 <= fetch_prune_tags_config)\n+\t\t\tprune_tags = fetch_prune_tags_config;\n+\t\telse\n+\t\t\tprune_tags = PRUNE_TAGS_BY_DEFAULT;\n+\t}\n+\n+\tif (argc > 0 || prune_tags) {\n+\t\tsize_t nr_alloc = st_add3(argc, prune_tags, 1);\n+\t\trefs = xcalloc(nr_alloc, sizeof(const char *));\n+\t\tif (prune_tags) {\n+\t\t\trefs[j++] = xstrfmt(\"refs/tags/*:refs/tags/*\");\n+\t\t\tref_nr++;\n+\t\t}\n+\t}\n+\n \tif (argc > 0) {\n-\t\tint j = 0;\n \t\tint i;\n-\t\trefs = xcalloc(st_add(argc, 1), sizeof(const char *));\n \t\tfor (i = 0; i < argc; i++) {\n \t\t\tif (!strcmp(argv[i], \"tag\")) {\n \t\t\t\ti++;\n@@ -1301,8 +1332,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \t\t\t\t\t\t    argv[i], argv[i]);\n \t\t\t} else\n \t\t\t\trefs[j++] = argv[i];\n+\t\t\tref_nr++;\n \t\t}\n-\t\tref_nr = j;\n \t}\n \n \tsigchain_push_common(unlock_pack_on_signal);\ndiff --git a/remote.c b/remote.c\nindex 4e93753e19..9eb6d24009 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -391,6 +391,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tremote->skip_default_update = git_config_bool(key, value);\n \telse if (!strcmp(subkey, \"prune\"))\n \t\tremote->prune = git_config_bool(key, value);\n+\telse if (!strcmp(subkey, \"prunetags\"))\n+\t\tremote->prune_tags = git_config_bool(key, value);\n \telse if (!strcmp(subkey, \"url\")) {\n \t\tconst char *v;\n \t\tif (git_config_string(&v, key, value))\ndiff --git a/remote.h b/remote.h\nindex 1f6611be21..04d7994cbf 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -47,6 +47,7 @@ struct remote {\n \tint skip_default_update;\n \tint mirror;\n \tint prune;\n+\tint prune_tags;\n \n \tconst char *receivepack;\n \tconst char *uploadpack;\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 18280df4fc..1a822861e0 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -617,6 +617,7 @@ test_configured_prune () {\n test_configured_prune unset unset unset unset kept   kept   \"\"\n test_configured_prune unset unset unset unset kept   kept   \"--no-prune\"\n test_configured_prune unset unset unset unset pruned kept   \"--prune\"\n+test_configured_prune unset unset unset unset pruned pruned \"--prune --prune-tags\" # WIP: broken, see commit msg\n test_configured_prune unset unset unset unset kept   pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n test_configured_prune unset unset unset unset pruned pruned \\\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"336871","messageId":"CAPig+cRVHQQR_V+GH1X-KVBtCRkGq=PewOQJu6WcnVa6N+sRNA@mail.gmail.com","threadId":"47614","inReplyTo":"20180119000027.28898-7-avarab@gmail.com","subject":"Re: [PATCH 06/11] git fetch doc: add a new section to explain the ins & outs of pruning","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-01-19T00:46:45Z","receivedAt":"2018-01-19T00:46:51Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Jan 18, 2018 at 7:00 PM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Add a new section to canonically explain how remote reference pruning\n> works, and how users should be careful about using it in conjunction\n> with tag refspecs in particular.\n>\n> A subsequent commit will update the git-remote documentation to refer\n> to this section, and details the motivation for writing this in the\n> first place.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\n> @@ -99,6 +99,55 @@ The latter use of the `remote.<repository>.fetch` values can be\n> +PRUNING\n> +-------\n> +\n> +Git has a default disposition to keeping data unless it's explicitly\n\ns/keeping/keep/ or s/to keeping/of keeping/\n\n> +thrown away, this extends to keeping a hold of local references to\n\ns/away,/away;/\ns/to keeping a hold of/holding onto/\n\n> +branches on remotes that have themselves deleted those branches.\n> +\n> +If left to accumulate these stale references might make performance\n\ns/accumulate/&,/\n\n> +worse on big and busy repos that have a lot of branch churn, and\n> +e.g. make the output of commands like `git branch -a --contains\n> +<commit>` needlessly verbose, as well as impacting anything else\n> +that'll work with the complete set of known references.\n> +\n> +These remote tracking references can be deleted as a one-off with\n> +either of:\n> +\n> +------------------------------------------------\n> +# While fetching\n> +$ git fetch --prune <name>\n> +\n> +# Only prune, don't fetch\n> +$ git remote <name>\n> +------------------------------------------------\n\nDid you mean \"git remote prune <name>\"?\n\n> +To prune references on a remote as part of your normal workflow\n\nThis reads as if it's possible to prune something on the remote\nmachine itself. Maybe just say:\n\n    To prune references as part of your normal workflow...\n\n> +without needing to remember to run that set `fetch.prune` globally, or\n> +`remote.<name>.prune` per-remote in the config. See\n> +linkgit:git-config[1].\n"},{"id":"337001","messageId":"20180121000304.32323-1-avarab@gmail.com","threadId":"47614","inReplyTo":"20180119000027.28898-1-avarab@gmail.com","subject":"[PATCH v2 00/12] document & test fetch pruning & add fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T00:02:52Z","receivedAt":"2018-01-21T00:03:22Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now v2 and fully non-RFC, changes:\n\nÆvar Arnfjörð Bjarmason (12):\n  fetch tests: refactor in preparation for testing tag pruning\n  fetch tests: arrange arguments for future readability\n  fetch tests: add a tag to be deleted to the pruning tests\n  fetch tests: double quote a variable for interpolation\n  fetch tests: test --prune and refspec interaction\n\nNo changes.\n\n  git fetch doc: add a new section to explain the ins & outs of pruning\n\nGrammar etc. fixes from Eric. Thanks!\n\n  git remote doc: correct dangerous lies about what prune does\n  git-fetch & config doc: link to the new PRUNING section\n\nNo changes.\n\n  fetch: don't redundantly NULL something calloc() gave us\n\nMinor rephrasing of the commit message.\n\n  fetch: stop accessing \"remote\" variable indirectly\n\nNEW: Amends some existing confusing code, whose pattern will be used\nby 12/12.\n\n  fetch tests: add scaffolding for the new fetch.pruneTags\n\nI screwed up positional arguments in the test description, fixed.\n\n  fetch: add a --fetch-prune option and fetch.pruneTags config\n\nNow actually works, and with a very different implementation which\ninvolves making the previously private add_fetch_refspec() function in\nremote.c part of the API.\n\n Documentation/config.txt        |  20 +++++-\n Documentation/fetch-options.txt |  18 ++++-\n Documentation/git-fetch.txt     |  73 +++++++++++++++++++\n Documentation/git-remote.txt    |  14 ++--\n builtin/fetch.c                 |  37 +++++++++-\n remote.c                        |   5 +-\n remote.h                        |   3 +\n t/t5510-fetch.sh                | 154 +++++++++++++++++++++++++++++-----------\n 8 files changed, 272 insertions(+), 52 deletions(-)\n\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337002","messageId":"20180121000304.32323-2-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v2 01/12] fetch tests: refactor in preparation for testing tag pruning","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T00:02:53Z","receivedAt":"2018-01-21T00:03:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In a subsequent commit this function will learn to test for tag\npruning, prepare for that by making space for more variables, and\nmaking it clear that \"expected\" here refers to branches.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 9 ++++++---\n 1 file changed, 6 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 668c54be41..11da97f9b7 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -549,9 +549,12 @@ set_config_tristate () {\n }\n \n test_configured_prune () {\n-\tfetch_prune=$1 remote_origin_prune=$2 cmdline=$3 expected=$4\n+\tfetch_prune=$1\n+\tremote_origin_prune=$2\n+\tcmdline=$3\n+\texpected_branch=$4\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; $4\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; branch:$4\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\t(\n@@ -572,7 +575,7 @@ test_configured_prune () {\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n \n \t\t\tgit fetch $cmdline &&\n-\t\t\tcase \"$expected\" in\n+\t\t\tcase \"$expected_branch\" in\n \t\t\tpruned)\n \t\t\t\ttest_must_fail git rev-parse --verify refs/remotes/origin/newbranch\n \t\t\t\t;;\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337003","messageId":"20180121000304.32323-3-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v2 02/12] fetch tests: arrange arguments for future readability","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T00:02:54Z","receivedAt":"2018-01-21T00:03:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Re-arrange the arguments to the test_configured_prune() function used\nin this test to pass the arguments to --fetch last. A subsequent\nchange will test for more elaborate fetch arguments, including long\nrefspecs. It'll be more readable to be able to wrap those on a new\nline of their own.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 82 ++++++++++++++++++++++++++++++--------------------------\n 1 file changed, 44 insertions(+), 38 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 11da97f9b7..ab8b25344d 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -551,10 +551,10 @@ set_config_tristate () {\n test_configured_prune () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n-\tcmdline=$3\n-\texpected_branch=$4\n+\texpected_branch=$3\n+\tcmdline=$4\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; branch:$4\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${4:+ $4}; branch:$3\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\t(\n@@ -587,41 +587,47 @@ test_configured_prune () {\n \t'\n }\n \n-test_configured_prune unset unset \"\"\t\tkept\n-test_configured_prune unset unset \"--no-prune\"\tkept\n-test_configured_prune unset unset \"--prune\"\tpruned\n-\n-test_configured_prune false unset \"\"\t\tkept\n-test_configured_prune false unset \"--no-prune\"\tkept\n-test_configured_prune false unset \"--prune\"\tpruned\n-\n-test_configured_prune true  unset \"\"\t\tpruned\n-test_configured_prune true  unset \"--prune\"\tpruned\n-test_configured_prune true  unset \"--no-prune\"\tkept\n-\n-test_configured_prune unset false \"\"\t\tkept\n-test_configured_prune unset false \"--no-prune\"\tkept\n-test_configured_prune unset false \"--prune\"\tpruned\n-\n-test_configured_prune false false \"\"\t\tkept\n-test_configured_prune false false \"--no-prune\"\tkept\n-test_configured_prune false false \"--prune\"\tpruned\n-\n-test_configured_prune true  false \"\"\t\tkept\n-test_configured_prune true  false \"--prune\"\tpruned\n-test_configured_prune true  false \"--no-prune\"\tkept\n-\n-test_configured_prune unset true  \"\"\t\tpruned\n-test_configured_prune unset true  \"--no-prune\"\tkept\n-test_configured_prune unset true  \"--prune\"\tpruned\n-\n-test_configured_prune false true  \"\"\t\tpruned\n-test_configured_prune false true  \"--no-prune\"\tkept\n-test_configured_prune false true  \"--prune\"\tpruned\n-\n-test_configured_prune true  true  \"\"\t\tpruned\n-test_configured_prune true  true  \"--prune\"\tpruned\n-test_configured_prune true  true  \"--no-prune\"\tkept\n+# $1 config: fetch.prune\n+# $2 config: remote.<name>.prune\n+# $3 expect: branch to be pruned?\n+# $4 git-fetch $cmdline:\n+#\n+#                     $1    $2    $3     $4\n+test_configured_prune unset unset kept   \"\"\n+test_configured_prune unset unset kept   \"--no-prune\"\n+test_configured_prune unset unset pruned \"--prune\"\n+\n+test_configured_prune false unset kept   \"\"\n+test_configured_prune false unset kept   \"--no-prune\"\n+test_configured_prune false unset pruned \"--prune\"\n+\n+test_configured_prune true  unset pruned \"\"\n+test_configured_prune true  unset pruned \"--prune\"\n+test_configured_prune true  unset kept   \"--no-prune\"\n+\n+test_configured_prune unset false kept   \"\"\n+test_configured_prune unset false kept   \"--no-prune\"\n+test_configured_prune unset false pruned \"--prune\"\n+\n+test_configured_prune false false kept   \"\"\n+test_configured_prune false false kept   \"--no-prune\"\n+test_configured_prune false false pruned \"--prune\"\n+\n+test_configured_prune true  false kept   \"\"\n+test_configured_prune true  false pruned \"--prune\"\n+test_configured_prune true  false kept   \"--no-prune\"\n+\n+test_configured_prune unset true  pruned \"\"\n+test_configured_prune unset true  kept   \"--no-prune\"\n+test_configured_prune unset true  pruned \"--prune\"\n+\n+test_configured_prune false true  pruned \"\"\n+test_configured_prune false true  kept   \"--no-prune\"\n+test_configured_prune false true  pruned \"--prune\"\n+\n+test_configured_prune true  true  pruned \"\"\n+test_configured_prune true  true  pruned \"--prune\"\n+test_configured_prune true  true  kept   \"--no-prune\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337004","messageId":"20180121000304.32323-4-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v2 03/12] fetch tests: add a tag to be deleted to the pruning tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T00:02:55Z","receivedAt":"2018-01-21T00:03:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a tag to be deleted to the fetch --prune tests. The tag is always\nkept for now, which is the expected behavior, but now I can add a test\nfor tag pruning in a later commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 93 ++++++++++++++++++++++++++++++++------------------------\n 1 file changed, 53 insertions(+), 40 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex ab8b25344d..fad65bd885 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -552,21 +552,25 @@ test_configured_prune () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n \texpected_branch=$3\n-\tcmdline=$4\n+\texpected_tag=$4\n+\tcmdline=$5\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${4:+ $4}; branch:$3\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n+\t\tgit tag -f newtag &&\n \t\t(\n \t\t\tcd one &&\n \t\t\ttest_unconfig fetch.prune &&\n \t\t\ttest_unconfig remote.origin.prune &&\n \t\t\tgit fetch &&\n-\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch\n+\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch &&\n+\t\t\tgit rev-parse --verify refs/tags/newtag\n \t\t) &&\n \n \t\t# now remove it\n \t\tgit branch -d newbranch &&\n+\t\tgit tag -d newtag &&\n \n \t\t# then test\n \t\t(\n@@ -582,6 +586,14 @@ test_configured_prune () {\n \t\t\tkept)\n \t\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch\n \t\t\t\t;;\n+\t\t\tesac &&\n+\t\t\tcase \"$expected_tag\" in\n+\t\t\tpruned)\n+\t\t\t\ttest_must_fail git rev-parse --verify refs/tags/newtag\n+\t\t\t\t;;\n+\t\t\tkept)\n+\t\t\t\tgit rev-parse --verify refs/tags/newtag\n+\t\t\t\t;;\n \t\t\tesac\n \t\t)\n \t'\n@@ -590,44 +602,45 @@ test_configured_prune () {\n # $1 config: fetch.prune\n # $2 config: remote.<name>.prune\n # $3 expect: branch to be pruned?\n-# $4 git-fetch $cmdline:\n+# $4 expect: tag to be pruned?\n+# $5 git-fetch $cmdline:\n #\n-#                     $1    $2    $3     $4\n-test_configured_prune unset unset kept   \"\"\n-test_configured_prune unset unset kept   \"--no-prune\"\n-test_configured_prune unset unset pruned \"--prune\"\n-\n-test_configured_prune false unset kept   \"\"\n-test_configured_prune false unset kept   \"--no-prune\"\n-test_configured_prune false unset pruned \"--prune\"\n-\n-test_configured_prune true  unset pruned \"\"\n-test_configured_prune true  unset pruned \"--prune\"\n-test_configured_prune true  unset kept   \"--no-prune\"\n-\n-test_configured_prune unset false kept   \"\"\n-test_configured_prune unset false kept   \"--no-prune\"\n-test_configured_prune unset false pruned \"--prune\"\n-\n-test_configured_prune false false kept   \"\"\n-test_configured_prune false false kept   \"--no-prune\"\n-test_configured_prune false false pruned \"--prune\"\n-\n-test_configured_prune true  false kept   \"\"\n-test_configured_prune true  false pruned \"--prune\"\n-test_configured_prune true  false kept   \"--no-prune\"\n-\n-test_configured_prune unset true  pruned \"\"\n-test_configured_prune unset true  kept   \"--no-prune\"\n-test_configured_prune unset true  pruned \"--prune\"\n-\n-test_configured_prune false true  pruned \"\"\n-test_configured_prune false true  kept   \"--no-prune\"\n-test_configured_prune false true  pruned \"--prune\"\n-\n-test_configured_prune true  true  pruned \"\"\n-test_configured_prune true  true  pruned \"--prune\"\n-test_configured_prune true  true  kept   \"--no-prune\"\n+#                     $1    $2    $3     $4     $5\n+test_configured_prune unset unset kept   kept   \"\"\n+test_configured_prune unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset unset pruned kept   \"--prune\"\n+\n+test_configured_prune false unset kept   kept   \"\"\n+test_configured_prune false unset kept   kept   \"--no-prune\"\n+test_configured_prune false unset pruned kept   \"--prune\"\n+\n+test_configured_prune true  unset pruned kept   \"\"\n+test_configured_prune true  unset pruned kept   \"--prune\"\n+test_configured_prune true  unset kept   kept   \"--no-prune\"\n+\n+test_configured_prune unset false kept   kept   \"\"\n+test_configured_prune unset false kept   kept   \"--no-prune\"\n+test_configured_prune unset false pruned kept   \"--prune\"\n+\n+test_configured_prune false false kept   kept   \"\"\n+test_configured_prune false false kept   kept   \"--no-prune\"\n+test_configured_prune false false pruned kept   \"--prune\"\n+\n+test_configured_prune true  false kept   kept   \"\"\n+test_configured_prune true  false pruned kept   \"--prune\"\n+test_configured_prune true  false kept   kept   \"--no-prune\"\n+\n+test_configured_prune unset true  pruned kept   \"\"\n+test_configured_prune unset true  kept   kept   \"--no-prune\"\n+test_configured_prune unset true  pruned kept   \"--prune\"\n+\n+test_configured_prune false true  pruned kept   \"\"\n+test_configured_prune false true  kept   kept   \"--no-prune\"\n+test_configured_prune false true  pruned kept   \"--prune\"\n+\n+test_configured_prune true  true  pruned kept   \"\"\n+test_configured_prune true  true  pruned kept   \"--prune\"\n+test_configured_prune true  true  kept   kept   \"--no-prune\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337005","messageId":"20180121000304.32323-5-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v2 04/12] fetch tests: double quote a variable for interpolation","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T00:02:56Z","receivedAt":"2018-01-21T00:03:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"If the $cmdline variable contains multiple arguments they won't be\ninterpolated correctly since the body of the test is single quoted. I\ndon't know what part of test-lib.sh is expanding variables within\nsingle-quoted strings, but interpolating this inline is the desired\nbehavior here.\n\nThis will be used in a subsequent commit to pass more than one\nvariable to git-fetch.\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 fad65bd885..542eb53a99 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -578,7 +578,7 @@ test_configured_prune () {\n \t\t\tset_config_tristate fetch.prune $fetch_prune &&\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n \n-\t\t\tgit fetch $cmdline &&\n+\t\t\tgit fetch '\"$cmdline\"' &&\n \t\t\tcase \"$expected_branch\" in\n \t\t\tpruned)\n \t\t\t\ttest_must_fail git rev-parse --verify refs/remotes/origin/newbranch\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337006","messageId":"20180121000304.32323-6-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v2 05/12] fetch tests: test --prune and refspec interaction","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T00:02:57Z","receivedAt":"2018-01-21T00:03:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for the interaction between explicitly provided refspecs\nand fetch.prune.\n\nThere's no point in adding this boilerplate to every combination of\nunset/false/true, it's instructive and sufficient to show that no\nmatter if the variable is unset, false or true the refspec on the\ncommand-line overrides any configuration variable.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 12 ++++++++++++\n 1 file changed, 12 insertions(+)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 542eb53a99..576c2598c9 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -609,6 +609,10 @@ test_configured_prune () {\n test_configured_prune unset unset kept   kept   \"\"\n test_configured_prune unset unset kept   kept   \"--no-prune\"\n test_configured_prune unset unset pruned kept   \"--prune\"\n+test_configured_prune unset unset kept   pruned \\\n+\t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n+test_configured_prune unset unset pruned pruned \\\n+\t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n \n test_configured_prune false unset kept   kept   \"\"\n test_configured_prune false unset kept   kept   \"--no-prune\"\n@@ -625,6 +629,10 @@ test_configured_prune unset false pruned kept   \"--prune\"\n test_configured_prune false false kept   kept   \"\"\n test_configured_prune false false kept   kept   \"--no-prune\"\n test_configured_prune false false pruned kept   \"--prune\"\n+test_configured_prune false false kept   pruned \\\n+\t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n+test_configured_prune false false pruned pruned \\\n+\t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n \n test_configured_prune true  false kept   kept   \"\"\n test_configured_prune true  false pruned kept   \"--prune\"\n@@ -641,6 +649,10 @@ test_configured_prune false true  pruned kept   \"--prune\"\n test_configured_prune true  true  pruned kept   \"\"\n test_configured_prune true  true  pruned kept   \"--prune\"\n test_configured_prune true  true  kept   kept   \"--no-prune\"\n+test_configured_prune true  true  kept   pruned \\\n+\t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n+test_configured_prune true  true  pruned pruned \\\n+\t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337007","messageId":"20180121000304.32323-7-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v2 06/12] git fetch doc: add a new section to explain the ins & outs of pruning","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T00:02:58Z","receivedAt":"2018-01-21T00:03:47Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a new section to canonically explain how remote reference pruning\nworks, and how users should be careful about using it in conjunction\nwith tag refspecs in particular.\n\nA subsequent commit will update the git-remote documentation to refer\nto this section, and details the motivation for writing this in the\nfirst place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-fetch.txt | 49 +++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 49 insertions(+)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex b153aefa68..18fac0da2e 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -99,6 +99,55 @@ The latter use of the `remote.<repository>.fetch` values can be\n overridden by giving the `--refmap=<refspec>` parameter(s) on the\n command line.\n \n+PRUNING\n+-------\n+\n+Git has a default disposition of keeping data unless it's explicitly\n+thrown away; this extends to holding onto local references to branches\n+on remotes that have themselves deleted those branches.\n+\n+If left to accumulate, these stale references might make performance\n+worse on big and busy repos that have a lot of branch churn, and\n+e.g. make the output of commands like `git branch -a --contains\n+<commit>` needlessly verbose, as well as impacting anything else\n+that'll work with the complete set of known references.\n+\n+These remote tracking references can be deleted as a one-off with\n+either of:\n+\n+------------------------------------------------\n+# While fetching\n+$ git fetch --prune <name>\n+\n+# Only prune, don't fetch\n+$ git remote prune <name>\n+------------------------------------------------\n+\n+To prune references as part of your normal workflow without needing to\n+remember to run that set `fetch.prune` globally, or\n+`remote.<name>.prune` per-remote in the config. See\n+linkgit:git-config[1].\n+\n+Here's where things get tricky and more specific. The pruning feature\n+doesn't actually care about branches, instead it'll prune local <->\n+remote references as a function of the refspec of the remote (see\n+`<refspec>` and <<CRTB,CONFIGURED REMOTE-TRACKING BRANCHES>> above).\n+\n+Therefore if the refspec for the remote includes\n+e.g. `refs/tags/*:refs/tags/*`, or you manually run e.g. `git fetch\n+--prune <name> \"refs/tags/*:refs/tags/*\"` it won't be stale remote\n+tracking branches that are deleted, but any local tag that doesn't\n+exist on the remote.\n+\n+This might not be what you expect, i.e. you want to prune remote\n+`<name>`, but also explicitly fetch tags from it, so when you fetch\n+from it you delete all your local tags, most of which may not have\n+come from the `<name>` remote in the first place.\n+\n+So be careful when using this with a refspec like\n+`refs/tags/*:refs/tags/*`, or any other refspec which might map\n+references from multiple remotes to the same local namespace.\n+\n OUTPUT\n ------\n \n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337008","messageId":"20180121000304.32323-8-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v2 07/12] git remote doc: correct dangerous lies about what prune does","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T00:02:59Z","receivedAt":"2018-01-21T00:03:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The \"git remote prune <name>\" command uses the same machinery as \"git\nfetch <name> --prune\", and shares all the same caveats, but its\ndocumentation has suggested that it'll just \"delete stale\nremote-tracking branches under <name>\".\n\nThis isn't true, and hasn't been true since at least v1.8.5.6 (the\noldest version I could be bothered to test).\n\nE.g. if \"+refs/tags/*:refs/tags/*\" is explicitly set in the refspec of\nthe remote it'll delete all local tags <name> doesn't know about.\n\nInstead, briefly give the reader just enough of a hint that this\noption might constitute a shotgun aimed at their foot, and point them\nto the new PRUNING section in the git-fetch documentation which\nexplains all the nuances of what this facility does.\n\nSee \"[BUG] git remote prune removes local tags, depending on fetch\nconfig\" (CACi5S_39wNrbfjLfn0xhCY+uewtFN2YmnAcRc86z6pjUTjWPHQ@mail.gmail.com)\nby Michael Giuffrida for the initial report.\n\nReported-by: Michael Giuffrida <michaelpg@chromium.org>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-remote.txt | 14 +++++++++-----\n 1 file changed, 9 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 577b969c1b..7183a72a23 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -172,10 +172,14 @@ With `-n` option, the remote heads are not queried first with\n \n 'prune'::\n \n-Deletes all stale remote-tracking branches under <name>.\n-These stale branches have already been removed from the remote repository\n-referenced by <name>, but are still locally available in\n-\"remotes/<name>\".\n+Deletes stale references associated with <name>. By default stale\n+remote-tracking branches under <name>, but depending on global\n+configuration and the configuration of the remote we might even prune\n+local tags. Equivalent to `git fetch <name> --prune`, except that no\n+new references will be fetched.\n++\n+See the PRUNING section of linkgit:git-fetch[1] for what it'll prune\n+depending on various configuration.\n +\n With `--dry-run` option, report what branches will be pruned, but do not\n actually prune them.\n@@ -189,7 +193,7 @@ remotes.default is not defined, all remotes which do not have the\n configuration parameter remote.<name>.skipDefaultUpdate set to true will\n be updated.  (See linkgit:git-config[1]).\n +\n-With `--prune` option, prune all the remotes that are updated.\n+With `--prune` option, run pruning against all the remotes that are updated.\n \n \n DISCUSSION\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337009","messageId":"20180121000304.32323-9-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v2 08/12] git-fetch & config doc: link to the new PRUNING section","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T00:03:00Z","receivedAt":"2018-01-21T00:03:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend the documentation for fetch.prune, fetch.<name>.prune and\n--prune to link to the recently added PRUNING section.\n\nI'd have liked to link directly to it with \"<<PRUNING>>\" from\nfetch-options.txt, since it's included in git-fetch.txt (git-pull.txt\nalso includes it, but doesn't include that option). However making a\nreference across files yields this error:\n\n    [...]/Documentation/git-fetch.xml:226: element xref: validity\n    error : IDREF attribute linkend references an unknown ID \"PRUNING\"\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt        | 6 +++++-\n Documentation/fetch-options.txt | 3 +++\n 2 files changed, 8 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 0e25b2c92b..0f27af5760 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1398,7 +1398,8 @@ fetch.unpackLimit::\n \n fetch.prune::\n \tIf true, fetch will automatically behave as if the `--prune`\n-\toption was given on the command line.  See also `remote.<name>.prune`.\n+\toption was given on the command line.  See also `remote.<name>.prune`\n+\tand the PRUNING section of linkgit:git-fetch[1].\n \n fetch.output::\n \tControl how ref update status is printed. Valid values are\n@@ -2944,6 +2945,9 @@ remote.<name>.prune::\n \tremove any remote-tracking references that no longer exist on the\n \tremote (as if the `--prune` option was given on the command line).\n \tOverrides `fetch.prune` settings, if any.\n++\n+See also `remote.<name>.prune` and the PRUNING section of\n+linkgit:git-fetch[1].\n \n remotes.<group>::\n \tThe list of remotes which are fetched by \"git remote update\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex fb6bebbc61..9f5c85ad96 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -74,6 +74,9 @@ ifndef::git-pull[]\n \tline or in the remote configuration, for example if the remote\n \twas cloned with the --mirror option), then they are also\n \tsubject to pruning.\n++\n+See the PRUNING section below for more details.\n+\n endif::git-pull[]\n \n ifndef::git-pull[]\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337010","messageId":"20180121000304.32323-10-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v2 09/12] fetch: don't redundantly NULL something calloc() gave us","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T00:03:01Z","receivedAt":"2018-01-21T00:03:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Stop redundantly NULL-ing the last element of the refs structure,\nwhich was retrieved via calloc(), and is thus guaranteed to be\npre-NULL'd.\n\nThis code dates back to b888d61c83 (\"Make fetch a builtin\",\n2007-09-10), where wasn't any reason to do this back then either, it's\njust something left over from when git-fetch was initially introduced.\n\nThe initial motivation for this change was to make a subsequent change\nwhich'll also modify the refs variable smaller, since it won't have to\ncopy this redundant \"NULL the last + 1 item\" pattern.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fetch.c | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 7bbcd26faf..b34665db9e 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1302,7 +1302,6 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \t\t\t} else\n \t\t\t\trefs[j++] = argv[i];\n \t\t}\n-\t\trefs[j] = NULL;\n \t\tref_nr = j;\n \t}\n \n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337011","messageId":"20180121000304.32323-11-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v2 10/12] fetch: stop accessing \"remote\" variable indirectly","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T00:03:02Z","receivedAt":"2018-01-21T00:04:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Access the \"remote\" variable passed to the fetch_one() directly rather\nthan through the gtransport wrapper struct constructed in this\nfunction for other purposes.\n\nThis makes the code more readable, as it's now obvious that the remote\nstruct doesn't somehow get munged by the prepare_transport() function\nabove, which takes the \"remote\" struct as an argument and constructs\nthe \"gtransport\" struct, containing among other things the \"remote\"\nstruct.\n\nThis pattern of accessing the container struct was added in\n737c5a9cde (\"fetch: make --prune configurable\", 2013-07-13) when this\ncode was initially introduced.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fetch.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex b34665db9e..a85c2002a9 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1280,8 +1280,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \n \tif (prune < 0) {\n \t\t/* no command line request */\n-\t\tif (0 <= gtransport->remote->prune)\n-\t\t\tprune = gtransport->remote->prune;\n+\t\tif (0 <= remote->prune)\n+\t\t\tprune = remote->prune;\n \t\telse if (0 <= fetch_prune_config)\n \t\t\tprune = fetch_prune_config;\n \t\telse\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337012","messageId":"20180121000304.32323-12-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v2 11/12] fetch tests: add scaffolding for the new fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T00:03:03Z","receivedAt":"2018-01-21T00:04:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The fetch.pruneTags configuration doesn't exist yet, but will be added\nin a subsequent commit. Since testing for it requires adding new\nparameters to the test_configured_prune function it's easier to review\nthis patch first to assert that no functional changes are introduced\nyet.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 90 ++++++++++++++++++++++++++++++--------------------------\n 1 file changed, 49 insertions(+), 41 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 576c2598c9..a1abea7e3f 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -551,18 +551,22 @@ set_config_tristate () {\n test_configured_prune () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n-\texpected_branch=$3\n-\texpected_tag=$4\n-\tcmdline=$5\n+\tfetch_prune_tags=$3\n+\tremote_origin_prune_tags=$4\n+\texpected_branch=$5\n+\texpected_tag=$6\n+\tcmdline=$7\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2 fetch.pruneTags=$3 remote.origin.pruneTags=$4${7:+ $7}; branch:$5 tag:$6\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\tgit tag -f newtag &&\n \t\t(\n \t\t\tcd one &&\n \t\t\ttest_unconfig fetch.prune &&\n+\t\t\ttest_unconfig fetch.pruneTags &&\n \t\t\ttest_unconfig remote.origin.prune &&\n+\t\t\ttest_unconfig remote.origin.pruneTags &&\n \t\t\tgit fetch &&\n \t\t\tgit rev-parse --verify refs/remotes/origin/newbranch &&\n \t\t\tgit rev-parse --verify refs/tags/newtag\n@@ -576,7 +580,9 @@ test_configured_prune () {\n \t\t(\n \t\t\tcd one &&\n \t\t\tset_config_tristate fetch.prune $fetch_prune &&\n+\t\t\tset_config_tristate fetch.pruneTags $fetch_prune_tags &&\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n+\t\t\tset_config_tristate remote.origin.pruneTags $remote_origin_prune_tags &&\n \n \t\t\tgit fetch '\"$cmdline\"' &&\n \t\t\tcase \"$expected_branch\" in\n@@ -601,57 +607,59 @@ test_configured_prune () {\n \n # $1 config: fetch.prune\n # $2 config: remote.<name>.prune\n-# $3 expect: branch to be pruned?\n-# $4 expect: tag to be pruned?\n-# $5 git-fetch $cmdline:\n+# $3 config: fetch.pruneTags\n+# $4 config: remote.<name>.pruneTags\n+# $5 expect: branch to be pruned?\n+# $6 expect: tag to be pruned?\n+# $7 git-fetch $cmdline:\n #\n-#                     $1    $2    $3     $4     $5\n-test_configured_prune unset unset kept   kept   \"\"\n-test_configured_prune unset unset kept   kept   \"--no-prune\"\n-test_configured_prune unset unset pruned kept   \"--prune\"\n-test_configured_prune unset unset kept   pruned \\\n+#                     $1    $2    $3    $4    $5     $6     $7\n+test_configured_prune unset unset unset unset kept   kept   \"\"\n+test_configured_prune unset unset unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset unset unset unset pruned kept   \"--prune\"\n+test_configured_prune unset unset unset unset kept   pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n-test_configured_prune unset unset pruned pruned \\\n+test_configured_prune unset unset unset unset pruned pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n \n-test_configured_prune false unset kept   kept   \"\"\n-test_configured_prune false unset kept   kept   \"--no-prune\"\n-test_configured_prune false unset pruned kept   \"--prune\"\n+test_configured_prune false unset unset unset kept   kept   \"\"\n+test_configured_prune false unset unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false unset unset unset pruned kept   \"--prune\"\n \n-test_configured_prune true  unset pruned kept   \"\"\n-test_configured_prune true  unset pruned kept   \"--prune\"\n-test_configured_prune true  unset kept   kept   \"--no-prune\"\n+test_configured_prune true  unset unset unset pruned kept   \"\"\n+test_configured_prune true  unset unset unset pruned kept   \"--prune\"\n+test_configured_prune true  unset unset unset kept   kept   \"--no-prune\"\n \n-test_configured_prune unset false kept   kept   \"\"\n-test_configured_prune unset false kept   kept   \"--no-prune\"\n-test_configured_prune unset false pruned kept   \"--prune\"\n+test_configured_prune unset false unset unset kept   kept   \"\"\n+test_configured_prune unset false unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset false unset unset pruned kept   \"--prune\"\n \n-test_configured_prune false false kept   kept   \"\"\n-test_configured_prune false false kept   kept   \"--no-prune\"\n-test_configured_prune false false pruned kept   \"--prune\"\n-test_configured_prune false false kept   pruned \\\n+test_configured_prune false false unset unset kept   kept   \"\"\n+test_configured_prune false false unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false false unset unset pruned kept   \"--prune\"\n+test_configured_prune false false unset unset kept   pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n-test_configured_prune false false pruned pruned \\\n+test_configured_prune false false unset unset pruned pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n \n-test_configured_prune true  false kept   kept   \"\"\n-test_configured_prune true  false pruned kept   \"--prune\"\n-test_configured_prune true  false kept   kept   \"--no-prune\"\n+test_configured_prune true  false unset unset kept   kept   \"\"\n+test_configured_prune true  false unset unset pruned kept   \"--prune\"\n+test_configured_prune true  false unset unset kept   kept   \"--no-prune\"\n \n-test_configured_prune unset true  pruned kept   \"\"\n-test_configured_prune unset true  kept   kept   \"--no-prune\"\n-test_configured_prune unset true  pruned kept   \"--prune\"\n+test_configured_prune unset true  unset unset pruned kept   \"\"\n+test_configured_prune unset true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset true  unset unset pruned kept   \"--prune\"\n \n-test_configured_prune false true  pruned kept   \"\"\n-test_configured_prune false true  kept   kept   \"--no-prune\"\n-test_configured_prune false true  pruned kept   \"--prune\"\n+test_configured_prune false true  unset unset pruned kept   \"\"\n+test_configured_prune false true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false true  unset unset pruned kept   \"--prune\"\n \n-test_configured_prune true  true  pruned kept   \"\"\n-test_configured_prune true  true  pruned kept   \"--prune\"\n-test_configured_prune true  true  kept   kept   \"--no-prune\"\n-test_configured_prune true  true  kept   pruned \\\n+test_configured_prune true  true  unset unset pruned kept   \"\"\n+test_configured_prune true  true  unset unset pruned kept   \"--prune\"\n+test_configured_prune true  true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune true  true  unset unset kept   pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n-test_configured_prune true  true  pruned pruned \\\n+test_configured_prune true  true  unset unset pruned pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n \n test_expect_success 'all boundary commits are excluded' '\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337013","messageId":"20180121000304.32323-13-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v2 12/12] fetch: add a --fetch-prune option and fetch.pruneTags config","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T00:03:04Z","receivedAt":"2018-01-21T00:04:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a --fetch-prune option to git-fetch, along with fetch.pruneTags\nconfig option. This allows for doing any of:\n\n    git fetch -p -P\n    git fetch --prune --prune-tags\n    git fetch -p -P origin\n    git fetch --prune --prune-tags origin\n\nOr simply:\n\n    git config fetch.prune true &&\n    git config fetch.pruneTags true &&\n    git fetch\n\nInstead of the much more verbose:\n\n    git fetch --prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\n\nBefore this feature it was painful to support the use-case of pulling\nfrom a repo which is having both its branches *and* tags deleted\nregularly, and wanting our local references to reflect upstream.\n\nAt work we create deployment tags in the repo for each rollout, and\nthere's *lots* of those, so they're archived within weeks for\nperformance reasons.\n\nWithout this change it's hard to centrally configure such repos in\n/etc/gitconfig (on servers that are only used for working with\nthem). You need to set fetch.prune=true globally, and then for each\nrepo:\n\n    git -C {} config --replace-all remote.origin.fetch \"refs/tags/*:refs/tags/*\" \"^refs/tags/*:refs/tags/*$\"\n\nNow I can simply set fetch.pruneTags=true in /etc/gitconfig as well,\nand users running \"git pull\" will automatically get the pruning\nsemantics we want.\n\nSee my \"Re: [BUG] git remote prune removes local tags, depending on\nfetch config\" (87po6ahx87.fsf@evledraar.gmail.com;\nhttps://public-inbox.org/git/87po6ahx87.fsf@evledraar.gmail.com/) for\nmore background info.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt        | 14 ++++++++++++++\n Documentation/fetch-options.txt | 15 ++++++++++++++-\n Documentation/git-fetch.txt     | 24 ++++++++++++++++++++++++\n builtin/fetch.c                 | 32 ++++++++++++++++++++++++++++++++\n remote.c                        |  5 ++++-\n remote.h                        |  3 +++\n t/t5510-fetch.sh                | 30 ++++++++++++++++++++++++++++++\n 7 files changed, 121 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 0f27af5760..ae86455876 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1401,6 +1401,14 @@ fetch.prune::\n \toption was given on the command line.  See also `remote.<name>.prune`\n \tand the PRUNING section of linkgit:git-fetch[1].\n \n+fetch.pruneTags::\n+\tIf true, fetch will automatically behave as if the\n+\t`refs/tags/*:refs/tags/*` refspec was provided when pruning,\n+\tif not set already. This allows for setting both this option\n+\tand `fetch.prune` to maintain a 1=1 mapping to upstrem\n+\trefs. See also `remote.<name>.pruneTags` and the PRUNING\n+\tsection of linkgit:git-fetch[1].\n+\n fetch.output::\n \tControl how ref update status is printed. Valid values are\n \t`full` and `compact`. Default value is `full`. See section\n@@ -2945,6 +2953,12 @@ remote.<name>.prune::\n \tremove any remote-tracking references that no longer exist on the\n \tremote (as if the `--prune` option was given on the command line).\n \tOverrides `fetch.prune` settings, if any.\n+\n+remote.<name>.pruneTags::\n+\tWhen set to true, fetching from this remote by default will also\n+\tremove any local tags that no longer exist on the remote if pruning\n+\tis activated in general via `remote.<name>.prune`, `fetch.prune` or\n+\t`--prune`. Overrides `fetch.pruneTags` settings, if any.\n +\n See also `remote.<name>.prune` and the PRUNING section of\n linkgit:git-fetch[1].\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 9f5c85ad96..dc13bed741 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -73,7 +73,20 @@ ifndef::git-pull[]\n \tare fetched due to an explicit refspec (either on the command\n \tline or in the remote configuration, for example if the remote\n \twas cloned with the --mirror option), then they are also\n-\tsubject to pruning.\n+\tsubject to pruning. Supplying `--prune-tags` is a shorthand for\n+\tproviding the tag refspec.\n++\n+See the PRUNING section below for more details.\n+\n+-P::\n+--prune-tags::\n+\tBefore fetching, remove any local tags that no longer exist on\n+\tthe remote if `--prune` is enabled. This option should be used\n+\tmore carefully, unlike `--prune` it will remove any local\n+\treferences (local tags) that have been created. This option is\n+\tmerely a shorthand for providing the explicit tag refspec\n+\talong with `--prune`, see the discussion about that in its\n+\tdocumentation.\n +\n See the PRUNING section below for more details.\n \ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex 18fac0da2e..5682ed4ae1 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -148,6 +148,30 @@ So be careful when using this with a refspec like\n `refs/tags/*:refs/tags/*`, or any other refspec which might map\n references from multiple remotes to the same local namespace.\n \n+Since keeping up-to-date with both branches and tags on the remote is\n+a common use-case the `--prune-tags` option can be supplied along with\n+`--prune` to prune local tags that don't exist on the remote. Tag\n+pruning can also be enabled with `fetch.pruneTags` or\n+`remote.<name>.pruneTags` in the config. See linkgit:git-config[1].\n+\n+The `--prune-tags` option is equivalent to having\n+`refs/tags/*:refs/tags/*` configured in the refspecs for the\n+remote. This can lead to some seemingly strange interactions:\n+\n+------------------------------------------------\n+# These both fetch tags\n+$ git fetch --no-tags origin 'refs/tags/*:refs/tags/*'\n+$ git fetch --no-tags --prune-tags origin\n+------------------------------------------------\n+\n+The reason it doesn't error out when provided without `--prune` or its\n+config versions is for flexibility of the configured versions, and to\n+maintain a 1=1 mapping between what the command line flags do, and\n+what the configuration versions do. It's reasonable to e.g. configure\n+`fetch.pruneTags=true` in `~/.gitconfig` to have tags pruned whenever\n+`git fetch --prune` is run, without making every invocation of `git\n+fetch` without `--prune` an error.\n+\n OUTPUT\n ------\n \ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex a85c2002a9..046066b6a1 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -38,6 +38,10 @@ static int fetch_prune_config = -1; /* unspecified */\n static int prune = -1; /* unspecified */\n #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */\n \n+static int fetch_prune_tags_config = -1; /* unspecified */\n+static int prune_tags = -1; /* unspecified */\n+#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */\n+\n static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;\n static int progress = -1;\n static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;\n@@ -64,6 +68,11 @@ static int git_fetch_config(const char *k, const char *v, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(k, \"fetch.prunetags\")) {\n+\t\tfetch_prune_tags_config = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(k, \"submodule.recurse\")) {\n \t\tint r = git_config_bool(k, v) ?\n \t\t\tRECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;\n@@ -126,6 +135,8 @@ static struct option builtin_fetch_options[] = {\n \t\t    N_(\"number of submodules fetched in parallel\")),\n \tOPT_BOOL('p', \"prune\", &prune,\n \t\t N_(\"prune remote-tracking branches no longer on remote\")),\n+\tOPT_BOOL('P', \"prune-tags\", &prune_tags,\n+\t\t N_(\"prune local tags not found on remote\")),\n \t{ OPTION_CALLBACK, 0, \"recurse-submodules\", &recurse_submodules, N_(\"on-demand\"),\n \t\t    N_(\"control recursive fetching of submodules\"),\n \t\t    PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },\n@@ -1212,6 +1223,8 @@ static void add_options_to_argv(struct argv_array *argv)\n \t\targv_array_push(argv, \"--dry-run\");\n \tif (prune != -1)\n \t\targv_array_push(argv, prune ? \"--prune\" : \"--no-prune\");\n+\tif (prune_tags != -1)\n+\t\targv_array_push(argv, prune_tags ? \"--prune-tags\" : \"--no-prune-tags\");\n \tif (update_head_ok)\n \t\targv_array_push(argv, \"--update-head-ok\");\n \tif (force)\n@@ -1315,6 +1328,22 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \treturn exit_code;\n }\n \n+static void maybe_add_prune_tags_refspec(struct remote *remote)\n+{\n+\tif (prune_tags < 0) {\n+\t\t/* no command line request */\n+\t\tif (0 <= remote->prune_tags)\n+\t\t\tprune_tags = remote->prune_tags;\n+\t\telse if (0 <= fetch_prune_tags_config)\n+\t\t\tprune_tags = fetch_prune_tags_config;\n+\t\telse\n+\t\t\tprune_tags = PRUNE_TAGS_BY_DEFAULT;\n+\t}\n+\n+\tif (prune_tags)\n+\t\tadd_fetch_refspec(remote, xstrdup(\"refs/tags/*:refs/tags/*\"));\n+}\n+\n int cmd_fetch(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n@@ -1368,6 +1397,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \t} else if (argc == 0) {\n \t\t/* No arguments -- use default remote */\n \t\tremote = remote_get(NULL);\n+\t\tmaybe_add_prune_tags_refspec(remote);\n \t\tresult = fetch_one(remote, argc, argv);\n \t} else if (multiple) {\n \t\t/* All arguments are assumed to be remotes or groups */\n@@ -1386,6 +1416,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \t\t} else {\n \t\t\t/* Zero or one remotes */\n \t\t\tremote = remote_get(argv[0]);\n+\t\t\tif (argc == 1)\n+\t\t\t\tmaybe_add_prune_tags_refspec(remote);\n \t\t\tresult = fetch_one(remote, argc-1, argv+1);\n \t\t}\n \t}\ndiff --git a/remote.c b/remote.c\nindex 4e93753e19..d8716643cb 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -95,7 +95,7 @@ static void add_push_refspec(struct remote *remote, const char *ref)\n \tremote->push_refspec[remote->push_refspec_nr++] = ref;\n }\n \n-static void add_fetch_refspec(struct remote *remote, const char *ref)\n+void add_fetch_refspec(struct remote *remote, const char *ref)\n {\n \tALLOC_GROW(remote->fetch_refspec,\n \t\t   remote->fetch_refspec_nr + 1,\n@@ -173,6 +173,7 @@ static struct remote *make_remote(const char *name, int len)\n \n \tret = xcalloc(1, sizeof(struct remote));\n \tret->prune = -1;  /* unspecified */\n+\tret->prune_tags = -1;  /* unspecified */\n \tALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);\n \tremotes[remotes_nr++] = ret;\n \tret->name = xstrndup(name, len);\n@@ -391,6 +392,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tremote->skip_default_update = git_config_bool(key, value);\n \telse if (!strcmp(subkey, \"prune\"))\n \t\tremote->prune = git_config_bool(key, value);\n+\telse if (!strcmp(subkey, \"prunetags\"))\n+\t\tremote->prune_tags = git_config_bool(key, value);\n \telse if (!strcmp(subkey, \"url\")) {\n \t\tconst char *v;\n \t\tif (git_config_string(&v, key, value))\ndiff --git a/remote.h b/remote.h\nindex 1f6611be21..df6dca24d4 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -47,6 +47,7 @@ struct remote {\n \tint skip_default_update;\n \tint mirror;\n \tint prune;\n+\tint prune_tags;\n \n \tconst char *receivepack;\n \tconst char *uploadpack;\n@@ -133,6 +134,8 @@ struct ref {\n #define REF_HEADS\t(1u << 1)\n #define REF_TAGS\t(1u << 2)\n \n+void add_fetch_refspec(struct remote *remote, const char *ref);\n+\n extern struct ref *find_ref_by_name(const struct ref *list, const char *name);\n \n struct ref *alloc_ref(const char *name);\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex a1abea7e3f..48f49b613a 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -662,6 +662,36 @@ test_configured_prune true  true  unset unset kept   pruned \\\n test_configured_prune true  true  unset unset pruned pruned \\\n \t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n \n+\n+# --prune-tags on its own does nothing, needs --prune as well, same\n+# for for fetch.pruneTags without fetch.prune\n+test_configured_prune unset unset unset unset kept kept     \"--prune-tags\"\n+test_configured_prune unset unset unset unset kept kept     \"origin --prune-tags\"\n+test_configured_prune unset unset true unset  kept kept     \"\"\n+test_configured_prune unset unset unset true  kept kept     \"\"\n+\n+# These will prune the tags\n+test_configured_prune unset unset unset unset pruned pruned \"--prune --prune-tags\"\n+test_configured_prune unset unset unset unset pruned pruned \"origin --prune --prune-tags\"\n+test_configured_prune true  unset true  unset pruned pruned \"\"\n+test_configured_prune unset true  unset true  pruned pruned \"\"\n+\n+# Check that remote.<name>.pruneTags overrides fetch.pruneTags as with\n+# remote.<name>.prune and fetch.prune\n+test_configured_prune false true  false true  pruned pruned \"\"\n+test_configured_prune true  false true  false kept   kept   \"\"\n+\n+# When --prune-tags is supplied it's ignored if an explict refspec is\n+# given, same for the configuration options.\n+test_configured_prune unset unset unset unset pruned kept \\\n+\t\"--prune --prune-tags origin '+refs/heads/*:refs/remotes/origin/*'\"\n+test_configured_prune unset unset true  unset pruned kept \\\n+\t\"--prune --prune-tags origin '+refs/heads/*:refs/remotes/origin/*'\"\n+test_configured_prune unset unset unset true pruned  kept \\\n+\t\"--prune --prune-tags origin '+refs/heads/*:refs/remotes/origin/*'\"\n+\n+test_configured_prune true unset true unset pruned pruned   \"\"\n+\n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n \ttest_commit oneside &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337023","messageId":"874lnfipo3.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-10-avarab@gmail.com","subject":"Re: [PATCH v2 09/12] fetch: don't redundantly NULL something calloc() gave us","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-21T08:25:48Z","receivedAt":"2018-01-21T08:36:07Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Jan 21 2018, Ævar Arnfjörð Bjarmason jotted:\n\n> 2007-09-10), where wasn't any reason to do this back then either, it's\n\ns/where wasn't/where there wasn't/\n\n> The initial motivation for this change was to make a subsequent change\n> which'll also modify the refs variable smaller, since it won't have to\n> copy this redundant \"NULL the last + 1 item\" pattern.\n\nWhich, FWIW is not happening later in this series anymore, so this is\njust a \"cleanup while I'm at it\" patch at this point. Will note that in\na v3 commit message pending more comments.\n"},{"id":"337102","messageId":"xmqqbmhln01n.fsf@gitster.mtv.corp.google.com","threadId":"47614","inReplyTo":"20180121000304.32323-5-avarab@gmail.com","subject":"Re: [PATCH v2 04/12] fetch tests: double quote a variable for interpolation","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-22T19:52:36Z","receivedAt":"2018-01-22T19:52:44Z","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> If the $cmdline variable contains multiple arguments they won't be\n> interpolated correctly since the body of the test is single quoted. I\n> don't know what part of test-lib.sh is expanding variables within\n> single-quoted strings,...\n\n    dothis='echo whatever $IFS separated strings'\n    test_expect_success label '\n            $dothis\n    '\n\nworks because test_expect_success ends up beint a glorified 'eval'\nand it sees the value of $dothis.  \n\n> but interpolating this inline is the desired\n> behavior here.\n\nI am not sure what you meant by this, though.\n\n> -\t\t\tgit fetch $cmdline &&\n> +\t\t\tgit fetch '\"$cmdline\"' &&\n\nWould this work with cmdline that needs to be quoted for the\nresulting shell script to be syntactically correct (e.g. cmdline\nwith a single dq in it)?  By stepping out of sq pair, you are\nallowing/asking the shell that forms test_expect_success command\nline arguments to interpolate cmdline, instead of asking the shell\nthat evals test_expect_success with its command line argument\nstrings.\n\nIn other words, I suspect that the caller of test_configured_prune\nnow must sq_quote the cmdline arguments it passes to this helper,\ni.e.\n\n\tcmdline=\"$(git rev-parse --sq-quote arg1 'arg\"2' arg3)\"\n\ttest_configured_prune ... \"$cmdline\" ...\n\nfor this patch to be correct.\n"},{"id":"337103","messageId":"xmqq7es9mzna.fsf@gitster.mtv.corp.google.com","threadId":"47614","inReplyTo":"20180121000304.32323-8-avarab@gmail.com","subject":"Re: [PATCH v2 07/12] git remote doc: correct dangerous lies about what prune does","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-22T20:01:13Z","receivedAt":"2018-01-22T20:01: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> +Deletes stale references associated with <name>. By default stale\n> +remote-tracking branches under <name>, but depending on global\n> +configuration and the configuration of the remote we might even prune\n> +local tags....\n\nAn optional clarification\n\n    s/prune local tags/& that haven't been pushed there/\n\n> +... Equivalent to `git fetch <name> --prune`, except that no\n> +new references will be fetched.\n\n`git fetch --prune <name>` (dashed options first and then non\noptions), just like you wrote in the previous step.\n"},{"id":"337105","messageId":"xmqq372xmxcu.fsf@gitster.mtv.corp.google.com","threadId":"47614","inReplyTo":"20180121000304.32323-13-avarab@gmail.com","subject":"Re: [PATCH v2 12/12] fetch: add a --fetch-prune option and fetch.pruneTags config","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-22T20:50:41Z","receivedAt":"2018-01-22T20:50:49Z","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> Add a --fetch-prune option to git-fetch, along with fetch.pruneTags\n> config option. This allows for doing any of:\n>\n>     git fetch -p -P\n>     git fetch --prune --prune-tags\n>     git fetch -p -P origin\n>     git fetch --prune --prune-tags origin\n>\n> Or simply:\n>\n>     git config fetch.prune true &&\n>     git config fetch.pruneTags true &&\n>     git fetch\n>\n> Instead of the much more verbose:\n>\n>     git fetch --prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\n>\n> Before this feature it was painful to support the use-case of pulling\n> from a repo which is having both its branches *and* tags deleted\n> regularly, and wanting our local references to reflect upstream.\n>\n> At work we create deployment tags in the repo for each rollout, and\n> there's *lots* of those, so they're archived within weeks for\n> performance reasons.\n>\n> Without this change it's hard to centrally configure such repos in\n> /etc/gitconfig (on servers that are only used for working with\n> them). You need to set fetch.prune=true globally, and then for each\n> repo:\n>\n>     git -C {} config --replace-all remote.origin.fetch \"refs/tags/*:refs/tags/*\" \"^refs/tags/*:refs/tags/*$\"\n\nI think the last one is supposed to be a regular expression on\nexisting values.  Shouldn't the asterisks be quoted?  \n\nOtherwise, it would appears as if \"refs/tags:refs/tags///\" are\nreplaced with \"refs/tags/*:refs/tags/*\", but it certainly is not\nwhat you are doing.  I also wonder why the existing one does not\nexpect a leading '+', which I think is what we place by default\nwhen you clone.\n\n> +-P::\n> +--prune-tags::\n> +\t.... This option is\n> +\tmerely a shorthand for providing the explicit tag refspec\n> +\talong with `--prune`, see the discussion about that in its\n> +\tdocumentation.\n\nSo would \"git fetch -P origin\" be like \"git fetch --prune --tags\norigin\"?\n\n>  +\n>  See the PRUNING section below for more details.\n>  \n> diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\n> index 18fac0da2e..5682ed4ae1 100644\n> --- a/Documentation/git-fetch.txt\n> +++ b/Documentation/git-fetch.txt\n> @@ -148,6 +148,30 @@ So be careful when using this with a refspec like\n>  `refs/tags/*:refs/tags/*`, or any other refspec which might map\n>  references from multiple remotes to the same local namespace.\n>  \n> +Since keeping up-to-date with both branches and tags on the remote is\n> +a common use-case the `--prune-tags` option can be supplied along with\n> +`--prune` to prune local tags that don't exist on the remote. Tag\n> +pruning can also be enabled with `fetch.pruneTags` or\n> +`remote.<name>.pruneTags` in the config. See linkgit:git-config[1].\n> +\n> +The `--prune-tags` option is equivalent to having\n> +`refs/tags/*:refs/tags/*` configured in the refspecs for the\n> +remote. This can lead to some seemingly strange interactions:\n> +\n> +------------------------------------------------\n> +# These both fetch tags\n> +$ git fetch --no-tags origin 'refs/tags/*:refs/tags/*'\n> +$ git fetch --no-tags --prune-tags origin\n> +------------------------------------------------\n\nThis description is too confusing.  First you say \"having\n... configured in the refspecs for the remote\", but configured\nrefspecs for the remote (I presume that you are missing 'fetch' from\nthat description and are talking about the \"remote.$name.fetch\"\nconfiguration variable) are overridden when you give explicit\nrefspecs from the command line, so the above two are not even\nequivalent.  The first one gives a refspec explicitly from the\ncommand line, so other configured refspecs like\n\n    [remote \"origin\"] fetch = +refs/heads/*:refs/remotes/origin/*\n\nshould be ignored, while the second one, if --prune-tags tells Git\nto behave as if \n\n    [remote \"origin\"] fetch = refs/tags/*:refs/tags/*\n\nalso exists in the config, would not cause other ones for the same\nremote from getting ignored.  So...\n"},{"id":"337114","messageId":"87wp09h4w3.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"xmqqbmhln01n.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 04/12] fetch tests: double quote a variable for interpolation","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-22T23:04:28Z","receivedAt":"2018-01-22T23:04:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Jan 22 2018, Junio C. Hamano jotted:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> If the $cmdline variable contains multiple arguments they won't be\n>> interpolated correctly since the body of the test is single quoted. I\n>> don't know what part of test-lib.sh is expanding variables within\n>> single-quoted strings,...\n>\n>     dothis='echo whatever $IFS separated strings'\n>     test_expect_success label '\n>             $dothis\n>     '\n>\n> works because test_expect_success ends up beint a glorified 'eval'\n> and it sees the value of $dothis.\n>\n>> but interpolating this inline is the desired\n>> behavior here.\n>\n> I am not sure what you meant by this, though.\n\nLooking into this again:\n\n    myvar=\"a b 'c' \\\"d\\\" \\\"\\\\\\\"e\\\\\\\"\\\"\"\n    test_expect_success 'blah' '\n    \t/usr/bin/perl -MData::Dumper -wE say\\ Dumper\\ \\\\@ARGV -- $myvar &&\n    \t/usr/bin/perl -MData::Dumper -wE say\\ Dumper\\ \\\\@ARGV -- '\"$myvar\"' &&\n    \techo $myvar &&\n    \tfalse\n    '\n\nProduces:\n\n    Initialized empty Git repository in /home/avar/g/git/t/trash directory.t5510-fetch/.git/\n    expecting success:\n    \t/usr/bin/perl -MData::Dumper -wE say\\ Dumper\\ \\\\@ARGV -- $myvar &&\n    \t/usr/bin/perl -MData::Dumper -wE say\\ Dumper\\ \\\\@ARGV -- a b 'c' \"d\" \"\\\"e\\\"\" &&\n    \techo $myvar &&\n    \tfalse\n\n    + /usr/bin/perl -MData::Dumper -wE say Dumper \\@ARGV -- a b 'c' \"d\" \"\\\"e\\\"\"\n    $VAR1 = [\n              'a',\n              'b',\n              '\\'c\\'',\n              '\"d\"',\n              '\"\\\\\"e\\\\\"\"'\n            ];\n\n    + /usr/bin/perl -MData::Dumper -wE say Dumper \\@ARGV -- a b c d \"e\"\n    $VAR1 = [\n              'a',\n              'b',\n              'c',\n              'd',\n              '\"e\"'\n            ];\n\n    + echo a b 'c' \"d\" \"\\\"e\\\"\"\n    a b 'c' \"d\" \"\\\"e\\\"\"\n    + false\n    error: last command exited with $?=1\n    not ok 1 - blah\n    #\n    #\t\t/usr/bin/perl -MData::Dumper -wE say\\ Dumper\\ \\\\@ARGV -- $myvar &&\n    #\t\t/usr/bin/perl -MData::Dumper -wE say\\ Dumper\\ \\\\@ARGV -- a b 'c' \"d\" \"\\\"e\\\"\" &&\n    #\t\techo $myvar &&\n    #\t\tfalse\n    #\n\nI.e. the desired effect is to get variables like the refspec like/this\nnot 'like/this'. I could also just apply this on top, which gives the\nsame end result, but now I wonder if starting some args with\ne.g. unescaped + and with : and * in the string is portable:\n\n    diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\n    index 48f49b613a..2d311059e9 100755\n    --- a/t/t5510-fetch.sh\n    +++ b/t/t5510-fetch.sh\n    @@ -587 +587 @@ test_configured_prune () {\n    -\t\t\tgit fetch '\"$cmdline\"' &&\n    +\t\t\tgit fetch $cmdline &&\n    @@ -621 +621 @@ test_configured_prune unset unset unset unset kept   pruned \\\n    -\t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n    +\t\"--prune origin refs/tags/*:refs/tags/*\"\n    @@ -623 +623 @@ test_configured_prune unset unset unset unset pruned pruned \\\n    -\t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n    +\t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n    @@ -641 +641 @@ test_configured_prune false false unset unset kept   pruned \\\n    -\t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n    +\t\"--prune origin refs/tags/*:refs/tags/*\"\n    @@ -643 +643 @@ test_configured_prune false false unset unset pruned pruned \\\n    -\t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n    +\t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n    @@ -661 +661 @@ test_configured_prune true  true  unset unset kept   pruned \\\n    -\t\"--prune origin 'refs/tags/*:refs/tags/*'\"\n    +\t\"--prune origin refs/tags/*:refs/tags/*\"\n    @@ -663 +663 @@ test_configured_prune true  true  unset unset pruned pruned \\\n    -\t\"--prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\"\n    +\t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n    @@ -684 +684 @@ test_configured_prune true  false true  false kept   kept   \"\"\n    -# When --prune-tags is supplied it's ignored if an explict refspec is\n    +# When --prune-tags is supplied its ignored if an explict refspec is\n    @@ -687 +687 @@ test_configured_prune unset unset unset unset pruned kept \\\n    -\t\"--prune --prune-tags origin '+refs/heads/*:refs/remotes/origin/*'\"\n    +\t\"--prune --prune-tags origin +refs/heads/*:refs/remotes/origin/*\"\n    @@ -689 +689 @@ test_configured_prune unset unset true  unset pruned kept \\\n    -\t\"--prune --prune-tags origin '+refs/heads/*:refs/remotes/origin/*'\"\n    +\t\"--prune --prune-tags origin +refs/heads/*:refs/remotes/origin/*\"\n    @@ -691 +691 @@ test_configured_prune unset unset unset true pruned  kept \\\n    -\t\"--prune --prune-tags origin '+refs/heads/*:refs/remotes/origin/*'\"\n    +\t\"--prune --prune-tags origin +refs/heads/*:refs/remotes/origin/*\"\n\n\n>> -\t\t\tgit fetch $cmdline &&\n>> +\t\t\tgit fetch '\"$cmdline\"' &&\n>\n> Would this work with cmdline that needs to be quoted for the\n> resulting shell script to be syntactically correct (e.g. cmdline\n> with a single dq in it)?  By stepping out of sq pair, you are\n> allowing/asking the shell that forms test_expect_success command\n> line arguments to interpolate cmdline, instead of asking the shell\n> that evals test_expect_success with its command line argument\n> strings.\n>\n> In other words, I suspect that the caller of test_configured_prune\n> now must sq_quote the cmdline arguments it passes to this helper,\n> i.e.\n>\n> \tcmdline=\"$(git rev-parse --sq-quote arg1 'arg\"2' arg3)\"\n> \ttest_configured_prune ... \"$cmdline\" ...\n>\n> for this patch to be correct.\n\nSorry at this point I'm confused about this whole thing. This doesn't\nwork and overquotes:\n\n    @@ -559,2 +569,3 @@ test_configured_prune () {\n\n    +\tcmdline_q=\"$(git rev-parse --sq-quote $cmdline)\"\n     \ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2 fetch.pruneTags=$3 remote.origin.pruneTags=$4${7:+ $7}; branch:$5 tag:$6\" '\n    @@ -586,3 +597,4 @@ test_configured_prune () {\n\n    -\t\t\tgit fetch '\"$cmdline\"' &&\n    +\t\t\t/usr/bin/perl -MData::Dumper -wE say\\ Dumper\\ \\\\@ARGV -- '\"$cmdline_q\"' &&\n    +\t\t\tgit fetch '\"$cmdline_q\"' &&\n     \t\t\tcase \"$expected_branch\" in\n\nSo does just using $cmdline_q without making the calling shell\ninterpolate it. Is there an idiom I should be following that I'm missing\nhere?\n"},{"id":"337115","messageId":"87vafth4uo.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"xmqq7es9mzna.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 07/12] git remote doc: correct dangerous lies about what prune does","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-22T23:05:19Z","receivedAt":"2018-01-22T23:05:26Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Jan 22 2018, Junio C. Hamano jotted:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> +Deletes stale references associated with <name>. By default stale\n>> +remote-tracking branches under <name>, but depending on global\n>> +configuration and the configuration of the remote we might even prune\n>> +local tags....\n>\n> An optional clarification\n>\n>     s/prune local tags/& that haven't been pushed there/\n>\n>> +... Equivalent to `git fetch <name> --prune`, except that no\n>> +new references will be fetched.\n>\n> `git fetch --prune <name>` (dashed options first and then non\n> options), just like you wrote in the previous step.\n\nThanks. Will fix both.\n"},{"id":"337124","messageId":"87tvvdh2vh.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"xmqq372xmxcu.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 12/12] fetch: add a --fetch-prune option and fetch.pruneTags config","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-22T23:48:02Z","receivedAt":"2018-01-22T23:48:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Jan 22 2018, Junio C. Hamano jotted:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Add a --fetch-prune option to git-fetch, along with fetch.pruneTags\n>> config option. This allows for doing any of:\n>>\n>>     git fetch -p -P\n>>     git fetch --prune --prune-tags\n>>     git fetch -p -P origin\n>>     git fetch --prune --prune-tags origin\n>>\n>> Or simply:\n>>\n>>     git config fetch.prune true &&\n>>     git config fetch.pruneTags true &&\n>>     git fetch\n>>\n>> Instead of the much more verbose:\n>>\n>>     git fetch --prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\n>>\n>> Before this feature it was painful to support the use-case of pulling\n>> from a repo which is having both its branches *and* tags deleted\n>> regularly, and wanting our local references to reflect upstream.\n>>\n>> At work we create deployment tags in the repo for each rollout, and\n>> there's *lots* of those, so they're archived within weeks for\n>> performance reasons.\n>>\n>> Without this change it's hard to centrally configure such repos in\n>> /etc/gitconfig (on servers that are only used for working with\n>> them). You need to set fetch.prune=true globally, and then for each\n>> repo:\n>>\n>>     git -C {} config --replace-all remote.origin.fetch \"refs/tags/*:refs/tags/*\" \"^refs/tags/*:refs/tags/*$\"\n>\n> I think the last one is supposed to be a regular expression on\n> existing values.  Shouldn't the asterisks be quoted?\n>\n> Otherwise, it would appears as if \"refs/tags:refs/tags///\" are\n> replaced with \"refs/tags/*:refs/tags/*\", but it certainly is not\n> what you are doing.\n\nYes, well spotted. I copied this from an escaped version and forgot to\nupdate that escaping. Will fix.\n\n> I also wonder why the existing one does not expect a leading '+',\n> which I think is what we place by default when you clone.\n\nI didn't raise this because I didn't want to get side-tracked with yet\nanother thing, but it appears to me that a + prefix in tags refspecs\ndoes absolutely nothing. Consider on a fresh git.git clobbering the new\nv2.16.1 tag locally:\n\n    $ git tag -a -m\"fake\" -f v2.16.1 v2.16.0\n    Updated tag 'v2.16.1' (was eb5fcb24f)\n\nThis should clobber it, and does:\n\n    $ git fetch --prune --dry-run origin '+refs/tags/*:refs/tags/*'\n    From github.com:git/git\n     t [tag update]          v2.16.1    -> v2.16.1\n\nBut so will this without +, which seems like a bug to me:\n\n    $ git fetch --prune --dry-run origin 'refs/tags/*:refs/tags/*'\n    From github.com:git/git\n     t [tag update]          v2.16.1    -> v2.16.1\n\nBut maybe I'm missing something.\n\n\n>> +-P::\n>> +--prune-tags::\n>> +\t.... This option is\n>> +\tmerely a shorthand for providing the explicit tag refspec\n>> +\talong with `--prune`, see the discussion about that in its\n>> +\tdocumentation.\n>\n> So would \"git fetch -P origin\" be like \"git fetch --prune --tags\n> origin\"?\n\nNo, as I understand it --tags just has the effect of considering remote\ntags that aren't tags of the branches you're fetching, so e.g. on a\nfresh git.git:\n\n    $ git tag -a -m\"fake\" -f v2.16.2 v2.16.0\n    $ git fetch --prune --tags --dry-run\n    $\n\nI.e. it does nothing. Whereas this will prune the new fake tag:\n\n    $ git fetch --prune origin 'refs/tags/*:refs/tags/*'\n    From github.com:git/git\n     - [deleted]             (none)     -> v2.16.2\n\nAnd so does the --prune-tags option:\n\n    $ ~/g/git/git-fetch --prune --prune-tags origin\n    From github.com:git/git\n     - [deleted]             (none)     -> v2.16.2\n\n\n>>  +\n>>  See the PRUNING section below for more details.\n>>\n>> diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\n>> index 18fac0da2e..5682ed4ae1 100644\n>> --- a/Documentation/git-fetch.txt\n>> +++ b/Documentation/git-fetch.txt\n>> @@ -148,6 +148,30 @@ So be careful when using this with a refspec like\n>>  `refs/tags/*:refs/tags/*`, or any other refspec which might map\n>>  references from multiple remotes to the same local namespace.\n>>\n>> +Since keeping up-to-date with both branches and tags on the remote is\n>> +a common use-case the `--prune-tags` option can be supplied along with\n>> +`--prune` to prune local tags that don't exist on the remote. Tag\n>> +pruning can also be enabled with `fetch.pruneTags` or\n>> +`remote.<name>.pruneTags` in the config. See linkgit:git-config[1].\n>> +\n>> +The `--prune-tags` option is equivalent to having\n>> +`refs/tags/*:refs/tags/*` configured in the refspecs for the\n>> +remote. This can lead to some seemingly strange interactions:\n>> +\n>> +------------------------------------------------\n>> +# These both fetch tags\n>> +$ git fetch --no-tags origin 'refs/tags/*:refs/tags/*'\n>> +$ git fetch --no-tags --prune-tags origin\n>> +------------------------------------------------\n>\n> This description is too confusing.  First you say \"having\n> ... configured in the refspecs for the remote\", but configured\n> refspecs for the remote (I presume that you are missing 'fetch' from\n> that description and are talking about the \"remote.$name.fetch\"\n> configuration variable) are overridden when you give explicit\n> refspecs from the command line, so the above two are not even\n> equivalent.  The first one gives a refspec explicitly from the\n> command line, so other configured refspecs like\n>\n>     [remote \"origin\"] fetch = +refs/heads/*:refs/remotes/origin/*\n>\n> should be ignored, while the second one, if --prune-tags tells Git\n> to behave as if\n>\n>     [remote \"origin\"] fetch = refs/tags/*:refs/tags/*\n>\n> also exists in the config, would not cause other ones for the same\n> remote from getting ignored.  So...\n\nIndeed, I'll reword this. FWIW I meant something closer to \"declared in\nthe refspecs of the remote\" not \"configured ... remote\".\n"},{"id":"337186","messageId":"20180123221326.28495-1-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v3 00/11] document & test fetch pruning & add fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-23T22:13:15Z","receivedAt":"2018-01-23T22:13:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Addresses Junio's comments on v2 + more fixes. The \n\nÆvar Arnfjörð Bjarmason (11):\n  fetch: don't redundantly NULL something calloc() gave us\n  fetch: stop accessing \"remote\" variable indirectly\n\nMoved these patches to the beginning. No changes except a note to the\ncommit message saying how these trivial changes relate (or not) to\nsubsequent changes.\n\n  fetch tests: refactor in preparation for testing tag pruning\n\nNo changes.\n\n  fetch tests: re-arrange arguments for future readability\n\ns/arrange/re-arrange/ in commit message subject.\n\n  fetch tests: add a tag to be deleted to the pruning tests\n\nNo changes.\n\n  fetch tests: test --prune and refspec interaction\n\nI'm now just skipping quoting things like +refs/... on the\ncommand-line, which as grepping the rest of the test suite shows is\nfine, this eliminated the need for \"fetch tests: double quote a\nvariable for interpolation\" so I've ejected it.\n\n  git fetch doc: add a new section to explain the ins & outs of pruning\n\nNo changes.\n\n  git remote doc: correct dangerous lies about what prune does\n\nRewording of doc per Junio's suggestion.\n\n\n  git-fetch & config doc: link to the new PRUNING section\n\nNo changes.\n\n  fetch tests: add scaffolding for the new fetch.pruneTags\n\nNo changes except continuing remove refspec quoting changes noted\nabove & using +refs/tags/... instead of refs/tags/... for consistency\nwith the last patch...\n\n  fetch: add a --fetch-prune option and fetch.pruneTags config\n\nWe now consistently use a + prefixed refspec for tag pruning, even\nthough it yields the same result. See the discussion in\n87tvvdh2vh.fsf@evledraar.gmail.com, I think it's less confusing.\n\nFix regex in --replace-all command in the commit message.\n\nRewording to address Junio's comments.\n\nThe short help for --prune-tags is now:\n\n    -           N_(\"prune local tags not found on remote\")),\n    +           N_(\"prune local tags no longer on remote and clobber changed tags\")),\n\nAdd --prune-tags to the bash completion.\n\n Documentation/config.txt               |  20 ++++-\n Documentation/fetch-options.txt        |  18 +++-\n Documentation/git-fetch.txt            |  76 +++++++++++++++++\n Documentation/git-remote.txt           |  14 +--\n builtin/fetch.c                        |  37 +++++++-\n contrib/completion/git-completion.bash |   2 +-\n remote.c                               |   5 +-\n remote.h                               |   3 +\n t/t5510-fetch.sh                       | 152 ++++++++++++++++++++++++---------\n 9 files changed, 275 insertions(+), 52 deletions(-)\n\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337187","messageId":"20180123221326.28495-2-avarab@gmail.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"[PATCH v3 01/11] fetch: don't redundantly NULL something calloc() gave us","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-23T22:13:16Z","receivedAt":"2018-01-23T22:13:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Stop redundantly NULL-ing the last element of the refs structure,\nwhich was retrieved via calloc(), and is thus guaranteed to be\npre-NULL'd.\n\nThis code dates back to b888d61c83 (\"Make fetch a builtin\",\n2007-09-10), where wasn't any reason to do this back then either, it's\njust something left over from when git-fetch was initially introduced.\n\nThe initial motivation for this change was to make a subsequent change\nwhich would also modify the refs variable smaller, since it won't have\nto copy this redundant \"NULL the last + 1 item\" pattern.\n\nAs it turns out that change was bad, and better done differently, but\nas this pattern is still pointless let's fix it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fetch.c | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 7bbcd26faf..b34665db9e 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1302,7 +1302,6 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \t\t\t} else\n \t\t\t\trefs[j++] = argv[i];\n \t\t}\n-\t\trefs[j] = NULL;\n \t\tref_nr = j;\n \t}\n \n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337188","messageId":"20180123221326.28495-4-avarab@gmail.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"[PATCH v3 03/11] fetch tests: refactor in preparation for testing tag pruning","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-23T22:13:18Z","receivedAt":"2018-01-23T22:14:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In a subsequent commit this function will learn to test for tag\npruning, prepare for that by making space for more variables, and\nmaking it clear that \"expected\" here refers to branches.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 9 ++++++---\n 1 file changed, 6 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 668c54be41..11da97f9b7 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -549,9 +549,12 @@ set_config_tristate () {\n }\n \n test_configured_prune () {\n-\tfetch_prune=$1 remote_origin_prune=$2 cmdline=$3 expected=$4\n+\tfetch_prune=$1\n+\tremote_origin_prune=$2\n+\tcmdline=$3\n+\texpected_branch=$4\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; $4\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; branch:$4\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\t(\n@@ -572,7 +575,7 @@ test_configured_prune () {\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n \n \t\t\tgit fetch $cmdline &&\n-\t\t\tcase \"$expected\" in\n+\t\t\tcase \"$expected_branch\" in\n \t\t\tpruned)\n \t\t\t\ttest_must_fail git rev-parse --verify refs/remotes/origin/newbranch\n \t\t\t\t;;\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337189","messageId":"20180123221326.28495-3-avarab@gmail.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"[PATCH v3 02/11] fetch: stop accessing \"remote\" variable indirectly","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-23T22:13:17Z","receivedAt":"2018-01-23T22:14:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Access the \"remote\" variable passed to the fetch_one() directly rather\nthan through the gtransport wrapper struct constructed in this\nfunction for other purposes.\n\nThis makes the code more readable, as it's now obvious that the remote\nstruct doesn't somehow get munged by the prepare_transport() function\nabove, which takes the \"remote\" struct as an argument and constructs\nthe \"gtransport\" struct, containing among other things the \"remote\"\nstruct.\n\nA subsequent change will copy this pattern to access a new\nremote->prune-tags field, but without hte use of the gtransport\nvariable. It's useful once that change lands to see that the two\npieces of code behave exactly the same.\n\nThis pattern of accessing the container struct was added in\n737c5a9cde (\"fetch: make --prune configurable\", 2013-07-13) when this\ncode was initially introduced.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fetch.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex b34665db9e..a85c2002a9 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1280,8 +1280,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \n \tif (prune < 0) {\n \t\t/* no command line request */\n-\t\tif (0 <= gtransport->remote->prune)\n-\t\t\tprune = gtransport->remote->prune;\n+\t\tif (0 <= remote->prune)\n+\t\t\tprune = remote->prune;\n \t\telse if (0 <= fetch_prune_config)\n \t\t\tprune = fetch_prune_config;\n \t\telse\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337190","messageId":"20180123221326.28495-7-avarab@gmail.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"[PATCH v3 06/11] fetch tests: test --prune and refspec interaction","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-23T22:13:21Z","receivedAt":"2018-01-23T22:14:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for the interaction between explicitly provided refspecs\nand fetch.prune.\n\nThere's no point in adding this boilerplate to every combination of\nunset/false/true, it's instructive and sufficient to show that no\nmatter if the variable is unset, false or true the refspec on the\ncommand-line overrides any configuration variable.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 12 ++++++++++++\n 1 file changed, 12 insertions(+)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex fad65bd885..e5e88ee474 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -609,6 +609,10 @@ test_configured_prune () {\n test_configured_prune unset unset kept   kept   \"\"\n test_configured_prune unset unset kept   kept   \"--no-prune\"\n test_configured_prune unset unset pruned kept   \"--prune\"\n+test_configured_prune unset unset kept   pruned \\\n+\t\"--prune origin +refs/tags/*:refs/tags/*\"\n+test_configured_prune unset unset pruned pruned \\\n+\t\"--prune origin +refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n test_configured_prune false unset kept   kept   \"\"\n test_configured_prune false unset kept   kept   \"--no-prune\"\n@@ -625,6 +629,10 @@ test_configured_prune unset false pruned kept   \"--prune\"\n test_configured_prune false false kept   kept   \"\"\n test_configured_prune false false kept   kept   \"--no-prune\"\n test_configured_prune false false pruned kept   \"--prune\"\n+test_configured_prune false false kept   pruned \\\n+\t\"--prune origin +refs/tags/*:refs/tags/*\"\n+test_configured_prune false false pruned pruned \\\n+\t\"--prune origin +refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n test_configured_prune true  false kept   kept   \"\"\n test_configured_prune true  false pruned kept   \"--prune\"\n@@ -641,6 +649,10 @@ test_configured_prune false true  pruned kept   \"--prune\"\n test_configured_prune true  true  pruned kept   \"\"\n test_configured_prune true  true  pruned kept   \"--prune\"\n test_configured_prune true  true  kept   kept   \"--no-prune\"\n+test_configured_prune true  true  kept   pruned \\\n+\t\"--prune origin +refs/tags/*:refs/tags/*\"\n+test_configured_prune true  true  pruned pruned \\\n+\t\"--prune origin +refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337191","messageId":"20180123221326.28495-8-avarab@gmail.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"[PATCH v3 07/11] git fetch doc: add a new section to explain the ins & outs of pruning","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-23T22:13:22Z","receivedAt":"2018-01-23T22:14:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a new section to canonically explain how remote reference pruning\nworks, and how users should be careful about using it in conjunction\nwith tag refspecs in particular.\n\nA subsequent commit will update the git-remote documentation to refer\nto this section, and details the motivation for writing this in the\nfirst place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-fetch.txt | 49 +++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 49 insertions(+)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex b153aefa68..18fac0da2e 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -99,6 +99,55 @@ The latter use of the `remote.<repository>.fetch` values can be\n overridden by giving the `--refmap=<refspec>` parameter(s) on the\n command line.\n \n+PRUNING\n+-------\n+\n+Git has a default disposition of keeping data unless it's explicitly\n+thrown away; this extends to holding onto local references to branches\n+on remotes that have themselves deleted those branches.\n+\n+If left to accumulate, these stale references might make performance\n+worse on big and busy repos that have a lot of branch churn, and\n+e.g. make the output of commands like `git branch -a --contains\n+<commit>` needlessly verbose, as well as impacting anything else\n+that'll work with the complete set of known references.\n+\n+These remote tracking references can be deleted as a one-off with\n+either of:\n+\n+------------------------------------------------\n+# While fetching\n+$ git fetch --prune <name>\n+\n+# Only prune, don't fetch\n+$ git remote prune <name>\n+------------------------------------------------\n+\n+To prune references as part of your normal workflow without needing to\n+remember to run that set `fetch.prune` globally, or\n+`remote.<name>.prune` per-remote in the config. See\n+linkgit:git-config[1].\n+\n+Here's where things get tricky and more specific. The pruning feature\n+doesn't actually care about branches, instead it'll prune local <->\n+remote references as a function of the refspec of the remote (see\n+`<refspec>` and <<CRTB,CONFIGURED REMOTE-TRACKING BRANCHES>> above).\n+\n+Therefore if the refspec for the remote includes\n+e.g. `refs/tags/*:refs/tags/*`, or you manually run e.g. `git fetch\n+--prune <name> \"refs/tags/*:refs/tags/*\"` it won't be stale remote\n+tracking branches that are deleted, but any local tag that doesn't\n+exist on the remote.\n+\n+This might not be what you expect, i.e. you want to prune remote\n+`<name>`, but also explicitly fetch tags from it, so when you fetch\n+from it you delete all your local tags, most of which may not have\n+come from the `<name>` remote in the first place.\n+\n+So be careful when using this with a refspec like\n+`refs/tags/*:refs/tags/*`, or any other refspec which might map\n+references from multiple remotes to the same local namespace.\n+\n OUTPUT\n ------\n \n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337192","messageId":"20180123221326.28495-10-avarab@gmail.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"[PATCH v3 09/11] git-fetch & config doc: link to the new PRUNING section","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-23T22:13:24Z","receivedAt":"2018-01-23T22:14:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend the documentation for fetch.prune, fetch.<name>.prune and\n--prune to link to the recently added PRUNING section.\n\nI'd have liked to link directly to it with \"<<PRUNING>>\" from\nfetch-options.txt, since it's included in git-fetch.txt (git-pull.txt\nalso includes it, but doesn't include that option). However making a\nreference across files yields this error:\n\n    [...]/Documentation/git-fetch.xml:226: element xref: validity\n    error : IDREF attribute linkend references an unknown ID \"PRUNING\"\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt        | 6 +++++-\n Documentation/fetch-options.txt | 3 +++\n 2 files changed, 8 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 0e25b2c92b..0f27af5760 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1398,7 +1398,8 @@ fetch.unpackLimit::\n \n fetch.prune::\n \tIf true, fetch will automatically behave as if the `--prune`\n-\toption was given on the command line.  See also `remote.<name>.prune`.\n+\toption was given on the command line.  See also `remote.<name>.prune`\n+\tand the PRUNING section of linkgit:git-fetch[1].\n \n fetch.output::\n \tControl how ref update status is printed. Valid values are\n@@ -2944,6 +2945,9 @@ remote.<name>.prune::\n \tremove any remote-tracking references that no longer exist on the\n \tremote (as if the `--prune` option was given on the command line).\n \tOverrides `fetch.prune` settings, if any.\n++\n+See also `remote.<name>.prune` and the PRUNING section of\n+linkgit:git-fetch[1].\n \n remotes.<group>::\n \tThe list of remotes which are fetched by \"git remote update\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex fb6bebbc61..9f5c85ad96 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -74,6 +74,9 @@ ifndef::git-pull[]\n \tline or in the remote configuration, for example if the remote\n \twas cloned with the --mirror option), then they are also\n \tsubject to pruning.\n++\n+See the PRUNING section below for more details.\n+\n endif::git-pull[]\n \n ifndef::git-pull[]\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337193","messageId":"20180123221326.28495-11-avarab@gmail.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"[PATCH v3 10/11] fetch tests: add scaffolding for the new fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-23T22:13:25Z","receivedAt":"2018-01-23T22:14:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The fetch.pruneTags configuration doesn't exist yet, but will be added\nin a subsequent commit. Since testing for it requires adding new\nparameters to the test_configured_prune function it's easier to review\nthis patch first to assert that no functional changes are introduced\nyet.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 90 ++++++++++++++++++++++++++++++--------------------------\n 1 file changed, 49 insertions(+), 41 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex e5e88ee474..840fd5ef02 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -551,18 +551,22 @@ set_config_tristate () {\n test_configured_prune () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n-\texpected_branch=$3\n-\texpected_tag=$4\n-\tcmdline=$5\n+\tfetch_prune_tags=$3\n+\tremote_origin_prune_tags=$4\n+\texpected_branch=$5\n+\texpected_tag=$6\n+\tcmdline=$7\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2 fetch.pruneTags=$3 remote.origin.pruneTags=$4${7:+ $7}; branch:$5 tag:$6\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\tgit tag -f newtag &&\n \t\t(\n \t\t\tcd one &&\n \t\t\ttest_unconfig fetch.prune &&\n+\t\t\ttest_unconfig fetch.pruneTags &&\n \t\t\ttest_unconfig remote.origin.prune &&\n+\t\t\ttest_unconfig remote.origin.pruneTags &&\n \t\t\tgit fetch &&\n \t\t\tgit rev-parse --verify refs/remotes/origin/newbranch &&\n \t\t\tgit rev-parse --verify refs/tags/newtag\n@@ -576,7 +580,9 @@ test_configured_prune () {\n \t\t(\n \t\t\tcd one &&\n \t\t\tset_config_tristate fetch.prune $fetch_prune &&\n+\t\t\tset_config_tristate fetch.pruneTags $fetch_prune_tags &&\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n+\t\t\tset_config_tristate remote.origin.pruneTags $remote_origin_prune_tags &&\n \n \t\t\tgit fetch $cmdline &&\n \t\t\tcase \"$expected_branch\" in\n@@ -601,57 +607,59 @@ test_configured_prune () {\n \n # $1 config: fetch.prune\n # $2 config: remote.<name>.prune\n-# $3 expect: branch to be pruned?\n-# $4 expect: tag to be pruned?\n-# $5 git-fetch $cmdline:\n+# $3 config: fetch.pruneTags\n+# $4 config: remote.<name>.pruneTags\n+# $5 expect: branch to be pruned?\n+# $6 expect: tag to be pruned?\n+# $7 git-fetch $cmdline:\n #\n-#                     $1    $2    $3     $4     $5\n-test_configured_prune unset unset kept   kept   \"\"\n-test_configured_prune unset unset kept   kept   \"--no-prune\"\n-test_configured_prune unset unset pruned kept   \"--prune\"\n-test_configured_prune unset unset kept   pruned \\\n+#                     $1    $2    $3    $4    $5     $6     $7\n+test_configured_prune unset unset unset unset kept   kept   \"\"\n+test_configured_prune unset unset unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset unset unset unset pruned kept   \"--prune\"\n+test_configured_prune unset unset unset unset kept   pruned \\\n \t\"--prune origin +refs/tags/*:refs/tags/*\"\n-test_configured_prune unset unset pruned pruned \\\n+test_configured_prune unset unset unset unset pruned pruned \\\n \t\"--prune origin +refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n-test_configured_prune false unset kept   kept   \"\"\n-test_configured_prune false unset kept   kept   \"--no-prune\"\n-test_configured_prune false unset pruned kept   \"--prune\"\n+test_configured_prune false unset unset unset kept   kept   \"\"\n+test_configured_prune false unset unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false unset unset unset pruned kept   \"--prune\"\n \n-test_configured_prune true  unset pruned kept   \"\"\n-test_configured_prune true  unset pruned kept   \"--prune\"\n-test_configured_prune true  unset kept   kept   \"--no-prune\"\n+test_configured_prune true  unset unset unset pruned kept   \"\"\n+test_configured_prune true  unset unset unset pruned kept   \"--prune\"\n+test_configured_prune true  unset unset unset kept   kept   \"--no-prune\"\n \n-test_configured_prune unset false kept   kept   \"\"\n-test_configured_prune unset false kept   kept   \"--no-prune\"\n-test_configured_prune unset false pruned kept   \"--prune\"\n+test_configured_prune unset false unset unset kept   kept   \"\"\n+test_configured_prune unset false unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset false unset unset pruned kept   \"--prune\"\n \n-test_configured_prune false false kept   kept   \"\"\n-test_configured_prune false false kept   kept   \"--no-prune\"\n-test_configured_prune false false pruned kept   \"--prune\"\n-test_configured_prune false false kept   pruned \\\n+test_configured_prune false false unset unset kept   kept   \"\"\n+test_configured_prune false false unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false false unset unset pruned kept   \"--prune\"\n+test_configured_prune false false unset unset kept   pruned \\\n \t\"--prune origin +refs/tags/*:refs/tags/*\"\n-test_configured_prune false false pruned pruned \\\n+test_configured_prune false false unset unset pruned pruned \\\n \t\"--prune origin +refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n-test_configured_prune true  false kept   kept   \"\"\n-test_configured_prune true  false pruned kept   \"--prune\"\n-test_configured_prune true  false kept   kept   \"--no-prune\"\n+test_configured_prune true  false unset unset kept   kept   \"\"\n+test_configured_prune true  false unset unset pruned kept   \"--prune\"\n+test_configured_prune true  false unset unset kept   kept   \"--no-prune\"\n \n-test_configured_prune unset true  pruned kept   \"\"\n-test_configured_prune unset true  kept   kept   \"--no-prune\"\n-test_configured_prune unset true  pruned kept   \"--prune\"\n+test_configured_prune unset true  unset unset pruned kept   \"\"\n+test_configured_prune unset true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset true  unset unset pruned kept   \"--prune\"\n \n-test_configured_prune false true  pruned kept   \"\"\n-test_configured_prune false true  kept   kept   \"--no-prune\"\n-test_configured_prune false true  pruned kept   \"--prune\"\n+test_configured_prune false true  unset unset pruned kept   \"\"\n+test_configured_prune false true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false true  unset unset pruned kept   \"--prune\"\n \n-test_configured_prune true  true  pruned kept   \"\"\n-test_configured_prune true  true  pruned kept   \"--prune\"\n-test_configured_prune true  true  kept   kept   \"--no-prune\"\n-test_configured_prune true  true  kept   pruned \\\n+test_configured_prune true  true  unset unset pruned kept   \"\"\n+test_configured_prune true  true  unset unset pruned kept   \"--prune\"\n+test_configured_prune true  true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune true  true  unset unset kept   pruned \\\n \t\"--prune origin +refs/tags/*:refs/tags/*\"\n-test_configured_prune true  true  pruned pruned \\\n+test_configured_prune true  true  unset unset pruned pruned \\\n \t\"--prune origin +refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n test_expect_success 'all boundary commits are excluded' '\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337194","messageId":"20180123221326.28495-12-avarab@gmail.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"[PATCH v3 11/11] fetch: add a --fetch-prune option and fetch.pruneTags config","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-23T22:13:26Z","receivedAt":"2018-01-23T22:14:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a --fetch-prune option to git-fetch, along with fetch.pruneTags\nconfig option. This allows for doing any of:\n\n    git fetch -p -P\n    git fetch --prune --prune-tags\n    git fetch -p -P origin\n    git fetch --prune --prune-tags origin\n\nOr simply:\n\n    git config fetch.prune true &&\n    git config fetch.pruneTags true &&\n    git fetch\n\nInstead of the much more verbose:\n\n    git fetch --prune origin '+refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\n\nBefore this feature it was painful to support the use-case of pulling\nfrom a repo which is having both its branches *and* tags deleted\nregularly, and have our local references to reflect upstream.\n\nAt work we create deployment tags in the repo for each rollout, and\nthere's *lots* of those, so they're archived within weeks for\nperformance reasons.\n\nWithout this change it's hard to centrally configure such repos in\n/etc/gitconfig (on servers that are only used for working with\nthem). You need to set fetch.prune=true globally, and then for each\nrepo:\n\n    git -C {} config --replace-all remote.origin.fetch \"+refs/tags/*:refs/tags/*\" \"^\\+*refs/tags/\\*:refs/tags/\\*$\"\n\nNow I can simply set fetch.pruneTags=true in /etc/gitconfig as well,\nand users running \"git pull\" will automatically get the pruning\nsemantics I want.\n\nEven though \"git remote\" has corresponding \"prune\" and \"update\n--prune\" subcommands I'm intentionally not adding a corresponding\nprune-tags or \"update --prune --prune-tags\" mode to that command.\n\nIt's advertised (as noted in my recent \"git remote doc: correct\ndangerous lies about what prune does\") as only modifying remote\ntracking references, whereas any --prune-tags option is always going\nto modify what from the user's perspective is a local copy of the tag,\nsince there's no such thing as a remote tracking tag.\n\nSee my \"Re: [BUG] git remote prune removes local tags, depending on\nfetch config\" (87po6ahx87.fsf@evledraar.gmail.com;\nhttps://public-inbox.org/git/87po6ahx87.fsf@evledraar.gmail.com/) for\nmore background info.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt               | 14 ++++++++++++++\n Documentation/fetch-options.txt        | 15 ++++++++++++++-\n Documentation/git-fetch.txt            | 27 +++++++++++++++++++++++++++\n builtin/fetch.c                        | 32 ++++++++++++++++++++++++++++++++\n contrib/completion/git-completion.bash |  2 +-\n remote.c                               |  5 ++++-\n remote.h                               |  3 +++\n t/t5510-fetch.sh                       | 30 ++++++++++++++++++++++++++++++\n 8 files changed, 125 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 0f27af5760..ae86455876 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1401,6 +1401,14 @@ fetch.prune::\n \toption was given on the command line.  See also `remote.<name>.prune`\n \tand the PRUNING section of linkgit:git-fetch[1].\n \n+fetch.pruneTags::\n+\tIf true, fetch will automatically behave as if the\n+\t`refs/tags/*:refs/tags/*` refspec was provided when pruning,\n+\tif not set already. This allows for setting both this option\n+\tand `fetch.prune` to maintain a 1=1 mapping to upstrem\n+\trefs. See also `remote.<name>.pruneTags` and the PRUNING\n+\tsection of linkgit:git-fetch[1].\n+\n fetch.output::\n \tControl how ref update status is printed. Valid values are\n \t`full` and `compact`. Default value is `full`. See section\n@@ -2945,6 +2953,12 @@ remote.<name>.prune::\n \tremove any remote-tracking references that no longer exist on the\n \tremote (as if the `--prune` option was given on the command line).\n \tOverrides `fetch.prune` settings, if any.\n+\n+remote.<name>.pruneTags::\n+\tWhen set to true, fetching from this remote by default will also\n+\tremove any local tags that no longer exist on the remote if pruning\n+\tis activated in general via `remote.<name>.prune`, `fetch.prune` or\n+\t`--prune`. Overrides `fetch.pruneTags` settings, if any.\n +\n See also `remote.<name>.prune` and the PRUNING section of\n linkgit:git-fetch[1].\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 9f5c85ad96..dc13bed741 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -73,7 +73,20 @@ ifndef::git-pull[]\n \tare fetched due to an explicit refspec (either on the command\n \tline or in the remote configuration, for example if the remote\n \twas cloned with the --mirror option), then they are also\n-\tsubject to pruning.\n+\tsubject to pruning. Supplying `--prune-tags` is a shorthand for\n+\tproviding the tag refspec.\n++\n+See the PRUNING section below for more details.\n+\n+-P::\n+--prune-tags::\n+\tBefore fetching, remove any local tags that no longer exist on\n+\tthe remote if `--prune` is enabled. This option should be used\n+\tmore carefully, unlike `--prune` it will remove any local\n+\treferences (local tags) that have been created. This option is\n+\tmerely a shorthand for providing the explicit tag refspec\n+\talong with `--prune`, see the discussion about that in its\n+\tdocumentation.\n +\n See the PRUNING section below for more details.\n \ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex 18fac0da2e..cdde6c50df 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -148,6 +148,33 @@ So be careful when using this with a refspec like\n `refs/tags/*:refs/tags/*`, or any other refspec which might map\n references from multiple remotes to the same local namespace.\n \n+Since keeping up-to-date with both branches and tags on the remote is\n+a common use-case the `--prune-tags` option can be supplied along with\n+`--prune` to prune local tags that don't exist on the remote, and\n+force-update those tags that differ. Tag pruning can also be enabled\n+with `fetch.pruneTags` or `remote.<name>.pruneTags` in the config. See\n+linkgit:git-config[1].\n+\n+The `--prune-tags` option is equivalent to having\n+`+refs/tags/*:refs/tags/*` declared in the refspecs of the\n+remote. This can lead to some seemingly strange interactions:\n+\n+------------------------------------------------\n+# These both fetch tags\n+$ git fetch --no-tags origin '+refs/tags/*:refs/tags/*'\n+$ git fetch --no-tags --prune-tags origin\n+------------------------------------------------\n+\n+The reason it doesn't error out when provided without `--prune` or its\n+config versions is for flexibility of the configured versions, and to\n+maintain a 1=1 mapping between what the command line flags do, and\n+what the configuration versions do.\n+\n+It's reasonable to e.g. configure `fetch.pruneTags=true` in\n+`~/.gitconfig` to have tags pruned whenever `git fetch --prune` is\n+run, without making every invocation of `git fetch` without `--prune`\n+an error.\n+\n OUTPUT\n ------\n \ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex a85c2002a9..0c898e64a8 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -38,6 +38,10 @@ static int fetch_prune_config = -1; /* unspecified */\n static int prune = -1; /* unspecified */\n #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */\n \n+static int fetch_prune_tags_config = -1; /* unspecified */\n+static int prune_tags = -1; /* unspecified */\n+#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */\n+\n static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;\n static int progress = -1;\n static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;\n@@ -64,6 +68,11 @@ static int git_fetch_config(const char *k, const char *v, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(k, \"fetch.prunetags\")) {\n+\t\tfetch_prune_tags_config = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(k, \"submodule.recurse\")) {\n \t\tint r = git_config_bool(k, v) ?\n \t\t\tRECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;\n@@ -126,6 +135,8 @@ static struct option builtin_fetch_options[] = {\n \t\t    N_(\"number of submodules fetched in parallel\")),\n \tOPT_BOOL('p', \"prune\", &prune,\n \t\t N_(\"prune remote-tracking branches no longer on remote\")),\n+\tOPT_BOOL('P', \"prune-tags\", &prune_tags,\n+\t\t N_(\"prune local tags no longer on remote and clobber changed tags\")),\n \t{ OPTION_CALLBACK, 0, \"recurse-submodules\", &recurse_submodules, N_(\"on-demand\"),\n \t\t    N_(\"control recursive fetching of submodules\"),\n \t\t    PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },\n@@ -1212,6 +1223,8 @@ static void add_options_to_argv(struct argv_array *argv)\n \t\targv_array_push(argv, \"--dry-run\");\n \tif (prune != -1)\n \t\targv_array_push(argv, prune ? \"--prune\" : \"--no-prune\");\n+\tif (prune_tags != -1)\n+\t\targv_array_push(argv, prune_tags ? \"--prune-tags\" : \"--no-prune-tags\");\n \tif (update_head_ok)\n \t\targv_array_push(argv, \"--update-head-ok\");\n \tif (force)\n@@ -1315,6 +1328,22 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \treturn exit_code;\n }\n \n+static void maybe_add_prune_tags_refspec(struct remote *remote)\n+{\n+\tif (prune_tags < 0) {\n+\t\t/* no command line request */\n+\t\tif (0 <= remote->prune_tags)\n+\t\t\tprune_tags = remote->prune_tags;\n+\t\telse if (0 <= fetch_prune_tags_config)\n+\t\t\tprune_tags = fetch_prune_tags_config;\n+\t\telse\n+\t\t\tprune_tags = PRUNE_TAGS_BY_DEFAULT;\n+\t}\n+\n+\tif (prune_tags)\n+\t\tadd_fetch_refspec(remote, xstrdup(\"+refs/tags/*:refs/tags/*\"));\n+}\n+\n int cmd_fetch(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n@@ -1368,6 +1397,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \t} else if (argc == 0) {\n \t\t/* No arguments -- use default remote */\n \t\tremote = remote_get(NULL);\n+\t\tmaybe_add_prune_tags_refspec(remote);\n \t\tresult = fetch_one(remote, argc, argv);\n \t} else if (multiple) {\n \t\t/* All arguments are assumed to be remotes or groups */\n@@ -1386,6 +1416,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \t\t} else {\n \t\t\t/* Zero or one remotes */\n \t\t\tremote = remote_get(argv[0]);\n+\t\t\tif (argc == 1)\n+\t\t\t\tmaybe_add_prune_tags_refspec(remote);\n \t\t\tresult = fetch_one(remote, argc-1, argv+1);\n \t\t}\n \t}\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 3683c772c5..4ecd0d4d7a 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1468,7 +1468,7 @@ __git_fetch_recurse_submodules=\"yes on-demand no\"\n __git_fetch_options=\"\n \t--quiet --verbose --append --upload-pack --force --keep --depth=\n \t--tags --no-tags --all --prune --dry-run --recurse-submodules=\n-\t--unshallow --update-shallow\n+\t--unshallow --update-shallow --prune-tags\n \"\n \n _git_fetch ()\ndiff --git a/remote.c b/remote.c\nindex 4e93753e19..d8716643cb 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -95,7 +95,7 @@ static void add_push_refspec(struct remote *remote, const char *ref)\n \tremote->push_refspec[remote->push_refspec_nr++] = ref;\n }\n \n-static void add_fetch_refspec(struct remote *remote, const char *ref)\n+void add_fetch_refspec(struct remote *remote, const char *ref)\n {\n \tALLOC_GROW(remote->fetch_refspec,\n \t\t   remote->fetch_refspec_nr + 1,\n@@ -173,6 +173,7 @@ static struct remote *make_remote(const char *name, int len)\n \n \tret = xcalloc(1, sizeof(struct remote));\n \tret->prune = -1;  /* unspecified */\n+\tret->prune_tags = -1;  /* unspecified */\n \tALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);\n \tremotes[remotes_nr++] = ret;\n \tret->name = xstrndup(name, len);\n@@ -391,6 +392,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tremote->skip_default_update = git_config_bool(key, value);\n \telse if (!strcmp(subkey, \"prune\"))\n \t\tremote->prune = git_config_bool(key, value);\n+\telse if (!strcmp(subkey, \"prunetags\"))\n+\t\tremote->prune_tags = git_config_bool(key, value);\n \telse if (!strcmp(subkey, \"url\")) {\n \t\tconst char *v;\n \t\tif (git_config_string(&v, key, value))\ndiff --git a/remote.h b/remote.h\nindex 1f6611be21..df6dca24d4 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -47,6 +47,7 @@ struct remote {\n \tint skip_default_update;\n \tint mirror;\n \tint prune;\n+\tint prune_tags;\n \n \tconst char *receivepack;\n \tconst char *uploadpack;\n@@ -133,6 +134,8 @@ struct ref {\n #define REF_HEADS\t(1u << 1)\n #define REF_TAGS\t(1u << 2)\n \n+void add_fetch_refspec(struct remote *remote, const char *ref);\n+\n extern struct ref *find_ref_by_name(const struct ref *list, const char *name);\n \n struct ref *alloc_ref(const char *name);\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 840fd5ef02..1bca9d0bd2 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -662,6 +662,36 @@ test_configured_prune true  true  unset unset kept   pruned \\\n test_configured_prune true  true  unset unset pruned pruned \\\n \t\"--prune origin +refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n+\n+# --prune-tags on its own does nothing, needs --prune as well, same\n+# for for fetch.pruneTags without fetch.prune\n+test_configured_prune unset unset unset unset kept kept     \"--prune-tags\"\n+test_configured_prune unset unset unset unset kept kept     \"origin --prune-tags\"\n+test_configured_prune unset unset true unset  kept kept     \"\"\n+test_configured_prune unset unset unset true  kept kept     \"\"\n+\n+# These will prune the tags\n+test_configured_prune unset unset unset unset pruned pruned \"--prune --prune-tags\"\n+test_configured_prune unset unset unset unset pruned pruned \"origin --prune --prune-tags\"\n+test_configured_prune true  unset true  unset pruned pruned \"\"\n+test_configured_prune unset true  unset true  pruned pruned \"\"\n+\n+# Check that remote.<name>.pruneTags overrides fetch.pruneTags as with\n+# remote.<name>.prune and fetch.prune\n+test_configured_prune false true  false true  pruned pruned \"\"\n+test_configured_prune true  false true  false kept   kept   \"\"\n+\n+# When --prune-tags is supplied it's ignored if an explict refspec is\n+# given, same for the configuration options.\n+test_configured_prune unset unset unset unset pruned kept \\\n+\t\"--prune --prune-tags origin +refs/heads/*:refs/remotes/origin/*\"\n+test_configured_prune unset unset true  unset pruned kept \\\n+\t\"--prune --prune-tags origin +refs/heads/*:refs/remotes/origin/*\"\n+test_configured_prune unset unset unset true pruned  kept \\\n+\t\"--prune --prune-tags origin +refs/heads/*:refs/remotes/origin/*\"\n+\n+test_configured_prune true unset true unset pruned pruned   \"\"\n+\n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n \ttest_commit oneside &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337195","messageId":"20180123221326.28495-6-avarab@gmail.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"[PATCH v3 05/11] fetch tests: add a tag to be deleted to the pruning tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-23T22:13:20Z","receivedAt":"2018-01-23T22:14:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a tag to be deleted to the fetch --prune tests. The tag is always\nkept for now, which is the expected behavior, but now I can add a test\nfor tag pruning in a later commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 93 ++++++++++++++++++++++++++++++++------------------------\n 1 file changed, 53 insertions(+), 40 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex ab8b25344d..fad65bd885 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -552,21 +552,25 @@ test_configured_prune () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n \texpected_branch=$3\n-\tcmdline=$4\n+\texpected_tag=$4\n+\tcmdline=$5\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${4:+ $4}; branch:$3\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n+\t\tgit tag -f newtag &&\n \t\t(\n \t\t\tcd one &&\n \t\t\ttest_unconfig fetch.prune &&\n \t\t\ttest_unconfig remote.origin.prune &&\n \t\t\tgit fetch &&\n-\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch\n+\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch &&\n+\t\t\tgit rev-parse --verify refs/tags/newtag\n \t\t) &&\n \n \t\t# now remove it\n \t\tgit branch -d newbranch &&\n+\t\tgit tag -d newtag &&\n \n \t\t# then test\n \t\t(\n@@ -582,6 +586,14 @@ test_configured_prune () {\n \t\t\tkept)\n \t\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch\n \t\t\t\t;;\n+\t\t\tesac &&\n+\t\t\tcase \"$expected_tag\" in\n+\t\t\tpruned)\n+\t\t\t\ttest_must_fail git rev-parse --verify refs/tags/newtag\n+\t\t\t\t;;\n+\t\t\tkept)\n+\t\t\t\tgit rev-parse --verify refs/tags/newtag\n+\t\t\t\t;;\n \t\t\tesac\n \t\t)\n \t'\n@@ -590,44 +602,45 @@ test_configured_prune () {\n # $1 config: fetch.prune\n # $2 config: remote.<name>.prune\n # $3 expect: branch to be pruned?\n-# $4 git-fetch $cmdline:\n+# $4 expect: tag to be pruned?\n+# $5 git-fetch $cmdline:\n #\n-#                     $1    $2    $3     $4\n-test_configured_prune unset unset kept   \"\"\n-test_configured_prune unset unset kept   \"--no-prune\"\n-test_configured_prune unset unset pruned \"--prune\"\n-\n-test_configured_prune false unset kept   \"\"\n-test_configured_prune false unset kept   \"--no-prune\"\n-test_configured_prune false unset pruned \"--prune\"\n-\n-test_configured_prune true  unset pruned \"\"\n-test_configured_prune true  unset pruned \"--prune\"\n-test_configured_prune true  unset kept   \"--no-prune\"\n-\n-test_configured_prune unset false kept   \"\"\n-test_configured_prune unset false kept   \"--no-prune\"\n-test_configured_prune unset false pruned \"--prune\"\n-\n-test_configured_prune false false kept   \"\"\n-test_configured_prune false false kept   \"--no-prune\"\n-test_configured_prune false false pruned \"--prune\"\n-\n-test_configured_prune true  false kept   \"\"\n-test_configured_prune true  false pruned \"--prune\"\n-test_configured_prune true  false kept   \"--no-prune\"\n-\n-test_configured_prune unset true  pruned \"\"\n-test_configured_prune unset true  kept   \"--no-prune\"\n-test_configured_prune unset true  pruned \"--prune\"\n-\n-test_configured_prune false true  pruned \"\"\n-test_configured_prune false true  kept   \"--no-prune\"\n-test_configured_prune false true  pruned \"--prune\"\n-\n-test_configured_prune true  true  pruned \"\"\n-test_configured_prune true  true  pruned \"--prune\"\n-test_configured_prune true  true  kept   \"--no-prune\"\n+#                     $1    $2    $3     $4     $5\n+test_configured_prune unset unset kept   kept   \"\"\n+test_configured_prune unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset unset pruned kept   \"--prune\"\n+\n+test_configured_prune false unset kept   kept   \"\"\n+test_configured_prune false unset kept   kept   \"--no-prune\"\n+test_configured_prune false unset pruned kept   \"--prune\"\n+\n+test_configured_prune true  unset pruned kept   \"\"\n+test_configured_prune true  unset pruned kept   \"--prune\"\n+test_configured_prune true  unset kept   kept   \"--no-prune\"\n+\n+test_configured_prune unset false kept   kept   \"\"\n+test_configured_prune unset false kept   kept   \"--no-prune\"\n+test_configured_prune unset false pruned kept   \"--prune\"\n+\n+test_configured_prune false false kept   kept   \"\"\n+test_configured_prune false false kept   kept   \"--no-prune\"\n+test_configured_prune false false pruned kept   \"--prune\"\n+\n+test_configured_prune true  false kept   kept   \"\"\n+test_configured_prune true  false pruned kept   \"--prune\"\n+test_configured_prune true  false kept   kept   \"--no-prune\"\n+\n+test_configured_prune unset true  pruned kept   \"\"\n+test_configured_prune unset true  kept   kept   \"--no-prune\"\n+test_configured_prune unset true  pruned kept   \"--prune\"\n+\n+test_configured_prune false true  pruned kept   \"\"\n+test_configured_prune false true  kept   kept   \"--no-prune\"\n+test_configured_prune false true  pruned kept   \"--prune\"\n+\n+test_configured_prune true  true  pruned kept   \"\"\n+test_configured_prune true  true  pruned kept   \"--prune\"\n+test_configured_prune true  true  kept   kept   \"--no-prune\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337196","messageId":"20180123221326.28495-9-avarab@gmail.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"[PATCH v3 08/11] git remote doc: correct dangerous lies about what prune does","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-23T22:13:23Z","receivedAt":"2018-01-23T22:14:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The \"git remote prune <name>\" command uses the same machinery as \"git\nfetch <name> --prune\", and shares all the same caveats, but its\ndocumentation has suggested that it'll just \"delete stale\nremote-tracking branches under <name>\".\n\nThis isn't true, and hasn't been true since at least v1.8.5.6 (the\noldest version I could be bothered to test).\n\nE.g. if \"+refs/tags/*:refs/tags/*\" is explicitly set in the refspec of\nthe remote it'll delete all local tags <name> doesn't know about.\n\nInstead, briefly give the reader just enough of a hint that this\noption might constitute a shotgun aimed at their foot, and point them\nto the new PRUNING section in the git-fetch documentation which\nexplains all the nuances of what this facility does.\n\nSee \"[BUG] git remote prune removes local tags, depending on fetch\nconfig\" (CACi5S_39wNrbfjLfn0xhCY+uewtFN2YmnAcRc86z6pjUTjWPHQ@mail.gmail.com)\nby Michael Giuffrida for the initial report.\n\nReported-by: Michael Giuffrida <michaelpg@chromium.org>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-remote.txt | 14 +++++++++-----\n 1 file changed, 9 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 577b969c1b..04e2410aec 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -172,10 +172,14 @@ With `-n` option, the remote heads are not queried first with\n \n 'prune'::\n \n-Deletes all stale remote-tracking branches under <name>.\n-These stale branches have already been removed from the remote repository\n-referenced by <name>, but are still locally available in\n-\"remotes/<name>\".\n+Deletes stale references associated with <name>. By default stale\n+remote-tracking branches under <name>, but depending on global\n+configuration and the configuration of the remote we might even prune\n+local tags that haven't been pushed there. Equivalent to `git fetch\n+--prune <name>`, except that no new references will be fetched.\n++\n+See the PRUNING section of linkgit:git-fetch[1] for what it'll prune\n+depending on various configuration.\n +\n With `--dry-run` option, report what branches will be pruned, but do not\n actually prune them.\n@@ -189,7 +193,7 @@ remotes.default is not defined, all remotes which do not have the\n configuration parameter remote.<name>.skipDefaultUpdate set to true will\n be updated.  (See linkgit:git-config[1]).\n +\n-With `--prune` option, prune all the remotes that are updated.\n+With `--prune` option, run pruning against all the remotes that are updated.\n \n \n DISCUSSION\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337197","messageId":"20180123221326.28495-5-avarab@gmail.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"[PATCH v3 04/11] fetch tests: re-arrange arguments for future readability","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-23T22:13:19Z","receivedAt":"2018-01-23T22:14:26Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Re-arrange the arguments to the test_configured_prune() function used\nin this test to pass the arguments to --fetch last. A subsequent\nchange will test for more elaborate fetch arguments, including long\nrefspecs. It'll be more readable to be able to wrap those on a new\nline of their own.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 82 ++++++++++++++++++++++++++++++--------------------------\n 1 file changed, 44 insertions(+), 38 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 11da97f9b7..ab8b25344d 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -551,10 +551,10 @@ set_config_tristate () {\n test_configured_prune () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n-\tcmdline=$3\n-\texpected_branch=$4\n+\texpected_branch=$3\n+\tcmdline=$4\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; branch:$4\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${4:+ $4}; branch:$3\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\t(\n@@ -587,41 +587,47 @@ test_configured_prune () {\n \t'\n }\n \n-test_configured_prune unset unset \"\"\t\tkept\n-test_configured_prune unset unset \"--no-prune\"\tkept\n-test_configured_prune unset unset \"--prune\"\tpruned\n-\n-test_configured_prune false unset \"\"\t\tkept\n-test_configured_prune false unset \"--no-prune\"\tkept\n-test_configured_prune false unset \"--prune\"\tpruned\n-\n-test_configured_prune true  unset \"\"\t\tpruned\n-test_configured_prune true  unset \"--prune\"\tpruned\n-test_configured_prune true  unset \"--no-prune\"\tkept\n-\n-test_configured_prune unset false \"\"\t\tkept\n-test_configured_prune unset false \"--no-prune\"\tkept\n-test_configured_prune unset false \"--prune\"\tpruned\n-\n-test_configured_prune false false \"\"\t\tkept\n-test_configured_prune false false \"--no-prune\"\tkept\n-test_configured_prune false false \"--prune\"\tpruned\n-\n-test_configured_prune true  false \"\"\t\tkept\n-test_configured_prune true  false \"--prune\"\tpruned\n-test_configured_prune true  false \"--no-prune\"\tkept\n-\n-test_configured_prune unset true  \"\"\t\tpruned\n-test_configured_prune unset true  \"--no-prune\"\tkept\n-test_configured_prune unset true  \"--prune\"\tpruned\n-\n-test_configured_prune false true  \"\"\t\tpruned\n-test_configured_prune false true  \"--no-prune\"\tkept\n-test_configured_prune false true  \"--prune\"\tpruned\n-\n-test_configured_prune true  true  \"\"\t\tpruned\n-test_configured_prune true  true  \"--prune\"\tpruned\n-test_configured_prune true  true  \"--no-prune\"\tkept\n+# $1 config: fetch.prune\n+# $2 config: remote.<name>.prune\n+# $3 expect: branch to be pruned?\n+# $4 git-fetch $cmdline:\n+#\n+#                     $1    $2    $3     $4\n+test_configured_prune unset unset kept   \"\"\n+test_configured_prune unset unset kept   \"--no-prune\"\n+test_configured_prune unset unset pruned \"--prune\"\n+\n+test_configured_prune false unset kept   \"\"\n+test_configured_prune false unset kept   \"--no-prune\"\n+test_configured_prune false unset pruned \"--prune\"\n+\n+test_configured_prune true  unset pruned \"\"\n+test_configured_prune true  unset pruned \"--prune\"\n+test_configured_prune true  unset kept   \"--no-prune\"\n+\n+test_configured_prune unset false kept   \"\"\n+test_configured_prune unset false kept   \"--no-prune\"\n+test_configured_prune unset false pruned \"--prune\"\n+\n+test_configured_prune false false kept   \"\"\n+test_configured_prune false false kept   \"--no-prune\"\n+test_configured_prune false false pruned \"--prune\"\n+\n+test_configured_prune true  false kept   \"\"\n+test_configured_prune true  false pruned \"--prune\"\n+test_configured_prune true  false kept   \"--no-prune\"\n+\n+test_configured_prune unset true  pruned \"\"\n+test_configured_prune unset true  kept   \"--no-prune\"\n+test_configured_prune unset true  pruned \"--prune\"\n+\n+test_configured_prune false true  pruned \"\"\n+test_configured_prune false true  kept   \"--no-prune\"\n+test_configured_prune false true  pruned \"--prune\"\n+\n+test_configured_prune true  true  pruned \"\"\n+test_configured_prune true  true  pruned \"--prune\"\n+test_configured_prune true  true  kept   \"--no-prune\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"337302","messageId":"xmqqo9ljc73n.fsf@gitster.mtv.corp.google.com","threadId":"47614","inReplyTo":"20180123221326.28495-12-avarab@gmail.com","subject":"Re: [PATCH v3 11/11] fetch: add a --fetch-prune option and fetch.pruneTags config","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-24T20:52:28Z","receivedAt":"2018-01-24T20:52:35Z","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>  git -C {} config --replace-all remote.origin.fetch \"+refs/tags/*:refs/tags/*\" \"^\\+*refs/tags/\\*:refs/tags/\\*$\"\n\nShouldn't the last arg be\n\n '^+\\*refs/tags/\\*:refs/tags/\\*$'\n\ninstead?\n"},{"id":"337303","messageId":"xmqqk1w7c72b.fsf@gitster.mtv.corp.google.com","threadId":"47614","inReplyTo":"20180123221326.28495-3-avarab@gmail.com","subject":"Re: [PATCH v3 02/11] fetch: stop accessing \"remote\" variable indirectly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-24T20:53:16Z","receivedAt":"2018-01-24T20:53:24Z","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> remote->prune-tags field, but without hte use of the gtransport\n\ns/-tags/_tags/; s/hte/the/;\n\nNo need to resend for fixing the above, as I've done so locally\nbefore queuing.\n\nThanks.\n"},{"id":"337307","messageId":"878tcnklzc.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"xmqqo9ljc73n.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 11/11] fetch: add a --fetch-prune option and fetch.pruneTags config","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-24T21:03:51Z","receivedAt":"2018-01-24T21:04:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Jan 24 2018, Junio C. Hamano jotted:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>>  git -C {} config --replace-all remote.origin.fetch \"+refs/tags/*:refs/tags/*\" \"^\\+*refs/tags/\\*:refs/tags/\\*$\"\n>\n> Shouldn't the last arg be\n>\n>  '^+\\*refs/tags/\\*:refs/tags/\\*$'\n>\n> instead?\n\n^+\\* isn't a valid pattern.\n\nThis invocation is to replace both +refs/tags/[...] and refs/tags/[...]\nwith +refs/tags/[...]. This would be more specific, i.e. ^\\+?\n\n     git config --replace-all remote.origin.fetch \"+refs/tags/*:refs/tags/*\" \"^\\+?refs/tags/\\*:refs/tags/\\*$\"\n\nBut I couldn't remeber offhand whether all regcomp() we use supports X?,\nwhereas they definitely do support X*.\n"},{"id":"337309","messageId":"xmqq4lnbc61z.fsf@gitster.mtv.corp.google.com","threadId":"47614","inReplyTo":"878tcnklzc.fsf@evledraar.gmail.com","subject":"Re: [PATCH v3 11/11] fetch: add a --fetch-prune option and fetch.pruneTags config","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-24T21:15:04Z","receivedAt":"2018-01-24T21:15:12Z","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 Wed, Jan 24 2018, Junio C. Hamano jotted:\n>\n>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>>\n>>>  git -C {} config --replace-all remote.origin.fetch \"+refs/tags/*:refs/tags/*\" \"^\\+*refs/tags/\\*:refs/tags/\\*$\"\n>>\n>> Shouldn't the last arg be\n>>\n>>  '^+\\*refs/tags/\\*:refs/tags/\\*$'\n>>\n>> instead?\n>\n> ^+\\* isn't a valid pattern.\n\nYikes, sorry for the noise.  You did mean \"there might be a plus\nsign at the beginning, but there may not be\".\n\n"},{"id":"337335","messageId":"xmqqwp06amfw.fsf@gitster.mtv.corp.google.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"Re: [PATCH v3 00/11] document & test fetch pruning & add fetch.pruneTags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-24T23:04:03Z","receivedAt":"2018-01-24T23:04:12Z","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>  Documentation/config.txt               |  20 ++++-\n>  Documentation/fetch-options.txt        |  18 +++-\n>  Documentation/git-fetch.txt            |  76 +++++++++++++++++\n>  Documentation/git-remote.txt           |  14 +--\n>  builtin/fetch.c                        |  37 +++++++-\n>  contrib/completion/git-completion.bash |   2 +-\n>  remote.c                               |   5 +-\n>  remote.h                               |   3 +\n>  t/t5510-fetch.sh                       | 152 ++++++++++++++++++++++++---------\n>  9 files changed, 275 insertions(+), 52 deletions(-)\n\nI've queued this round but saw a few minor conflicts with another\ntopi in flight, so please double check the result when I push the\nresult out shortly.\n\nThanks.\n"},{"id":"337338","messageId":"877es6yh40.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"xmqqwp06amfw.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 00/11] document & test fetch pruning & add fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-01-24T23:25:19Z","receivedAt":"2018-01-24T23:25:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Jan 24 2018, Junio C. Hamano jotted:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>>  Documentation/config.txt               |  20 ++++-\n>>  Documentation/fetch-options.txt        |  18 +++-\n>>  Documentation/git-fetch.txt            |  76 +++++++++++++++++\n>>  Documentation/git-remote.txt           |  14 +--\n>>  builtin/fetch.c                        |  37 +++++++-\n>>  contrib/completion/git-completion.bash |   2 +-\n>>  remote.c                               |   5 +-\n>>  remote.h                               |   3 +\n>>  t/t5510-fetch.sh                       | 152 ++++++++++++++++++++++++---------\n>>  9 files changed, 275 insertions(+), 52 deletions(-)\n>\n> I've queued this round but saw a few minor conflicts with another\n> topi in flight, so please double check the result when I push the\n> result out shortly.\n\nYikes. fetch.c is busy these days. Looked it over, looks good to me. In\nparticular the potential logic error introduced by mis-merging \"fetch:\nrefactor calculation of remote list\" has a test which would fail.\n"},{"id":"338520","messageId":"87h8quytmq.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"Re: [PATCH v3 00/11] document & test fetch pruning & add fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-06T16:23:25Z","receivedAt":"2018-02-06T16:23:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Jan 23 2018, Ævar Arnfjörð Bjarmason jotted:\n\n> I'm now just skipping quoting things like +refs/... on the\n> command-line, which as grepping the rest of the test suite shows is\n> fine, this eliminated the need for \"fetch tests: double quote a\n> variable for interpolation\" so I've ejected it.\n\nThere's a segfault bug in 11/11, which wasn't found because the test\nsuite doesn't test `git fetch <url>` just `git fetch <named>` and this\nis handled differently.\n\nI'll send a fix soon, but don't merge this down from pu for now.\n\nIn order to test for that I brought that cmdline quoting patch back, I\ncan't find a better way to do that, and in addition I have this similar\nWIP patch:\n\n    diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\n    index 2f5bd966be..8fe4f3c13b 100755\n    --- a/t/t5510-fetch.sh\n    +++ b/t/t5510-fetch.sh\n    @@ -549,13 +549,39 @@ set_config_tristate () {\n     }\n\n     test_configured_prune () {\n    +\ttest_configured_prune_guts \"$@\" \"name\"\n    +\ttest_configured_prune_guts \"$@\" \"link\"\n    +}\n    +\n    +test_configured_prune_guts () {\n     \tfetch_prune=$1\n     \tremote_origin_prune=$2\n     \texpected_branch=$3\n     \texpected_tag=$4\n     \tcmdline=$5\n    -\n    -\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n    +\tmode=$6\n    +\n    +\tif ! test -e prune-guts-setup\n    +\tthen\n    +\t\ttest_expect_success 'prune_guts setup' '\n    +\t\t\tgit -C one config remote.origin.url >one.remote-url &&\n    +\t\t\tgit -C one config remote.origin.fetch >one.remote-fetch &&\n    +\t\t\ttouch prune-guts-setup\n    +\t\t'\n    +\tfi\n    +\n    +\tif test \"$mode\" = 'link'\n    +\tthen\n    +\t\tremote_url=\"file://$(cat one.remote-url)\"\n    +\t\tremote_fetch=\"$(cat one.remote-fetch)\"\n    +\t\tcmdline_setup=\"\\\"$remote_url\\\" \\\"$remote_fetch\\\"\"\n    +\t\tif test \"$cmdline\" != \"\"\n    +\t\tthen\n    +\t\t\tcmdline=$(printf \"%s\" \"$cmdline\" | sed -e 's! origin! \"'\"$remote_url\"'\"!g')\n    +\t\tfi\n    +\tfi\n    +\n    +\ttest_expect_success \"$mode prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n     \t\t# make sure a newbranch is there in . and also in one\n     \t\tgit branch -f newbranch &&\n     \t\tgit tag -f newtag &&\n    @@ -563,7 +589,7 @@ test_configured_prune () {\n     \t\t\tcd one &&\n     \t\t\ttest_unconfig fetch.prune &&\n     \t\t\ttest_unconfig remote.origin.prune &&\n    -\t\t\tgit fetch &&\n    +\t\t\tgit fetch '\"$cmdline_setup\"' &&\n     \t\t\tgit rev-parse --verify refs/remotes/origin/newbranch &&\n     \t\t\tgit rev-parse --verify refs/tags/newtag\n     \t\t) &&\n\nIt'll be amended a bit more, but the general idea is there, because of\nhow this whole quoting mess looks like I have to resort to the above\nhack outside of the test setup.\n"},{"id":"338717","messageId":"20180208161936.8196-1-avarab@gmail.com","threadId":"47614","inReplyTo":"20180123221326.28495-1-avarab@gmail.com","subject":"[PATCH v2 00/17] document & test fetch pruning & add fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:19Z","receivedAt":"2018-02-08T16:20:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As noted in my 87h8quytmq.fsf@evledraar.gmail.com there was a bug I\nnoticed in v3 where it would segfault on some git-fetch invocations,\nbut there were not tests anywhere that caught that.\n\nSo in addition to fixing that issue, this fleshens out the testing\nbeing set up as part of this series so we'll test those sorts of\ninvocations. It would segfault on some `git fetch <url>`, not `git\nfetch <name>`.\n\nÆvar Arnfjörð Bjarmason (17):\n  fetch: don't redundantly NULL something calloc() gave us\n\nRephrased commit message.\n\n  fetch: trivially refactor assignment to ref_nr\n\nNew, makes a subsequent change smaller.\n\n  fetch: stop accessing \"remote\" variable indirectly\n\nTypo fix in commit message noted by Junio.\n\n  remote: add a macro for \"refs/tags/*:refs/tags/*\"\n\nNew, makes a subsequent change smaller.\n\n  fetch tests: refactor in preparation for testing tag pruning\n  fetch tests: re-arrange arguments for future readability\n  fetch tests: add a tag to be deleted to the pruning tests\n\nNo changes.\n\n  fetch tests: test --prune and refspec interaction\n\nChanged +refs/tags/*:refs/tags/ to refs/tags/*:refs/tags/. No\nfunctional difference, since git doesn't care. Just to be consistent\nwith the macro added earlier & doing the same in commit messages &\ntests later in the series.\n\n  fetch tests: double quote a variable for interpolation\n\nNow back from an earlier version, needed for a later change.\n\n  fetch tests: expand case/esac for later change\n\nNew, makes the next patch smaller / easier to review.\n\n  fetch tests: fetch <url> <spec> as well as fetch [<remote>]\n\nFor all `git fetch <name>` we now run another version of the test\nwhere we test an equivalent `git fetch <url>`. This sort of exhaustive\ntesting was missing in our whole test suite, and would have caught the\nsegfault in v3.\n\n  git fetch doc: add a new section to explain the ins & outs of pruning\n  git remote doc: correct dangerous lies about what prune does\n  git-fetch & config doc: link to the new PRUNING section\n\nNo changes except omitting the \"+\" in front of refs/tags/[...] as\nnoted above.\n\n  fetch tests: add scaffolding for the new fetch.pruneTags\n\nDitto \"+\" change + minor changes carried over from previous patches.\n\n  fetch: add a --fetch-prune option and fetch.pruneTags config\n\nThe bug in v3 was that the remote->fetch variable needs to chaned in\nlockstep with remote->fetch_refspec, but only the latter was\nchanged. Codepaths that fetched by URL would under --prune-tags expect\nas many items in both, and segfault on the access to remote->fetch.\n\nAs explained in the amended commit message the API is not amenable to\nALLOC_GROW, so there's now a add_prune_tags_to_fetch_refspec()\nfunction in remote.c which adds the new element to remote->fetch via\nxrealloc() + memcpy().\n\nCareful review of that most welcome.\n\nThere's lots more tests that catch the case where it segfaulted.\n\n  fetch: make the --fetch-prune work with <url>\n\nThe previous patch was changed to document that this wouldn't work:\n\n    git fetch <url of origin> --prune --prune-tag\n\nThis makes it work, at the cost of some complexity in fetch_one(). I\nthink it makes sense to keep this, I just wanted to split it off from\nthe previous patch to clearly show the hoops we need to jump through\nfor that one case.\n\n Documentation/config.txt               |  20 ++-\n Documentation/fetch-options.txt        |  17 ++-\n Documentation/git-fetch.txt            |  87 ++++++++++++\n Documentation/git-remote.txt           |  14 +-\n builtin/fetch.c                        |  54 ++++++--\n contrib/completion/git-completion.bash |   2 +-\n remote.c                               |  15 ++\n remote.h                               |   5 +\n t/t5510-fetch.sh                       | 242 +++++++++++++++++++++++++++------\n 9 files changed, 395 insertions(+), 61 deletions(-)\n\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338718","messageId":"20180208161936.8196-2-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 01/17] fetch: don't redundantly NULL something calloc() gave us","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:20Z","receivedAt":"2018-02-08T16:20:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Stop redundantly NULL-ing the last element of the refs structure,\nwhich was retrieved via calloc(), and is thus guaranteed to be\npre-NULL'd.\n\nThis code dates back to b888d61c83 (\"Make fetch a builtin\",\n2007-09-10), where wasn't any reason to do this back then either, it's\njust boilerplate left over from when git-fetch was initially\nintroduced.\n\nThe motivation for this change was to make a subsequent change which\nwould also modify the refs variable smaller, since it won't have to\ncopy this redundant \"NULL the last + 1 item\" pattern.\n\nWe may not end up keeping that change, but as this pattern is still\npointless, so let's fix it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fetch.c | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 7bbcd26faf..b34665db9e 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1302,7 +1302,6 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \t\t\t} else\n \t\t\t\trefs[j++] = argv[i];\n \t\t}\n-\t\trefs[j] = NULL;\n \t\tref_nr = j;\n \t}\n \n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338720","messageId":"20180208161936.8196-3-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 02/17] fetch: trivially refactor assignment to ref_nr","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:21Z","receivedAt":"2018-02-08T16:20:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Trivially refactor an assignment to make a subsequent patch\nsmaller. The \"ref_nr\" variable is initialized to 0 earlier, just as\n\"j\" is, and \"j\" is only incremented in that loop, so this change isn't\na logic error.\n\nThis change makes a subsequent change which splits the incrementing of\n\"ref_nr\" into two blocks.\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 b34665db9e..72085e30b9 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1301,8 +1301,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \t\t\t\t\t\t    argv[i], argv[i]);\n \t\t\t} else\n \t\t\t\trefs[j++] = argv[i];\n+\t\t\tref_nr++;\n \t\t}\n-\t\tref_nr = j;\n \t}\n \n \tsigchain_push_common(unlock_pack_on_signal);\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338721","messageId":"20180208161936.8196-4-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 03/17] fetch: stop accessing \"remote\" variable indirectly","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:22Z","receivedAt":"2018-02-08T16:20:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Access the \"remote\" variable passed to the fetch_one() directly rather\nthan through the gtransport wrapper struct constructed in this\nfunction for other purposes.\n\nThis makes the code more readable, as it's now obvious that the remote\nstruct doesn't somehow get munged by the prepare_transport() function\nabove, which takes the \"remote\" struct as an argument and constructs\nthe \"gtransport\" struct, containing among other things the \"remote\"\nstruct.\n\nA subsequent change will copy this pattern to access a new\nremote->prune_tags field, but without the use of the gtransport\nvariable. It's useful once that change lands to see that the two\npieces of code behave exactly the same.\n\nThis pattern of accessing the container struct was added in\n737c5a9cde (\"fetch: make --prune configurable\", 2013-07-13) when this\ncode was initially introduced.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fetch.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 72085e30b9..a7705bc150 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1280,8 +1280,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \n \tif (prune < 0) {\n \t\t/* no command line request */\n-\t\tif (0 <= gtransport->remote->prune)\n-\t\t\tprune = gtransport->remote->prune;\n+\t\tif (0 <= remote->prune)\n+\t\t\tprune = remote->prune;\n \t\telse if (0 <= fetch_prune_config)\n \t\t\tprune = fetch_prune_config;\n \t\telse\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338722","messageId":"20180208161936.8196-6-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 05/17] fetch tests: refactor in preparation for testing tag pruning","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:24Z","receivedAt":"2018-02-08T16:20:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In a subsequent commit this function will learn to test for tag\npruning, prepare for that by making space for more variables, and\nmaking it clear that \"expected\" here refers to branches.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 9 ++++++---\n 1 file changed, 6 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 668c54be41..11da97f9b7 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -549,9 +549,12 @@ set_config_tristate () {\n }\n \n test_configured_prune () {\n-\tfetch_prune=$1 remote_origin_prune=$2 cmdline=$3 expected=$4\n+\tfetch_prune=$1\n+\tremote_origin_prune=$2\n+\tcmdline=$3\n+\texpected_branch=$4\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; $4\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; branch:$4\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\t(\n@@ -572,7 +575,7 @@ test_configured_prune () {\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n \n \t\t\tgit fetch $cmdline &&\n-\t\t\tcase \"$expected\" in\n+\t\t\tcase \"$expected_branch\" in\n \t\t\tpruned)\n \t\t\t\ttest_must_fail git rev-parse --verify refs/remotes/origin/newbranch\n \t\t\t\t;;\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338723","messageId":"20180208161936.8196-8-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 07/17] fetch tests: add a tag to be deleted to the pruning tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:26Z","receivedAt":"2018-02-08T16:20:22Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a tag to be deleted to the fetch --prune tests. The tag is always\nkept for now, which is the expected behavior, but now I can add a test\nfor tag pruning in a later commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 93 ++++++++++++++++++++++++++++++++------------------------\n 1 file changed, 53 insertions(+), 40 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex ab8b25344d..fad65bd885 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -552,21 +552,25 @@ test_configured_prune () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n \texpected_branch=$3\n-\tcmdline=$4\n+\texpected_tag=$4\n+\tcmdline=$5\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${4:+ $4}; branch:$3\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n+\t\tgit tag -f newtag &&\n \t\t(\n \t\t\tcd one &&\n \t\t\ttest_unconfig fetch.prune &&\n \t\t\ttest_unconfig remote.origin.prune &&\n \t\t\tgit fetch &&\n-\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch\n+\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch &&\n+\t\t\tgit rev-parse --verify refs/tags/newtag\n \t\t) &&\n \n \t\t# now remove it\n \t\tgit branch -d newbranch &&\n+\t\tgit tag -d newtag &&\n \n \t\t# then test\n \t\t(\n@@ -582,6 +586,14 @@ test_configured_prune () {\n \t\t\tkept)\n \t\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch\n \t\t\t\t;;\n+\t\t\tesac &&\n+\t\t\tcase \"$expected_tag\" in\n+\t\t\tpruned)\n+\t\t\t\ttest_must_fail git rev-parse --verify refs/tags/newtag\n+\t\t\t\t;;\n+\t\t\tkept)\n+\t\t\t\tgit rev-parse --verify refs/tags/newtag\n+\t\t\t\t;;\n \t\t\tesac\n \t\t)\n \t'\n@@ -590,44 +602,45 @@ test_configured_prune () {\n # $1 config: fetch.prune\n # $2 config: remote.<name>.prune\n # $3 expect: branch to be pruned?\n-# $4 git-fetch $cmdline:\n+# $4 expect: tag to be pruned?\n+# $5 git-fetch $cmdline:\n #\n-#                     $1    $2    $3     $4\n-test_configured_prune unset unset kept   \"\"\n-test_configured_prune unset unset kept   \"--no-prune\"\n-test_configured_prune unset unset pruned \"--prune\"\n-\n-test_configured_prune false unset kept   \"\"\n-test_configured_prune false unset kept   \"--no-prune\"\n-test_configured_prune false unset pruned \"--prune\"\n-\n-test_configured_prune true  unset pruned \"\"\n-test_configured_prune true  unset pruned \"--prune\"\n-test_configured_prune true  unset kept   \"--no-prune\"\n-\n-test_configured_prune unset false kept   \"\"\n-test_configured_prune unset false kept   \"--no-prune\"\n-test_configured_prune unset false pruned \"--prune\"\n-\n-test_configured_prune false false kept   \"\"\n-test_configured_prune false false kept   \"--no-prune\"\n-test_configured_prune false false pruned \"--prune\"\n-\n-test_configured_prune true  false kept   \"\"\n-test_configured_prune true  false pruned \"--prune\"\n-test_configured_prune true  false kept   \"--no-prune\"\n-\n-test_configured_prune unset true  pruned \"\"\n-test_configured_prune unset true  kept   \"--no-prune\"\n-test_configured_prune unset true  pruned \"--prune\"\n-\n-test_configured_prune false true  pruned \"\"\n-test_configured_prune false true  kept   \"--no-prune\"\n-test_configured_prune false true  pruned \"--prune\"\n-\n-test_configured_prune true  true  pruned \"\"\n-test_configured_prune true  true  pruned \"--prune\"\n-test_configured_prune true  true  kept   \"--no-prune\"\n+#                     $1    $2    $3     $4     $5\n+test_configured_prune unset unset kept   kept   \"\"\n+test_configured_prune unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset unset pruned kept   \"--prune\"\n+\n+test_configured_prune false unset kept   kept   \"\"\n+test_configured_prune false unset kept   kept   \"--no-prune\"\n+test_configured_prune false unset pruned kept   \"--prune\"\n+\n+test_configured_prune true  unset pruned kept   \"\"\n+test_configured_prune true  unset pruned kept   \"--prune\"\n+test_configured_prune true  unset kept   kept   \"--no-prune\"\n+\n+test_configured_prune unset false kept   kept   \"\"\n+test_configured_prune unset false kept   kept   \"--no-prune\"\n+test_configured_prune unset false pruned kept   \"--prune\"\n+\n+test_configured_prune false false kept   kept   \"\"\n+test_configured_prune false false kept   kept   \"--no-prune\"\n+test_configured_prune false false pruned kept   \"--prune\"\n+\n+test_configured_prune true  false kept   kept   \"\"\n+test_configured_prune true  false pruned kept   \"--prune\"\n+test_configured_prune true  false kept   kept   \"--no-prune\"\n+\n+test_configured_prune unset true  pruned kept   \"\"\n+test_configured_prune unset true  kept   kept   \"--no-prune\"\n+test_configured_prune unset true  pruned kept   \"--prune\"\n+\n+test_configured_prune false true  pruned kept   \"\"\n+test_configured_prune false true  kept   kept   \"--no-prune\"\n+test_configured_prune false true  pruned kept   \"--prune\"\n+\n+test_configured_prune true  true  pruned kept   \"\"\n+test_configured_prune true  true  pruned kept   \"--prune\"\n+test_configured_prune true  true  kept   kept   \"--no-prune\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338724","messageId":"20180208161936.8196-12-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 11/17] fetch tests: fetch <url> <spec> as well as fetch [<remote>]","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:30Z","receivedAt":"2018-02-08T16:20:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"When a remote URL is supplied on the command-line the internals of the\nfetch are different, in particular the code in get_ref_map(). An\nearlier version of the subsequent fetch.pruneTags patch hid a segfault\nbecause the difference wasn't tested for.\n\nNow all the tests are run as both of the variants of:\n\n    git fetch\n    git -c [...] fetch $(git config remote.origin.url) $(git config remote.origin.fetch)\n\nI'm using -c because while the [fetch] config just set by\nset_config_tristate will be picked up, the remote.origin.* config\nwon't override it as intended.\n\nWork around that and turn this into a purely command-line test by\nalways setting the variables on the command-line, and translate any\nsetting of remote.origin.X into fetch.X.\n\nThe reason for choosing the names \"name\" and \"link\" as opposed to\ne.g. \"named\" and \"url\" is because they're the same length, which makes\nthe test output easier to read as it will be aligned.\n\nDue to shellscript quoting madness it's not worthwhile to do all of\nthis within a test_expect_success, but do the parts that can easily be\ndone there, including the one-time setting of variables that don't\nchange between runs to be used by subsequent runs in the 'prune_type\nsetup' test.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 47 +++++++++++++++++++++++++++++++++++++++++++----\n 1 file changed, 43 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex dfc749f576..73ba83454f 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -548,18 +548,52 @@ set_config_tristate () {\n \t\t;;\n \t*)\n \t\tgit config \"$1\" \"$2\"\n+\t\tkey=$(echo $1 | sed -e 's/^remote\\.origin/fetch/')\n+\t\tgit_fetch_c=\"$git_fetch_c -c $key=$2\"\n \t\t;;\n \tesac\n }\n \n test_configured_prune () {\n+\ttest_configured_prune_type \"$@\" \"name\"\n+\ttest_configured_prune_type \"$@\" \"link\"\n+}\n+\n+test_configured_prune_type () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n \texpected_branch=$3\n \texpected_tag=$4\n \tcmdline=$5\n-\n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n+\tmode=$6\n+\n+\tif ! test -e prune-type-setup-done\n+\tthen\n+\t\ttest_expect_success 'prune_type setup' '\n+\t\t\tgit -C one config remote.origin.url >one.remote-url &&\n+\t\t\tgit -C one config remote.origin.fetch >one.remote-fetch &&\n+\t\t\tremote_url=\"file://$(cat one.remote-url)\" &&\n+\t\t\tremote_fetch=\"$(cat one.remote-fetch)\" &&\n+\t\t\tcmdline_setup=\"\\\"$remote_url\\\" \\\"$remote_fetch\\\"\" &&\n+\t\t\ttouch prune-type-setup-done\n+\t\t'\n+\tfi\n+\n+\tif test \"$mode\" = 'link'\n+\tthen\n+\t\tnew_cmdline=\"\"\n+\n+\t\tif test \"$cmdline\" = \"\"\n+\t\tthen\n+\t\t\tnew_cmdline=$cmdline_setup\n+\t\telse\n+\t\t\tnew_cmdline=$(printf \"%s\" \"$cmdline\" | perl -pe 's[origin(?!/)][\"'\"$remote_url\"'\"]g')\n+\t\tfi\n+\n+\t\tcmdline=\"$new_cmdline\"\n+\tfi\n+\n+\ttest_expect_success \"$mode prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\tgit tag -f newtag &&\n@@ -567,7 +601,7 @@ test_configured_prune () {\n \t\t\tcd one &&\n \t\t\ttest_unconfig fetch.prune &&\n \t\t\ttest_unconfig remote.origin.prune &&\n-\t\t\tgit fetch &&\n+\t\t\tgit fetch '\"$cmdline_setup\"' &&\n \t\t\tgit rev-parse --verify refs/remotes/origin/newbranch &&\n \t\t\tgit rev-parse --verify refs/tags/newtag\n \t\t) &&\n@@ -579,10 +613,15 @@ test_configured_prune () {\n \t\t# then test\n \t\t(\n \t\t\tcd one &&\n+\t\t\tgit_fetch_c=\"\" &&\n \t\t\tset_config_tristate fetch.prune $fetch_prune &&\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n \n-\t\t\tgit fetch '\"$cmdline\"' &&\n+\t\t\tif test \"$mode\" != \"link\"\n+\t\t\tthen\n+\t\t\t\tgit_fetch_c=\"\"\n+\t\t\tfi &&\n+\t\t\tgit$git_fetch_c fetch '\"$cmdline\"' &&\n \t\t\tcase \"$expected_branch\" in\n \t\t\tpruned)\n \t\t\t\ttest_must_fail git rev-parse --verify refs/remotes/origin/newbranch\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338725","messageId":"20180208161936.8196-13-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 12/17] git fetch doc: add a new section to explain the ins & outs of pruning","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:31Z","receivedAt":"2018-02-08T16:20:30Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a new section to canonically explain how remote reference pruning\nworks, and how users should be careful about using it in conjunction\nwith tag refspecs in particular.\n\nA subsequent commit will update the git-remote documentation to refer\nto this section, and details the motivation for writing this in the\nfirst place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-fetch.txt | 49 +++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 49 insertions(+)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex b153aefa68..18fac0da2e 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -99,6 +99,55 @@ The latter use of the `remote.<repository>.fetch` values can be\n overridden by giving the `--refmap=<refspec>` parameter(s) on the\n command line.\n \n+PRUNING\n+-------\n+\n+Git has a default disposition of keeping data unless it's explicitly\n+thrown away; this extends to holding onto local references to branches\n+on remotes that have themselves deleted those branches.\n+\n+If left to accumulate, these stale references might make performance\n+worse on big and busy repos that have a lot of branch churn, and\n+e.g. make the output of commands like `git branch -a --contains\n+<commit>` needlessly verbose, as well as impacting anything else\n+that'll work with the complete set of known references.\n+\n+These remote tracking references can be deleted as a one-off with\n+either of:\n+\n+------------------------------------------------\n+# While fetching\n+$ git fetch --prune <name>\n+\n+# Only prune, don't fetch\n+$ git remote prune <name>\n+------------------------------------------------\n+\n+To prune references as part of your normal workflow without needing to\n+remember to run that set `fetch.prune` globally, or\n+`remote.<name>.prune` per-remote in the config. See\n+linkgit:git-config[1].\n+\n+Here's where things get tricky and more specific. The pruning feature\n+doesn't actually care about branches, instead it'll prune local <->\n+remote references as a function of the refspec of the remote (see\n+`<refspec>` and <<CRTB,CONFIGURED REMOTE-TRACKING BRANCHES>> above).\n+\n+Therefore if the refspec for the remote includes\n+e.g. `refs/tags/*:refs/tags/*`, or you manually run e.g. `git fetch\n+--prune <name> \"refs/tags/*:refs/tags/*\"` it won't be stale remote\n+tracking branches that are deleted, but any local tag that doesn't\n+exist on the remote.\n+\n+This might not be what you expect, i.e. you want to prune remote\n+`<name>`, but also explicitly fetch tags from it, so when you fetch\n+from it you delete all your local tags, most of which may not have\n+come from the `<name>` remote in the first place.\n+\n+So be careful when using this with a refspec like\n+`refs/tags/*:refs/tags/*`, or any other refspec which might map\n+references from multiple remotes to the same local namespace.\n+\n OUTPUT\n ------\n \n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338726","messageId":"20180208161936.8196-14-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 13/17] git remote doc: correct dangerous lies about what prune does","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:32Z","receivedAt":"2018-02-08T16:20:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The \"git remote prune <name>\" command uses the same machinery as \"git\nfetch <name> --prune\", and shares all the same caveats, but its\ndocumentation has suggested that it'll just \"delete stale\nremote-tracking branches under <name>\".\n\nThis isn't true, and hasn't been true since at least v1.8.5.6 (the\noldest version I could be bothered to test).\n\nE.g. if \"refs/tags/*:refs/tags/*\" is explicitly set in the refspec of\nthe remote it'll delete all local tags <name> doesn't know about.\n\nInstead, briefly give the reader just enough of a hint that this\noption might constitute a shotgun aimed at their foot, and point them\nto the new PRUNING section in the git-fetch documentation which\nexplains all the nuances of what this facility does.\n\nSee \"[BUG] git remote prune removes local tags, depending on fetch\nconfig\" (CACi5S_39wNrbfjLfn0xhCY+uewtFN2YmnAcRc86z6pjUTjWPHQ@mail.gmail.com)\nby Michael Giuffrida for the initial report.\n\nReported-by: Michael Giuffrida <michaelpg@chromium.org>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-remote.txt | 14 +++++++++-----\n 1 file changed, 9 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 577b969c1b..04e2410aec 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -172,10 +172,14 @@ With `-n` option, the remote heads are not queried first with\n \n 'prune'::\n \n-Deletes all stale remote-tracking branches under <name>.\n-These stale branches have already been removed from the remote repository\n-referenced by <name>, but are still locally available in\n-\"remotes/<name>\".\n+Deletes stale references associated with <name>. By default stale\n+remote-tracking branches under <name>, but depending on global\n+configuration and the configuration of the remote we might even prune\n+local tags that haven't been pushed there. Equivalent to `git fetch\n+--prune <name>`, except that no new references will be fetched.\n++\n+See the PRUNING section of linkgit:git-fetch[1] for what it'll prune\n+depending on various configuration.\n +\n With `--dry-run` option, report what branches will be pruned, but do not\n actually prune them.\n@@ -189,7 +193,7 @@ remotes.default is not defined, all remotes which do not have the\n configuration parameter remote.<name>.skipDefaultUpdate set to true will\n be updated.  (See linkgit:git-config[1]).\n +\n-With `--prune` option, prune all the remotes that are updated.\n+With `--prune` option, run pruning against all the remotes that are updated.\n \n \n DISCUSSION\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338727","messageId":"20180208161936.8196-17-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 16/17] fetch: add a --fetch-prune option and fetch.pruneTags config","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:35Z","receivedAt":"2018-02-08T16:20:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a --fetch-prune option to git-fetch, along with fetch.pruneTags\nconfig option. This allows for doing any of:\n\n    git fetch -p -P\n    git fetch --prune --prune-tags\n    git fetch -p -P origin\n    git fetch --prune --prune-tags origin\n\nOr simply:\n\n    git config fetch.prune true &&\n    git config fetch.pruneTags true &&\n    git fetch\n\nInstead of the much more verbose:\n\n    git fetch --prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\n\nBefore this feature it was painful to support the use-case of pulling\nfrom a repo which is having both its branches *and* tags deleted\nregularly, and have our local references to reflect upstream.\n\nAt work we create deployment tags in the repo for each rollout, and\nthere's *lots* of those, so they're archived within weeks for\nperformance reasons.\n\nWithout this change it's hard to centrally configure such repos in\n/etc/gitconfig (on servers that are only used for working with\nthem). You need to set fetch.prune=true globally, and then for each\nrepo:\n\n    git -C {} config --replace-all remote.origin.fetch \"refs/tags/*:refs/tags/*\" \"^\\+*refs/tags/\\*:refs/tags/\\*$\"\n\nNow I can simply set fetch.pruneTags=true in /etc/gitconfig as well,\nand users running \"git pull\" will automatically get the pruning\nsemantics I want.\n\nEven though \"git remote\" has corresponding \"prune\" and \"update\n--prune\" subcommands I'm intentionally not adding a corresponding\nprune-tags or \"update --prune --prune-tags\" mode to that command.\n\nIt's advertised (as noted in my recent \"git remote doc: correct\ndangerous lies about what prune does\") as only modifying remote\ntracking references, whereas any --prune-tags option is always going\nto modify what from the user's perspective is a local copy of the tag,\nsince there's no such thing as a remote tracking tag.\n\nIdeally add_prune_tags_to_fetch_refspec() would be something that\nwould use ALLOC_GROW() to grow the 'fetch` member of the 'remote'\nstruct. Instead I'm realloc-ing remote->fetch and adding the\ntag_refspec to the end.\n\nThe reason is that parse_{fetch,push}_refspec which allocate the\nrefspec (ultimately remote->fetch) struct are called many places that\ndon't have access to a 'remote' struct. It would be hard to change all\ntheir callsites to be amenable to carry around the bookkeeping\nvariables required for dynamic allocation.\n\nAll the other callers of the API first incrementally construct the\nstring version of the refspec in remote->fetch_refspec via\nadd_fetch_refspec(), before finally calling parse_fetch_refspec() via\nsome variation of remote_get().\n\nIt's less of a pain to deal with the one special case that needs to\nmodify already constructed refspecs than to chase down and change all\nthe other callsites. The API I'm adding is intentionally not\ngeneralized because if we add more of these we'd probably want to\nre-visit how this is done.\n\nSee my \"Re: [BUG] git remote prune removes local tags, depending on\nfetch config\" (87po6ahx87.fsf@evledraar.gmail.com;\nhttps://public-inbox.org/git/87po6ahx87.fsf@evledraar.gmail.com/) for\nmore background info.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt               | 14 +++++++\n Documentation/fetch-options.txt        | 14 ++++++-\n Documentation/git-fetch.txt            | 47 +++++++++++++++++++++++\n builtin/fetch.c                        | 32 ++++++++++++++--\n contrib/completion/git-completion.bash |  2 +-\n remote.c                               | 14 +++++++\n remote.h                               |  3 ++\n t/t5510-fetch.sh                       | 69 ++++++++++++++++++++++++++++++++++\n 8 files changed, 190 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 0f27af5760..e254bfd531 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1401,6 +1401,14 @@ fetch.prune::\n \toption was given on the command line.  See also `remote.<name>.prune`\n \tand the PRUNING section of linkgit:git-fetch[1].\n \n+fetch.pruneTags::\n+\tIf true, fetch will automatically behave as if the\n+\t`refs/tags/*:refs/tags/*` refspec was provided when pruning,\n+\tif not set already. This allows for setting both this option\n+\tand `fetch.prune` to maintain a 1=1 mapping to upstream\n+\trefs. See also `remote.<name>.pruneTags` and the PRUNING\n+\tsection of linkgit:git-fetch[1].\n+\n fetch.output::\n \tControl how ref update status is printed. Valid values are\n \t`full` and `compact`. Default value is `full`. See section\n@@ -2945,6 +2953,12 @@ remote.<name>.prune::\n \tremove any remote-tracking references that no longer exist on the\n \tremote (as if the `--prune` option was given on the command line).\n \tOverrides `fetch.prune` settings, if any.\n+\n+remote.<name>.pruneTags::\n+\tWhen set to true, fetching from this remote by default will also\n+\tremove any local tags that no longer exist on the remote if pruning\n+\tis activated in general via `remote.<name>.prune`, `fetch.prune` or\n+\t`--prune`. Overrides `fetch.pruneTags` settings, if any.\n +\n See also `remote.<name>.prune` and the PRUNING section of\n linkgit:git-fetch[1].\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 9f5c85ad96..8631e365f4 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -73,7 +73,19 @@ ifndef::git-pull[]\n \tare fetched due to an explicit refspec (either on the command\n \tline or in the remote configuration, for example if the remote\n \twas cloned with the --mirror option), then they are also\n-\tsubject to pruning.\n+\tsubject to pruning. Supplying `--prune-tags` is a shorthand for\n+\tproviding the tag refspec.\n++\n+See the PRUNING section below for more details.\n+\n+-P::\n+--prune-tags::\n+\tBefore fetching, remove any local tags that no longer exist on\n+\tthe remote if `--prune` is enabled. This option should be used\n+\tmore carefully, unlike `--prune` it will remove any local\n+\treferences (local tags) that have been created. This option is\n+\ta shorthand for providing the explicit tag refspec along with\n+\t`--prune`, see the discussion about that in its documentation.\n +\n See the PRUNING section below for more details.\n \ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex 18fac0da2e..574206d139 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -148,6 +148,53 @@ So be careful when using this with a refspec like\n `refs/tags/*:refs/tags/*`, or any other refspec which might map\n references from multiple remotes to the same local namespace.\n \n+Since keeping up-to-date with both branches and tags on the remote is\n+a common use-case the `--prune-tags` option can be supplied along with\n+`--prune` to prune local tags that don't exist on the remote, and\n+force-update those tags that differ. Tag pruning can also be enabled\n+with `fetch.pruneTags` or `remote.<name>.pruneTags` in the config. See\n+linkgit:git-config[1].\n+\n+The `--prune-tags` option is equivalent to having\n+`refs/tags/*:refs/tags/*` declared in the refspecs of the remote. This\n+can lead to some seemingly strange interactions:\n+\n+------------------------------------------------\n+# These both fetch tags\n+$ git fetch --no-tags origin 'refs/tags/*:refs/tags/*'\n+$ git fetch --no-tags --prune-tags origin\n+------------------------------------------------\n+\n+The reason it doesn't error out when provided without `--prune` or its\n+config versions is for flexibility of the configured versions, and to\n+maintain a 1=1 mapping between what the command line flags do, and\n+what the configuration versions do.\n+\n+It's reasonable to e.g. configure `fetch.pruneTags=true` in\n+`~/.gitconfig` to have tags pruned whenever `git fetch --prune` is\n+run, without making every invocation of `git fetch` without `--prune`\n+an error.\n+\n+Another special case of `--prune-tags` is that\n+`refs/tags/*:refs/tags/*` will not be implicitly provided if an URL is\n+being fetched. I.e.:\n+\n+------------------------------------------------\n+$ git fetch <url> --prune --prune-tags\n+------------------------------------------------\n+\n+Will prune no tags, as opposed to:\n+\n+------------------------------------------------\n+$ git fetch origin --prune --prune-tags\n+------------------------------------------------\n+\n+To prune tags given a URL supply the refspec explicitly:\n+\n+------------------------------------------------\n+$ git fetch <url> --prune 'refs/tags/*:refs/tags/*'\n+------------------------------------------------\n+\n OUTPUT\n ------\n \ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex a7705bc150..55a0fc37be 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -38,6 +38,10 @@ static int fetch_prune_config = -1; /* unspecified */\n static int prune = -1; /* unspecified */\n #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */\n \n+static int fetch_prune_tags_config = -1; /* unspecified */\n+static int prune_tags = -1; /* unspecified */\n+#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */\n+\n static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;\n static int progress = -1;\n static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;\n@@ -64,6 +68,11 @@ static int git_fetch_config(const char *k, const char *v, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(k, \"fetch.prunetags\")) {\n+\t\tfetch_prune_tags_config = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(k, \"submodule.recurse\")) {\n \t\tint r = git_config_bool(k, v) ?\n \t\t\tRECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;\n@@ -126,6 +135,8 @@ static struct option builtin_fetch_options[] = {\n \t\t    N_(\"number of submodules fetched in parallel\")),\n \tOPT_BOOL('p', \"prune\", &prune,\n \t\t N_(\"prune remote-tracking branches no longer on remote\")),\n+\tOPT_BOOL('P', \"prune-tags\", &prune_tags,\n+\t\t N_(\"prune local tags no longer on remote and clobber changed tags\")),\n \t{ OPTION_CALLBACK, 0, \"recurse-submodules\", &recurse_submodules, N_(\"on-demand\"),\n \t\t    N_(\"control recursive fetching of submodules\"),\n \t\t    PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },\n@@ -1212,6 +1223,8 @@ static void add_options_to_argv(struct argv_array *argv)\n \t\targv_array_push(argv, \"--dry-run\");\n \tif (prune != -1)\n \t\targv_array_push(argv, prune ? \"--prune\" : \"--no-prune\");\n+\tif (prune_tags != -1)\n+\t\targv_array_push(argv, prune_tags ? \"--prune-tags\" : \"--no-prune-tags\");\n \tif (update_head_ok)\n \t\targv_array_push(argv, \"--update-head-ok\");\n \tif (force)\n@@ -1265,7 +1278,7 @@ static int fetch_multiple(struct string_list *list)\n \treturn result;\n }\n \n-static int fetch_one(struct remote *remote, int argc, const char **argv)\n+static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok)\n {\n \tstatic const char **refs = NULL;\n \tstruct refspec *refspec;\n@@ -1288,6 +1301,19 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \t\t\tprune = PRUNE_BY_DEFAULT;\n \t}\n \n+\tif (prune_tags < 0) {\n+\t\t/* no command line request */\n+\t\tif (0 <= remote->prune_tags)\n+\t\t\tprune_tags = remote->prune_tags;\n+\t\telse if (0 <= fetch_prune_tags_config)\n+\t\t\tprune_tags = fetch_prune_tags_config;\n+\t\telse\n+\t\t\tprune_tags = PRUNE_TAGS_BY_DEFAULT;\n+\t}\n+\n+\tif (prune_tags_ok && prune_tags && remote_is_configured(remote, 0))\n+\t\tadd_prune_tags_to_fetch_refspec(remote);\n+\n \tif (argc > 0) {\n \t\tint j = 0;\n \t\tint i;\n@@ -1368,7 +1394,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \t} else if (argc == 0) {\n \t\t/* No arguments -- use default remote */\n \t\tremote = remote_get(NULL);\n-\t\tresult = fetch_one(remote, argc, argv);\n+\t\tresult = fetch_one(remote, argc, argv, 1);\n \t} else if (multiple) {\n \t\t/* All arguments are assumed to be remotes or groups */\n \t\tfor (i = 0; i < argc; i++)\n@@ -1386,7 +1412,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \t\t} else {\n \t\t\t/* Zero or one remotes */\n \t\t\tremote = remote_get(argv[0]);\n-\t\t\tresult = fetch_one(remote, argc-1, argv+1);\n+\t\t\tresult = fetch_one(remote, argc-1, argv+1, argc == 1);\n \t\t}\n \t}\n \ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 3683c772c5..4ecd0d4d7a 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1468,7 +1468,7 @@ __git_fetch_recurse_submodules=\"yes on-demand no\"\n __git_fetch_options=\"\n \t--quiet --verbose --append --upload-pack --force --keep --depth=\n \t--tags --no-tags --all --prune --dry-run --recurse-submodules=\n-\t--unshallow --update-shallow\n+\t--unshallow --update-shallow --prune-tags\n \"\n \n _git_fetch ()\ndiff --git a/remote.c b/remote.c\nindex 356c123e3e..d670eed4c4 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -104,6 +104,17 @@ static void add_fetch_refspec(struct remote *remote, const char *ref)\n \tremote->fetch_refspec[remote->fetch_refspec_nr++] = ref;\n }\n \n+void add_prune_tags_to_fetch_refspec(struct remote *remote)\n+{\n+\tint nr = remote->fetch_refspec_nr;\n+\tint bufsize = nr  + 1;\n+\tint size = sizeof(struct refspec);\n+\n+\tremote->fetch = xrealloc(remote->fetch, size  * bufsize);\n+\tmemcpy(&remote->fetch[nr], tag_refspec, size);\n+\tadd_fetch_refspec(remote, xstrdup(TAG_REFSPEC));\n+}\n+\n static void add_url(struct remote *remote, const char *url)\n {\n \tALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);\n@@ -174,6 +185,7 @@ static struct remote *make_remote(const char *name, int len)\n \n \tret = xcalloc(1, sizeof(struct remote));\n \tret->prune = -1;  /* unspecified */\n+\tret->prune_tags = -1;  /* unspecified */\n \tALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);\n \tremotes[remotes_nr++] = ret;\n \tret->name = xstrndup(name, len);\n@@ -392,6 +404,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tremote->skip_default_update = git_config_bool(key, value);\n \telse if (!strcmp(subkey, \"prune\"))\n \t\tremote->prune = git_config_bool(key, value);\n+\telse if (!strcmp(subkey, \"prunetags\"))\n+\t\tremote->prune_tags = git_config_bool(key, value);\n \telse if (!strcmp(subkey, \"url\")) {\n \t\tconst char *v;\n \t\tif (git_config_string(&v, key, value))\ndiff --git a/remote.h b/remote.h\nindex 80fea6dd11..271afe1bab 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -47,6 +47,7 @@ struct remote {\n \tint skip_default_update;\n \tint mirror;\n \tint prune;\n+\tint prune_tags;\n \n \tconst char *receivepack;\n \tconst char *uploadpack;\n@@ -299,4 +300,6 @@ void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);\n \n #define TAG_REFSPEC \"refs/tags/*:refs/tags/*\"\n \n+void add_prune_tags_to_fetch_refspec(struct remote *remote);\n+\n #endif\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 501d15dd42..6f3ab7695a 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -592,6 +592,15 @@ test_configured_prune_type () {\n \t\t\tnew_cmdline=$(printf \"%s\" \"$cmdline\" | perl -pe 's[origin(?!/)][\"'\"$remote_url\"'\"]g')\n \t\tfi\n \n+\t\tif test \"$fetch_prune_tags\" = 'true' ||\n+\t\t   test \"$remote_origin_prune_tags\" = 'true'\n+\t\tthen\n+\t\t\tif ! printf '%s' \"$cmdline\" | grep -q refs/remotes/origin/\n+\t\t\tthen\n+\t\t\t\tnew_cmdline=\"$new_cmdline refs/tags/*:refs/tags/*\"\n+\t\t\tfi\n+\t\tfi\n+\n \t\tcmdline=\"$new_cmdline\"\n \tfi\n \n@@ -705,6 +714,66 @@ test_configured_prune true  true  unset unset kept   pruned \\\n test_configured_prune true  true  unset unset pruned pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n+# --prune-tags on its own does nothing, needs --prune as well, same\n+# for for fetch.pruneTags without fetch.prune\n+test_configured_prune unset unset unset unset kept kept     \"--prune-tags\"\n+test_configured_prune unset unset true unset  kept kept     \"\"\n+test_configured_prune unset unset unset true  kept kept     \"\"\n+\n+# These will prune the tags\n+test_configured_prune unset unset unset unset pruned pruned \"--prune --prune-tags\"\n+test_configured_prune true  unset true  unset pruned pruned \"\"\n+test_configured_prune unset true  unset true  pruned pruned \"\"\n+\n+# remote.<name>.pruneTags overrides fetch.pruneTags, just like\n+# remote.<name>.prune overrides fetch.prune if set.\n+test_configured_prune true  unset true unset pruned pruned  \"\"\n+test_configured_prune false true  false true  pruned pruned \"\"\n+test_configured_prune true  false true  false kept   kept   \"\"\n+\n+# When --prune-tags is supplied it's ignored if an explict refspec is\n+# given, same for the configuration options.\n+test_configured_prune unset unset unset unset pruned kept \\\n+\t\"--prune --prune-tags origin +refs/heads/*:refs/remotes/origin/*\"\n+test_configured_prune unset unset true  unset pruned kept \\\n+\t\"--prune origin +refs/heads/*:refs/remotes/origin/*\"\n+test_configured_prune unset unset unset true pruned  kept \\\n+\t\"--prune origin +refs/heads/*:refs/remotes/origin/*\"\n+\n+# Pruning that also takes place if s!origin!<file:// url of remote>!,\n+# or otherwise uses the file://-specific codepath. However, because\n+# there's no implicit +refs/heads/*:refs/remotes/origin/* refspec and\n+# supplying it on the command-line negate --prune-tags the branches\n+# will not be pruned.\n+test_configured_prune_type unset unset unset unset kept kept     \"origin --prune-tags\" \"name\"\n+test_configured_prune_type unset unset unset unset kept kept     \"origin --prune-tags\" \"link\"\n+test_configured_prune_type unset unset unset unset pruned pruned \"origin --prune --prune-tags\" \"name\"\n+test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune --prune-tags\" \"link\"\n+test_configured_prune_type unset unset unset unset pruned pruned \"--prune --prune-tags origin\" \"name\"\n+test_configured_prune_type unset unset unset unset kept   kept   \"--prune --prune-tags origin\" \"link\"\n+test_configured_prune_type unset unset true  unset pruned pruned \"--prune origin\" \"name\"\n+test_configured_prune_type unset unset true  unset kept   pruned \"--prune origin\" \"link\"\n+test_configured_prune_type unset unset unset true  pruned pruned \"--prune origin\" \"name\"\n+test_configured_prune_type unset unset unset true  kept   pruned \"--prune origin\" \"link\"\n+test_configured_prune_type true  unset true  unset pruned pruned \"origin\" \"name\"\n+test_configured_prune_type true  unset true  unset kept   pruned \"origin\" \"link\"\n+test_configured_prune_type unset  true true  unset pruned pruned \"origin\" \"name\"\n+test_configured_prune_type unset  true true  unset kept   pruned \"origin\" \"link\"\n+test_configured_prune_type unset  true unset true  pruned pruned \"origin\" \"name\"\n+test_configured_prune_type unset  true unset true  kept   pruned \"origin\" \"link\"\n+\n+# When all remote.origin.fetch settings are deleted a --prune\n+# --prune-tags still implicitly supplies refs/tags/*:refs/tags/* so\n+# tags, but not tracking branches, will be deleted.\n+test_expect_success 'remove remote.origin.fetch \"one\"' '\n+\t(\n+\t\tcd one &&\n+\t\tgit config --unset-all remote.origin.fetch\n+\t)\n+'\n+test_configured_prune_type unset unset unset unset kept pruned \"origin --prune --prune-tags\" \"name\"\n+test_configured_prune_type unset unset unset unset kept kept   \"origin --prune --prune-tags\" \"link\"\n+\n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n \ttest_commit oneside &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338728","messageId":"20180208161936.8196-16-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 15/17] fetch tests: add scaffolding for the new fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:34Z","receivedAt":"2018-02-08T16:20:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The fetch.pruneTags configuration doesn't exist yet, but will be added\nin a subsequent commit. Since testing for it requires adding new\nparameters to the test_configured_prune function it's easier to review\nthis patch first to assert that no functional changes are introduced\nyet.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 92 ++++++++++++++++++++++++++++++--------------------------\n 1 file changed, 50 insertions(+), 42 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 73ba83454f..501d15dd42 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -562,10 +562,12 @@ test_configured_prune () {\n test_configured_prune_type () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n-\texpected_branch=$3\n-\texpected_tag=$4\n-\tcmdline=$5\n-\tmode=$6\n+\tfetch_prune_tags=$3\n+\tremote_origin_prune_tags=$4\n+\texpected_branch=$5\n+\texpected_tag=$6\n+\tcmdline=$7\n+\tmode=$8\n \n \tif ! test -e prune-type-setup-done\n \tthen\n@@ -593,14 +595,16 @@ test_configured_prune_type () {\n \t\tcmdline=\"$new_cmdline\"\n \tfi\n \n-\ttest_expect_success \"$mode prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n+\ttest_expect_success \"$mode prune fetch.prune=$1 remote.origin.prune=$2 fetch.pruneTags=$3 remote.origin.pruneTags=$4${7:+ $7}; branch:$5 tag:$6\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\tgit tag -f newtag &&\n \t\t(\n \t\t\tcd one &&\n \t\t\ttest_unconfig fetch.prune &&\n+\t\t\ttest_unconfig fetch.pruneTags &&\n \t\t\ttest_unconfig remote.origin.prune &&\n+\t\t\ttest_unconfig remote.origin.pruneTags &&\n \t\t\tgit fetch '\"$cmdline_setup\"' &&\n \t\t\tgit rev-parse --verify refs/remotes/origin/newbranch &&\n \t\t\tgit rev-parse --verify refs/tags/newtag\n@@ -615,7 +619,9 @@ test_configured_prune_type () {\n \t\t\tcd one &&\n \t\t\tgit_fetch_c=\"\" &&\n \t\t\tset_config_tristate fetch.prune $fetch_prune &&\n+\t\t\tset_config_tristate fetch.pruneTags $fetch_prune_tags &&\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n+\t\t\tset_config_tristate remote.origin.pruneTags $remote_origin_prune_tags &&\n \n \t\t\tif test \"$mode\" != \"link\"\n \t\t\tthen\n@@ -644,57 +650,59 @@ test_configured_prune_type () {\n \n # $1 config: fetch.prune\n # $2 config: remote.<name>.prune\n-# $3 expect: branch to be pruned?\n-# $4 expect: tag to be pruned?\n-# $5 git-fetch $cmdline:\n+# $3 config: fetch.pruneTags\n+# $4 config: remote.<name>.pruneTags\n+# $5 expect: branch to be pruned?\n+# $6 expect: tag to be pruned?\n+# $7 git-fetch $cmdline:\n #\n-#                     $1    $2    $3     $4     $5\n-test_configured_prune unset unset kept   kept   \"\"\n-test_configured_prune unset unset kept   kept   \"--no-prune\"\n-test_configured_prune unset unset pruned kept   \"--prune\"\n-test_configured_prune unset unset kept   pruned \\\n+#                     $1    $2    $3    $4    $5     $6     $7\n+test_configured_prune unset unset unset unset kept   kept   \"\"\n+test_configured_prune unset unset unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset unset unset unset pruned kept   \"--prune\"\n+test_configured_prune unset unset unset unset kept   pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/*\"\n-test_configured_prune unset unset pruned pruned \\\n+test_configured_prune unset unset unset unset pruned pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n-test_configured_prune false unset kept   kept   \"\"\n-test_configured_prune false unset kept   kept   \"--no-prune\"\n-test_configured_prune false unset pruned kept   \"--prune\"\n+test_configured_prune false unset unset unset kept   kept   \"\"\n+test_configured_prune false unset unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false unset unset unset pruned kept   \"--prune\"\n \n-test_configured_prune true  unset pruned kept   \"\"\n-test_configured_prune true  unset pruned kept   \"--prune\"\n-test_configured_prune true  unset kept   kept   \"--no-prune\"\n+test_configured_prune true  unset unset unset pruned kept   \"\"\n+test_configured_prune true  unset unset unset pruned kept   \"--prune\"\n+test_configured_prune true  unset unset unset kept   kept   \"--no-prune\"\n \n-test_configured_prune unset false kept   kept   \"\"\n-test_configured_prune unset false kept   kept   \"--no-prune\"\n-test_configured_prune unset false pruned kept   \"--prune\"\n+test_configured_prune unset false unset unset kept   kept   \"\"\n+test_configured_prune unset false unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset false unset unset pruned kept   \"--prune\"\n \n-test_configured_prune false false kept   kept   \"\"\n-test_configured_prune false false kept   kept   \"--no-prune\"\n-test_configured_prune false false pruned kept   \"--prune\"\n-test_configured_prune false false kept   pruned \\\n+test_configured_prune false false unset unset kept   kept   \"\"\n+test_configured_prune false false unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false false unset unset pruned kept   \"--prune\"\n+test_configured_prune false false unset unset kept   pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/*\"\n-test_configured_prune false false pruned pruned \\\n+test_configured_prune false false unset unset pruned pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n-test_configured_prune true  false kept   kept   \"\"\n-test_configured_prune true  false pruned kept   \"--prune\"\n-test_configured_prune true  false kept   kept   \"--no-prune\"\n+test_configured_prune true  false unset unset kept   kept   \"\"\n+test_configured_prune true  false unset unset pruned kept   \"--prune\"\n+test_configured_prune true  false unset unset kept   kept   \"--no-prune\"\n \n-test_configured_prune unset true  pruned kept   \"\"\n-test_configured_prune unset true  kept   kept   \"--no-prune\"\n-test_configured_prune unset true  pruned kept   \"--prune\"\n+test_configured_prune unset true  unset unset pruned kept   \"\"\n+test_configured_prune unset true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset true  unset unset pruned kept   \"--prune\"\n \n-test_configured_prune false true  pruned kept   \"\"\n-test_configured_prune false true  kept   kept   \"--no-prune\"\n-test_configured_prune false true  pruned kept   \"--prune\"\n+test_configured_prune false true  unset unset pruned kept   \"\"\n+test_configured_prune false true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false true  unset unset pruned kept   \"--prune\"\n \n-test_configured_prune true  true  pruned kept   \"\"\n-test_configured_prune true  true  pruned kept   \"--prune\"\n-test_configured_prune true  true  kept   kept   \"--no-prune\"\n-test_configured_prune true  true  kept   pruned \\\n+test_configured_prune true  true  unset unset pruned kept   \"\"\n+test_configured_prune true  true  unset unset pruned kept   \"--prune\"\n+test_configured_prune true  true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune true  true  unset unset kept   pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/*\"\n-test_configured_prune true  true  pruned pruned \\\n+test_configured_prune true  true  unset unset pruned pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n test_expect_success 'all boundary commits are excluded' '\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338729","messageId":"20180208161936.8196-15-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 14/17] git-fetch & config doc: link to the new PRUNING section","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:33Z","receivedAt":"2018-02-08T16:20:47Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend the documentation for fetch.prune, fetch.<name>.prune and\n--prune to link to the recently added PRUNING section.\n\nI'd have liked to link directly to it with \"<<PRUNING>>\" from\nfetch-options.txt, since it's included in git-fetch.txt (git-pull.txt\nalso includes it, but doesn't include that option). However making a\nreference across files yields this error:\n\n    [...]/Documentation/git-fetch.xml:226: element xref: validity\n    error : IDREF attribute linkend references an unknown ID \"PRUNING\"\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt        | 6 +++++-\n Documentation/fetch-options.txt | 3 +++\n 2 files changed, 8 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 0e25b2c92b..0f27af5760 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1398,7 +1398,8 @@ fetch.unpackLimit::\n \n fetch.prune::\n \tIf true, fetch will automatically behave as if the `--prune`\n-\toption was given on the command line.  See also `remote.<name>.prune`.\n+\toption was given on the command line.  See also `remote.<name>.prune`\n+\tand the PRUNING section of linkgit:git-fetch[1].\n \n fetch.output::\n \tControl how ref update status is printed. Valid values are\n@@ -2944,6 +2945,9 @@ remote.<name>.prune::\n \tremove any remote-tracking references that no longer exist on the\n \tremote (as if the `--prune` option was given on the command line).\n \tOverrides `fetch.prune` settings, if any.\n++\n+See also `remote.<name>.prune` and the PRUNING section of\n+linkgit:git-fetch[1].\n \n remotes.<group>::\n \tThe list of remotes which are fetched by \"git remote update\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex fb6bebbc61..9f5c85ad96 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -74,6 +74,9 @@ ifndef::git-pull[]\n \tline or in the remote configuration, for example if the remote\n \twas cloned with the --mirror option), then they are also\n \tsubject to pruning.\n++\n+See the PRUNING section below for more details.\n+\n endif::git-pull[]\n \n ifndef::git-pull[]\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338730","messageId":"20180208161936.8196-18-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 17/17] fetch: make the --fetch-prune work with <url>","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:36Z","receivedAt":"2018-02-08T16:20:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the new --prune-tags option work properly when git-fetch is\ninvoked with a <url> parameter instead of a <remote name>\nparameter.\n\nThis change is split off from the introduction of --prune-tags due to\nthe relative complexity of munging the incoming argv, which is easier\nto review as a separate change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-fetch.txt | 21 ++++++---------------\n builtin/fetch.c             | 17 ++++++++++++++---\n t/t5510-fetch.sh            |  6 +++---\n 3 files changed, 23 insertions(+), 21 deletions(-)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex 574206d139..03666f6215 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -175,24 +175,15 @@ It's reasonable to e.g. configure `fetch.pruneTags=true` in\n run, without making every invocation of `git fetch` without `--prune`\n an error.\n \n-Another special case of `--prune-tags` is that\n-`refs/tags/*:refs/tags/*` will not be implicitly provided if an URL is\n-being fetched. I.e.:\n-\n-------------------------------------------------\n-$ git fetch <url> --prune --prune-tags\n-------------------------------------------------\n-\n-Will prune no tags, as opposed to:\n+Pruning tags with `--prune-tags` also works when fetching a URL\n+instead of a named remote. These will all prune tags not found on\n+origin:\n \n ------------------------------------------------\n $ git fetch origin --prune --prune-tags\n-------------------------------------------------\n-\n-To prune tags given a URL supply the refspec explicitly:\n-\n-------------------------------------------------\n-$ git fetch <url> --prune 'refs/tags/*:refs/tags/*'\n+$ git fetch origin --prune 'refs/tags/*:refs/tags/*'\n+$ git fetch <url of origin> --prune --prune-tags\n+$ git fetch <url of origin> --prune 'refs/tags/*:refs/tags/*'\n ------------------------------------------------\n \n OUTPUT\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 55a0fc37be..c96f17a9a3 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1283,7 +1283,10 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru\n \tstatic const char **refs = NULL;\n \tstruct refspec *refspec;\n \tint ref_nr = 0;\n+\tint j = 0;\n \tint exit_code;\n+\tint maybe_prune_tags;\n+\tint remote_via_config = remote_is_configured(remote, 0);\n \n \tif (!remote)\n \t\tdie(_(\"No remote repository specified.  Please, specify either a URL or a\\n\"\n@@ -1311,13 +1314,21 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru\n \t\t\tprune_tags = PRUNE_TAGS_BY_DEFAULT;\n \t}\n \n-\tif (prune_tags_ok && prune_tags && remote_is_configured(remote, 0))\n+\tmaybe_prune_tags = prune_tags_ok && prune_tags;\n+\tif (maybe_prune_tags && remote_via_config)\n \t\tadd_prune_tags_to_fetch_refspec(remote);\n \n+\tif (argc > 0 || (maybe_prune_tags && !remote_via_config)) {\n+\t\tsize_t nr_alloc = st_add3(argc, maybe_prune_tags, 1);\n+\t\trefs = xcalloc(nr_alloc, sizeof(const char *));\n+\t\tif (maybe_prune_tags) {\n+\t\t\trefs[j++] = xstrdup(\"refs/tags/*:refs/tags/*\");\n+\t\t\tref_nr++;\n+\t\t}\n+\t}\n+\n \tif (argc > 0) {\n-\t\tint j = 0;\n \t\tint i;\n-\t\trefs = xcalloc(st_add(argc, 1), sizeof(const char *));\n \t\tfor (i = 0; i < argc; i++) {\n \t\t\tif (!strcmp(argv[i], \"tag\")) {\n \t\t\t\ti++;\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 6f3ab7695a..297590814d 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -748,9 +748,9 @@ test_configured_prune unset unset unset true pruned  kept \\\n test_configured_prune_type unset unset unset unset kept kept     \"origin --prune-tags\" \"name\"\n test_configured_prune_type unset unset unset unset kept kept     \"origin --prune-tags\" \"link\"\n test_configured_prune_type unset unset unset unset pruned pruned \"origin --prune --prune-tags\" \"name\"\n-test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune --prune-tags\" \"link\"\n+test_configured_prune_type unset unset unset unset kept   pruned \"origin --prune --prune-tags\" \"link\"\n test_configured_prune_type unset unset unset unset pruned pruned \"--prune --prune-tags origin\" \"name\"\n-test_configured_prune_type unset unset unset unset kept   kept   \"--prune --prune-tags origin\" \"link\"\n+test_configured_prune_type unset unset unset unset kept   pruned \"--prune --prune-tags origin\" \"link\"\n test_configured_prune_type unset unset true  unset pruned pruned \"--prune origin\" \"name\"\n test_configured_prune_type unset unset true  unset kept   pruned \"--prune origin\" \"link\"\n test_configured_prune_type unset unset unset true  pruned pruned \"--prune origin\" \"name\"\n@@ -772,7 +772,7 @@ test_expect_success 'remove remote.origin.fetch \"one\"' '\n \t)\n '\n test_configured_prune_type unset unset unset unset kept pruned \"origin --prune --prune-tags\" \"name\"\n-test_configured_prune_type unset unset unset unset kept kept   \"origin --prune --prune-tags\" \"link\"\n+test_configured_prune_type unset unset unset unset kept pruned \"origin --prune --prune-tags\" \"link\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338731","messageId":"20180208161936.8196-11-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 10/17] fetch tests: expand case/esac for later change","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:29Z","receivedAt":"2018-02-08T16:21:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Expand a compact case/esac statement for a later change that'll add\nmore logic to the body of the \"*\" case. This is a whitespace-only\nchange.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 88d38e0819..dfc749f576 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -543,8 +543,12 @@ test_expect_success \"should be able to fetch with duplicate refspecs\" '\n set_config_tristate () {\n \t# var=$1 val=$2\n \tcase \"$2\" in\n-\tunset)  test_unconfig \"$1\" ;;\n-\t*)\tgit config \"$1\" \"$2\" ;;\n+\tunset)\n+\t\ttest_unconfig \"$1\"\n+\t\t;;\n+\t*)\n+\t\tgit config \"$1\" \"$2\"\n+\t\t;;\n \tesac\n }\n \n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338732","messageId":"20180208161936.8196-10-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 09/17] fetch tests: double quote a variable for interpolation","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:28Z","receivedAt":"2018-02-08T16:21:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"If the $cmdline variable contains arguments with spaces they won't be\ninterpolated correctly, since the body of the test is single quoted,\nand because test-lib.sh does its own eval().\n\nThis will be used in a subsequent commit to pass arguments that need\nto be quoted to git-fetch, i.e. a file:// path to fetch, which will\nhave a space in it.\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 dacdb8759c..88d38e0819 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -578,7 +578,7 @@ test_configured_prune () {\n \t\t\tset_config_tristate fetch.prune $fetch_prune &&\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n \n-\t\t\tgit fetch $cmdline &&\n+\t\t\tgit fetch '\"$cmdline\"' &&\n \t\t\tcase \"$expected_branch\" in\n \t\t\tpruned)\n \t\t\t\ttest_must_fail git rev-parse --verify refs/remotes/origin/newbranch\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338733","messageId":"20180208161936.8196-7-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 06/17] fetch tests: re-arrange arguments for future readability","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:25Z","receivedAt":"2018-02-08T16:21:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Re-arrange the arguments to the test_configured_prune() function used\nin this test to pass the arguments to --fetch last. A subsequent\nchange will test for more elaborate fetch arguments, including long\nrefspecs. It'll be more readable to be able to wrap those on a new\nline of their own.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 82 ++++++++++++++++++++++++++++++--------------------------\n 1 file changed, 44 insertions(+), 38 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 11da97f9b7..ab8b25344d 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -551,10 +551,10 @@ set_config_tristate () {\n test_configured_prune () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n-\tcmdline=$3\n-\texpected_branch=$4\n+\texpected_branch=$3\n+\tcmdline=$4\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; branch:$4\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${4:+ $4}; branch:$3\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\t(\n@@ -587,41 +587,47 @@ test_configured_prune () {\n \t'\n }\n \n-test_configured_prune unset unset \"\"\t\tkept\n-test_configured_prune unset unset \"--no-prune\"\tkept\n-test_configured_prune unset unset \"--prune\"\tpruned\n-\n-test_configured_prune false unset \"\"\t\tkept\n-test_configured_prune false unset \"--no-prune\"\tkept\n-test_configured_prune false unset \"--prune\"\tpruned\n-\n-test_configured_prune true  unset \"\"\t\tpruned\n-test_configured_prune true  unset \"--prune\"\tpruned\n-test_configured_prune true  unset \"--no-prune\"\tkept\n-\n-test_configured_prune unset false \"\"\t\tkept\n-test_configured_prune unset false \"--no-prune\"\tkept\n-test_configured_prune unset false \"--prune\"\tpruned\n-\n-test_configured_prune false false \"\"\t\tkept\n-test_configured_prune false false \"--no-prune\"\tkept\n-test_configured_prune false false \"--prune\"\tpruned\n-\n-test_configured_prune true  false \"\"\t\tkept\n-test_configured_prune true  false \"--prune\"\tpruned\n-test_configured_prune true  false \"--no-prune\"\tkept\n-\n-test_configured_prune unset true  \"\"\t\tpruned\n-test_configured_prune unset true  \"--no-prune\"\tkept\n-test_configured_prune unset true  \"--prune\"\tpruned\n-\n-test_configured_prune false true  \"\"\t\tpruned\n-test_configured_prune false true  \"--no-prune\"\tkept\n-test_configured_prune false true  \"--prune\"\tpruned\n-\n-test_configured_prune true  true  \"\"\t\tpruned\n-test_configured_prune true  true  \"--prune\"\tpruned\n-test_configured_prune true  true  \"--no-prune\"\tkept\n+# $1 config: fetch.prune\n+# $2 config: remote.<name>.prune\n+# $3 expect: branch to be pruned?\n+# $4 git-fetch $cmdline:\n+#\n+#                     $1    $2    $3     $4\n+test_configured_prune unset unset kept   \"\"\n+test_configured_prune unset unset kept   \"--no-prune\"\n+test_configured_prune unset unset pruned \"--prune\"\n+\n+test_configured_prune false unset kept   \"\"\n+test_configured_prune false unset kept   \"--no-prune\"\n+test_configured_prune false unset pruned \"--prune\"\n+\n+test_configured_prune true  unset pruned \"\"\n+test_configured_prune true  unset pruned \"--prune\"\n+test_configured_prune true  unset kept   \"--no-prune\"\n+\n+test_configured_prune unset false kept   \"\"\n+test_configured_prune unset false kept   \"--no-prune\"\n+test_configured_prune unset false pruned \"--prune\"\n+\n+test_configured_prune false false kept   \"\"\n+test_configured_prune false false kept   \"--no-prune\"\n+test_configured_prune false false pruned \"--prune\"\n+\n+test_configured_prune true  false kept   \"\"\n+test_configured_prune true  false pruned \"--prune\"\n+test_configured_prune true  false kept   \"--no-prune\"\n+\n+test_configured_prune unset true  pruned \"\"\n+test_configured_prune unset true  kept   \"--no-prune\"\n+test_configured_prune unset true  pruned \"--prune\"\n+\n+test_configured_prune false true  pruned \"\"\n+test_configured_prune false true  kept   \"--no-prune\"\n+test_configured_prune false true  pruned \"--prune\"\n+\n+test_configured_prune true  true  pruned \"\"\n+test_configured_prune true  true  pruned \"--prune\"\n+test_configured_prune true  true  kept   \"--no-prune\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338734","messageId":"20180208161936.8196-9-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 08/17] fetch tests: test --prune and refspec interaction","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:27Z","receivedAt":"2018-02-08T16:21:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for the interaction between explicitly provided refspecs\nand fetch.prune.\n\nThere's no point in adding this boilerplate to every combination of\nunset/false/true, it's instructive and sufficient to show that no\nmatter if the variable is unset, false or true the refspec on the\ncommand-line overrides any configuration variable.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 12 ++++++++++++\n 1 file changed, 12 insertions(+)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex fad65bd885..dacdb8759c 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -609,6 +609,10 @@ test_configured_prune () {\n test_configured_prune unset unset kept   kept   \"\"\n test_configured_prune unset unset kept   kept   \"--no-prune\"\n test_configured_prune unset unset pruned kept   \"--prune\"\n+test_configured_prune unset unset kept   pruned \\\n+\t\"--prune origin refs/tags/*:refs/tags/*\"\n+test_configured_prune unset unset pruned pruned \\\n+\t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n test_configured_prune false unset kept   kept   \"\"\n test_configured_prune false unset kept   kept   \"--no-prune\"\n@@ -625,6 +629,10 @@ test_configured_prune unset false pruned kept   \"--prune\"\n test_configured_prune false false kept   kept   \"\"\n test_configured_prune false false kept   kept   \"--no-prune\"\n test_configured_prune false false pruned kept   \"--prune\"\n+test_configured_prune false false kept   pruned \\\n+\t\"--prune origin refs/tags/*:refs/tags/*\"\n+test_configured_prune false false pruned pruned \\\n+\t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n test_configured_prune true  false kept   kept   \"\"\n test_configured_prune true  false pruned kept   \"--prune\"\n@@ -641,6 +649,10 @@ test_configured_prune false true  pruned kept   \"--prune\"\n test_configured_prune true  true  pruned kept   \"\"\n test_configured_prune true  true  pruned kept   \"--prune\"\n test_configured_prune true  true  kept   kept   \"--no-prune\"\n+test_configured_prune true  true  kept   pruned \\\n+\t\"--prune origin refs/tags/*:refs/tags/*\"\n+test_configured_prune true  true  pruned pruned \\\n+\t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338735","messageId":"20180208161936.8196-5-avarab@gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"[PATCH v2 04/17] remote: add a macro for \"refs/tags/*:refs/tags/*\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T16:19:23Z","receivedAt":"2018-02-08T16:21:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a macro with the refspec string \"refs/tags/*:refs/tags/*\". There's\nbeen a pre-defined struct version of this since e0aaa29ff3 (\"Have a\nconstant extern refspec for \"--tags\"\", 2008-04-17), but nothing that\ncould be passed to e.g. add_fetch_refspec().\n\nThis will be used in subsequent commits to avoid hardcoding this\nstring in multiple places.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n remote.c | 1 +\n remote.h | 2 ++\n 2 files changed, 3 insertions(+)\n\ndiff --git a/remote.c b/remote.c\nindex 4e93753e19..356c123e3e 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -22,6 +22,7 @@ static struct refspec s_tag_refspec = {\n \t\"refs/tags/*\"\n };\n \n+/* See TAG_REFSPEC for the string version */\n const struct refspec *tag_refspec = &s_tag_refspec;\n \n struct counted_string {\ndiff --git a/remote.h b/remote.h\nindex 1f6611be21..80fea6dd11 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -297,4 +297,6 @@ extern int parseopt_push_cas_option(const struct option *, const char *arg, int\n extern int is_empty_cas(const struct push_cas_option *);\n void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);\n \n+#define TAG_REFSPEC \"refs/tags/*:refs/tags/*\"\n+\n #endif\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338746","messageId":"87d11fmjer.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-1-avarab@gmail.com","subject":"Re: [PATCH v2 00/17] document & test fetch pruning & add fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-08T18:21:48Z","receivedAt":"2018-02-08T18:21:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Feb 08 2018, Ævar Arnfjörð Bjarmason jotted:\n\n> As noted in my 87h8quytmq.fsf@evledraar.gmail.com there was a bug I\n> noticed in v3 where it would segfault on some git-fetch invocations,\n> but there were not tests anywhere that caught that.\n\n...and of course this whole submission this time around should be v4,\nnot v2, but I didn't notice that I didn't adjust the subject prefix\nbefore sending. Junio: Hopefully you can pick it up anyway without too\nmuch trouble, sorry.\n\nFWIW I've deployed this to production @ work to some tens of k of\nmachines (low \"k\" of which have users using git) without any issues.\n"},{"id":"338754","messageId":"xmqqlgg3qn3l.fsf@gitster-ct.c.googlers.com","threadId":"47614","inReplyTo":"87d11fmjer.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 00/17] document & test fetch pruning & add fetch.pruneTags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-08T19:48:30Z","receivedAt":"2018-02-08T19:48:40Z","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, Feb 08 2018, Ævar Arnfjörð Bjarmason jotted:\n>\n>> As noted in my 87h8quytmq.fsf@evledraar.gmail.com there was a bug I\n>> noticed in v3 where it would segfault on some git-fetch invocations,\n>> but there were not tests anywhere that caught that.\n>\n> ...and of course this whole submission this time around should be v4,\n> not v2, but I didn't notice that I didn't adjust the subject prefix\n> before sending. Junio: Hopefully you can pick it up anyway without too\n> much trouble, sorry.\n>\n> FWIW I've deployed this to production @ work to some tens of k of\n> machines (low \"k\" of which have users using git) without any issues.\n\nWill replace.  I found that the resolution of conflicts necessary\nwith the jh/partial-clone topic is a bit different from the previous\nversion due to addition of an extra parameter to fetch_one(), and I\nthink I didn't botch it, but please double check when I push the\nresults out in a few hours.\n\nThanks.\n"},{"id":"338804","messageId":"CAPig+cTwQj0UPvRy9xAF1kbyR8vx4WCsvb0KXsS5GQcDLiqcuQ@mail.gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-2-avarab@gmail.com","subject":"Re: [PATCH v2 01/17] fetch: don't redundantly NULL something calloc() gave us","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-02-09T04:36:54Z","receivedAt":"2018-02-09T04:37:03Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 8, 2018 at 11:19 AM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Stop redundantly NULL-ing the last element of the refs structure,\n> which was retrieved via calloc(), and is thus guaranteed to be\n> pre-NULL'd.\n> [...]\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/builtin/fetch.c b/builtin/fetch.c\n> @@ -1302,7 +1302,6 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n>                         } else\n>                                 refs[j++] = argv[i];\n>                 }\n> -               refs[j] = NULL;\n>                 ref_nr = j;\n>         }\n\nThis is purely subjective, and I neglected to mention it as early as\nv1, but I find that this change hurts readability. Specifically, as\nI'm scanning or reading code, explicit termination conditions, like\nthis NULL assignment, are things I'm expecting to see; they're part of\nthe idiom of the language. When they're missing, I have to stop, go\nback, and study the code more carefully to see if the \"missing bit\" is\na bug or is intentional. And, it's easy to misread xcalloc() as\nxmalloc(), meaning that it's likely that one studying the code would\nconclude that the missing NULL assignment is a bug.\n\nIf anything, if this patch wants to eliminate redundancy, I'd expect\nit to change xcalloc() to xmalloc() and keep the NULL assignment, thus\nleaving the idiom intact.\n"},{"id":"338805","messageId":"CAPig+cQhCkQknxhH0T_ULpvtStBLv9UiBCAeXgRee_uikF-asg@mail.gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-3-avarab@gmail.com","subject":"Re: [PATCH v2 02/17] fetch: trivially refactor assignment to ref_nr","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-02-09T04:41:18Z","receivedAt":"2018-02-09T04:41:23Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 8, 2018 at 11:19 AM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Trivially refactor an assignment to make a subsequent patch\n> smaller. The \"ref_nr\" variable is initialized to 0 earlier, just as\n> \"j\" is, and \"j\" is only incremented in that loop, so this change isn't\n> a logic error.\n>\n> This change makes a subsequent change which splits the incrementing of\n> \"ref_nr\" into two blocks.\n\n\"This change _simplifies_ a subsequent...\"\n\nor\n\n\"This change make a subsequent change _easier_...\"\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n"},{"id":"338806","messageId":"CAPig+cS4xhyYSnUHXshB=2SPYELe_HXUwxL-OGU4zx=PMFXdqg@mail.gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-12-avarab@gmail.com","subject":"Re: [PATCH v2 11/17] fetch tests: fetch <url> <spec> as well as fetch [<remote>]","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-02-09T05:17:05Z","receivedAt":"2018-02-09T05:17:12Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 8, 2018 at 11:19 AM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> When a remote URL is supplied on the command-line the internals of the\n> fetch are different, in particular the code in get_ref_map(). An\n> earlier version of the subsequent fetch.pruneTags patch hid a segfault\n> because the difference wasn't tested for.\n>\n> Now all the tests are run as both of the variants of:\n>\n>     git fetch\n>     git -c [...] fetch $(git config remote.origin.url) $(git config remote.origin.fetch)\n>\n> I'm using -c because while the [fetch] config just set by\n> set_config_tristate will be picked up, the remote.origin.* config\n> won't override it as intended.\n>\n> Work around that and turn this into a purely command-line test by\n> always setting the variables on the command-line, and translate any\n> setting of remote.origin.X into fetch.X.\n> [...]\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\n> @@ -548,18 +548,52 @@ set_config_tristate () {\n>         *)\n>                 git config \"$1\" \"$2\"\n> +               key=$(echo $1 | sed -e 's/^remote\\.origin/fetch/')\n\nFaster (thus more Windows-friendly) assuming that $1 always starts\nwith \"remote.origin\":\n\n    key=fetch${u#remote.origin}\n\n> +               git_fetch_c=\"$git_fetch_c -c $key=$2\"\n>                 ;;\n>         esac\n>  }\n>\n> +test_configured_prune_type () {\n>         fetch_prune=$1\n>         remote_origin_prune=$2\n>         expected_branch=$3\n>         expected_tag=$4\n>         cmdline=$5\n> -\n> -       test_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n> +       mode=$6\n> +\n> +       if ! test -e prune-type-setup-done\n> +       then\n> +               test_expect_success 'prune_type setup' '\n> +                       git -C one config remote.origin.url >one.remote-url &&\n> +                       git -C one config remote.origin.fetch >one.remote-fetch &&\n> +                       remote_url=\"file://$(cat one.remote-url)\" &&\n> +                       remote_fetch=\"$(cat one.remote-fetch)\" &&\n\nIs there a reason that these values need to be captured to files\n(which are otherwise not used) before being assigned to variables?\nThat is, wouldn't this work?\n\n    remote_url=\"file://$(git -C one config remote.origin.url)\" &&\n    remote_fetch=\"$(git -C one config remote.origin.fetch)\" &&\n\n> +                       cmdline_setup=\"\\\"$remote_url\\\" \\\"$remote_fetch\\\"\" &&\n> +                       touch prune-type-setup-done\n\nWhy does \"prune-type-setup-done\" need to be a file rather than a\nsimple shell variable (which is global by default even when assigned\ninside test_expect_success)?\n\nAlso, since the purpose of this code seems to compute 'cmdline_setup'\njust once, can't you do away with 'prune-type-setup-done' altogether\nand change:\n\n    if ! test -e prune-type-setup-done\n\nto:\n\n    if test -z \"$cmdline_setup\"\n\n> +               '\n> +       fi\n"},{"id":"338807","messageId":"CAPig+cTa0UeUyLRL93ZPYvauiFpmn9Yur0=Ou+Abr=UUeqiPSA@mail.gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-13-avarab@gmail.com","subject":"Re: [PATCH v2 12/17] git fetch doc: add a new section to explain the ins & outs of pruning","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-02-09T05:26:27Z","receivedAt":"2018-02-09T05:26:33Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 8, 2018 at 11:19 AM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Add a new section to canonically explain how remote reference pruning\n> works, and how users should be careful about using it in conjunction\n> with tag refspecs in particular.\n> [...]\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\n> @@ -99,6 +99,55 @@ The latter use of the `remote.<repository>.fetch` values can be\n> +PRUNING\n> +-------\n> +[...]\n> +If left to accumulate, these stale references might make performance\n> +worse on big and busy repos that have a lot of branch churn, and\n> +e.g. make the output of commands like `git branch -a --contains\n> +<commit>` needlessly verbose, as well as impacting anything else\n> +that'll work with the complete set of known references.\n> +\n> +These remote tracking references can be deleted as a one-off with\n\nI think we call these \"remote-tracking\" (note the hyphen), which are\nlocal but track something remote, rather than \"remote tracking\" (no\nhyphen) which would themselves be remote.\n\n> +either of:\n> +\n> +------------------------------------------------\n> +# While fetching\n> +$ git fetch --prune <name>\n> +\n> +# Only prune, don't fetch\n> +$ git remote prune <name>\n> +------------------------------------------------\n> +\n> +To prune references as part of your normal workflow without needing to\n> +remember to run that set `fetch.prune` globally, or\n\ns/that/&,/\n\n> +`remote.<name>.prune` per-remote in the config. See\n> +linkgit:git-config[1].\n"},{"id":"338808","messageId":"CAPig+cQCuMyWYrsMq8=FDJC5D+EuZk=eoK6ri3Lgs62VPzAX2A@mail.gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-14-avarab@gmail.com","subject":"Re: [PATCH v2 13/17] git remote doc: correct dangerous lies about what prune does","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-02-09T05:33:49Z","receivedAt":"2018-02-09T05:33:55Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 8, 2018 at 11:19 AM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> The \"git remote prune <name>\" command uses the same machinery as \"git\n> fetch <name> --prune\", and shares all the same caveats, but its\n> documentation has suggested that it'll just \"delete stale\n> remote-tracking branches under <name>\".\n>\n> This isn't true, and hasn't been true since at least v1.8.5.6 (the\n> oldest version I could be bothered to test).\n>\n> E.g. if \"refs/tags/*:refs/tags/*\" is explicitly set in the refspec of\n> the remote it'll delete all local tags <name> doesn't know about.\n\nNot worth a re-roll as this is only the commit message: s/remote/&,/\n\nMore below...\n\n> Instead, briefly give the reader just enough of a hint that this\n> option might constitute a shotgun aimed at their foot, and point them\n> to the new PRUNING section in the git-fetch documentation which\n> explains all the nuances of what this facility does.\n> [...]\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\n> @@ -172,10 +172,14 @@ With `-n` option, the remote heads are not queried first with\n> -Deletes all stale remote-tracking branches under <name>.\n> -These stale branches have already been removed from the remote repository\n> -referenced by <name>, but are still locally available in\n> -\"remotes/<name>\".\n> +Deletes stale references associated with <name>. By default stale\n\nNit: s/default/&,/\n\n> +remote-tracking branches under <name>, but depending on global\n> +configuration and the configuration of the remote we might even prune\n> +local tags that haven't been pushed there.\n\nThis is a bit difficult to digest grammatically due to the phrase\nbeing incomplete. It could say either:\n\n    By default, stale ... under <name> _are deleted_, but\n    depending...\n\nor:\n\n    Deletes stale ... <name>; by default, stale...\n"},{"id":"338812","messageId":"CAPig+cS3OCi9CDykF-5EAx=J3ouXPo+7HE_CB8DfmWccBxyxgA@mail.gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-17-avarab@gmail.com","subject":"Re: [PATCH v2 16/17] fetch: add a --fetch-prune option and fetch.pruneTags config","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-02-09T06:58:55Z","receivedAt":"2018-02-09T06:59:02Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 8, 2018 at 11:19 AM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Add a --fetch-prune option to git-fetch, along with fetch.pruneTags\n> config option. [...]\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\n> @@ -592,6 +592,15 @@ test_configured_prune_type () {\n> +               if test \"$fetch_prune_tags\" = 'true' ||\n> +                  test \"$remote_origin_prune_tags\" = 'true'\n> +               then\n> +                       if ! printf '%s' \"$cmdline\" | grep -q refs/remotes/origin/\n\nIs $cmdline guaranteed to end with a newline? Historically, not all\n'grep's would be able to match the last line if it was not properly\nterminated. Perhaps you want '%s\\n' instead?\n\n> +                       then\n> +                               new_cmdline=\"$new_cmdline refs/tags/*:refs/tags/*\"\n> +                       fi\n> +               fi\n> +\n> @@ -705,6 +714,66 @@ test_configured_prune true  true  unset unset kept   pruned \\\n> +# When --prune-tags is supplied it's ignored if an explict refspec is\n\ns/explict/explicit/\n\n> +# given, same for the configuration options.\n> +\n> +# Pruning that also takes place if s!origin!<file:// url of remote>!,\n> +# or otherwise uses the file://-specific codepath. However, because\n> +# there's no implicit +refs/heads/*:refs/remotes/origin/* refspec and\n> +# supplying it on the command-line negate --prune-tags the branches\n\ns/negate/&s/\ns/--prune-tags/&,/\n\n> +# will not be pruned.\n"},{"id":"338813","messageId":"CAPig+cRJzKF9a4=-qkdUH1kUzvx3azf_gY85yqU0Y7NHXTJrdQ@mail.gmail.com","threadId":"47614","inReplyTo":"20180208161936.8196-18-avarab@gmail.com","subject":"Re: [PATCH v2 17/17] fetch: make the --fetch-prune work with <url>","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-02-09T07:03:33Z","receivedAt":"2018-02-09T07:03:41Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 8, 2018 at 11:19 AM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> fetch: make the --fetch-prune work with <url>\n\nDo you mean s/--fetch-prune/--prune-tags/ ?\n\n> Make the new --prune-tags option work properly when git-fetch is\n> invoked with a <url> parameter instead of a <remote name>\n> parameter.\n>\n> This change is split off from the introduction of --prune-tags due to\n> the relative complexity of munging the incoming argv, which is easier\n> to review as a separate change.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n"},{"id":"338817","messageId":"87a7wimv1k.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"CAPig+cRJzKF9a4=-qkdUH1kUzvx3azf_gY85yqU0Y7NHXTJrdQ@mail.gmail.com","subject":"Re: [PATCH v2 17/17] fetch: make the --fetch-prune work with <url>","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T08:22:47Z","receivedAt":"2018-02-09T08:22:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Feb 09 2018, Eric Sunshine jotted:\n\n> On Thu, Feb 8, 2018 at 11:19 AM, Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>> fetch: make the --fetch-prune work with <url>\n>\n> Do you mean s/--fetch-prune/--prune-tags/ ?\n\nYes, sorry. Will fix.\n\n>> Make the new --prune-tags option work properly when git-fetch is\n>> invoked with a <url> parameter instead of a <remote name>\n>> parameter.\n>>\n>> This change is split off from the introduction of --prune-tags due to\n>> the relative complexity of munging the incoming argv, which is easier\n>> to review as a separate change.\n>>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\n"},{"id":"338818","messageId":"878tc2mv0g.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"CAPig+cS3OCi9CDykF-5EAx=J3ouXPo+7HE_CB8DfmWccBxyxgA@mail.gmail.com","subject":"Re: [PATCH v2 16/17] fetch: add a --fetch-prune option and fetch.pruneTags config","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T08:23:27Z","receivedAt":"2018-02-09T08:23:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Feb 09 2018, Eric Sunshine jotted:\n\n> On Thu, Feb 8, 2018 at 11:19 AM, Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>> Add a --fetch-prune option to git-fetch, along with fetch.pruneTags\n>> config option. [...]\n>>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>> diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\n>> @@ -592,6 +592,15 @@ test_configured_prune_type () {\n>> +               if test \"$fetch_prune_tags\" = 'true' ||\n>> +                  test \"$remote_origin_prune_tags\" = 'true'\n>> +               then\n>> +                       if ! printf '%s' \"$cmdline\" | grep -q refs/remotes/origin/\n>\n> Is $cmdline guaranteed to end with a newline? Historically, not all\n> 'grep's would be able to match the last line if it was not properly\n> terminated. Perhaps you want '%s\\n' instead?\n\nGood point. No it's not, will fix that.\n\n>> +                       then\n>> +                               new_cmdline=\"$new_cmdline refs/tags/*:refs/tags/*\"\n>> +                       fi\n>> +               fi\n>> +\n>> @@ -705,6 +714,66 @@ test_configured_prune true  true  unset unset kept   pruned \\\n>> +# When --prune-tags is supplied it's ignored if an explict refspec is\n>\n> s/explict/explicit/\n>\n>> +# given, same for the configuration options.\n>> +\n>> +# Pruning that also takes place if s!origin!<file:// url of remote>!,\n>> +# or otherwise uses the file://-specific codepath. However, because\n>> +# there's no implicit +refs/heads/*:refs/remotes/origin/* refspec and\n>> +# supplying it on the command-line negate --prune-tags the branches\n>\n> s/negate/&s/\n> s/--prune-tags/&,/\n>\n>> +# will not be pruned.\n\n*nod*\n"},{"id":"338895","messageId":"871shum182.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"CAPig+cTwQj0UPvRy9xAF1kbyR8vx4WCsvb0KXsS5GQcDLiqcuQ@mail.gmail.com","subject":"Re: [PATCH v2 01/17] fetch: don't redundantly NULL something calloc() gave us","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T19:06:53Z","receivedAt":"2018-02-09T19:07:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Feb 09 2018, Eric Sunshine jotted:\n\n> On Thu, Feb 8, 2018 at 11:19 AM, Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>> Stop redundantly NULL-ing the last element of the refs structure,\n>> which was retrieved via calloc(), and is thus guaranteed to be\n>> pre-NULL'd.\n>> [...]\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>> diff --git a/builtin/fetch.c b/builtin/fetch.c\n>> @@ -1302,7 +1302,6 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n>>                         } else\n>>                                 refs[j++] = argv[i];\n>>                 }\n>> -               refs[j] = NULL;\n>>                 ref_nr = j;\n>>         }\n>\n> This is purely subjective, and I neglected to mention it as early as\n> v1, but I find that this change hurts readability. Specifically, as\n> I'm scanning or reading code, explicit termination conditions, like\n> this NULL assignment, are things I'm expecting to see; they're part of\n> the idiom of the language. When they're missing, I have to stop, go\n> back, and study the code more carefully to see if the \"missing bit\" is\n> a bug or is intentional. And, it's easy to misread xcalloc() as\n> xmalloc(), meaning that it's likely that one studying the code would\n> conclude that the missing NULL assignment is a bug.\n>\n> If anything, if this patch wants to eliminate redundancy, I'd expect\n> it to change xcalloc() to xmalloc() and keep the NULL assignment, thus\n> leaving the idiom intact.\n\nThanks for all your review, really appreciate it.\n\nI'm going to keep this as-is, reasons:\n\n * With calloc it's easier to look at the values in a debugger, you get\n   NULLs instead of some random garbage for e.g. the ref src/dst. It\n   makes it clear it's unset / the tail value.\n\n * Ditto fewer things to step through / inpect in a debugger. E.g. I\n   have a dump of variables before/after in the debugger, with\n   assignments like this it's just adding verbosity & something to\n   eyeball for something that's never going to change.\n\n * If there's a bug in the code using calloc is likely to reveal it\n   sooner, since you'll be deref-ing NULL instead of some stray\n   (possibly still valid) pointer you got from malloc.\n\n * It looks more in line with established idioms in the codebase. I\n   wasn't able to find other cases where we were double-NULL ing\n   calloc'd data, but rather stuff like this:\n\n       git grep -W '\\bpattern\\b' -- '*/ls-remote.c'\n"},{"id":"338907","messageId":"87zi4ikjyr.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"CAPig+cS4xhyYSnUHXshB=2SPYELe_HXUwxL-OGU4zx=PMFXdqg@mail.gmail.com","subject":"Re: [PATCH v2 11/17] fetch tests: fetch <url> <spec> as well as fetch [<remote>]","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:05:00Z","receivedAt":"2018-02-09T20:05:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Feb 09 2018, Eric Sunshine jotted:\n\n> On Thu, Feb 8, 2018 at 11:19 AM, Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>> When a remote URL is supplied on the command-line the internals of the\n>> fetch are different, in particular the code in get_ref_map(). An\n>> earlier version of the subsequent fetch.pruneTags patch hid a segfault\n>> because the difference wasn't tested for.\n>>\n>> Now all the tests are run as both of the variants of:\n>>\n>>     git fetch\n>>     git -c [...] fetch $(git config remote.origin.url) $(git config remote.origin.fetch)\n>>\n>> I'm using -c because while the [fetch] config just set by\n>> set_config_tristate will be picked up, the remote.origin.* config\n>> won't override it as intended.\n>>\n>> Work around that and turn this into a purely command-line test by\n>> always setting the variables on the command-line, and translate any\n>> setting of remote.origin.X into fetch.X.\n>> [...]\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>> diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\n>> @@ -548,18 +548,52 @@ set_config_tristate () {\n>>         *)\n>>                 git config \"$1\" \"$2\"\n>> +               key=$(echo $1 | sed -e 's/^remote\\.origin/fetch/')\n>\n> Faster (thus more Windows-friendly) assuming that $1 always starts\n> with \"remote.origin\":\n>\n>     key=fetch${u#remote.origin}\n\nTests fail with this and I'm not excited to be the first user in git's\ntest suite to use some novel shell feature, no existing uses of\n${u[...].\n\nI also think stuff like this is on the wrong side of cleverness\nv.s. benefit. I can't find any reference to this syntax in bash or dash\nmanpages (forward-search \"${u\"), but echo | sed is obvious, and it's not\ngoing to make a big difference for Windows.\n\n>> +               git_fetch_c=\"$git_fetch_c -c $key=$2\"\n>>                 ;;\n>>         esac\n>>  }\n>>\n>> +test_configured_prune_type () {\n>>         fetch_prune=$1\n>>         remote_origin_prune=$2\n>>         expected_branch=$3\n>>         expected_tag=$4\n>>         cmdline=$5\n>> -\n>> -       test_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n>> +       mode=$6\n>> +\n>> +       if ! test -e prune-type-setup-done\n>> +       then\n>> +               test_expect_success 'prune_type setup' '\n>> +                       git -C one config remote.origin.url >one.remote-url &&\n>> +                       git -C one config remote.origin.fetch >one.remote-fetch &&\n>> +                       remote_url=\"file://$(cat one.remote-url)\" &&\n>> +                       remote_fetch=\"$(cat one.remote-fetch)\" &&\n>\n> Is there a reason that these values need to be captured to files\n> (which are otherwise not used) before being assigned to variables?\n> That is, wouldn't this work?\n>\n>     remote_url=\"file://$(git -C one config remote.origin.url)\" &&\n>     remote_fetch=\"$(git -C one config remote.origin.fetch)\" &&\n\nNope, I'll just do that. This was cruft left over from an earlier\nversion which I didn't clean up.\n\n>> +                       cmdline_setup=\"\\\"$remote_url\\\" \\\"$remote_fetch\\\"\" &&\n>> +                       touch prune-type-setup-done\n>\n> Why does \"prune-type-setup-done\" need to be a file rather than a\n> simple shell variable (which is global by default even when assigned\n> inside test_expect_success)?\n>\n> Also, since the purpose of this code seems to compute 'cmdline_setup'\n> just once, can't you do away with 'prune-type-setup-done' altogether\n> and change:\n>\n>     if ! test -e prune-type-setup-done\n>\n> to:\n>\n>     if test -z \"$cmdline_setup\"\n>\n>> +               '\n>> +       fi\n\nYup, that's much simpler. Thanks.\n"},{"id":"338910","messageId":"20180209202733.GA18966@sigill.intra.peff.net","threadId":"47614","inReplyTo":"87zi4ikjyr.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 11/17] fetch tests: fetch <url> <spec> as well as fetch [<remote>]","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-02-09T20:27:33Z","receivedAt":"2018-02-09T20:27:39Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Feb 09, 2018 at 09:05:00PM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> >> diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\n> >> @@ -548,18 +548,52 @@ set_config_tristate () {\n> >>         *)\n> >>                 git config \"$1\" \"$2\"\n> >> +               key=$(echo $1 | sed -e 's/^remote\\.origin/fetch/')\n> >\n> > Faster (thus more Windows-friendly) assuming that $1 always starts\n> > with \"remote.origin\":\n> >\n> >     key=fetch${u#remote.origin}\n> \n> Tests fail with this and I'm not excited to be the first user in git's\n> test suite to use some novel shell feature, no existing uses of\n> ${u[...].\n> \n> I also think stuff like this is on the wrong side of cleverness\n> v.s. benefit. I can't find any reference to this syntax in bash or dash\n> manpages (forward-search \"${u\"), but echo | sed is obvious, and it's not\n> going to make a big difference for Windows.\n\nThe \"u\" isn't the magic, it's the \"#\". I.e.:\n\n  key=fetch${1#remote.origin}\n\nand it's used all over the place in our scripts.  I'm not sure why Eric\nwrote \"u\". :)\n\n-Peff\n"},{"id":"338911","messageId":"20180209203216.11560-1-avarab@gmail.com","threadId":"47614","inReplyTo":"20180121000304.32323-1-avarab@gmail.com","subject":"[PATCH v5 00/17] document & test fetch pruning & add fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:31:59Z","receivedAt":"2018-02-09T20:32:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Here's a v5 (correct subject line this time!). Many thanks to Eric for\na thorough review.\n\nI'll spare you the per-patch changelog. These are all minor commit\nmessage / doc / comment wording changes, with the exception of making\na bit of the test code better, and adding a \\n for grep\nportability. tbdiff at the end.\n\nÆvar Arnfjörð Bjarmason (17):\n  fetch: don't redundantly NULL something calloc() gave us\n  fetch: trivially refactor assignment to ref_nr\n  fetch: stop accessing \"remote\" variable indirectly\n  remote: add a macro for \"refs/tags/*:refs/tags/*\"\n  fetch tests: refactor in preparation for testing tag pruning\n  fetch tests: re-arrange arguments for future readability\n  fetch tests: add a tag to be deleted to the pruning tests\n  fetch tests: test --prune and refspec interaction\n  fetch tests: double quote a variable for interpolation\n  fetch tests: expand case/esac for later change\n  fetch tests: fetch <url> <spec> as well as fetch [<remote>]\n  git fetch doc: add a new section to explain the ins & outs of pruning\n  git remote doc: correct dangerous lies about what prune does\n  git-fetch & config doc: link to the new PRUNING section\n  fetch tests: add scaffolding for the new fetch.pruneTags\n  fetch: add a --prune-tags option and fetch.pruneTags config\n  fetch: make the --prune-tags work with <url>\n\n Documentation/config.txt               |  20 ++-\n Documentation/fetch-options.txt        |  17 ++-\n Documentation/git-fetch.txt            |  87 ++++++++++++\n Documentation/git-remote.txt           |  14 +-\n builtin/fetch.c                        |  54 ++++++--\n contrib/completion/git-completion.bash |   2 +-\n remote.c                               |  15 +++\n remote.h                               |   5 +\n t/t5510-fetch.sh                       | 238 +++++++++++++++++++++++++++------\n 9 files changed, 391 insertions(+), 61 deletions(-)\n\ntbdiff:\n\n 1: da0992d97c =  1: da0992d97c fetch: don't redundantly NULL something calloc() gave us\n 2: c781d18a29 !  2: 899430a3b2 fetch: trivially refactor assignment to ref_nr\n    @@ -7,8 +7,8 @@\n         \"j\" is, and \"j\" is only incremented in that loop, so this change isn't\n         a logic error.\n         \n    -    This change makes a subsequent change which splits the incrementing of\n    -    \"ref_nr\" into two blocks.\n    +    This change simplifies a subsequent change, which will split the\n    +    incrementing of \"ref_nr\" into two blocks.\n         \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n 3: 1203ef6e35 =  3: 98e3a28bdf fetch: stop accessing \"remote\" variable indirectly\n 4: 1d7956c444 =  4: 384c1fc318 remote: add a macro for \"refs/tags/*:refs/tags/*\"\n 5: a0dc3eb024 =  5: ec92777861 fetch tests: refactor in preparation for testing tag pruning\n 6: 7ed1561e3d =  6: 1c23526223 fetch tests: re-arrange arguments for future readability\n 7: 6dbf0e688d =  7: 59abe07b71 fetch tests: add a tag to be deleted to the pruning tests\n 8: 9fc3589793 =  8: af7acef671 fetch tests: test --prune and refspec interaction\n 9: a4487f9389 =  9: cb72187362 fetch tests: double quote a variable for interpolation\n10: b8c07e2d42 = 10: 300c1c0985 fetch tests: expand case/esac for later change\n11: 6341131ee0 ! 11: 550e7df594 fetch tests: fetch <url> <spec> as well as fetch [<remote>]\n    @@ -60,15 +60,12 @@\n     -\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n     +\tmode=$6\n     +\n    -+\tif ! test -e prune-type-setup-done\n    ++\tif test -z \"$cmdline_setup\"\n     +\tthen\n    -+\t\ttest_expect_success 'prune_type setup' '\n    -+\t\t\tgit -C one config remote.origin.url >one.remote-url &&\n    -+\t\t\tgit -C one config remote.origin.fetch >one.remote-fetch &&\n    -+\t\t\tremote_url=\"file://$(cat one.remote-url)\" &&\n    -+\t\t\tremote_fetch=\"$(cat one.remote-fetch)\" &&\n    -+\t\t\tcmdline_setup=\"\\\"$remote_url\\\" \\\"$remote_fetch\\\"\" &&\n    -+\t\t\ttouch prune-type-setup-done\n    ++\t\ttest_expect_success 'setup cmdline_setup variable for subsequent test' '\n    ++\t\t\tremote_url=\"file://$(git -C one config remote.origin.url)\" &&\n    ++\t\t\tremote_fetch=\"$(git -C one config remote.origin.fetch)\" &&\n    ++\t\t\tcmdline_setup=\"\\\"$remote_url\\\" \\\"$remote_fetch\\\"\"\n     +\t\t'\n     +\tfi\n     +\n12: 824c3ed4c1 ! 12: 273f4c603f git fetch doc: add a new section to explain the ins & outs of pruning\n    @@ -32,7 +32,7 @@\n     +<commit>` needlessly verbose, as well as impacting anything else\n     +that'll work with the complete set of known references.\n     +\n    -+These remote tracking references can be deleted as a one-off with\n    ++These remote-tracking references can be deleted as a one-off with\n     +either of:\n     +\n     +------------------------------------------------\n    @@ -44,13 +44,13 @@\n     +------------------------------------------------\n     +\n     +To prune references as part of your normal workflow without needing to\n    -+remember to run that set `fetch.prune` globally, or\n    ++remember to run that, set `fetch.prune` globally, or\n     +`remote.<name>.prune` per-remote in the config. See\n     +linkgit:git-config[1].\n     +\n     +Here's where things get tricky and more specific. The pruning feature\n     +doesn't actually care about branches, instead it'll prune local <->\n    -+remote references as a function of the refspec of the remote (see\n    ++remote-references as a function of the refspec of the remote (see\n     +`<refspec>` and <<CRTB,CONFIGURED REMOTE-TRACKING BRANCHES>> above).\n     +\n     +Therefore if the refspec for the remote includes\n13: 59de997172 ! 13: ada313b523 git remote doc: correct dangerous lies about what prune does\n    @@ -11,7 +11,7 @@\n         oldest version I could be bothered to test).\n         \n         E.g. if \"refs/tags/*:refs/tags/*\" is explicitly set in the refspec of\n    -    the remote it'll delete all local tags <name> doesn't know about.\n    +    the remote, it'll delete all local tags <name> doesn't know about.\n         \n         Instead, briefly give the reader just enough of a hint that this\n         option might constitute a shotgun aimed at their foot, and point them\n    @@ -36,11 +36,11 @@\n     -These stale branches have already been removed from the remote repository\n     -referenced by <name>, but are still locally available in\n     -\"remotes/<name>\".\n    -+Deletes stale references associated with <name>. By default stale\n    -+remote-tracking branches under <name>, but depending on global\n    -+configuration and the configuration of the remote we might even prune\n    -+local tags that haven't been pushed there. Equivalent to `git fetch\n    -+--prune <name>`, except that no new references will be fetched.\n    ++Deletes stale references associated with <name>. By default, stale\n    ++remote-tracking branches under <name> are deleted, but depending on\n    ++global configuration and the configuration of the remote we might even\n    ++prune local tags that haven't been pushed there. Equivalent to `git\n    ++fetch --prune <name>`, except that no new references will be fetched.\n     ++\n     +See the PRUNING section of linkgit:git-fetch[1] for what it'll prune\n     +depending on various configuration.\n14: 5dfc9177bb = 14: eeb9102d9f git-fetch & config doc: link to the new PRUNING section\n15: fc0890eb9c ! 15: 6c1facccda fetch tests: add scaffolding for the new fetch.pruneTags\n    @@ -28,7 +28,7 @@\n     +\tcmdline=$7\n     +\tmode=$8\n      \n    - \tif ! test -e prune-type-setup-done\n    + \tif test -z \"$cmdline_setup\"\n      \tthen\n     @@\n      \t\tcmdline=\"$new_cmdline\"\n16: 03b1393ea2 ! 16: 1d8d6e29fc fetch: add a --prune-tags option and fetch.pruneTags config\n    @@ -1,9 +1,10 @@\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    -    fetch: add a --fetch-prune option and fetch.pruneTags config\n    -    \n    -    Add a --fetch-prune option to git-fetch, along with fetch.pruneTags\n    -    config option. This allows for doing any of:\n    +    fetch: add a --prune-tags option and fetch.pruneTags config\n    +    \n    +    Add a --prune-tags option to git-fetch, along with fetch.pruneTags\n    +    config option and a -P shorthand (-p is --prune). This allows for\n    +    doing any of:\n         \n             git fetch -p -P\n             git fetch --prune --prune-tags\n    @@ -366,7 +367,7 @@\n     +\t\tif test \"$fetch_prune_tags\" = 'true' ||\n     +\t\t   test \"$remote_origin_prune_tags\" = 'true'\n     +\t\tthen\n    -+\t\t\tif ! printf '%s' \"$cmdline\" | grep -q refs/remotes/origin/\n    ++\t\t\tif ! printf '%s' \"$cmdline\\n\" | grep -q refs/remotes/origin/\n     +\t\t\tthen\n     +\t\t\t\tnew_cmdline=\"$new_cmdline refs/tags/*:refs/tags/*\"\n     +\t\t\tfi\n    @@ -396,7 +397,7 @@\n     +test_configured_prune false true  false true  pruned pruned \"\"\n     +test_configured_prune true  false true  false kept   kept   \"\"\n     +\n    -+# When --prune-tags is supplied it's ignored if an explict refspec is\n    ++# When --prune-tags is supplied it's ignored if an explicit refspec is\n     +# given, same for the configuration options.\n     +test_configured_prune unset unset unset unset pruned kept \\\n     +\t\"--prune --prune-tags origin +refs/heads/*:refs/remotes/origin/*\"\n    @@ -405,13 +406,15 @@\n     +test_configured_prune unset unset unset true pruned  kept \\\n     +\t\"--prune origin +refs/heads/*:refs/remotes/origin/*\"\n     +\n    -+# Pruning that also takes place if s!origin!<file:// url of remote>!,\n    -+# or otherwise uses the file://-specific codepath. However, because\n    -+# there's no implicit +refs/heads/*:refs/remotes/origin/* refspec and\n    -+# supplying it on the command-line negate --prune-tags the branches\n    -+# will not be pruned.\n    -+test_configured_prune_type unset unset unset unset kept kept     \"origin --prune-tags\" \"name\"\n    -+test_configured_prune_type unset unset unset unset kept kept     \"origin --prune-tags\" \"link\"\n    ++# Pruning that also takes place if a file:// url replaces a named\n    ++# remote, with the exception of --prune-tags on the command-line\n    ++# (arbitrary limitation).\n    ++#\n    ++# However, because there's no implicit\n    ++# +refs/heads/*:refs/remotes/origin/* refspec and supplying it on the\n    ++# command-line negates --prune-tags, the branches will not be pruned.\n    ++test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune-tags\" \"name\"\n    ++test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune-tags\" \"link\"\n     +test_configured_prune_type unset unset unset unset pruned pruned \"origin --prune --prune-tags\" \"name\"\n     +test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune --prune-tags\" \"link\"\n     +test_configured_prune_type unset unset unset unset pruned pruned \"--prune --prune-tags origin\" \"name\"\n    @@ -427,9 +430,8 @@\n     +test_configured_prune_type unset  true unset true  pruned pruned \"origin\" \"name\"\n     +test_configured_prune_type unset  true unset true  kept   pruned \"origin\" \"link\"\n     +\n    -+# When all remote.origin.fetch settings are deleted a --prune\n    -+# --prune-tags still implicitly supplies refs/tags/*:refs/tags/* so\n    -+# tags, but not tracking branches, will be deleted.\n    ++# Interaction between --prune-tags and no \"fetch\" config in the remote\n    ++# at all.\n     +test_expect_success 'remove remote.origin.fetch \"one\"' '\n     +\t(\n     +\t\tcd one &&\n17: f0dca21626 ! 17: ddcd576c5c fetch: make the --prune-tags work with <url>\n    @@ -1,6 +1,6 @@\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    -    fetch: make the --fetch-prune work with <url>\n    +    fetch: make the --prune-tags work with <url>\n         \n         Make the new --prune-tags option work properly when git-fetch is\n         invoked with a <url> parameter instead of a <remote name>\n    @@ -91,8 +91,18 @@\n     --- a/t/t5510-fetch.sh\n     +++ b/t/t5510-fetch.sh\n     @@\n    - test_configured_prune_type unset unset unset unset kept kept     \"origin --prune-tags\" \"name\"\n    - test_configured_prune_type unset unset unset unset kept kept     \"origin --prune-tags\" \"link\"\n    + \t\"--prune origin +refs/heads/*:refs/remotes/origin/*\"\n    + \n    + # Pruning that also takes place if a file:// url replaces a named\n    +-# remote, with the exception of --prune-tags on the command-line\n    +-# (arbitrary limitation).\n    +-#\n    +-# However, because there's no implicit\n    ++# remote. However, because there's no implicit\n    + # +refs/heads/*:refs/remotes/origin/* refspec and supplying it on the\n    + # command-line negates --prune-tags, the branches will not be pruned.\n    + test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune-tags\" \"name\"\n    + test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune-tags\" \"link\"\n      test_configured_prune_type unset unset unset unset pruned pruned \"origin --prune --prune-tags\" \"name\"\n     -test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune --prune-tags\" \"link\"\n     +test_configured_prune_type unset unset unset unset kept   pruned \"origin --prune --prune-tags\" \"link\"\n    @@ -103,6 +113,18 @@\n      test_configured_prune_type unset unset true  unset kept   pruned \"--prune origin\" \"link\"\n      test_configured_prune_type unset unset unset true  pruned pruned \"--prune origin\" \"name\"\n     @@\n    + test_configured_prune_type unset  true unset true  pruned pruned \"origin\" \"name\"\n    + test_configured_prune_type unset  true unset true  kept   pruned \"origin\" \"link\"\n    + \n    +-# Interaction between --prune-tags and no \"fetch\" config in the remote\n    +-# at all.\n    ++# When all remote.origin.fetch settings are deleted a --prune\n    ++# --prune-tags still implicitly supplies refs/tags/*:refs/tags/* so\n    ++# tags, but not tracking branches, will be deleted.\n    + test_expect_success 'remove remote.origin.fetch \"one\"' '\n    + \t(\n    + \t\tcd one &&\n    +@@\n      \t)\n      '\n      test_configured_prune_type unset unset unset unset kept pruned \"origin --prune --prune-tags\" \"name\"\n\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338912","messageId":"20180209203216.11560-2-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 01/17] fetch: don't redundantly NULL something calloc() gave us","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:00Z","receivedAt":"2018-02-09T20:32:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Stop redundantly NULL-ing the last element of the refs structure,\nwhich was retrieved via calloc(), and is thus guaranteed to be\npre-NULL'd.\n\nThis code dates back to b888d61c83 (\"Make fetch a builtin\",\n2007-09-10), where wasn't any reason to do this back then either, it's\njust boilerplate left over from when git-fetch was initially\nintroduced.\n\nThe motivation for this change was to make a subsequent change which\nwould also modify the refs variable smaller, since it won't have to\ncopy this redundant \"NULL the last + 1 item\" pattern.\n\nWe may not end up keeping that change, but as this pattern is still\npointless, so let's fix it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fetch.c | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 7bbcd26faf..b34665db9e 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1302,7 +1302,6 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \t\t\t} else\n \t\t\t\trefs[j++] = argv[i];\n \t\t}\n-\t\trefs[j] = NULL;\n \t\tref_nr = j;\n \t}\n \n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338913","messageId":"20180209203216.11560-3-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 02/17] fetch: trivially refactor assignment to ref_nr","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:01Z","receivedAt":"2018-02-09T20:32:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Trivially refactor an assignment to make a subsequent patch\nsmaller. The \"ref_nr\" variable is initialized to 0 earlier, just as\n\"j\" is, and \"j\" is only incremented in that loop, so this change isn't\na logic error.\n\nThis change simplifies a subsequent change, which will split the\nincrementing of \"ref_nr\" into two blocks.\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 b34665db9e..72085e30b9 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1301,8 +1301,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \t\t\t\t\t\t    argv[i], argv[i]);\n \t\t\t} else\n \t\t\t\trefs[j++] = argv[i];\n+\t\t\tref_nr++;\n \t\t}\n-\t\tref_nr = j;\n \t}\n \n \tsigchain_push_common(unlock_pack_on_signal);\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338914","messageId":"20180209203216.11560-4-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 03/17] fetch: stop accessing \"remote\" variable indirectly","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:02Z","receivedAt":"2018-02-09T20:32:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Access the \"remote\" variable passed to the fetch_one() directly rather\nthan through the gtransport wrapper struct constructed in this\nfunction for other purposes.\n\nThis makes the code more readable, as it's now obvious that the remote\nstruct doesn't somehow get munged by the prepare_transport() function\nabove, which takes the \"remote\" struct as an argument and constructs\nthe \"gtransport\" struct, containing among other things the \"remote\"\nstruct.\n\nA subsequent change will copy this pattern to access a new\nremote->prune_tags field, but without the use of the gtransport\nvariable. It's useful once that change lands to see that the two\npieces of code behave exactly the same.\n\nThis pattern of accessing the container struct was added in\n737c5a9cde (\"fetch: make --prune configurable\", 2013-07-13) when this\ncode was initially introduced.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fetch.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 72085e30b9..a7705bc150 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1280,8 +1280,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \n \tif (prune < 0) {\n \t\t/* no command line request */\n-\t\tif (0 <= gtransport->remote->prune)\n-\t\t\tprune = gtransport->remote->prune;\n+\t\tif (0 <= remote->prune)\n+\t\t\tprune = remote->prune;\n \t\telse if (0 <= fetch_prune_config)\n \t\t\tprune = fetch_prune_config;\n \t\telse\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338915","messageId":"20180209203216.11560-5-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 04/17] remote: add a macro for \"refs/tags/*:refs/tags/*\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:03Z","receivedAt":"2018-02-09T20:32:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a macro with the refspec string \"refs/tags/*:refs/tags/*\". There's\nbeen a pre-defined struct version of this since e0aaa29ff3 (\"Have a\nconstant extern refspec for \"--tags\"\", 2008-04-17), but nothing that\ncould be passed to e.g. add_fetch_refspec().\n\nThis will be used in subsequent commits to avoid hardcoding this\nstring in multiple places.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n remote.c | 1 +\n remote.h | 2 ++\n 2 files changed, 3 insertions(+)\n\ndiff --git a/remote.c b/remote.c\nindex 4e93753e19..356c123e3e 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -22,6 +22,7 @@ static struct refspec s_tag_refspec = {\n \t\"refs/tags/*\"\n };\n \n+/* See TAG_REFSPEC for the string version */\n const struct refspec *tag_refspec = &s_tag_refspec;\n \n struct counted_string {\ndiff --git a/remote.h b/remote.h\nindex 1f6611be21..80fea6dd11 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -297,4 +297,6 @@ extern int parseopt_push_cas_option(const struct option *, const char *arg, int\n extern int is_empty_cas(const struct push_cas_option *);\n void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);\n \n+#define TAG_REFSPEC \"refs/tags/*:refs/tags/*\"\n+\n #endif\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338916","messageId":"20180209203216.11560-6-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 05/17] fetch tests: refactor in preparation for testing tag pruning","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:04Z","receivedAt":"2018-02-09T20:32:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In a subsequent commit this function will learn to test for tag\npruning, prepare for that by making space for more variables, and\nmaking it clear that \"expected\" here refers to branches.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 9 ++++++---\n 1 file changed, 6 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 668c54be41..11da97f9b7 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -549,9 +549,12 @@ set_config_tristate () {\n }\n \n test_configured_prune () {\n-\tfetch_prune=$1 remote_origin_prune=$2 cmdline=$3 expected=$4\n+\tfetch_prune=$1\n+\tremote_origin_prune=$2\n+\tcmdline=$3\n+\texpected_branch=$4\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; $4\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; branch:$4\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\t(\n@@ -572,7 +575,7 @@ test_configured_prune () {\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n \n \t\t\tgit fetch $cmdline &&\n-\t\t\tcase \"$expected\" in\n+\t\t\tcase \"$expected_branch\" in\n \t\t\tpruned)\n \t\t\t\ttest_must_fail git rev-parse --verify refs/remotes/origin/newbranch\n \t\t\t\t;;\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338917","messageId":"20180209203216.11560-9-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 08/17] fetch tests: test --prune and refspec interaction","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:07Z","receivedAt":"2018-02-09T20:32:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for the interaction between explicitly provided refspecs\nand fetch.prune.\n\nThere's no point in adding this boilerplate to every combination of\nunset/false/true, it's instructive and sufficient to show that no\nmatter if the variable is unset, false or true the refspec on the\ncommand-line overrides any configuration variable.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 12 ++++++++++++\n 1 file changed, 12 insertions(+)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex fad65bd885..dacdb8759c 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -609,6 +609,10 @@ test_configured_prune () {\n test_configured_prune unset unset kept   kept   \"\"\n test_configured_prune unset unset kept   kept   \"--no-prune\"\n test_configured_prune unset unset pruned kept   \"--prune\"\n+test_configured_prune unset unset kept   pruned \\\n+\t\"--prune origin refs/tags/*:refs/tags/*\"\n+test_configured_prune unset unset pruned pruned \\\n+\t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n test_configured_prune false unset kept   kept   \"\"\n test_configured_prune false unset kept   kept   \"--no-prune\"\n@@ -625,6 +629,10 @@ test_configured_prune unset false pruned kept   \"--prune\"\n test_configured_prune false false kept   kept   \"\"\n test_configured_prune false false kept   kept   \"--no-prune\"\n test_configured_prune false false pruned kept   \"--prune\"\n+test_configured_prune false false kept   pruned \\\n+\t\"--prune origin refs/tags/*:refs/tags/*\"\n+test_configured_prune false false pruned pruned \\\n+\t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n test_configured_prune true  false kept   kept   \"\"\n test_configured_prune true  false pruned kept   \"--prune\"\n@@ -641,6 +649,10 @@ test_configured_prune false true  pruned kept   \"--prune\"\n test_configured_prune true  true  pruned kept   \"\"\n test_configured_prune true  true  pruned kept   \"--prune\"\n test_configured_prune true  true  kept   kept   \"--no-prune\"\n+test_configured_prune true  true  kept   pruned \\\n+\t\"--prune origin refs/tags/*:refs/tags/*\"\n+test_configured_prune true  true  pruned pruned \\\n+\t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338918","messageId":"20180209203216.11560-10-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 09/17] fetch tests: double quote a variable for interpolation","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:08Z","receivedAt":"2018-02-09T20:32:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"If the $cmdline variable contains arguments with spaces they won't be\ninterpolated correctly, since the body of the test is single quoted,\nand because test-lib.sh does its own eval().\n\nThis will be used in a subsequent commit to pass arguments that need\nto be quoted to git-fetch, i.e. a file:// path to fetch, which will\nhave a space in it.\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 dacdb8759c..88d38e0819 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -578,7 +578,7 @@ test_configured_prune () {\n \t\t\tset_config_tristate fetch.prune $fetch_prune &&\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n \n-\t\t\tgit fetch $cmdline &&\n+\t\t\tgit fetch '\"$cmdline\"' &&\n \t\t\tcase \"$expected_branch\" in\n \t\t\tpruned)\n \t\t\t\ttest_must_fail git rev-parse --verify refs/remotes/origin/newbranch\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338919","messageId":"20180209203216.11560-11-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 10/17] fetch tests: expand case/esac for later change","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:09Z","receivedAt":"2018-02-09T20:32:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Expand a compact case/esac statement for a later change that'll add\nmore logic to the body of the \"*\" case. This is a whitespace-only\nchange.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 88d38e0819..dfc749f576 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -543,8 +543,12 @@ test_expect_success \"should be able to fetch with duplicate refspecs\" '\n set_config_tristate () {\n \t# var=$1 val=$2\n \tcase \"$2\" in\n-\tunset)  test_unconfig \"$1\" ;;\n-\t*)\tgit config \"$1\" \"$2\" ;;\n+\tunset)\n+\t\ttest_unconfig \"$1\"\n+\t\t;;\n+\t*)\n+\t\tgit config \"$1\" \"$2\"\n+\t\t;;\n \tesac\n }\n \n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338920","messageId":"20180209203216.11560-12-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 11/17] fetch tests: fetch <url> <spec> as well as fetch [<remote>]","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:10Z","receivedAt":"2018-02-09T20:33:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"When a remote URL is supplied on the command-line the internals of the\nfetch are different, in particular the code in get_ref_map(). An\nearlier version of the subsequent fetch.pruneTags patch hid a segfault\nbecause the difference wasn't tested for.\n\nNow all the tests are run as both of the variants of:\n\n    git fetch\n    git -c [...] fetch $(git config remote.origin.url) $(git config remote.origin.fetch)\n\nI'm using -c because while the [fetch] config just set by\nset_config_tristate will be picked up, the remote.origin.* config\nwon't override it as intended.\n\nWork around that and turn this into a purely command-line test by\nalways setting the variables on the command-line, and translate any\nsetting of remote.origin.X into fetch.X.\n\nThe reason for choosing the names \"name\" and \"link\" as opposed to\ne.g. \"named\" and \"url\" is because they're the same length, which makes\nthe test output easier to read as it will be aligned.\n\nDue to shellscript quoting madness it's not worthwhile to do all of\nthis within a test_expect_success, but do the parts that can easily be\ndone there, including the one-time setting of variables that don't\nchange between runs to be used by subsequent runs in the 'prune_type\nsetup' test.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 44 ++++++++++++++++++++++++++++++++++++++++----\n 1 file changed, 40 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex dfc749f576..9c87fa6106 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -548,18 +548,49 @@ set_config_tristate () {\n \t\t;;\n \t*)\n \t\tgit config \"$1\" \"$2\"\n+\t\tkey=$(echo $1 | sed -e 's/^remote\\.origin/fetch/')\n+\t\tgit_fetch_c=\"$git_fetch_c -c $key=$2\"\n \t\t;;\n \tesac\n }\n \n test_configured_prune () {\n+\ttest_configured_prune_type \"$@\" \"name\"\n+\ttest_configured_prune_type \"$@\" \"link\"\n+}\n+\n+test_configured_prune_type () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n \texpected_branch=$3\n \texpected_tag=$4\n \tcmdline=$5\n-\n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n+\tmode=$6\n+\n+\tif test -z \"$cmdline_setup\"\n+\tthen\n+\t\ttest_expect_success 'setup cmdline_setup variable for subsequent test' '\n+\t\t\tremote_url=\"file://$(git -C one config remote.origin.url)\" &&\n+\t\t\tremote_fetch=\"$(git -C one config remote.origin.fetch)\" &&\n+\t\t\tcmdline_setup=\"\\\"$remote_url\\\" \\\"$remote_fetch\\\"\"\n+\t\t'\n+\tfi\n+\n+\tif test \"$mode\" = 'link'\n+\tthen\n+\t\tnew_cmdline=\"\"\n+\n+\t\tif test \"$cmdline\" = \"\"\n+\t\tthen\n+\t\t\tnew_cmdline=$cmdline_setup\n+\t\telse\n+\t\t\tnew_cmdline=$(printf \"%s\" \"$cmdline\" | perl -pe 's[origin(?!/)][\"'\"$remote_url\"'\"]g')\n+\t\tfi\n+\n+\t\tcmdline=\"$new_cmdline\"\n+\tfi\n+\n+\ttest_expect_success \"$mode prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\tgit tag -f newtag &&\n@@ -567,7 +598,7 @@ test_configured_prune () {\n \t\t\tcd one &&\n \t\t\ttest_unconfig fetch.prune &&\n \t\t\ttest_unconfig remote.origin.prune &&\n-\t\t\tgit fetch &&\n+\t\t\tgit fetch '\"$cmdline_setup\"' &&\n \t\t\tgit rev-parse --verify refs/remotes/origin/newbranch &&\n \t\t\tgit rev-parse --verify refs/tags/newtag\n \t\t) &&\n@@ -579,10 +610,15 @@ test_configured_prune () {\n \t\t# then test\n \t\t(\n \t\t\tcd one &&\n+\t\t\tgit_fetch_c=\"\" &&\n \t\t\tset_config_tristate fetch.prune $fetch_prune &&\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n \n-\t\t\tgit fetch '\"$cmdline\"' &&\n+\t\t\tif test \"$mode\" != \"link\"\n+\t\t\tthen\n+\t\t\t\tgit_fetch_c=\"\"\n+\t\t\tfi &&\n+\t\t\tgit$git_fetch_c fetch '\"$cmdline\"' &&\n \t\t\tcase \"$expected_branch\" in\n \t\t\tpruned)\n \t\t\t\ttest_must_fail git rev-parse --verify refs/remotes/origin/newbranch\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338921","messageId":"20180209203216.11560-14-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 13/17] git remote doc: correct dangerous lies about what prune does","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:12Z","receivedAt":"2018-02-09T20:33:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The \"git remote prune <name>\" command uses the same machinery as \"git\nfetch <name> --prune\", and shares all the same caveats, but its\ndocumentation has suggested that it'll just \"delete stale\nremote-tracking branches under <name>\".\n\nThis isn't true, and hasn't been true since at least v1.8.5.6 (the\noldest version I could be bothered to test).\n\nE.g. if \"refs/tags/*:refs/tags/*\" is explicitly set in the refspec of\nthe remote, it'll delete all local tags <name> doesn't know about.\n\nInstead, briefly give the reader just enough of a hint that this\noption might constitute a shotgun aimed at their foot, and point them\nto the new PRUNING section in the git-fetch documentation which\nexplains all the nuances of what this facility does.\n\nSee \"[BUG] git remote prune removes local tags, depending on fetch\nconfig\" (CACi5S_39wNrbfjLfn0xhCY+uewtFN2YmnAcRc86z6pjUTjWPHQ@mail.gmail.com)\nby Michael Giuffrida for the initial report.\n\nReported-by: Michael Giuffrida <michaelpg@chromium.org>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-remote.txt | 14 +++++++++-----\n 1 file changed, 9 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 577b969c1b..4feddc0293 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -172,10 +172,14 @@ With `-n` option, the remote heads are not queried first with\n \n 'prune'::\n \n-Deletes all stale remote-tracking branches under <name>.\n-These stale branches have already been removed from the remote repository\n-referenced by <name>, but are still locally available in\n-\"remotes/<name>\".\n+Deletes stale references associated with <name>. By default, stale\n+remote-tracking branches under <name> are deleted, but depending on\n+global configuration and the configuration of the remote we might even\n+prune local tags that haven't been pushed there. Equivalent to `git\n+fetch --prune <name>`, except that no new references will be fetched.\n++\n+See the PRUNING section of linkgit:git-fetch[1] for what it'll prune\n+depending on various configuration.\n +\n With `--dry-run` option, report what branches will be pruned, but do not\n actually prune them.\n@@ -189,7 +193,7 @@ remotes.default is not defined, all remotes which do not have the\n configuration parameter remote.<name>.skipDefaultUpdate set to true will\n be updated.  (See linkgit:git-config[1]).\n +\n-With `--prune` option, prune all the remotes that are updated.\n+With `--prune` option, run pruning against all the remotes that are updated.\n \n \n DISCUSSION\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338922","messageId":"20180209203216.11560-13-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 12/17] git fetch doc: add a new section to explain the ins & outs of pruning","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:11Z","receivedAt":"2018-02-09T20:33:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a new section to canonically explain how remote reference pruning\nworks, and how users should be careful about using it in conjunction\nwith tag refspecs in particular.\n\nA subsequent commit will update the git-remote documentation to refer\nto this section, and details the motivation for writing this in the\nfirst place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-fetch.txt | 49 +++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 49 insertions(+)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex b153aefa68..e94bcfb8c3 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -99,6 +99,55 @@ The latter use of the `remote.<repository>.fetch` values can be\n overridden by giving the `--refmap=<refspec>` parameter(s) on the\n command line.\n \n+PRUNING\n+-------\n+\n+Git has a default disposition of keeping data unless it's explicitly\n+thrown away; this extends to holding onto local references to branches\n+on remotes that have themselves deleted those branches.\n+\n+If left to accumulate, these stale references might make performance\n+worse on big and busy repos that have a lot of branch churn, and\n+e.g. make the output of commands like `git branch -a --contains\n+<commit>` needlessly verbose, as well as impacting anything else\n+that'll work with the complete set of known references.\n+\n+These remote-tracking references can be deleted as a one-off with\n+either of:\n+\n+------------------------------------------------\n+# While fetching\n+$ git fetch --prune <name>\n+\n+# Only prune, don't fetch\n+$ git remote prune <name>\n+------------------------------------------------\n+\n+To prune references as part of your normal workflow without needing to\n+remember to run that, set `fetch.prune` globally, or\n+`remote.<name>.prune` per-remote in the config. See\n+linkgit:git-config[1].\n+\n+Here's where things get tricky and more specific. The pruning feature\n+doesn't actually care about branches, instead it'll prune local <->\n+remote-references as a function of the refspec of the remote (see\n+`<refspec>` and <<CRTB,CONFIGURED REMOTE-TRACKING BRANCHES>> above).\n+\n+Therefore if the refspec for the remote includes\n+e.g. `refs/tags/*:refs/tags/*`, or you manually run e.g. `git fetch\n+--prune <name> \"refs/tags/*:refs/tags/*\"` it won't be stale remote\n+tracking branches that are deleted, but any local tag that doesn't\n+exist on the remote.\n+\n+This might not be what you expect, i.e. you want to prune remote\n+`<name>`, but also explicitly fetch tags from it, so when you fetch\n+from it you delete all your local tags, most of which may not have\n+come from the `<name>` remote in the first place.\n+\n+So be careful when using this with a refspec like\n+`refs/tags/*:refs/tags/*`, or any other refspec which might map\n+references from multiple remotes to the same local namespace.\n+\n OUTPUT\n ------\n \n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338923","messageId":"20180209203216.11560-7-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 06/17] fetch tests: re-arrange arguments for future readability","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:05Z","receivedAt":"2018-02-09T20:33:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Re-arrange the arguments to the test_configured_prune() function used\nin this test to pass the arguments to --fetch last. A subsequent\nchange will test for more elaborate fetch arguments, including long\nrefspecs. It'll be more readable to be able to wrap those on a new\nline of their own.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 82 ++++++++++++++++++++++++++++++--------------------------\n 1 file changed, 44 insertions(+), 38 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 11da97f9b7..ab8b25344d 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -551,10 +551,10 @@ set_config_tristate () {\n test_configured_prune () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n-\tcmdline=$3\n-\texpected_branch=$4\n+\texpected_branch=$3\n+\tcmdline=$4\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; branch:$4\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${4:+ $4}; branch:$3\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\t(\n@@ -587,41 +587,47 @@ test_configured_prune () {\n \t'\n }\n \n-test_configured_prune unset unset \"\"\t\tkept\n-test_configured_prune unset unset \"--no-prune\"\tkept\n-test_configured_prune unset unset \"--prune\"\tpruned\n-\n-test_configured_prune false unset \"\"\t\tkept\n-test_configured_prune false unset \"--no-prune\"\tkept\n-test_configured_prune false unset \"--prune\"\tpruned\n-\n-test_configured_prune true  unset \"\"\t\tpruned\n-test_configured_prune true  unset \"--prune\"\tpruned\n-test_configured_prune true  unset \"--no-prune\"\tkept\n-\n-test_configured_prune unset false \"\"\t\tkept\n-test_configured_prune unset false \"--no-prune\"\tkept\n-test_configured_prune unset false \"--prune\"\tpruned\n-\n-test_configured_prune false false \"\"\t\tkept\n-test_configured_prune false false \"--no-prune\"\tkept\n-test_configured_prune false false \"--prune\"\tpruned\n-\n-test_configured_prune true  false \"\"\t\tkept\n-test_configured_prune true  false \"--prune\"\tpruned\n-test_configured_prune true  false \"--no-prune\"\tkept\n-\n-test_configured_prune unset true  \"\"\t\tpruned\n-test_configured_prune unset true  \"--no-prune\"\tkept\n-test_configured_prune unset true  \"--prune\"\tpruned\n-\n-test_configured_prune false true  \"\"\t\tpruned\n-test_configured_prune false true  \"--no-prune\"\tkept\n-test_configured_prune false true  \"--prune\"\tpruned\n-\n-test_configured_prune true  true  \"\"\t\tpruned\n-test_configured_prune true  true  \"--prune\"\tpruned\n-test_configured_prune true  true  \"--no-prune\"\tkept\n+# $1 config: fetch.prune\n+# $2 config: remote.<name>.prune\n+# $3 expect: branch to be pruned?\n+# $4 git-fetch $cmdline:\n+#\n+#                     $1    $2    $3     $4\n+test_configured_prune unset unset kept   \"\"\n+test_configured_prune unset unset kept   \"--no-prune\"\n+test_configured_prune unset unset pruned \"--prune\"\n+\n+test_configured_prune false unset kept   \"\"\n+test_configured_prune false unset kept   \"--no-prune\"\n+test_configured_prune false unset pruned \"--prune\"\n+\n+test_configured_prune true  unset pruned \"\"\n+test_configured_prune true  unset pruned \"--prune\"\n+test_configured_prune true  unset kept   \"--no-prune\"\n+\n+test_configured_prune unset false kept   \"\"\n+test_configured_prune unset false kept   \"--no-prune\"\n+test_configured_prune unset false pruned \"--prune\"\n+\n+test_configured_prune false false kept   \"\"\n+test_configured_prune false false kept   \"--no-prune\"\n+test_configured_prune false false pruned \"--prune\"\n+\n+test_configured_prune true  false kept   \"\"\n+test_configured_prune true  false pruned \"--prune\"\n+test_configured_prune true  false kept   \"--no-prune\"\n+\n+test_configured_prune unset true  pruned \"\"\n+test_configured_prune unset true  kept   \"--no-prune\"\n+test_configured_prune unset true  pruned \"--prune\"\n+\n+test_configured_prune false true  pruned \"\"\n+test_configured_prune false true  kept   \"--no-prune\"\n+test_configured_prune false true  pruned \"--prune\"\n+\n+test_configured_prune true  true  pruned \"\"\n+test_configured_prune true  true  pruned \"--prune\"\n+test_configured_prune true  true  kept   \"--no-prune\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338924","messageId":"20180209203216.11560-8-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 07/17] fetch tests: add a tag to be deleted to the pruning tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:06Z","receivedAt":"2018-02-09T20:33:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a tag to be deleted to the fetch --prune tests. The tag is always\nkept for now, which is the expected behavior, but now I can add a test\nfor tag pruning in a later commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 93 ++++++++++++++++++++++++++++++++------------------------\n 1 file changed, 53 insertions(+), 40 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex ab8b25344d..fad65bd885 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -552,21 +552,25 @@ test_configured_prune () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n \texpected_branch=$3\n-\tcmdline=$4\n+\texpected_tag=$4\n+\tcmdline=$5\n \n-\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${4:+ $4}; branch:$3\" '\n+\ttest_expect_success \"prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n+\t\tgit tag -f newtag &&\n \t\t(\n \t\t\tcd one &&\n \t\t\ttest_unconfig fetch.prune &&\n \t\t\ttest_unconfig remote.origin.prune &&\n \t\t\tgit fetch &&\n-\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch\n+\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch &&\n+\t\t\tgit rev-parse --verify refs/tags/newtag\n \t\t) &&\n \n \t\t# now remove it\n \t\tgit branch -d newbranch &&\n+\t\tgit tag -d newtag &&\n \n \t\t# then test\n \t\t(\n@@ -582,6 +586,14 @@ test_configured_prune () {\n \t\t\tkept)\n \t\t\t\tgit rev-parse --verify refs/remotes/origin/newbranch\n \t\t\t\t;;\n+\t\t\tesac &&\n+\t\t\tcase \"$expected_tag\" in\n+\t\t\tpruned)\n+\t\t\t\ttest_must_fail git rev-parse --verify refs/tags/newtag\n+\t\t\t\t;;\n+\t\t\tkept)\n+\t\t\t\tgit rev-parse --verify refs/tags/newtag\n+\t\t\t\t;;\n \t\t\tesac\n \t\t)\n \t'\n@@ -590,44 +602,45 @@ test_configured_prune () {\n # $1 config: fetch.prune\n # $2 config: remote.<name>.prune\n # $3 expect: branch to be pruned?\n-# $4 git-fetch $cmdline:\n+# $4 expect: tag to be pruned?\n+# $5 git-fetch $cmdline:\n #\n-#                     $1    $2    $3     $4\n-test_configured_prune unset unset kept   \"\"\n-test_configured_prune unset unset kept   \"--no-prune\"\n-test_configured_prune unset unset pruned \"--prune\"\n-\n-test_configured_prune false unset kept   \"\"\n-test_configured_prune false unset kept   \"--no-prune\"\n-test_configured_prune false unset pruned \"--prune\"\n-\n-test_configured_prune true  unset pruned \"\"\n-test_configured_prune true  unset pruned \"--prune\"\n-test_configured_prune true  unset kept   \"--no-prune\"\n-\n-test_configured_prune unset false kept   \"\"\n-test_configured_prune unset false kept   \"--no-prune\"\n-test_configured_prune unset false pruned \"--prune\"\n-\n-test_configured_prune false false kept   \"\"\n-test_configured_prune false false kept   \"--no-prune\"\n-test_configured_prune false false pruned \"--prune\"\n-\n-test_configured_prune true  false kept   \"\"\n-test_configured_prune true  false pruned \"--prune\"\n-test_configured_prune true  false kept   \"--no-prune\"\n-\n-test_configured_prune unset true  pruned \"\"\n-test_configured_prune unset true  kept   \"--no-prune\"\n-test_configured_prune unset true  pruned \"--prune\"\n-\n-test_configured_prune false true  pruned \"\"\n-test_configured_prune false true  kept   \"--no-prune\"\n-test_configured_prune false true  pruned \"--prune\"\n-\n-test_configured_prune true  true  pruned \"\"\n-test_configured_prune true  true  pruned \"--prune\"\n-test_configured_prune true  true  kept   \"--no-prune\"\n+#                     $1    $2    $3     $4     $5\n+test_configured_prune unset unset kept   kept   \"\"\n+test_configured_prune unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset unset pruned kept   \"--prune\"\n+\n+test_configured_prune false unset kept   kept   \"\"\n+test_configured_prune false unset kept   kept   \"--no-prune\"\n+test_configured_prune false unset pruned kept   \"--prune\"\n+\n+test_configured_prune true  unset pruned kept   \"\"\n+test_configured_prune true  unset pruned kept   \"--prune\"\n+test_configured_prune true  unset kept   kept   \"--no-prune\"\n+\n+test_configured_prune unset false kept   kept   \"\"\n+test_configured_prune unset false kept   kept   \"--no-prune\"\n+test_configured_prune unset false pruned kept   \"--prune\"\n+\n+test_configured_prune false false kept   kept   \"\"\n+test_configured_prune false false kept   kept   \"--no-prune\"\n+test_configured_prune false false pruned kept   \"--prune\"\n+\n+test_configured_prune true  false kept   kept   \"\"\n+test_configured_prune true  false pruned kept   \"--prune\"\n+test_configured_prune true  false kept   kept   \"--no-prune\"\n+\n+test_configured_prune unset true  pruned kept   \"\"\n+test_configured_prune unset true  kept   kept   \"--no-prune\"\n+test_configured_prune unset true  pruned kept   \"--prune\"\n+\n+test_configured_prune false true  pruned kept   \"\"\n+test_configured_prune false true  kept   kept   \"--no-prune\"\n+test_configured_prune false true  pruned kept   \"--prune\"\n+\n+test_configured_prune true  true  pruned kept   \"\"\n+test_configured_prune true  true  pruned kept   \"--prune\"\n+test_configured_prune true  true  kept   kept   \"--no-prune\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338925","messageId":"20180209203216.11560-15-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 14/17] git-fetch & config doc: link to the new PRUNING section","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:13Z","receivedAt":"2018-02-09T20:33:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend the documentation for fetch.prune, fetch.<name>.prune and\n--prune to link to the recently added PRUNING section.\n\nI'd have liked to link directly to it with \"<<PRUNING>>\" from\nfetch-options.txt, since it's included in git-fetch.txt (git-pull.txt\nalso includes it, but doesn't include that option). However making a\nreference across files yields this error:\n\n    [...]/Documentation/git-fetch.xml:226: element xref: validity\n    error : IDREF attribute linkend references an unknown ID \"PRUNING\"\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt        | 6 +++++-\n Documentation/fetch-options.txt | 3 +++\n 2 files changed, 8 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 0e25b2c92b..0f27af5760 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1398,7 +1398,8 @@ fetch.unpackLimit::\n \n fetch.prune::\n \tIf true, fetch will automatically behave as if the `--prune`\n-\toption was given on the command line.  See also `remote.<name>.prune`.\n+\toption was given on the command line.  See also `remote.<name>.prune`\n+\tand the PRUNING section of linkgit:git-fetch[1].\n \n fetch.output::\n \tControl how ref update status is printed. Valid values are\n@@ -2944,6 +2945,9 @@ remote.<name>.prune::\n \tremove any remote-tracking references that no longer exist on the\n \tremote (as if the `--prune` option was given on the command line).\n \tOverrides `fetch.prune` settings, if any.\n++\n+See also `remote.<name>.prune` and the PRUNING section of\n+linkgit:git-fetch[1].\n \n remotes.<group>::\n \tThe list of remotes which are fetched by \"git remote update\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex fb6bebbc61..9f5c85ad96 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -74,6 +74,9 @@ ifndef::git-pull[]\n \tline or in the remote configuration, for example if the remote\n \twas cloned with the --mirror option), then they are also\n \tsubject to pruning.\n++\n+See the PRUNING section below for more details.\n+\n endif::git-pull[]\n \n ifndef::git-pull[]\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338926","messageId":"20180209203216.11560-16-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 15/17] fetch tests: add scaffolding for the new fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:14Z","receivedAt":"2018-02-09T20:33:21Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The fetch.pruneTags configuration doesn't exist yet, but will be added\nin a subsequent commit. Since testing for it requires adding new\nparameters to the test_configured_prune function it's easier to review\nthis patch first to assert that no functional changes are introduced\nyet.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t5510-fetch.sh | 92 ++++++++++++++++++++++++++++++--------------------------\n 1 file changed, 50 insertions(+), 42 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 9c87fa6106..1713111006 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -562,10 +562,12 @@ test_configured_prune () {\n test_configured_prune_type () {\n \tfetch_prune=$1\n \tremote_origin_prune=$2\n-\texpected_branch=$3\n-\texpected_tag=$4\n-\tcmdline=$5\n-\tmode=$6\n+\tfetch_prune_tags=$3\n+\tremote_origin_prune_tags=$4\n+\texpected_branch=$5\n+\texpected_tag=$6\n+\tcmdline=$7\n+\tmode=$8\n \n \tif test -z \"$cmdline_setup\"\n \tthen\n@@ -590,14 +592,16 @@ test_configured_prune_type () {\n \t\tcmdline=\"$new_cmdline\"\n \tfi\n \n-\ttest_expect_success \"$mode prune fetch.prune=$1 remote.origin.prune=$2${5:+ $5}; branch:$3 tag:$4\" '\n+\ttest_expect_success \"$mode prune fetch.prune=$1 remote.origin.prune=$2 fetch.pruneTags=$3 remote.origin.pruneTags=$4${7:+ $7}; branch:$5 tag:$6\" '\n \t\t# make sure a newbranch is there in . and also in one\n \t\tgit branch -f newbranch &&\n \t\tgit tag -f newtag &&\n \t\t(\n \t\t\tcd one &&\n \t\t\ttest_unconfig fetch.prune &&\n+\t\t\ttest_unconfig fetch.pruneTags &&\n \t\t\ttest_unconfig remote.origin.prune &&\n+\t\t\ttest_unconfig remote.origin.pruneTags &&\n \t\t\tgit fetch '\"$cmdline_setup\"' &&\n \t\t\tgit rev-parse --verify refs/remotes/origin/newbranch &&\n \t\t\tgit rev-parse --verify refs/tags/newtag\n@@ -612,7 +616,9 @@ test_configured_prune_type () {\n \t\t\tcd one &&\n \t\t\tgit_fetch_c=\"\" &&\n \t\t\tset_config_tristate fetch.prune $fetch_prune &&\n+\t\t\tset_config_tristate fetch.pruneTags $fetch_prune_tags &&\n \t\t\tset_config_tristate remote.origin.prune $remote_origin_prune &&\n+\t\t\tset_config_tristate remote.origin.pruneTags $remote_origin_prune_tags &&\n \n \t\t\tif test \"$mode\" != \"link\"\n \t\t\tthen\n@@ -641,57 +647,59 @@ test_configured_prune_type () {\n \n # $1 config: fetch.prune\n # $2 config: remote.<name>.prune\n-# $3 expect: branch to be pruned?\n-# $4 expect: tag to be pruned?\n-# $5 git-fetch $cmdline:\n+# $3 config: fetch.pruneTags\n+# $4 config: remote.<name>.pruneTags\n+# $5 expect: branch to be pruned?\n+# $6 expect: tag to be pruned?\n+# $7 git-fetch $cmdline:\n #\n-#                     $1    $2    $3     $4     $5\n-test_configured_prune unset unset kept   kept   \"\"\n-test_configured_prune unset unset kept   kept   \"--no-prune\"\n-test_configured_prune unset unset pruned kept   \"--prune\"\n-test_configured_prune unset unset kept   pruned \\\n+#                     $1    $2    $3    $4    $5     $6     $7\n+test_configured_prune unset unset unset unset kept   kept   \"\"\n+test_configured_prune unset unset unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset unset unset unset pruned kept   \"--prune\"\n+test_configured_prune unset unset unset unset kept   pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/*\"\n-test_configured_prune unset unset pruned pruned \\\n+test_configured_prune unset unset unset unset pruned pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n-test_configured_prune false unset kept   kept   \"\"\n-test_configured_prune false unset kept   kept   \"--no-prune\"\n-test_configured_prune false unset pruned kept   \"--prune\"\n+test_configured_prune false unset unset unset kept   kept   \"\"\n+test_configured_prune false unset unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false unset unset unset pruned kept   \"--prune\"\n \n-test_configured_prune true  unset pruned kept   \"\"\n-test_configured_prune true  unset pruned kept   \"--prune\"\n-test_configured_prune true  unset kept   kept   \"--no-prune\"\n+test_configured_prune true  unset unset unset pruned kept   \"\"\n+test_configured_prune true  unset unset unset pruned kept   \"--prune\"\n+test_configured_prune true  unset unset unset kept   kept   \"--no-prune\"\n \n-test_configured_prune unset false kept   kept   \"\"\n-test_configured_prune unset false kept   kept   \"--no-prune\"\n-test_configured_prune unset false pruned kept   \"--prune\"\n+test_configured_prune unset false unset unset kept   kept   \"\"\n+test_configured_prune unset false unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset false unset unset pruned kept   \"--prune\"\n \n-test_configured_prune false false kept   kept   \"\"\n-test_configured_prune false false kept   kept   \"--no-prune\"\n-test_configured_prune false false pruned kept   \"--prune\"\n-test_configured_prune false false kept   pruned \\\n+test_configured_prune false false unset unset kept   kept   \"\"\n+test_configured_prune false false unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false false unset unset pruned kept   \"--prune\"\n+test_configured_prune false false unset unset kept   pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/*\"\n-test_configured_prune false false pruned pruned \\\n+test_configured_prune false false unset unset pruned pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n-test_configured_prune true  false kept   kept   \"\"\n-test_configured_prune true  false pruned kept   \"--prune\"\n-test_configured_prune true  false kept   kept   \"--no-prune\"\n+test_configured_prune true  false unset unset kept   kept   \"\"\n+test_configured_prune true  false unset unset pruned kept   \"--prune\"\n+test_configured_prune true  false unset unset kept   kept   \"--no-prune\"\n \n-test_configured_prune unset true  pruned kept   \"\"\n-test_configured_prune unset true  kept   kept   \"--no-prune\"\n-test_configured_prune unset true  pruned kept   \"--prune\"\n+test_configured_prune unset true  unset unset pruned kept   \"\"\n+test_configured_prune unset true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune unset true  unset unset pruned kept   \"--prune\"\n \n-test_configured_prune false true  pruned kept   \"\"\n-test_configured_prune false true  kept   kept   \"--no-prune\"\n-test_configured_prune false true  pruned kept   \"--prune\"\n+test_configured_prune false true  unset unset pruned kept   \"\"\n+test_configured_prune false true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune false true  unset unset pruned kept   \"--prune\"\n \n-test_configured_prune true  true  pruned kept   \"\"\n-test_configured_prune true  true  pruned kept   \"--prune\"\n-test_configured_prune true  true  kept   kept   \"--no-prune\"\n-test_configured_prune true  true  kept   pruned \\\n+test_configured_prune true  true  unset unset pruned kept   \"\"\n+test_configured_prune true  true  unset unset pruned kept   \"--prune\"\n+test_configured_prune true  true  unset unset kept   kept   \"--no-prune\"\n+test_configured_prune true  true  unset unset kept   pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/*\"\n-test_configured_prune true  true  pruned pruned \\\n+test_configured_prune true  true  unset unset pruned pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n test_expect_success 'all boundary commits are excluded' '\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338927","messageId":"20180209203216.11560-17-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 16/17] fetch: add a --prune-tags option and fetch.pruneTags config","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:15Z","receivedAt":"2018-02-09T20:33:25Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a --prune-tags option to git-fetch, along with fetch.pruneTags\nconfig option and a -P shorthand (-p is --prune). This allows for\ndoing any of:\n\n    git fetch -p -P\n    git fetch --prune --prune-tags\n    git fetch -p -P origin\n    git fetch --prune --prune-tags origin\n\nOr simply:\n\n    git config fetch.prune true &&\n    git config fetch.pruneTags true &&\n    git fetch\n\nInstead of the much more verbose:\n\n    git fetch --prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'\n\nBefore this feature it was painful to support the use-case of pulling\nfrom a repo which is having both its branches *and* tags deleted\nregularly, and have our local references to reflect upstream.\n\nAt work we create deployment tags in the repo for each rollout, and\nthere's *lots* of those, so they're archived within weeks for\nperformance reasons.\n\nWithout this change it's hard to centrally configure such repos in\n/etc/gitconfig (on servers that are only used for working with\nthem). You need to set fetch.prune=true globally, and then for each\nrepo:\n\n    git -C {} config --replace-all remote.origin.fetch \"refs/tags/*:refs/tags/*\" \"^\\+*refs/tags/\\*:refs/tags/\\*$\"\n\nNow I can simply set fetch.pruneTags=true in /etc/gitconfig as well,\nand users running \"git pull\" will automatically get the pruning\nsemantics I want.\n\nEven though \"git remote\" has corresponding \"prune\" and \"update\n--prune\" subcommands I'm intentionally not adding a corresponding\nprune-tags or \"update --prune --prune-tags\" mode to that command.\n\nIt's advertised (as noted in my recent \"git remote doc: correct\ndangerous lies about what prune does\") as only modifying remote\ntracking references, whereas any --prune-tags option is always going\nto modify what from the user's perspective is a local copy of the tag,\nsince there's no such thing as a remote tracking tag.\n\nIdeally add_prune_tags_to_fetch_refspec() would be something that\nwould use ALLOC_GROW() to grow the 'fetch` member of the 'remote'\nstruct. Instead I'm realloc-ing remote->fetch and adding the\ntag_refspec to the end.\n\nThe reason is that parse_{fetch,push}_refspec which allocate the\nrefspec (ultimately remote->fetch) struct are called many places that\ndon't have access to a 'remote' struct. It would be hard to change all\ntheir callsites to be amenable to carry around the bookkeeping\nvariables required for dynamic allocation.\n\nAll the other callers of the API first incrementally construct the\nstring version of the refspec in remote->fetch_refspec via\nadd_fetch_refspec(), before finally calling parse_fetch_refspec() via\nsome variation of remote_get().\n\nIt's less of a pain to deal with the one special case that needs to\nmodify already constructed refspecs than to chase down and change all\nthe other callsites. The API I'm adding is intentionally not\ngeneralized because if we add more of these we'd probably want to\nre-visit how this is done.\n\nSee my \"Re: [BUG] git remote prune removes local tags, depending on\nfetch config\" (87po6ahx87.fsf@evledraar.gmail.com;\nhttps://public-inbox.org/git/87po6ahx87.fsf@evledraar.gmail.com/) for\nmore background info.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt               | 14 +++++++\n Documentation/fetch-options.txt        | 14 ++++++-\n Documentation/git-fetch.txt            | 47 +++++++++++++++++++++++\n builtin/fetch.c                        | 32 ++++++++++++++--\n contrib/completion/git-completion.bash |  2 +-\n remote.c                               | 14 +++++++\n remote.h                               |  3 ++\n t/t5510-fetch.sh                       | 70 ++++++++++++++++++++++++++++++++++\n 8 files changed, 191 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 0f27af5760..e254bfd531 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1401,6 +1401,14 @@ fetch.prune::\n \toption was given on the command line.  See also `remote.<name>.prune`\n \tand the PRUNING section of linkgit:git-fetch[1].\n \n+fetch.pruneTags::\n+\tIf true, fetch will automatically behave as if the\n+\t`refs/tags/*:refs/tags/*` refspec was provided when pruning,\n+\tif not set already. This allows for setting both this option\n+\tand `fetch.prune` to maintain a 1=1 mapping to upstream\n+\trefs. See also `remote.<name>.pruneTags` and the PRUNING\n+\tsection of linkgit:git-fetch[1].\n+\n fetch.output::\n \tControl how ref update status is printed. Valid values are\n \t`full` and `compact`. Default value is `full`. See section\n@@ -2945,6 +2953,12 @@ remote.<name>.prune::\n \tremove any remote-tracking references that no longer exist on the\n \tremote (as if the `--prune` option was given on the command line).\n \tOverrides `fetch.prune` settings, if any.\n+\n+remote.<name>.pruneTags::\n+\tWhen set to true, fetching from this remote by default will also\n+\tremove any local tags that no longer exist on the remote if pruning\n+\tis activated in general via `remote.<name>.prune`, `fetch.prune` or\n+\t`--prune`. Overrides `fetch.pruneTags` settings, if any.\n +\n See also `remote.<name>.prune` and the PRUNING section of\n linkgit:git-fetch[1].\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 9f5c85ad96..8631e365f4 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -73,7 +73,19 @@ ifndef::git-pull[]\n \tare fetched due to an explicit refspec (either on the command\n \tline or in the remote configuration, for example if the remote\n \twas cloned with the --mirror option), then they are also\n-\tsubject to pruning.\n+\tsubject to pruning. Supplying `--prune-tags` is a shorthand for\n+\tproviding the tag refspec.\n++\n+See the PRUNING section below for more details.\n+\n+-P::\n+--prune-tags::\n+\tBefore fetching, remove any local tags that no longer exist on\n+\tthe remote if `--prune` is enabled. This option should be used\n+\tmore carefully, unlike `--prune` it will remove any local\n+\treferences (local tags) that have been created. This option is\n+\ta shorthand for providing the explicit tag refspec along with\n+\t`--prune`, see the discussion about that in its documentation.\n +\n See the PRUNING section below for more details.\n \ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex e94bcfb8c3..af12310f75 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -148,6 +148,53 @@ So be careful when using this with a refspec like\n `refs/tags/*:refs/tags/*`, or any other refspec which might map\n references from multiple remotes to the same local namespace.\n \n+Since keeping up-to-date with both branches and tags on the remote is\n+a common use-case the `--prune-tags` option can be supplied along with\n+`--prune` to prune local tags that don't exist on the remote, and\n+force-update those tags that differ. Tag pruning can also be enabled\n+with `fetch.pruneTags` or `remote.<name>.pruneTags` in the config. See\n+linkgit:git-config[1].\n+\n+The `--prune-tags` option is equivalent to having\n+`refs/tags/*:refs/tags/*` declared in the refspecs of the remote. This\n+can lead to some seemingly strange interactions:\n+\n+------------------------------------------------\n+# These both fetch tags\n+$ git fetch --no-tags origin 'refs/tags/*:refs/tags/*'\n+$ git fetch --no-tags --prune-tags origin\n+------------------------------------------------\n+\n+The reason it doesn't error out when provided without `--prune` or its\n+config versions is for flexibility of the configured versions, and to\n+maintain a 1=1 mapping between what the command line flags do, and\n+what the configuration versions do.\n+\n+It's reasonable to e.g. configure `fetch.pruneTags=true` in\n+`~/.gitconfig` to have tags pruned whenever `git fetch --prune` is\n+run, without making every invocation of `git fetch` without `--prune`\n+an error.\n+\n+Another special case of `--prune-tags` is that\n+`refs/tags/*:refs/tags/*` will not be implicitly provided if an URL is\n+being fetched. I.e.:\n+\n+------------------------------------------------\n+$ git fetch <url> --prune --prune-tags\n+------------------------------------------------\n+\n+Will prune no tags, as opposed to:\n+\n+------------------------------------------------\n+$ git fetch origin --prune --prune-tags\n+------------------------------------------------\n+\n+To prune tags given a URL supply the refspec explicitly:\n+\n+------------------------------------------------\n+$ git fetch <url> --prune 'refs/tags/*:refs/tags/*'\n+------------------------------------------------\n+\n OUTPUT\n ------\n \ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex a7705bc150..55a0fc37be 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -38,6 +38,10 @@ static int fetch_prune_config = -1; /* unspecified */\n static int prune = -1; /* unspecified */\n #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */\n \n+static int fetch_prune_tags_config = -1; /* unspecified */\n+static int prune_tags = -1; /* unspecified */\n+#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */\n+\n static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;\n static int progress = -1;\n static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;\n@@ -64,6 +68,11 @@ static int git_fetch_config(const char *k, const char *v, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(k, \"fetch.prunetags\")) {\n+\t\tfetch_prune_tags_config = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(k, \"submodule.recurse\")) {\n \t\tint r = git_config_bool(k, v) ?\n \t\t\tRECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;\n@@ -126,6 +135,8 @@ static struct option builtin_fetch_options[] = {\n \t\t    N_(\"number of submodules fetched in parallel\")),\n \tOPT_BOOL('p', \"prune\", &prune,\n \t\t N_(\"prune remote-tracking branches no longer on remote\")),\n+\tOPT_BOOL('P', \"prune-tags\", &prune_tags,\n+\t\t N_(\"prune local tags no longer on remote and clobber changed tags\")),\n \t{ OPTION_CALLBACK, 0, \"recurse-submodules\", &recurse_submodules, N_(\"on-demand\"),\n \t\t    N_(\"control recursive fetching of submodules\"),\n \t\t    PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },\n@@ -1212,6 +1223,8 @@ static void add_options_to_argv(struct argv_array *argv)\n \t\targv_array_push(argv, \"--dry-run\");\n \tif (prune != -1)\n \t\targv_array_push(argv, prune ? \"--prune\" : \"--no-prune\");\n+\tif (prune_tags != -1)\n+\t\targv_array_push(argv, prune_tags ? \"--prune-tags\" : \"--no-prune-tags\");\n \tif (update_head_ok)\n \t\targv_array_push(argv, \"--update-head-ok\");\n \tif (force)\n@@ -1265,7 +1278,7 @@ static int fetch_multiple(struct string_list *list)\n \treturn result;\n }\n \n-static int fetch_one(struct remote *remote, int argc, const char **argv)\n+static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok)\n {\n \tstatic const char **refs = NULL;\n \tstruct refspec *refspec;\n@@ -1288,6 +1301,19 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \t\t\tprune = PRUNE_BY_DEFAULT;\n \t}\n \n+\tif (prune_tags < 0) {\n+\t\t/* no command line request */\n+\t\tif (0 <= remote->prune_tags)\n+\t\t\tprune_tags = remote->prune_tags;\n+\t\telse if (0 <= fetch_prune_tags_config)\n+\t\t\tprune_tags = fetch_prune_tags_config;\n+\t\telse\n+\t\t\tprune_tags = PRUNE_TAGS_BY_DEFAULT;\n+\t}\n+\n+\tif (prune_tags_ok && prune_tags && remote_is_configured(remote, 0))\n+\t\tadd_prune_tags_to_fetch_refspec(remote);\n+\n \tif (argc > 0) {\n \t\tint j = 0;\n \t\tint i;\n@@ -1368,7 +1394,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \t} else if (argc == 0) {\n \t\t/* No arguments -- use default remote */\n \t\tremote = remote_get(NULL);\n-\t\tresult = fetch_one(remote, argc, argv);\n+\t\tresult = fetch_one(remote, argc, argv, 1);\n \t} else if (multiple) {\n \t\t/* All arguments are assumed to be remotes or groups */\n \t\tfor (i = 0; i < argc; i++)\n@@ -1386,7 +1412,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \t\t} else {\n \t\t\t/* Zero or one remotes */\n \t\t\tremote = remote_get(argv[0]);\n-\t\t\tresult = fetch_one(remote, argc-1, argv+1);\n+\t\t\tresult = fetch_one(remote, argc-1, argv+1, argc == 1);\n \t\t}\n \t}\n \ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 3683c772c5..4ecd0d4d7a 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1468,7 +1468,7 @@ __git_fetch_recurse_submodules=\"yes on-demand no\"\n __git_fetch_options=\"\n \t--quiet --verbose --append --upload-pack --force --keep --depth=\n \t--tags --no-tags --all --prune --dry-run --recurse-submodules=\n-\t--unshallow --update-shallow\n+\t--unshallow --update-shallow --prune-tags\n \"\n \n _git_fetch ()\ndiff --git a/remote.c b/remote.c\nindex 356c123e3e..d670eed4c4 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -104,6 +104,17 @@ static void add_fetch_refspec(struct remote *remote, const char *ref)\n \tremote->fetch_refspec[remote->fetch_refspec_nr++] = ref;\n }\n \n+void add_prune_tags_to_fetch_refspec(struct remote *remote)\n+{\n+\tint nr = remote->fetch_refspec_nr;\n+\tint bufsize = nr  + 1;\n+\tint size = sizeof(struct refspec);\n+\n+\tremote->fetch = xrealloc(remote->fetch, size  * bufsize);\n+\tmemcpy(&remote->fetch[nr], tag_refspec, size);\n+\tadd_fetch_refspec(remote, xstrdup(TAG_REFSPEC));\n+}\n+\n static void add_url(struct remote *remote, const char *url)\n {\n \tALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);\n@@ -174,6 +185,7 @@ static struct remote *make_remote(const char *name, int len)\n \n \tret = xcalloc(1, sizeof(struct remote));\n \tret->prune = -1;  /* unspecified */\n+\tret->prune_tags = -1;  /* unspecified */\n \tALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);\n \tremotes[remotes_nr++] = ret;\n \tret->name = xstrndup(name, len);\n@@ -392,6 +404,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tremote->skip_default_update = git_config_bool(key, value);\n \telse if (!strcmp(subkey, \"prune\"))\n \t\tremote->prune = git_config_bool(key, value);\n+\telse if (!strcmp(subkey, \"prunetags\"))\n+\t\tremote->prune_tags = git_config_bool(key, value);\n \telse if (!strcmp(subkey, \"url\")) {\n \t\tconst char *v;\n \t\tif (git_config_string(&v, key, value))\ndiff --git a/remote.h b/remote.h\nindex 80fea6dd11..271afe1bab 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -47,6 +47,7 @@ struct remote {\n \tint skip_default_update;\n \tint mirror;\n \tint prune;\n+\tint prune_tags;\n \n \tconst char *receivepack;\n \tconst char *uploadpack;\n@@ -299,4 +300,6 @@ void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);\n \n #define TAG_REFSPEC \"refs/tags/*:refs/tags/*\"\n \n+void add_prune_tags_to_fetch_refspec(struct remote *remote);\n+\n #endif\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 1713111006..227dd70b7b 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -589,6 +589,15 @@ test_configured_prune_type () {\n \t\t\tnew_cmdline=$(printf \"%s\" \"$cmdline\" | perl -pe 's[origin(?!/)][\"'\"$remote_url\"'\"]g')\n \t\tfi\n \n+\t\tif test \"$fetch_prune_tags\" = 'true' ||\n+\t\t   test \"$remote_origin_prune_tags\" = 'true'\n+\t\tthen\n+\t\t\tif ! printf '%s' \"$cmdline\\n\" | grep -q refs/remotes/origin/\n+\t\t\tthen\n+\t\t\t\tnew_cmdline=\"$new_cmdline refs/tags/*:refs/tags/*\"\n+\t\t\tfi\n+\t\tfi\n+\n \t\tcmdline=\"$new_cmdline\"\n \tfi\n \n@@ -702,6 +711,67 @@ test_configured_prune true  true  unset unset kept   pruned \\\n test_configured_prune true  true  unset unset pruned pruned \\\n \t\"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*\"\n \n+# --prune-tags on its own does nothing, needs --prune as well, same\n+# for for fetch.pruneTags without fetch.prune\n+test_configured_prune unset unset unset unset kept kept     \"--prune-tags\"\n+test_configured_prune unset unset true unset  kept kept     \"\"\n+test_configured_prune unset unset unset true  kept kept     \"\"\n+\n+# These will prune the tags\n+test_configured_prune unset unset unset unset pruned pruned \"--prune --prune-tags\"\n+test_configured_prune true  unset true  unset pruned pruned \"\"\n+test_configured_prune unset true  unset true  pruned pruned \"\"\n+\n+# remote.<name>.pruneTags overrides fetch.pruneTags, just like\n+# remote.<name>.prune overrides fetch.prune if set.\n+test_configured_prune true  unset true unset pruned pruned  \"\"\n+test_configured_prune false true  false true  pruned pruned \"\"\n+test_configured_prune true  false true  false kept   kept   \"\"\n+\n+# When --prune-tags is supplied it's ignored if an explicit refspec is\n+# given, same for the configuration options.\n+test_configured_prune unset unset unset unset pruned kept \\\n+\t\"--prune --prune-tags origin +refs/heads/*:refs/remotes/origin/*\"\n+test_configured_prune unset unset true  unset pruned kept \\\n+\t\"--prune origin +refs/heads/*:refs/remotes/origin/*\"\n+test_configured_prune unset unset unset true pruned  kept \\\n+\t\"--prune origin +refs/heads/*:refs/remotes/origin/*\"\n+\n+# Pruning that also takes place if a file:// url replaces a named\n+# remote, with the exception of --prune-tags on the command-line\n+# (arbitrary limitation).\n+#\n+# However, because there's no implicit\n+# +refs/heads/*:refs/remotes/origin/* refspec and supplying it on the\n+# command-line negates --prune-tags, the branches will not be pruned.\n+test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune-tags\" \"name\"\n+test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune-tags\" \"link\"\n+test_configured_prune_type unset unset unset unset pruned pruned \"origin --prune --prune-tags\" \"name\"\n+test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune --prune-tags\" \"link\"\n+test_configured_prune_type unset unset unset unset pruned pruned \"--prune --prune-tags origin\" \"name\"\n+test_configured_prune_type unset unset unset unset kept   kept   \"--prune --prune-tags origin\" \"link\"\n+test_configured_prune_type unset unset true  unset pruned pruned \"--prune origin\" \"name\"\n+test_configured_prune_type unset unset true  unset kept   pruned \"--prune origin\" \"link\"\n+test_configured_prune_type unset unset unset true  pruned pruned \"--prune origin\" \"name\"\n+test_configured_prune_type unset unset unset true  kept   pruned \"--prune origin\" \"link\"\n+test_configured_prune_type true  unset true  unset pruned pruned \"origin\" \"name\"\n+test_configured_prune_type true  unset true  unset kept   pruned \"origin\" \"link\"\n+test_configured_prune_type unset  true true  unset pruned pruned \"origin\" \"name\"\n+test_configured_prune_type unset  true true  unset kept   pruned \"origin\" \"link\"\n+test_configured_prune_type unset  true unset true  pruned pruned \"origin\" \"name\"\n+test_configured_prune_type unset  true unset true  kept   pruned \"origin\" \"link\"\n+\n+# Interaction between --prune-tags and no \"fetch\" config in the remote\n+# at all.\n+test_expect_success 'remove remote.origin.fetch \"one\"' '\n+\t(\n+\t\tcd one &&\n+\t\tgit config --unset-all remote.origin.fetch\n+\t)\n+'\n+test_configured_prune_type unset unset unset unset kept pruned \"origin --prune --prune-tags\" \"name\"\n+test_configured_prune_type unset unset unset unset kept kept   \"origin --prune --prune-tags\" \"link\"\n+\n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n \ttest_commit oneside &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338928","messageId":"20180209203216.11560-18-avarab@gmail.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"[PATCH v5 17/17] fetch: make the --prune-tags work with <url>","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T20:32:16Z","receivedAt":"2018-02-09T20:33:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the new --prune-tags option work properly when git-fetch is\ninvoked with a <url> parameter instead of a <remote name>\nparameter.\n\nThis change is split off from the introduction of --prune-tags due to\nthe relative complexity of munging the incoming argv, which is easier\nto review as a separate change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-fetch.txt | 21 ++++++---------------\n builtin/fetch.c             | 17 ++++++++++++++---\n t/t5510-fetch.sh            | 16 +++++++---------\n 3 files changed, 27 insertions(+), 27 deletions(-)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex af12310f75..e319935597 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -175,24 +175,15 @@ It's reasonable to e.g. configure `fetch.pruneTags=true` in\n run, without making every invocation of `git fetch` without `--prune`\n an error.\n \n-Another special case of `--prune-tags` is that\n-`refs/tags/*:refs/tags/*` will not be implicitly provided if an URL is\n-being fetched. I.e.:\n-\n-------------------------------------------------\n-$ git fetch <url> --prune --prune-tags\n-------------------------------------------------\n-\n-Will prune no tags, as opposed to:\n+Pruning tags with `--prune-tags` also works when fetching a URL\n+instead of a named remote. These will all prune tags not found on\n+origin:\n \n ------------------------------------------------\n $ git fetch origin --prune --prune-tags\n-------------------------------------------------\n-\n-To prune tags given a URL supply the refspec explicitly:\n-\n-------------------------------------------------\n-$ git fetch <url> --prune 'refs/tags/*:refs/tags/*'\n+$ git fetch origin --prune 'refs/tags/*:refs/tags/*'\n+$ git fetch <url of origin> --prune --prune-tags\n+$ git fetch <url of origin> --prune 'refs/tags/*:refs/tags/*'\n ------------------------------------------------\n \n OUTPUT\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 55a0fc37be..c96f17a9a3 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1283,7 +1283,10 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru\n \tstatic const char **refs = NULL;\n \tstruct refspec *refspec;\n \tint ref_nr = 0;\n+\tint j = 0;\n \tint exit_code;\n+\tint maybe_prune_tags;\n+\tint remote_via_config = remote_is_configured(remote, 0);\n \n \tif (!remote)\n \t\tdie(_(\"No remote repository specified.  Please, specify either a URL or a\\n\"\n@@ -1311,13 +1314,21 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru\n \t\t\tprune_tags = PRUNE_TAGS_BY_DEFAULT;\n \t}\n \n-\tif (prune_tags_ok && prune_tags && remote_is_configured(remote, 0))\n+\tmaybe_prune_tags = prune_tags_ok && prune_tags;\n+\tif (maybe_prune_tags && remote_via_config)\n \t\tadd_prune_tags_to_fetch_refspec(remote);\n \n+\tif (argc > 0 || (maybe_prune_tags && !remote_via_config)) {\n+\t\tsize_t nr_alloc = st_add3(argc, maybe_prune_tags, 1);\n+\t\trefs = xcalloc(nr_alloc, sizeof(const char *));\n+\t\tif (maybe_prune_tags) {\n+\t\t\trefs[j++] = xstrdup(\"refs/tags/*:refs/tags/*\");\n+\t\t\tref_nr++;\n+\t\t}\n+\t}\n+\n \tif (argc > 0) {\n-\t\tint j = 0;\n \t\tint i;\n-\t\trefs = xcalloc(st_add(argc, 1), sizeof(const char *));\n \t\tfor (i = 0; i < argc; i++) {\n \t\t\tif (!strcmp(argv[i], \"tag\")) {\n \t\t\t\ti++;\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 227dd70b7b..dce2371302 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -738,18 +738,15 @@ test_configured_prune unset unset unset true pruned  kept \\\n \t\"--prune origin +refs/heads/*:refs/remotes/origin/*\"\n \n # Pruning that also takes place if a file:// url replaces a named\n-# remote, with the exception of --prune-tags on the command-line\n-# (arbitrary limitation).\n-#\n-# However, because there's no implicit\n+# remote. However, because there's no implicit\n # +refs/heads/*:refs/remotes/origin/* refspec and supplying it on the\n # command-line negates --prune-tags, the branches will not be pruned.\n test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune-tags\" \"name\"\n test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune-tags\" \"link\"\n test_configured_prune_type unset unset unset unset pruned pruned \"origin --prune --prune-tags\" \"name\"\n-test_configured_prune_type unset unset unset unset kept   kept   \"origin --prune --prune-tags\" \"link\"\n+test_configured_prune_type unset unset unset unset kept   pruned \"origin --prune --prune-tags\" \"link\"\n test_configured_prune_type unset unset unset unset pruned pruned \"--prune --prune-tags origin\" \"name\"\n-test_configured_prune_type unset unset unset unset kept   kept   \"--prune --prune-tags origin\" \"link\"\n+test_configured_prune_type unset unset unset unset kept   pruned \"--prune --prune-tags origin\" \"link\"\n test_configured_prune_type unset unset true  unset pruned pruned \"--prune origin\" \"name\"\n test_configured_prune_type unset unset true  unset kept   pruned \"--prune origin\" \"link\"\n test_configured_prune_type unset unset unset true  pruned pruned \"--prune origin\" \"name\"\n@@ -761,8 +758,9 @@ test_configured_prune_type unset  true true  unset kept   pruned \"origin\" \"link\"\n test_configured_prune_type unset  true unset true  pruned pruned \"origin\" \"name\"\n test_configured_prune_type unset  true unset true  kept   pruned \"origin\" \"link\"\n \n-# Interaction between --prune-tags and no \"fetch\" config in the remote\n-# at all.\n+# When all remote.origin.fetch settings are deleted a --prune\n+# --prune-tags still implicitly supplies refs/tags/*:refs/tags/* so\n+# tags, but not tracking branches, will be deleted.\n test_expect_success 'remove remote.origin.fetch \"one\"' '\n \t(\n \t\tcd one &&\n@@ -770,7 +768,7 @@ test_expect_success 'remove remote.origin.fetch \"one\"' '\n \t)\n '\n test_configured_prune_type unset unset unset unset kept pruned \"origin --prune --prune-tags\" \"name\"\n-test_configured_prune_type unset unset unset unset kept kept   \"origin --prune --prune-tags\" \"link\"\n+test_configured_prune_type unset unset unset unset kept pruned \"origin --prune --prune-tags\" \"link\"\n \n test_expect_success 'all boundary commits are excluded' '\n \ttest_commit base &&\n-- \n2.15.1.424.g9478a66081\n\n"},{"id":"338931","messageId":"xmqqfu69x4mr.fsf@gitster-ct.c.googlers.com","threadId":"47614","inReplyTo":"87zi4ikjyr.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 11/17] fetch tests: fetch <url> <spec> as well as fetch [<remote>]","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-09T20:57:48Z","receivedAt":"2018-02-09T20:57:55Z","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>>> +               key=$(echo $1 | sed -e 's/^remote\\.origin/fetch/')\n>>\n>> Faster (thus more Windows-friendly) assuming that $1 always starts\n>> with \"remote.origin\":\n>>\n>>     key=fetch${u#remote.origin}\n>\n> Tests fail with this and I'm not excited to be the first user in git's\n> test suite to use some novel shell feature, no existing uses of\n> ${u[...].\n\ns/u/1/, I think.\n"},{"id":"338935","messageId":"87y3k1lvom.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"xmqqlgg3qn3l.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 00/17] document & test fetch pruning & add fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-09T21:06:33Z","receivedAt":"2018-02-09T21:06:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Feb 08 2018, Junio C. Hamano jotted:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> On Thu, Feb 08 2018, Ævar Arnfjörð Bjarmason jotted:\n>>\n>>> As noted in my 87h8quytmq.fsf@evledraar.gmail.com there was a bug I\n>>> noticed in v3 where it would segfault on some git-fetch invocations,\n>>> but there were not tests anywhere that caught that.\n>>\n>> ...and of course this whole submission this time around should be v4,\n>> not v2, but I didn't notice that I didn't adjust the subject prefix\n>> before sending. Junio: Hopefully you can pick it up anyway without too\n>> much trouble, sorry.\n>>\n>> FWIW I've deployed this to production @ work to some tens of k of\n>> machines (low \"k\" of which have users using git) without any issues.\n>\n> Will replace.  I found that the resolution of conflicts necessary\n> with the jh/partial-clone topic is a bit different from the previous\n> version due to addition of an extra parameter to fetch_one(), and I\n> think I didn't botch it, but please double check when I push the\n> results out in a few hours.\n\nThanks. FWIW I've looked over that resolution in pu and it LGTM.\n"},{"id":"338936","messageId":"xmqqbmgxx3w7.fsf@gitster-ct.c.googlers.com","threadId":"47614","inReplyTo":"xmqqfu69x4mr.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 11/17] fetch tests: fetch <url> <spec> as well as fetch [<remote>]","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-09T21:13:44Z","receivedAt":"2018-02-09T21:13:53Z","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> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>>>> +               key=$(echo $1 | sed -e 's/^remote\\.origin/fetch/')\n>>>\n>>> Faster (thus more Windows-friendly) assuming that $1 always starts\n>>> with \"remote.origin\":\n>>>\n>>>     key=fetch${u#remote.origin}\n>>\n>> Tests fail with this and I'm not excited to be the first user in git's\n>> test suite to use some novel shell feature, no existing uses of\n>> ${u[...].\n>\n> s/u/1/, I think.\n\nAh, that's been already pointed out.  Sorry for the noise.\n"},{"id":"338937","messageId":"CAPig+cSVgzp4CdmzmGo6j31Tiv6Yf3o1EA6gcv_z9gdKvicsyw@mail.gmail.com","threadId":"47614","inReplyTo":"20180209202733.GA18966@sigill.intra.peff.net","subject":"Re: [PATCH v2 11/17] fetch tests: fetch <url> <spec> as well as fetch [<remote>]","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-02-09T21:14:52Z","receivedAt":"2018-02-09T21:14:58Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Feb 9, 2018 at 3:27 PM, Jeff King <peff@peff.net> wrote:\n> On Fri, Feb 09, 2018 at 09:05:00PM +0100, Ævar Arnfjörð Bjarmason wrote:\n>> >> +               key=$(echo $1 | sed -e 's/^remote\\.origin/fetch/')\n>> >\n>> > Faster (thus more Windows-friendly) assuming that $1 always starts\n>> > with \"remote.origin\":\n>> >\n>> >     key=fetch${u#remote.origin}\n>>\n>> Tests fail with this and I'm not excited to be the first user in git's\n>> test suite to use some novel shell feature, no existing uses of\n>> ${u[...].\n>>\n>> I also think stuff like this is on the wrong side of cleverness\n>> v.s. benefit. I can't find any reference to this syntax in bash or dash\n>> manpages (forward-search \"${u\"), but echo | sed is obvious, and it's not\n>> going to make a big difference for Windows.\n>\n> The \"u\" isn't the magic, it's the \"#\". I.e.:\n>\n>   key=fetch${1#remote.origin}\n>\n> and it's used all over the place in our scripts.  I'm not sure why Eric\n> wrote \"u\". :)\n\nBecause I was testing the expression interactively in the shell and\nassigned to a variable arbitrarily named \"u\". When I copy/pasted the\nworking expression from the shell session into the email, I stupidly\nforgot to change the \"u\" to \"1\".\n"},{"id":"339878","messageId":"xmqq606pyiqo.fsf@gitster-ct.c.googlers.com","threadId":"47614","inReplyTo":"20180209203216.11560-1-avarab@gmail.com","subject":"Re: [PATCH v5 00/17] document & test fetch pruning & add fetch.pruneTags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-22T00:23:59Z","receivedAt":"2018-02-22T00:24: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> Here's a v5 (correct subject line this time!). Many thanks to Eric for\n> a thorough review.\n\nWe haven't seen any comments on this round.  Is everybody happy?\n\nI do not have a strong opinion on the new feature, either for or\nagainst.  I didn't find anything majorly questionable in the\nexecution, though, so...\n"},{"id":"339923","messageId":"874lm9f6pe.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"xmqq606pyiqo.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5 00/17] document & test fetch pruning & add fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-22T14:18:53Z","receivedAt":"2018-02-22T14:19:07Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Feb 22 2018, Junio C. Hamano jotted:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Here's a v5 (correct subject line this time!). Many thanks to Eric for\n>> a thorough review.\n>\n> We haven't seen any comments on this round.  Is everybody happy?\n>\n> I do not have a strong opinion on the new feature, either for or\n> against.  I didn't find anything majorly questionable in the\n> execution, though, so...\n\nI've been running that here on thousands of boxes (that are actively\nusing it) for 2 weeks now without issue. Would be great to have it\nmerged down & in 2.17.\n"},{"id":"339963","messageId":"xmqq7er4wzub.fsf@gitster-ct.c.googlers.com","threadId":"47614","inReplyTo":"874lm9f6pe.fsf@evledraar.gmail.com","subject":"Re: [PATCH v5 00/17] document & test fetch pruning & add fetch.pruneTags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-22T20:09:48Z","receivedAt":"2018-02-22T20:09:55Z","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, Feb 22 2018, Junio C. Hamano jotted:\n>\n>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>>\n>>> Here's a v5 (correct subject line this time!). Many thanks to Eric for\n>>> a thorough review.\n>>\n>> We haven't seen any comments on this round.  Is everybody happy?\n>>\n>> I do not have a strong opinion on the new feature, either for or\n>> against.  I didn't find anything majorly questionable in the\n>> execution, though, so...\n>\n> I've been running that here on thousands of boxes (that are actively\n> using it) for 2 weeks now without issue. Would be great to have it\n> merged down & in 2.17.\n\nIf those thousands of boxes are all employing one specific workflow\nthat is helped by these changes, and the workflow is that other\npeople do not care about (or even worse, actively do not want to let\ntheir junior project members to use without thinking), then a\ndata-point from the original author does not amount to much ;-)\n\nLet's see how others find it useful and/or if the changed code gets\nin the way of others (I am not absolutely sure if the changes are\nfree of regression to existing users who do not use the new\nfeature).\n\nThanks.\n"},{"id":"340001","messageId":"87zi40dqm1.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"xmqq7er4wzub.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5 00/17] document & test fetch pruning & add fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-23T09:04:06Z","receivedAt":"2018-02-23T09:04:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Feb 22 2018, Junio C. Hamano jotted:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> On Thu, Feb 22 2018, Junio C. Hamano jotted:\n>>\n>>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>>>\n>>>> Here's a v5 (correct subject line this time!). Many thanks to Eric for\n>>>> a thorough review.\n>>>\n>>> We haven't seen any comments on this round.  Is everybody happy?\n>>>\n>>> I do not have a strong opinion on the new feature, either for or\n>>> against.  I didn't find anything majorly questionable in the\n>>> execution, though, so...\n>>\n>> I've been running that here on thousands of boxes (that are actively\n>> using it) for 2 weeks now without issue. Would be great to have it\n>> merged down & in 2.17.\n>\n> If those thousands of boxes are all employing one specific workflow\n> that is helped by these changes, and the workflow is that other\n> people do not care about (or even worse, actively do not want to let\n> their junior project members to use without thinking), then a\n> data-point from the original author does not amount to much ;-)\n\nOf course, I should have been clearer. I just meant to chime in with the\ndatapoint that I'm fairly sure this doesn't have any serious bugs given\nthe wide internal testing it's gotten.\n\n> Let's see how others find it useful and/or if the changed code gets\n> in the way of others (I am not absolutely sure if the changes are\n> free of regression to existing users who do not use the new\n> feature).\n\nI think if you're on the fence about merging it down (and others don't\nchime in saying the want it / like it) it makes sense to merge down\n1-14/17 and we could discard 15-17/17 for now for a later re-submission\nand discussion once the earlier part of the series lands in master.\n\nThe earlier part of the series is just trivial code changes that don't\nchange any functionality, more documentation for how existing\nfunctionality works, and more thorough testing of existing\nfunctionality.\n"},{"id":"340074","messageId":"xmqqsh9rs992.fsf@gitster-ct.c.googlers.com","threadId":"47614","inReplyTo":"87zi40dqm1.fsf@evledraar.gmail.com","subject":"Re: [PATCH v5 00/17] document & test fetch pruning & add fetch.pruneTags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-23T21:10:01Z","receivedAt":"2018-02-23T21:10:11Z","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>> Let's see how others find it useful and/or if the changed code gets\n>> in the way of others (I am not absolutely sure if the changes are\n>> free of regression to existing users who do not use the new\n>> feature).\n>\n> I think if you're on the fence about merging it down (and others don't\n> chime in saying the want it / like it) it makes sense to merge down\n> 1-14/17 and we could discard 15-17/17 for now for a later re-submission\n> and discussion once the earlier part of the series lands in master.\n\nOr we merge everything and let people discover glitches and\ncomplain.  Reverting the last pieces can wait until then ;-).\n\n\n"},{"id":"340184","messageId":"87vaemdpzp.fsf@evledraar.gmail.com","threadId":"47614","inReplyTo":"xmqqsh9rs992.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5 00/17] document & test fetch pruning & add fetch.pruneTags","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-02-24T21:42:02Z","receivedAt":"2018-02-24T21:42:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Feb 23 2018, Junio C. Hamano jotted:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>>> Let's see how others find it useful and/or if the changed code gets\n>>> in the way of others (I am not absolutely sure if the changes are\n>>> free of regression to existing users who do not use the new\n>>> feature).\n>>\n>> I think if you're on the fence about merging it down (and others don't\n>> chime in saying the want it / like it) it makes sense to merge down\n>> 1-14/17 and we could discard 15-17/17 for now for a later re-submission\n>> and discussion once the earlier part of the series lands in master.\n>\n> Or we merge everything and let people discover glitches and\n> complain.  Reverting the last pieces can wait until then ;-).\n\nThanks, that obviously works for me.\n"}]}