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 <chris@rorvick.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Chris Rorvick committed Nov 29, 2012 at 19:41 UTC dbfeddb12e5bb540ed3c852eebda3df9117bd150
9 files changed +62 -13
Documentation/git-push.txt
+6 -5
@@ -51,11 +51,12 @@ be named. If `:`<dst> is omitted, the same ref as <src> will be
51 updated.
52 +
53 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.
60 +
61 `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
62 +
builtin/push.c
+1 -1
@@ -222,7 +222,7 @@ static const char message_advice_checkout_pull_push[] =
222
223 static const char message_advice_ref_already_exists[] =
224 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.");
226
227 static void advise_pull_before_push(void)
228 {
builtin/send-pack.c
+5
@@ -44,6 +44,11 @@ static void print_helper_status(struct ref *ref)
44 msg = "non-fast forward";
45 break;
46
47 + case REF_STATUS_REJECT_ALREADY_EXISTS:
48 + res = "error";
49 + msg = "already exists";
50 + break;
51 +
52 case REF_STATUS_REJECT_NODELETE:
53 case REF_STATUS_REMOTE_REJECT:
54 res = "error";
cache.h
+1
@@ -1011,6 +1011,7 @@ struct ref {
1011 REF_STATUS_NONE = 0,
1012 REF_STATUS_OK,
1013 REF_STATUS_REJECT_NONFASTFORWARD,
1014 + REF_STATUS_REJECT_ALREADY_EXISTS,
1015 REF_STATUS_REJECT_NODELETE,
1016 REF_STATUS_UPTODATE,
1017 REF_STATUS_REMOTE_REJECT,
remote.c
+14 -4
@@ -1315,14 +1315,18 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
1315 *
1316 * (1) if the old thing does not exist, it is OK.
1317 *
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
1323 * to overwrite it; you would not know what you are losing
1324 * otherwise.
1325 *
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
1327 * descendant of old, it is OK.
1328 *
1325 - * (4) regardless of all of the above, removing :B is
1329 + * (5) regardless of all of the above, removing :B is
1330 * always allowed.
1331 */
1332
@@ -1337,7 +1341,13 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
1341 !has_sha1_file(ref->old_sha1)
1342 || !ref_newer(ref->new_sha1, ref->old_sha1);
1343
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) {
1351 ref->requires_force = 1;
1352 if (!force_ref_update) {
1353 ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
send-pack.c
+1
@@ -229,6 +229,7 @@ int send_pack(struct send_pack_args *args,
229 /* Check for statuses set by set_ref_status_for_push() */
230 switch (ref->status) {
231 case REF_STATUS_REJECT_NONFASTFORWARD:
232 + case REF_STATUS_REJECT_ALREADY_EXISTS:
233 case REF_STATUS_UPTODATE:
234 continue;
235 default:
t/t5516-fetch-push.sh
+22 -1
@@ -368,7 +368,7 @@ test_expect_success 'push with colon-less refspec (2)' '
368 git branch -D frotz
369 fi &&
370 git tag -f frotz &&
371 - git push testrepo frotz &&
371 + git push -f testrepo frotz &&
372 check_push_result $the_commit tags/frotz &&
373 check_push_result $the_first_commit heads/frotz
374
@@ -929,6 +929,27 @@ test_expect_success 'push into aliased refs (inconsistent)' '
929 )
930 '
931
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 +
953 test_expect_success 'push --porcelain' '
954 mk_empty &&
955 echo >.git/foo "To testrepo" &&
transport-helper.c
+6
@@ -661,6 +661,11 @@ static void push_update_ref_status(struct strbuf *buf,
661 free(msg);
662 msg = NULL;
663 }
664 + else if (!strcmp(msg, "already exists")) {
665 + status = REF_STATUS_REJECT_ALREADY_EXISTS;
666 + free(msg);
667 + msg = NULL;
668 + }
669 }
670
671 if (*ref)
@@ -720,6 +725,7 @@ static int push_refs_with_push(struct transport *transport,
725 /* Check for statuses set by set_ref_status_for_push() */
726 switch (ref->status) {
727 case REF_STATUS_REJECT_NONFASTFORWARD:
728 + case REF_STATUS_REJECT_ALREADY_EXISTS:
729 case REF_STATUS_UPTODATE:
730 continue;
731 default:
transport.c
+6 -2
@@ -695,6 +695,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
695 print_ref_status('!', "[rejected]", ref, ref->peer_ref,
696 "non-fast-forward", porcelain);
697 break;
698 + case REF_STATUS_REJECT_ALREADY_EXISTS:
699 + print_ref_status('!', "[rejected]", ref, ref->peer_ref,
700 + "already exists", porcelain);
701 + break;
702 case REF_STATUS_REMOTE_REJECT:
703 print_ref_status('!', "[remote rejected]", ref,
704 ref->deletion ? NULL : ref->peer_ref,
@@ -740,12 +744,12 @@ void transport_print_push_status(const char *dest, struct ref *refs,
744 ref->status != REF_STATUS_OK)
745 n += print_one_push_status(ref, dest, n, porcelain);
746 if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {
743 - if (ref->not_forwardable)
744 - *reject_reasons |= REJECT_ALREADY_EXISTS;
747 if (!strcmp(head, ref->name))
748 *reject_reasons |= REJECT_NON_FF_HEAD;
749 else
750 *reject_reasons |= REJECT_NON_FF_OTHER;
751 + } else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) {
752 + *reject_reasons |= REJECT_ALREADY_EXISTS;
753 }
754 }
755 }