{"thread":{"id":"65002","subject":"[PATCH 0/2] fetch: clobber existing tags with --prune-tags","startedAt":"2026-02-15T20:23:38Z","lastAt":"2026-09-13T22:57:03Z","messageCount":8,"participants":["Orgad Shaneh via GitGitGadget","Orgad Shaneh","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"536070","messageId":"pull.2200.git.git.1771187016.gitgitgadget@gmail.com","threadId":"65002","inReplyTo":null,"subject":"[PATCH 0/2] fetch: clobber existing tags with --prune-tags","fromName":"Orgad Shaneh via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-02-15T20:23:34Z","receivedAt":"2026-02-15T20:23:38Z","isPatch":true,"body":"This was documented but not implemented.\n\nIn the flag description: prune local tags no longer on remote and clobber\nchanged tags\n\nIn the documentation: ... to prune local tags that don't exist on the\nremote, and force-update those tags that differ.\n\nOrgad Shaneh (2):\n  fetch: add a test for --force flag\n  fetch: clobber existing tags with --prune-tags\n\n builtin/fetch.c       |  2 +-\n t/t5516-fetch-push.sh | 13 +++++++++++--\n 2 files changed, 12 insertions(+), 3 deletions(-)\n\n\nbase-commit: 852829b3dd2fe4e7c7fc4d8badde644cf1b66c74\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2200%2Forgads%2Ffetch-prune-clobber-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2200/orgads/fetch-prune-clobber-v1\nPull-Request: https://github.com/git/git/pull/2200\n-- \ngitgitgadget\n"},{"id":"536071","messageId":"3ef70fe673170925ccf2901a625fb458837e9fcf.1771187016.git.gitgitgadget@gmail.com","threadId":"65002","inReplyTo":"pull.2200.git.git.1771187016.gitgitgadget@gmail.com","subject":"[PATCH 1/2] fetch: add a test for --force flag","fromName":"Orgad Shaneh via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-02-15T20:23:35Z","receivedAt":"2026-02-15T20:23:39Z","isPatch":true,"body":"From: Orgad Shaneh <orgad.shaneh@audiocodes.com>\n\nThere are two ways to force-fetch tags that have changed: either by\nspecifying an explicit forced ref (+refs/tags/*:refs/tags/*), or by\nusing --force.\n\nThe first approach was already tested, but the second was not. This\nhas now been addressed.\n\nSigned-off-by: Orgad Shaneh <orgad.shaneh@audiocodes.com>\n---\n t/t5516-fetch-push.sh | 5 ++++-\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 29e2f17608..31df7faf56 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1105,7 +1105,10 @@ test_force_fetch_tag () {\n \t\t\tgit commit -m 'file1' &&\n \t\t\tgit tag $tag_args testTag &&\n \t\t\ttest_must_fail git -C ../child1 fetch origin tag testTag &&\n-\t\t\tgit -C ../child1 fetch origin '+refs/tags/*:refs/tags/*'\n+\t\t\tgit -C ../child1 fetch origin '+refs/tags/*:refs/tags/*' &&\n+\t\t\tgit tag $tag_args testTag HEAD^ &&\n+\t\t\ttest_must_fail git -C ../child1 fetch origin tag testTag &&\n+\t\t\tgit -C ../child1 fetch --force origin tag testTag\n \t\t)\n \t\"\n }\n-- \ngitgitgadget\n\n"},{"id":"536072","messageId":"b444fa7af9f39960652209143c9845a47efd58e1.1771187016.git.gitgitgadget@gmail.com","threadId":"65002","inReplyTo":"pull.2200.git.git.1771187016.gitgitgadget@gmail.com","subject":"[PATCH 2/2] fetch: clobber existing tags with --prune-tags","fromName":"Orgad Shaneh via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-02-15T20:23:36Z","receivedAt":"2026-02-15T20:23:41Z","isPatch":true,"body":"From: Orgad Shaneh <orgad.shaneh@audiocodes.com>\n\nThis was documented but not implemented.\n\nIn the flag description:\nprune local tags no longer on remote *and clobber changed tags*\n\nIn the documentation:\n... to prune local tags that don't exist on the remote, *and\nforce-update those tags that differ*.\n\nSigned-off-by: Orgad Shaneh <orgad.shaneh@audiocodes.com>\n---\n builtin/fetch.c       |  2 +-\n t/t5516-fetch-push.sh | 10 ++++++++--\n 2 files changed, 9 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex a3bc7e9380..c212f50b86 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -981,7 +981,7 @@ static int update_local_ref(struct ref *ref,\n \t    starts_with(ref->name, \"refs/tags/\")) {\n \t\tstruct ref_update_display_info *info;\n \n-\t\tif (force || ref->force) {\n+\t\tif (force || ref->force || prune_tags) {\n \t\t\tint r;\n \n \t\t\tr = s_update_ref(\"updating tag\", ref, transaction, 0);\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 31df7faf56..4d29043baf 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1092,7 +1092,7 @@ test_force_fetch_tag () {\n \ttag_type_description=$1\n \ttag_args=$2\n \n-\ttest_expect_success \"fetch will not clobber an existing $tag_type_description without --force\" \"\n+\ttest_expect_success \"fetch will not clobber an existing $tag_type_description without --force or --prune-tags\" \"\n \t\tmk_test testrepo heads/main &&\n \t\tmk_child testrepo child1 &&\n \t\tmk_child testrepo child2 &&\n@@ -1108,7 +1108,13 @@ test_force_fetch_tag () {\n \t\t\tgit -C ../child1 fetch origin '+refs/tags/*:refs/tags/*' &&\n \t\t\tgit tag $tag_args testTag HEAD^ &&\n \t\t\ttest_must_fail git -C ../child1 fetch origin tag testTag &&\n-\t\t\tgit -C ../child1 fetch --force origin tag testTag\n+\t\t\tgit -C ../child1 fetch --force origin tag testTag &&\n+\t\t\tgit tag $tag_args testTag HEAD &&\n+\t\t\ttest_must_fail git -C ../child1 fetch origin tag testTag &&\n+\t\t\tgit -C ../child1 fetch --prune-tags origin tag testTag &&\n+\t\t\tgit tag $tag_args testTag HEAD^ &&\n+\t\t\ttest_must_fail git -C ../child1 fetch origin tag testTag &&\n+\t\t\tgit -C ../child1 -c fetch.prunetags=true fetch origin tag testTag\n \t\t)\n \t\"\n }\n-- \ngitgitgadget\n"},{"id":"539281","messageId":"CAGHpTB+qbtrBQd23hobQqJjG1+nwkHqBP1fcYjSPvrJhWcD21g@mail.gmail.com","threadId":"65002","inReplyTo":"b444fa7af9f39960652209143c9845a47efd58e1.1771187016.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/2] fetch: clobber existing tags with --prune-tags","fromName":"Orgad Shaneh","fromEmail":"orgads@gmail.com","sentAt":"2026-03-18T08:27:06Z","receivedAt":"2026-03-18T08:27:18Z","isPatch":true,"body":"On Sun, Feb 15, 2026 at 10:23 PM Orgad Shaneh via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Orgad Shaneh <orgad.shaneh@audiocodes.com>\n>\n> This was documented but not implemented.\n>\n> In the flag description:\n> prune local tags no longer on remote *and clobber changed tags*\n>\n> In the documentation:\n> ... to prune local tags that don't exist on the remote, *and\n> force-update those tags that differ*.\n>\n> Signed-off-by: Orgad Shaneh <orgad.shaneh@audiocodes.com>\n> ---\n>  builtin/fetch.c       |  2 +-\n>  t/t5516-fetch-push.sh | 10 ++++++++--\n>  2 files changed, 9 insertions(+), 3 deletions(-)\n>\n> diff --git a/builtin/fetch.c b/builtin/fetch.c\n> index a3bc7e9380..c212f50b86 100644\n> --- a/builtin/fetch.c\n> +++ b/builtin/fetch.c\n> @@ -981,7 +981,7 @@ static int update_local_ref(struct ref *ref,\n>             starts_with(ref->name, \"refs/tags/\")) {\n>                 struct ref_update_display_info *info;\n>\n> -               if (force || ref->force) {\n> +               if (force || ref->force || prune_tags) {\n>                         int r;\n>\n>                         r = s_update_ref(\"updating tag\", ref, transaction, 0);\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index 31df7faf56..4d29043baf 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -1092,7 +1092,7 @@ test_force_fetch_tag () {\n>         tag_type_description=$1\n>         tag_args=$2\n>\n> -       test_expect_success \"fetch will not clobber an existing $tag_type_description without --force\" \"\n> +       test_expect_success \"fetch will not clobber an existing $tag_type_description without --force or --prune-tags\" \"\n>                 mk_test testrepo heads/main &&\n>                 mk_child testrepo child1 &&\n>                 mk_child testrepo child2 &&\n> @@ -1108,7 +1108,13 @@ test_force_fetch_tag () {\n>                         git -C ../child1 fetch origin '+refs/tags/*:refs/tags/*' &&\n>                         git tag $tag_args testTag HEAD^ &&\n>                         test_must_fail git -C ../child1 fetch origin tag testTag &&\n> -                       git -C ../child1 fetch --force origin tag testTag\n> +                       git -C ../child1 fetch --force origin tag testTag &&\n> +                       git tag $tag_args testTag HEAD &&\n> +                       test_must_fail git -C ../child1 fetch origin tag testTag &&\n> +                       git -C ../child1 fetch --prune-tags origin tag testTag &&\n> +                       git tag $tag_args testTag HEAD^ &&\n> +                       test_must_fail git -C ../child1 fetch origin tag testTag &&\n> +                       git -C ../child1 -c fetch.prunetags=true fetch origin tag testTag\n>                 )\n>         \"\n>  }\n> --\n> gitgitgadget\n\nGently pinging this thread. I submitted this patch about a month ago\nand would appreciate any feedback once someone finds a moment to\nreview it.\n\nThanks!\n"},{"id":"539609","messageId":"xmqq1phdavik.fsf@gitster.g","threadId":"65002","inReplyTo":"b444fa7af9f39960652209143c9845a47efd58e1.1771187016.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/2] fetch: clobber existing tags with --prune-tags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-21T18:26:59Z","receivedAt":"2026-03-21T18:27:03Z","isPatch":true,"body":"\"Orgad Shaneh via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Orgad Shaneh <orgad.shaneh@audiocodes.com>\n>\n> This was documented but not implemented.\n\nAnd instead what did the command do / how did the command behave?\n\nIt also is curious when it was \"broken\".  It could be that it was\nbroken from day one when 97716d21 (fetch: add a --prune-tags option\nand fetch.pruneTags config, 2018-02-09) was written, c1a7902f (Merge\nbranch 'ab/fetch-prune', 2018-03-06) merged it, and Git 2.17 was\nshipped with it, but knowing Ævar (by the way where is he these\ndays???), I somehow doubt it.\n\nAnd unless it was broken from day one, we must find out if the\nchange in behaviour was deliberate, in which case it would be the\ndoucmentation and not the implementation that needs fixing.\n\n> In the flag description:\n> prune local tags no longer on remote *and clobber changed tags*\n>\n> In the documentation:\n> ... to prune local tags that don't exist on the remote, *and\n> force-update those tags that differ*.\n> ...\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index 31df7faf56..4d29043baf 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -1092,7 +1092,7 @@ test_force_fetch_tag () {\n>  \ttag_type_description=$1\n>  \ttag_args=$2\n>  \n> -\ttest_expect_success \"fetch will not clobber an existing $tag_type_description without --force\" \"\n> +\ttest_expect_success \"fetch will not clobber an existing $tag_type_description without --force or --prune-tags\" \"\n>  \t\tmk_test testrepo heads/main &&\n>  \t\tmk_child testrepo child1 &&\n>  \t\tmk_child testrepo child2 &&\n> @@ -1108,7 +1108,13 @@ test_force_fetch_tag () {\n>  \t\t\tgit -C ../child1 fetch origin '+refs/tags/*:refs/tags/*' &&\n>  \t\t\tgit tag $tag_args testTag HEAD^ &&\n>  \t\t\ttest_must_fail git -C ../child1 fetch origin tag testTag &&\n> -\t\t\tgit -C ../child1 fetch --force origin tag testTag\n> +\t\t\tgit -C ../child1 fetch --force origin tag testTag &&\n> +\t\t\tgit tag $tag_args testTag HEAD &&\n> +\t\t\ttest_must_fail git -C ../child1 fetch origin tag testTag &&\n> +\t\t\tgit -C ../child1 fetch --prune-tags origin tag testTag &&\n> +\t\t\tgit tag $tag_args testTag HEAD^ &&\n> +\t\t\ttest_must_fail git -C ../child1 fetch origin tag testTag &&\n> +\t\t\tgit -C ../child1 -c fetch.prunetags=true fetch origin tag testTag\n>  \t\t)\n>  \t\"\n>  }\n\nNone of the steps we see in the added test do not seem to check that\n--prune-tags does clobber existing tag that no longer exists on the\nother side.  It only checks the \"git fetch\" command exits with\nstatus 0, but does not see if the tag actually went away after the\noperation is done.\n\n"},{"id":"540037","messageId":"CAGHpTBJKaTURMJmJ6W4iiCAy3-M2YWb48rF8GMoEPCfpGcE=QA@mail.gmail.com","threadId":"65002","inReplyTo":"xmqq1phdavik.fsf@gitster.g","subject":"Re: [PATCH 2/2] fetch: clobber existing tags with --prune-tags","fromName":"Orgad Shaneh","fromEmail":"orgads@gmail.com","sentAt":"2026-03-26T07:57:31Z","receivedAt":"2026-03-26T07:57:45Z","isPatch":true,"body":"On Sat, Mar 21, 2026 at 8:27 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"Orgad Shaneh via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n> > From: Orgad Shaneh <orgad.shaneh@audiocodes.com>\n> >\n> > This was documented but not implemented.\n>\n> And instead what did the command do / how did the command behave?\n\nEven when passing --prune-tags or setting fetch.pruneTags, git fetch\nfails to force-update tags, resulting in the following error:\n\n! [rejected]        testTag    -> testTag  (would clobber existing tag)\n\nFurthermore, when a tag clobber is rejected, the entire fetch operation\nfails, preventing the update of other refs and even new tags.\n\n> It also is curious when it was \"broken\".  It could be that it was\n> broken from day one when 97716d21 (fetch: add a --prune-tags option\n> and fetch.pruneTags config, 2018-02-09) was written, c1a7902f (Merge\n> branch 'ab/fetch-prune', 2018-03-06) merged it, and Git 2.17 was\n> shipped with it, but knowing Ævar (by the way where is he these\n> days???), I somehow doubt it.\n>\n> And unless it was broken from day one, we must find out if the\n> change in behaviour was deliberate, in which case it would be the\n> doucmentation and not the implementation that needs fixing.\n\nI dug into the feature history and found:\n1. The flag was added in 97716d21 (fetch: add a --prune-tags option\n   and fetch.pruneTags config, 2018-02-09) as you mentioned.\n2. Tag clobbering protection was added later in 0bc8d71b (fetch: stop\n   clobbering existing tags without --force, 2018-08-31).\n\nIt appears the --prune-tags flag was overlooked in that later commit,\neither in its behavior or its documentation.\n\nSince there is currently no way to suppress this error globally, if you\nconsider the current behavior acceptable and prefer to fix the\ndocumentation, I suggest adding a fetch.force config to allow clobbering.\n\n> >                       git -C ../child1 fetch origin '+refs/tags/*:refs/tags/*' &&\n> >                       git tag $tag_args testTag HEAD^ &&\n> >                       test_must_fail git -C ../child1 fetch origin tag testTag &&\n> > -                     git -C ../child1 fetch --force origin tag testTag\n> > +                     git -C ../child1 fetch --force origin tag testTag &&\n> > +                     git tag $tag_args testTag HEAD &&\n> > +                     test_must_fail git -C ../child1 fetch origin tag testTag &&\n> > +                     git -C ../child1 fetch --prune-tags origin tag testTag &&\n> > +                     git tag $tag_args testTag HEAD^ &&\n> > +                     test_must_fail git -C ../child1 fetch origin tag testTag &&\n> > +                     git -C ../child1 -c fetch.prunetags=true fetch origin tag testTag\n> >               )\n> >       \"\n> >  }\n>\n> None of the steps we see in the added test do not seem to check that\n> --prune-tags does clobber existing tag that no longer exists on the\n> other side.  It only checks the \"git fetch\" command exits with\n> status 0, but does not see if the tag actually went away after the\n> operation is done.\n\nIn these tests, the tag is being replaced rather than deleted. Existing\ntests for the pruning mechanism itself are located in t/t5510-fetch.sh.\n\nWould you like me to add a check for the tag content itself to verify\nthe update? I suppose I should do the same for the existing test cases\nin that block as well.\n\n- Orgad\n"},{"id":"552646","messageId":"CAGHpTBLvZGAaqkue47Ne9DHPSwxx4fPo5KusP5=sh3C2AxgAMA@mail.gmail.com","threadId":"65002","inReplyTo":"CAGHpTBJKaTURMJmJ6W4iiCAy3-M2YWb48rF8GMoEPCfpGcE=QA@mail.gmail.com","subject":"Re: [PATCH 2/2] fetch: clobber existing tags with --prune-tags","fromName":"Orgad Shaneh","fromEmail":"orgads@gmail.com","sentAt":"2026-09-13T19:03:43Z","receivedAt":"2026-09-13T19:03:55Z","isPatch":true,"body":"On Thu, Mar 26, 2026 at 9:57 AM Orgad Shaneh <orgads@gmail.com> wrote:\n>\n> On Sat, Mar 21, 2026 at 8:27 PM Junio C Hamano <gitster@pobox.com> wrote:\n> >\n> > None of the steps we see in the added test do not seem to check that\n> > --prune-tags does clobber existing tag that no longer exists on the\n> > other side.  It only checks the \"git fetch\" command exits with\n> > status 0, but does not see if the tag actually went away after the\n> > operation is done.\n>\n> In these tests, the tag is being replaced rather than deleted. Existing\n> tests for the pruning mechanism itself are located in t/t5510-fetch.sh.\n>\n> Would you like me to add a check for the tag content itself to verify\n> the update? I suppose I should do the same for the existing test cases\n> in that block as well.\n\nJunio?\n\n- Orgad\n"},{"id":"552655","messageId":"xmqqy0d47oaq.fsf@gitster.g","threadId":"65002","inReplyTo":"CAGHpTBLvZGAaqkue47Ne9DHPSwxx4fPo5KusP5=sh3C2AxgAMA@mail.gmail.com","subject":"Re: [PATCH 2/2] fetch: clobber existing tags with --prune-tags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-09-13T22:57:01Z","receivedAt":"2026-09-13T22:57:03Z","isPatch":true,"body":"Orgad Shaneh <orgads@gmail.com> writes:\n\n> On Thu, Mar 26, 2026 at 9:57 AM Orgad Shaneh <orgads@gmail.com> wrote:\n>>\n>> On Sat, Mar 21, 2026 at 8:27 PM Junio C Hamano <gitster@pobox.com> wrote:\n>> >\n>> > None of the steps we see in the added test do not seem to check that\n>> > --prune-tags does clobber existing tag that no longer exists on the\n>> > other side.  It only checks the \"git fetch\" command exits with\n>> > status 0, but does not see if the tag actually went away after the\n>> > operation is done.\n>>\n>> In these tests, the tag is being replaced rather than deleted. Existing\n>> tests for the pruning mechanism itself are located in t/t5510-fetch.sh.\n>>\n>> Would you like me to add a check for the tag content itself to verify\n>> the update? I suppose I should do the same for the existing test cases\n>> in that block as well.\n>\n> Junio?\n\nSorry, but I do not have 6 month old discussions in my context\nwindow, so no immediate comment.  I'll respond only after I swap the\ncontext back in but not today (yet).\n\nThanks.\n\n\n"}]}