Skip to content

Commit dbfeddb

Browse files
crorvickgitster
authored andcommitted
push: require force for refs under refs/tags/
References are allowed to update from one commit-ish to another if the former is an ancestor of the latter. This behavior is oriented to branches which are expected to move with commits. Tag references are expected to be static in a repository, though, thus an update to something under refs/tags/ should be rejected unless the update is forced. Signed-off-by: Chris Rorvick <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 8c5f6f7 commit dbfeddb

File tree

9 files changed

+62
-13
lines changed

9 files changed

+62
-13
lines changed

Documentation/git-push.txt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,12 @@ be named. If `:`<dst> is omitted, the same ref as <src> will be
5151
updated.
5252
+
5353
The object referenced by <src> is used to update the <dst> reference
54-
on the remote side, but by default this is only allowed if the
55-
update can fast-forward <dst>. By having the optional leading `+`,
56-
you can tell git to update the <dst> ref even when the update is not a
57-
fast-forward. This does *not* attempt to merge <src> into <dst>. See
58-
EXAMPLES below for details.
54+
on the remote side. By default this is only allowed if <dst> is not
55+
under refs/tags/, and then only if it can fast-forward <dst>. By having
56+
the optional leading `+`, you can tell git to update the <dst> ref even
57+
if it is not allowed by default (e.g., it is not a fast-forward.) This
58+
does *not* attempt to merge <src> into <dst>. See EXAMPLES below for
59+
details.
5960
+
6061
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
6162
+

builtin/push.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ static const char message_advice_checkout_pull_push[] =
222222

223223
static const char message_advice_ref_already_exists[] =
224224
N_("Updates were rejected because the destination reference already exists\n"
225-
"in the remote and the update is not a fast-forward.");
225+
"in the remote.");
226226

227227
static void advise_pull_before_push(void)
228228
{

builtin/send-pack.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ static void print_helper_status(struct ref *ref)
4444
msg = "non-fast forward";
4545
break;
4646

47+
case REF_STATUS_REJECT_ALREADY_EXISTS:
48+
res = "error";
49+
msg = "already exists";
50+
break;
51+
4752
case REF_STATUS_REJECT_NODELETE:
4853
case REF_STATUS_REMOTE_REJECT:
4954
res = "error";

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,7 @@ struct ref {
10111011
REF_STATUS_NONE = 0,
10121012
REF_STATUS_OK,
10131013
REF_STATUS_REJECT_NONFASTFORWARD,
1014+
REF_STATUS_REJECT_ALREADY_EXISTS,
10141015
REF_STATUS_REJECT_NODELETE,
10151016
REF_STATUS_UPTODATE,
10161017
REF_STATUS_REMOTE_REJECT,

remote.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,14 +1315,18 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
13151315
*
13161316
* (1) if the old thing does not exist, it is OK.
13171317
*
1318-
* (2) if you do not have the old thing, you are not allowed
1318+
* (2) if the destination is under refs/tags/ you are
1319+
* not allowed to overwrite it; tags are expected
1320+
* to be static once created
1321+
*
1322+
* (3) if you do not have the old thing, you are not allowed
13191323
* to overwrite it; you would not know what you are losing
13201324
* otherwise.
13211325
*
1322-
* (3) if both new and old are commit-ish, and new is a
1326+
* (4) if both new and old are commit-ish, and new is a
13231327
* descendant of old, it is OK.
13241328
*
1325-
* (4) regardless of all of the above, removing :B is
1329+
* (5) regardless of all of the above, removing :B is
13261330
* always allowed.
13271331
*/
13281332

@@ -1337,7 +1341,13 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
13371341
!has_sha1_file(ref->old_sha1)
13381342
|| !ref_newer(ref->new_sha1, ref->old_sha1);
13391343

1340-
if (ref->nonfastforward) {
1344+
if (ref->not_forwardable) {
1345+
ref->requires_force = 1;
1346+
if (!force_ref_update) {
1347+
ref->status = REF_STATUS_REJECT_ALREADY_EXISTS;
1348+
continue;
1349+
}
1350+
} else if (ref->nonfastforward) {
13411351
ref->requires_force = 1;
13421352
if (!force_ref_update) {
13431353
ref->status = REF_STATUS_REJECT_NONFASTFORWARD;

send-pack.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ int send_pack(struct send_pack_args *args,
229229
/* Check for statuses set by set_ref_status_for_push() */
230230
switch (ref->status) {
231231
case REF_STATUS_REJECT_NONFASTFORWARD:
232+
case REF_STATUS_REJECT_ALREADY_EXISTS:
232233
case REF_STATUS_UPTODATE:
233234
continue;
234235
default:

t/t5516-fetch-push.sh

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ test_expect_success 'push with colon-less refspec (2)' '
368368
git branch -D frotz
369369
fi &&
370370
git tag -f frotz &&
371-
git push testrepo frotz &&
371+
git push -f testrepo frotz &&
372372
check_push_result $the_commit tags/frotz &&
373373
check_push_result $the_first_commit heads/frotz
374374
@@ -929,6 +929,27 @@ test_expect_success 'push into aliased refs (inconsistent)' '
929929
)
930930
'
931931

932+
test_expect_success 'push requires --force to update lightweight tag' '
933+
mk_test heads/master &&
934+
mk_child child1 &&
935+
mk_child child2 &&
936+
(
937+
cd child1 &&
938+
git tag Tag &&
939+
git push ../child2 Tag &&
940+
git push ../child2 Tag &&
941+
>file1 &&
942+
git add file1 &&
943+
git commit -m "file1" &&
944+
git tag -f Tag &&
945+
test_must_fail git push ../child2 Tag &&
946+
git push --force ../child2 Tag &&
947+
git tag -f Tag &&
948+
test_must_fail git push ../child2 Tag HEAD~ &&
949+
git push --force ../child2 Tag
950+
)
951+
'
952+
932953
test_expect_success 'push --porcelain' '
933954
mk_empty &&
934955
echo >.git/foo "To testrepo" &&

transport-helper.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,11 @@ static void push_update_ref_status(struct strbuf *buf,
661661
free(msg);
662662
msg = NULL;
663663
}
664+
else if (!strcmp(msg, "already exists")) {
665+
status = REF_STATUS_REJECT_ALREADY_EXISTS;
666+
free(msg);
667+
msg = NULL;
668+
}
664669
}
665670

666671
if (*ref)
@@ -720,6 +725,7 @@ static int push_refs_with_push(struct transport *transport,
720725
/* Check for statuses set by set_ref_status_for_push() */
721726
switch (ref->status) {
722727
case REF_STATUS_REJECT_NONFASTFORWARD:
728+
case REF_STATUS_REJECT_ALREADY_EXISTS:
723729
case REF_STATUS_UPTODATE:
724730
continue;
725731
default:

transport.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
695695
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
696696
"non-fast-forward", porcelain);
697697
break;
698+
case REF_STATUS_REJECT_ALREADY_EXISTS:
699+
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
700+
"already exists", porcelain);
701+
break;
698702
case REF_STATUS_REMOTE_REJECT:
699703
print_ref_status('!', "[remote rejected]", ref,
700704
ref->deletion ? NULL : ref->peer_ref,
@@ -740,12 +744,12 @@ void transport_print_push_status(const char *dest, struct ref *refs,
740744
ref->status != REF_STATUS_OK)
741745
n += print_one_push_status(ref, dest, n, porcelain);
742746
if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {
743-
if (ref->not_forwardable)
744-
*reject_reasons |= REJECT_ALREADY_EXISTS;
745747
if (!strcmp(head, ref->name))
746748
*reject_reasons |= REJECT_NON_FF_HEAD;
747749
else
748750
*reject_reasons |= REJECT_NON_FF_OTHER;
751+
} else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) {
752+
*reject_reasons |= REJECT_ALREADY_EXISTS;
749753
}
750754
}
751755
}

0 commit comments

Comments
 (0)