git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Ævar Arnfjörð Bjarmason" <avarab@gmail.com>
To: git@vger.kernel.org
Cc: "Wink Saville" <wink@saville.com>,
	"Jacob Keller" <jacob.keller@gmail.com>,
	"Bryan Turner" <bturner@atlassian.com>,
	"Junio C Hamano" <gitster@pobox.com>,
	"Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>,
	"Jeff King" <peff@peff.net>,
	"SZEDER Gábor" <szeder.dev@gmail.com>,
	"Kaartic Sivaraam" <kaartic.sivaraam@gmail.com>,
	"Ævar Arnfjörð Bjarmason" <avarab@gmail.com>
Subject: [PATCH v2 10/10] fetch: stop clobbering existing tags without --force
Date: Tue, 31 Jul 2018 13:07:18 +0000	[thread overview]
Message-ID: <20180731130718.25222-11-avarab@gmail.com> (raw)
In-Reply-To: <20180429202100.32353-1-avarab@gmail.com>

Change "fetch" to treat "+" in refspecs (aka --force) to mean we
should clobber a local tag of the same name.

This changes the long-standing behavior of "fetch" added in
853a3697dc ("[PATCH] Multi-head fetch.", 2005-08-20), before this
change all tag fetches effectively had --force enabled. See the
git-fetch-script code in fast_forward_local() with the comment:

    > Tags need not be pointing at commits so there is no way to
    > guarantee "fast-forward" anyway.

That commit and the rest of the history of "fetch" shows that the
"+" (--force) part of refpecs was only conceived for branch updates,
while tags have accepted any changes from upstream unconditionally and
clobbered the local tag object. Changing this behavior has been
discussed as early as 2011[1].

The current behavior doesn't make sense to me, it easily results in
local tags accidentally being clobbered.  We could namespace our tags
per-remote and not locally populate refs/tags/*, but as with my
97716d217c ("fetch: add a --prune-tags option and fetch.pruneTags
config", 2018-02-09) it's easier to work around the current
implementation than to fix the root cause.

So this change implements suggestion #1 from Jeff's 2011 E-Mail[1],
"fetch" now only clobbers the tag if either "+" is provided as part of
the refspec, or if "--force" is provided on the command-line.

This also makes it nicely symmetrical with how "tag" itself works when
creating tags. I.e. we refuse to clobber any existing tags unless
"--force" is supplied. Now we can refuse all such clobbering, whether
it would happen by clobbering a local tag with "tag", or by fetching
it from the remote with "fetch".

It's still not at all nicely symmetrical with how "git push" works, as
discussed in the updated pull-fetch-param.txt documentation, but this
change brings them more into line with one another. I don't think
there's any reason "fetch" couldn't fully converge with the behavior
used by "push", but that's a topic for another change.

One of the tests added in 31b808a032 ("clone --single: limit the fetch
refspec to fetched branch", 2012-09-20) is being changed to use
--force where a clone would clobber a tag. This changes nothing about
the existing behavior of the test.

1. https://public-inbox.org/git/20111123221658.GA22313@sigill.intra.peff.net/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Documentation/fetch-options.txt    | 15 ++++++++++-----
 Documentation/pull-fetch-param.txt | 20 +++++++++++++++-----
 builtin/fetch.c                    | 20 +++++++++++++-------
 t/t5516-fetch-push.sh              |  5 +++--
 t/t5612-clone-refspec.sh           |  4 ++--
 5 files changed, 43 insertions(+), 21 deletions(-)

diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 97d3217df9..5b624caf58 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -49,11 +49,16 @@ endif::git-pull[]
 
 -f::
 --force::
-	When 'git fetch' is used with `<rbranch>:<lbranch>`
-	refspec, it refuses to update the local branch
-	`<lbranch>` unless the remote branch `<rbranch>` it
-	fetches is a descendant of `<lbranch>`.  This option
-	overrides that check.
+	When 'git fetch' is used with `<src>:<dst>` refspec it may
+	refuse to update the local branch as discussed
+ifdef::git-pull[]
+	in the `<refspec>` part of the linkgit:git-fetch[1]
+	documentation.
+endif::git-pull[]
+ifndef::git-pull[]
+	in the `<refspec>` part below.
+endif::git-pull[]
+	This option overrides that check.
 
 -k::
 --keep::
diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt
index f1fb08dc68..acb8e1a4f0 100644
--- a/Documentation/pull-fetch-param.txt
+++ b/Documentation/pull-fetch-param.txt
@@ -33,11 +33,21 @@ name.
 it requests fetching everything up to the given tag.
 +
 The remote ref that matches <src>
-is fetched, and if <dst> is not an empty string, the local
-ref that matches it is fast-forwarded using <src>.
-If the optional plus `+` is used, the local ref
-is updated even if it does not result in a fast-forward
-update.
+is fetched, and if <dst> is not an empty string, an attempt
+is made to update the local ref that matches it.
++
+Whether that update is allowed without `--force` depends on the ref
+namespace it's being fetched to, and the type of object being
+fetched. If it's a commit under `refs/heads/*` only fast-forwards are
+allowed.
++
+By having the optional leading `+` to a refspec (or using `--force`
+command line option) you can tell Git to update the local ref even if
+it is not allowed by its respective namespace clobbering rules.
++
+Before Git version 2.19 tag objects under `refs/tags/*` would not be
+protected from updates, but since then the `+` (or `--force`) syntax
+is required to clobber them.
 +
 [NOTE]
 When the remote branch you want to fetch is known to
diff --git a/builtin/fetch.c b/builtin/fetch.c
index ac06f6a576..683f70d71e 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -113,7 +113,7 @@ static struct option builtin_fetch_options[] = {
 		 N_("append to .git/FETCH_HEAD instead of overwriting")),
 	OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
 		   N_("path to upload pack on remote end")),
-	OPT__FORCE(&force, N_("force overwrite of local branch"), 0),
+	OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
 	OPT_BOOL('m', "multiple", &multiple,
 		 N_("fetch from multiple remotes")),
 	OPT_SET_INT('t', "tags", &tags,
@@ -664,12 +664,18 @@ static int update_local_ref(struct ref *ref,
 
 	if (!is_null_oid(&ref->old_oid) &&
 	    starts_with(ref->name, "refs/tags/")) {
-		int r;
-		r = s_update_ref("updating tag", ref, 0);
-		format_display(display, r ? '!' : 't', _("[tag update]"),
-			       r ? _("unable to update local ref") : NULL,
-			       remote, pretty_ref, summary_width);
-		return r;
+		if (force || ref->force) {
+			int r;
+			r = s_update_ref("updating tag", ref, 0);
+			format_display(display, r ? '!' : 't', _("[tag update]"),
+				       r ? _("unable to update local ref") : NULL,
+				       remote, pretty_ref, summary_width);
+			return r;
+		} else {
+			format_display(display, '!', _("[rejected]"), _("would clobber existing tag"),
+				       remote, pretty_ref, summary_width);
+			return 1;
+		}
 	}
 
 	current = lookup_commit_reference_gently(&ref->old_oid, 1);
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 8912312be7..8647f9cc9e 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -1015,7 +1015,7 @@ test_force_fetch_tag () {
 	tag_type_description=$1
 	tag_args=$2
 
-	test_expect_success "fetch will clobber an existing $tag_type_description" "
+	test_expect_success "fetch will not clobber an existing $tag_type_description without --force" "
 		mk_test testrepo heads/master &&
 		mk_child testrepo child1 &&
 		mk_child testrepo child2 &&
@@ -1027,7 +1027,8 @@ test_force_fetch_tag () {
 			git add file1 &&
 			git commit -m 'file1' &&
 			git tag $tag_args Tag &&
-			git -C ../child1 fetch origin tag Tag
+			test_must_fail git -C ../child1 fetch origin tag Tag &&
+			git -C ../child1 fetch origin '+refs/tags/*:refs/tags/*'
 		)
 	"
 }
diff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh
index fac5a73851..6ea8f50dae 100755
--- a/t/t5612-clone-refspec.sh
+++ b/t/t5612-clone-refspec.sh
@@ -104,7 +104,7 @@ test_expect_success 'clone with --no-tags' '
 test_expect_success '--single-branch while HEAD pointing at master' '
 	(
 		cd dir_master &&
-		git fetch &&
+		git fetch --force &&
 		git for-each-ref refs/remotes/origin |
 		sed -e "/HEAD$/d" \
 		    -e "s|/remotes/origin/|/heads/|" >../actual
@@ -115,7 +115,7 @@ test_expect_success '--single-branch while HEAD pointing at master' '
 	test_cmp expect actual &&
 	(
 		cd dir_master &&
-		git fetch --tags &&
+		git fetch --tags --force &&
 		git for-each-ref refs/tags >../actual
 	) &&
 	git for-each-ref refs/tags >expect &&
-- 
2.18.0.345.g5c9ce644c3


  parent reply	other threads:[~2018-07-31 13:07 UTC|newest]

Thread overview: 101+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-04-24 19:57 Fetching tags overwrites existing tags Wink Saville
2018-04-24 23:48 ` Jacob Keller
2018-04-25  0:52 ` Junio C Hamano
2018-04-25  1:29   ` Jacob Keller
2018-04-25  1:31   ` Wink Saville
2018-04-26 19:39     ` Wink Saville
2018-04-26 22:50       ` Junio C Hamano
2018-04-26 23:24         ` Junio C Hamano
2018-04-27 18:50           ` [RFC PATCH v2] Teach remote add the --prefix-tags option Wink Saville
2018-04-27 19:08           ` Fetching tags overwrites existing tags Wink Saville
2018-04-27 19:13             ` Bryan Turner
2018-05-04 15:56               ` Jacob Keller
2018-04-28  7:26             ` Jacob Keller
2018-04-28 18:27           ` [RFC PATCH v3] Teach remote add the --remote-tags option Wink Saville
2018-04-28 19:00             ` Wink Saville
2018-04-28 21:27               ` Wink Saville
2018-05-01 16:59           ` [RFC PATCH v4 0/3] Optional sub hierarchy for remote tags Wink Saville
2018-05-01 19:24             ` Ævar Arnfjörð Bjarmason
2018-05-01 19:45               ` Jacob Keller
2018-05-01 20:34                 ` Wink Saville
2018-05-01 23:24                 ` Junio C Hamano
2018-05-02  0:08                   ` Jacob Keller
2018-05-01 23:28               ` Junio C Hamano
2018-05-01 16:59           ` [RFC PATCH v4 1/3] Teach remote add the --remote-tags option Wink Saville
2018-05-01 18:50             ` Ævar Arnfjörð Bjarmason
2018-05-08 10:26             ` Kaartic Sivaraam
2018-05-01 16:59           ` [RFC PATCH v4 2/3] Teach tag to list remote-tags Wink Saville
2018-05-01 16:59           ` [RFC PATCH v4 3/3] Test git remote add -f --remote-tags Wink Saville
2018-04-27 19:46 ` Fetching tags overwrites existing tags Ævar Arnfjörð Bjarmason
2018-04-29 20:20   ` [PATCH 0/8] "git fetch" should not clobber existing tags without --force Ævar Arnfjörð Bjarmason
2018-07-31 13:07     ` [PATCH v2 00/10] " Ævar Arnfjörð Bjarmason
2018-08-13 19:22       ` [PATCH v3 0/7] Prep for " Ævar Arnfjörð Bjarmason
2018-08-13 20:29         ` Junio C Hamano
2018-08-13 20:37           ` Ævar Arnfjörð Bjarmason
2018-08-30 20:12         ` [PATCH v4 0/6] " Ævar Arnfjörð Bjarmason
2018-08-31 20:09           ` [PATCH v5 0/9] git " Ævar Arnfjörð Bjarmason
2018-08-31 20:09           ` [PATCH v5 1/9] fetch: change "branch" to "reference" in --force -h output Ævar Arnfjörð Bjarmason
2018-08-31 20:09           ` [PATCH v5 2/9] push tests: make use of unused $1 in test description Ævar Arnfjörð Bjarmason
2018-08-31 21:07             ` Junio C Hamano
2018-08-31 22:02               ` Ævar Arnfjörð Bjarmason
2018-08-31 20:09           ` [PATCH v5 3/9] push tests: use spaces in interpolated string Ævar Arnfjörð Bjarmason
2018-08-31 20:09           ` [PATCH v5 4/9] fetch tests: add a test for clobbering tag behavior Ævar Arnfjörð Bjarmason
2018-08-31 20:10           ` [PATCH v5 5/9] push doc: remove confusing mention of remote merger Ævar Arnfjörð Bjarmason
2018-08-31 20:10           ` [PATCH v5 6/9] push doc: move mention of "tag <tag>" later in the prose Ævar Arnfjörð Bjarmason
2018-08-31 20:10           ` [PATCH v5 7/9] push doc: correct lies about how push refspecs work Ævar Arnfjörð Bjarmason
2018-08-31 20:10           ` [PATCH v5 8/9] fetch: document local ref updates with/without --force Ævar Arnfjörð Bjarmason
2018-08-31 20:10           ` [PATCH v5 9/9] fetch: stop clobbering existing tags without --force Ævar Arnfjörð Bjarmason
2018-08-30 20:12         ` [PATCH v4 1/6] fetch: change "branch" to "reference" in --force -h output Ævar Arnfjörð Bjarmason
2018-08-30 20:12         ` [PATCH v4 2/6] push tests: correct quoting in interpolated string Ævar Arnfjörð Bjarmason
2018-08-30 21:20           ` Junio C Hamano
2018-08-30 20:12         ` [PATCH v4 3/6] fetch tests: add a test for clobbering tag behavior Ævar Arnfjörð Bjarmason
2018-08-30 21:22           ` Junio C Hamano
2018-08-30 20:12         ` [PATCH v4 4/6] push doc: correct lies about how push refspecs work Ævar Arnfjörð Bjarmason
2018-08-30 21:31           ` Junio C Hamano
2018-08-30 22:34           ` Ævar Arnfjörð Bjarmason
2018-08-31 16:24             ` Junio C Hamano
2018-08-31 16:35               ` Ævar Arnfjörð Bjarmason
2018-08-30 20:12         ` [PATCH v4 5/6] fetch: document local ref updates with/without --force Ævar Arnfjörð Bjarmason
2018-08-30 20:12         ` [PATCH v4 6/6] fetch: stop clobbering existing tags without --force Ævar Arnfjörð Bjarmason
2018-08-30 21:43           ` Junio C Hamano
2018-08-13 19:22       ` [PATCH v3 1/7] fetch tests: change "Tag" test tag to "testTag" Ævar Arnfjörð Bjarmason
2018-08-13 19:22       ` [PATCH v3 2/7] push tests: remove redundant 'git push' invocation Ævar Arnfjörð Bjarmason
2018-08-13 19:22       ` [PATCH v3 3/7] push tests: fix logic error in "push" test assertion Ævar Arnfjörð Bjarmason
2018-08-13 19:22       ` [PATCH v3 4/7] push tests: add more testing for forced tag pushing Ævar Arnfjörð Bjarmason
2018-08-13 19:22       ` [PATCH v3 5/7] push tests: assert re-pushing annotated tags Ævar Arnfjörð Bjarmason
2018-08-13 19:22       ` [PATCH v3 6/7] fetch tests: correct a comment "remove it" -> "remove them" Ævar Arnfjörð Bjarmason
2018-08-13 19:22       ` [PATCH v3 7/7] pull doc: fix a long-standing grammar error Ævar Arnfjörð Bjarmason
2018-07-31 13:07     ` [PATCH v2 01/10] fetch tests: change "Tag" test tag to "testTag" Ævar Arnfjörð Bjarmason
2018-07-31 13:07     ` [PATCH v2 02/10] push tests: remove redundant 'git push' invocation Ævar Arnfjörð Bjarmason
2018-07-31 13:07     ` [PATCH v2 03/10] push tests: fix logic error in "push" test assertion Ævar Arnfjörð Bjarmason
2018-07-31 13:07     ` [PATCH v2 04/10] push tests: add more testing for forced tag pushing Ævar Arnfjörð Bjarmason
2018-07-31 13:07     ` [PATCH v2 05/10] push tests: assert re-pushing annotated tags Ævar Arnfjörð Bjarmason
2018-07-31 13:07     ` [PATCH v2 06/10] push doc: correct lies about how push refspecs work Ævar Arnfjörð Bjarmason
2018-07-31 17:40       ` Junio C Hamano
2018-08-30 14:52         ` Ævar Arnfjörð Bjarmason
2018-08-30 15:23           ` Junio C Hamano
2018-08-30 16:59             ` Ævar Arnfjörð Bjarmason
2018-07-31 13:07     ` [PATCH v2 07/10] fetch tests: correct a comment "remove it" -> "remove them" Ævar Arnfjörð Bjarmason
2018-07-31 13:07     ` [PATCH v2 08/10] fetch tests: add a test clobbering tag behavior Ævar Arnfjörð Bjarmason
2018-07-31 17:48       ` Junio C Hamano
2018-07-31 13:07     ` [PATCH v2 09/10] pull doc: fix a long-standing grammar error Ævar Arnfjörð Bjarmason
2018-07-31 13:07     ` Ævar Arnfjörð Bjarmason [this message]
2018-07-31 18:03       ` [PATCH v2 10/10] fetch: stop clobbering existing tags without --force Junio C Hamano
2018-04-29 20:20   ` [PATCH 1/8] push tests: remove redundant 'git push' invocation Ævar Arnfjörð Bjarmason
2018-04-29 20:20   ` [PATCH 2/8] push tests: fix logic error in "push" test assertion Ævar Arnfjörð Bjarmason
2018-04-29 20:20   ` [PATCH 3/8] push tests: add more testing for forced tag pushing Ævar Arnfjörð Bjarmason
2018-05-07 10:09     ` Kaartic Sivaraam
2018-05-08  2:35     ` Junio C Hamano
2018-05-08  3:19       ` Junio C Hamano
2018-05-08  9:52         ` Kaartic Sivaraam
2018-05-08 10:19     ` Kaartic Sivaraam
2018-04-29 20:20   ` [PATCH 4/8] push tests: assert re-pushing annotated tags Ævar Arnfjörð Bjarmason
2018-05-08  4:30     ` Junio C Hamano
2018-05-08 14:05     ` SZEDER Gábor
2018-04-29 20:20   ` [PATCH 5/8] push doc: correct lies about how push refspecs work Ævar Arnfjörð Bjarmason
2018-05-08  5:14     ` Junio C Hamano
2018-04-29 20:20   ` [PATCH 6/8] fetch tests: correct a comment "remove it" -> "remove them" Ævar Arnfjörð Bjarmason
2018-04-29 20:20   ` [PATCH 7/8] fetch tests: add a test clobbering tag behavior Ævar Arnfjörð Bjarmason
2018-04-29 20:21   ` [PATCH 8/8] fetch: stop clobbering existing tags without --force Ævar Arnfjörð Bjarmason
2018-05-08  5:37     ` Junio C Hamano
2018-05-01 17:11 ` Fetching tags overwrites existing tags Wink Saville

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180731130718.25222-11-avarab@gmail.com \
    --to=avarab@gmail.com \
    --cc=bturner@atlassian.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=jacob.keller@gmail.com \
    --cc=kaartic.sivaraam@gmail.com \
    --cc=peff@peff.net \
    --cc=szeder.dev@gmail.com \
    --cc=u.kleine-koenig@pengutronix.de \
    --cc=wink@saville.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).