Skip to content

Commit f310ce3

Browse files
committed
Merge branch 'ab/fetch-tags-noclobber' into pu
"git fetch" used to apply the same "fast-forward" rule and allow tags to move without "--force" option, which made little sense, which has been corrected. * ab/fetch-tags-noclobber: fetch: stop clobbering existing tags without --force pull doc: fix a long-standing grammar error fetch tests: add a test clobbering tag behavior fetch tests: correct a comment "remove it" -> "remove them" push doc: correct lies about how push refspecs work push tests: assert re-pushing annotated tags push tests: add more testing for forced tag pushing push tests: fix logic error in "push" test assertion push tests: remove redundant 'git push' invocation fetch tests: change "Tag" test tag to "testTag"
2 parents 5c04954 + 15d6de1 commit f310ce3

8 files changed

+137
-51
lines changed

Diff for: Documentation/fetch-options.txt

+10-5
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,16 @@ endif::git-pull[]
6868

6969
-f::
7070
--force::
71-
When 'git fetch' is used with `<rbranch>:<lbranch>`
72-
refspec, it refuses to update the local branch
73-
`<lbranch>` unless the remote branch `<rbranch>` it
74-
fetches is a descendant of `<lbranch>`. This option
75-
overrides that check.
71+
When 'git fetch' is used with `<src>:<dst>` refspec it may
72+
refuse to update the local branch as discussed
73+
ifdef::git-pull[]
74+
in the `<refspec>` part of the linkgit:git-fetch[1]
75+
documentation.
76+
endif::git-pull[]
77+
ifndef::git-pull[]
78+
in the `<refspec>` part below.
79+
endif::git-pull[]
80+
This option overrides that check.
7681

7782
-k::
7883
--keep::

Diff for: Documentation/git-push.txt

+22-8
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ OPTIONS[[OPTIONS]]
6060
by a colon `:`, followed by the destination ref <dst>.
6161
+
6262
The <src> is often the name of the branch you would want to push, but
63-
it can be any arbitrary "SHA-1 expression", such as `master~4` or
64-
`HEAD` (see linkgit:gitrevisions[7]).
63+
it can be any arbitrary expression to a commit, such as `master~4` or
64+
`HEAD` (see linkgit:gitrevisions[7]). It can also refer to tag
65+
objects, trees or blobs if the <dst> is outside of `refs/heads/*`.
6566
+
6667
The <dst> tells which ref on the remote side is updated with this
6768
push. Arbitrary expressions cannot be used here, an actual ref must
@@ -74,12 +75,25 @@ without any `<refspec>` on the command line. Otherwise, missing
7475
`:<dst>` means to update the same ref as the `<src>`.
7576
+
7677
The object referenced by <src> is used to update the <dst> reference
77-
on the remote side. By default this is only allowed if <dst> is not
78-
a tag (annotated or lightweight), and then only if it can fast-forward
79-
<dst>. By having the optional leading `+`, you can tell Git to update
80-
the <dst> ref even if it is not allowed by default (e.g., it is not a
81-
fast-forward.) This does *not* attempt to merge <src> into <dst>. See
82-
EXAMPLES below for details.
78+
on the remote side. Whether this is allowed depends on where in
79+
`refs/*` the <dst> reference lives. The `refs/heads/*` namespace will
80+
only accept commit objects, and then only they can be
81+
fast-forwarded. The `refs/tags/*` namespace will accept any kind of
82+
object, and any changes to them and others types of objects will be
83+
rejected. Finally, it's possible to push any type of object to any
84+
namespace outside of `refs/{tags,heads}/*`, but these will be treated
85+
as branches for the purposes of whether `--force` is required, even in
86+
the case where a tag object is pushed. That tag object will be
87+
overwritten by another tag object (or commit!) without `--force` if
88+
the new tag happens to point to a commit that's a fast-forward of the
89+
commit it replaces.
90+
+
91+
By having the optional leading `+` to a refspec (or using `--force`
92+
command line option) you can tell Git to update the <dst> ref even if
93+
it is not allowed by its respective namespace clobbering rules (e.g.,
94+
it is not a fast-forward. in the case of `refs/heads/*` updates) This
95+
does *not* attempt to merge <src> into <dst>. See EXAMPLES below for
96+
details.
8397
+
8498
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
8599
+

Diff for: Documentation/gitrevisions.txt

+4-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ walk the revision graph (such as linkgit:git-log[1]), all commits which are
1919
reachable from that commit. For commands that walk the revision graph one can
2020
also specify a range of revisions explicitly.
2121

22-
In addition, some Git commands (such as linkgit:git-show[1]) also take
23-
revision parameters which denote other objects than commits, e.g. blobs
24-
("files") or trees ("directories of files").
22+
In addition, some Git commands (such as linkgit:git-show[1] and
23+
linkgit:git-push[1]) can also take revision parameters which denote
24+
other objects than commits, e.g. blobs ("files") or trees
25+
("directories of files").
2526

2627
include::revisions.txt[]
2728

Diff for: Documentation/pull-fetch-param.txt

+15-5
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,21 @@ name.
3333
it requests fetching everything up to the given tag.
3434
+
3535
The remote ref that matches <src>
36-
is fetched, and if <dst> is not empty string, the local
37-
ref that matches it is fast-forwarded using <src>.
38-
If the optional plus `+` is used, the local ref
39-
is updated even if it does not result in a fast-forward
40-
update.
36+
is fetched, and if <dst> is not an empty string, an attempt
37+
is made to update the local ref that matches it.
38+
+
39+
Whether that update is allowed without `--force` depends on the ref
40+
namespace it's being fetched to, and the type of object being
41+
fetched. If it's a commit under `refs/heads/*` only fast-forwards are
42+
allowed.
43+
+
44+
By having the optional leading `+` to a refspec (or using `--force`
45+
command line option) you can tell Git to update the local ref even if
46+
it is not allowed by its respective namespace clobbering rules.
47+
+
48+
Before Git version 2.19 tag objects under `refs/tags/*` would not be
49+
protected from updates, but since then the `+` (or `--force`) syntax
50+
is required to clobber them.
4151
+
4252
[NOTE]
4353
When the remote branch you want to fetch is known to

Diff for: builtin/fetch.c

+13-7
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ static struct option builtin_fetch_options[] = {
116116
N_("append to .git/FETCH_HEAD instead of overwriting")),
117117
OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
118118
N_("path to upload pack on remote end")),
119-
OPT__FORCE(&force, N_("force overwrite of local branch"), 0),
119+
OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
120120
OPT_BOOL('m', "multiple", &multiple,
121121
N_("fetch from multiple remotes")),
122122
OPT_SET_INT('t', "tags", &tags,
@@ -669,12 +669,18 @@ static int update_local_ref(struct ref *ref,
669669

670670
if (!is_null_oid(&ref->old_oid) &&
671671
starts_with(ref->name, "refs/tags/")) {
672-
int r;
673-
r = s_update_ref("updating tag", ref, 0);
674-
format_display(display, r ? '!' : 't', _("[tag update]"),
675-
r ? _("unable to update local ref") : NULL,
676-
remote, pretty_ref, summary_width);
677-
return r;
672+
if (force || ref->force) {
673+
int r;
674+
r = s_update_ref("updating tag", ref, 0);
675+
format_display(display, r ? '!' : 't', _("[tag update]"),
676+
r ? _("unable to update local ref") : NULL,
677+
remote, pretty_ref, summary_width);
678+
return r;
679+
} else {
680+
format_display(display, '!', _("[rejected]"), _("would clobber existing tag"),
681+
remote, pretty_ref, summary_width);
682+
return 1;
683+
}
678684
}
679685

680686
current = lookup_commit_reference_gently(the_repository,

Diff for: t/t5510-fetch.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,7 @@ test_configured_prune_type () {
648648
git rev-parse --verify refs/tags/newtag
649649
) &&
650650
651-
# now remove it
651+
# now remove them
652652
git branch -d newbranch &&
653653
git tag -d newtag &&
654654

Diff for: t/t5516-fetch-push.sh

+70-20
Original file line numberDiff line numberDiff line change
@@ -965,26 +965,76 @@ test_expect_success 'push into aliased refs (inconsistent)' '
965965
)
966966
'
967967

968-
test_expect_success 'push requires --force to update lightweight tag' '
969-
mk_test testrepo heads/master &&
970-
mk_child testrepo child1 &&
971-
mk_child testrepo child2 &&
972-
(
973-
cd child1 &&
974-
git tag Tag &&
975-
git push ../child2 Tag &&
976-
git push ../child2 Tag &&
977-
>file1 &&
978-
git add file1 &&
979-
git commit -m "file1" &&
980-
git tag -f Tag &&
981-
test_must_fail git push ../child2 Tag &&
982-
git push --force ../child2 Tag &&
983-
git tag -f Tag &&
984-
test_must_fail git push ../child2 Tag HEAD~ &&
985-
git push --force ../child2 Tag
986-
)
987-
'
968+
test_force_push_tag () {
969+
tag_type_description=$1
970+
tag_args=$2
971+
972+
test_expect_success 'force pushing required to update lightweight tag' "
973+
mk_test testrepo heads/master &&
974+
mk_child testrepo child1 &&
975+
mk_child testrepo child2 &&
976+
(
977+
cd child1 &&
978+
git tag testTag &&
979+
git push ../child2 testTag &&
980+
>file1 &&
981+
git add file1 &&
982+
git commit -m 'file1' &&
983+
git tag $tag_args testTag &&
984+
test_must_fail git push ../child2 testTag &&
985+
git push --force ../child2 testTag &&
986+
git tag $tag_args testTag HEAD~ &&
987+
test_must_fail git push ../child2 testTag &&
988+
git push --force ../child2 testTag &&
989+
990+
# Clobbering without + in refspec needs --force
991+
git tag -f testTag &&
992+
test_must_fail git push ../child2 'refs/tags/*:refs/tags/*' &&
993+
git push --force ../child2 'refs/tags/*:refs/tags/*' &&
994+
995+
# Clobbering with + in refspec does not need --force
996+
git tag -f testTag HEAD~ &&
997+
git push ../child2 '+refs/tags/*:refs/tags/*' &&
998+
999+
# Clobbering with --no-force still obeys + in refspec
1000+
git tag -f testTag &&
1001+
git push --no-force ../child2 '+refs/tags/*:refs/tags/*' &&
1002+
1003+
# Clobbering with/without --force and 'tag <name>' format
1004+
git tag -f testTag HEAD~ &&
1005+
test_must_fail git push ../child2 tag testTag &&
1006+
git push --force ../child2 tag testTag
1007+
)
1008+
"
1009+
}
1010+
1011+
test_force_push_tag "lightweight tag" "-f"
1012+
test_force_push_tag "annotated tag" "-f -a -m'msg'"
1013+
1014+
test_force_fetch_tag () {
1015+
tag_type_description=$1
1016+
tag_args=$2
1017+
1018+
test_expect_success "fetch will not clobber an existing $tag_type_description without --force" "
1019+
mk_test testrepo heads/master &&
1020+
mk_child testrepo child1 &&
1021+
mk_child testrepo child2 &&
1022+
(
1023+
cd testrepo &&
1024+
git tag Tag &&
1025+
git -C ../child1 fetch origin tag Tag &&
1026+
>file1 &&
1027+
git add file1 &&
1028+
git commit -m 'file1' &&
1029+
git tag $tag_args Tag &&
1030+
test_must_fail git -C ../child1 fetch origin tag Tag &&
1031+
git -C ../child1 fetch origin '+refs/tags/*:refs/tags/*'
1032+
)
1033+
"
1034+
}
1035+
1036+
test_force_fetch_tag "lightweight tag" "-f"
1037+
test_force_fetch_tag "annotated tag" "-f -a -m'msg'"
9881038

9891039
test_expect_success 'push --porcelain' '
9901040
mk_empty testrepo &&

Diff for: t/t5612-clone-refspec.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ test_expect_success 'clone with --no-tags' '
103103
test_expect_success '--single-branch while HEAD pointing at master' '
104104
(
105105
cd dir_master &&
106-
git fetch &&
106+
git fetch --force &&
107107
git for-each-ref refs/remotes/origin |
108108
sed -e "/HEAD$/d" \
109109
-e "s|/remotes/origin/|/heads/|" >../actual
@@ -114,7 +114,7 @@ test_expect_success '--single-branch while HEAD pointing at master' '
114114
test_cmp expect actual &&
115115
(
116116
cd dir_master &&
117-
git fetch --tags &&
117+
git fetch --tags --force &&
118118
git for-each-ref refs/tags >../actual
119119
) &&
120120
git for-each-ref refs/tags >expect &&

0 commit comments

Comments
 (0)