push --force-with-lease: tie it all together

This teaches the deepest part of the callchain for "git push" (and "git send-pack") to enforce "the old value of the ref must be this, otherwise fail this push" (aka "compare-and-swap" / "--lockref"). Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jul 8, 2013 at 14:42 UTC 631b5ef219c41027c144218e25075062b91f9471
6 files changed +54 -13
builtin/send-pack.c
+5
@@ -55,6 +55,11 @@ static void print_helper_status(struct ref *ref)
55 msg = "needs force";
56 break;
57
58 + case REF_STATUS_REJECT_STALE:
59 + res = "error";
60 + msg = "stale info";
61 + break;
62 +
63 case REF_STATUS_REJECT_ALREADY_EXISTS:
64 res = "error";
65 msg = "already exists";
remote.c
+36 -13
@@ -1396,12 +1396,13 @@ int match_push_refs(struct ref *src, struct ref **dst,
1396 }
1397
1398 void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
1399 - int force_update)
1399 + int force_update)
1400 {
1401 struct ref *ref;
1402
1403 for (ref = remote_refs; ref; ref = ref->next) {
1404 int force_ref_update = ref->force || force_update;
1405 + int reject_reason = 0;
1406
1407 if (ref->peer_ref)
1408 hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
@@ -1416,6 +1417,26 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
1417 }
1418
1419 /*
1420 + * Bypass the usual "must fast-forward" check but
1421 + * replace it with a weaker "the old value must be
1422 + * this value we observed". If the remote ref has
1423 + * moved and is now different from what we expect,
1424 + * reject any push.
1425 + *
1426 + * It also is an error if the user told us to check
1427 + * with the remote-tracking branch to find the value
1428 + * to expect, but we did not have such a tracking
1429 + * branch.
1430 + */
1431 + if (ref->expect_old_sha1) {
1432 + if (ref->expect_old_no_trackback ||
1433 + hashcmp(ref->old_sha1, ref->old_sha1_expect))
1434 + reject_reason = REF_STATUS_REJECT_STALE;
1435 + }
1436 +
1437 + /*
1438 + * The usual "must fast-forward" rules.
1439 + *
1440 * Decide whether an individual refspec A:B can be
1441 * pushed. The push will succeed if any of the
1442 * following are true:
@@ -1433,24 +1454,26 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
1454 * passing the --force argument
1455 */
1456
1436 - if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
1437 - int why = 0; /* why would this push require --force? */
1438 -
1457 + else if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
1458 if (!prefixcmp(ref->name, "refs/tags/"))
1440 - why = REF_STATUS_REJECT_ALREADY_EXISTS;
1459 + reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
1460 else if (!has_sha1_file(ref->old_sha1))
1442 - why = REF_STATUS_REJECT_FETCH_FIRST;
1461 + reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
1462 else if (!lookup_commit_reference_gently(ref->old_sha1, 1) ||
1463 !lookup_commit_reference_gently(ref->new_sha1, 1))
1445 - why = REF_STATUS_REJECT_NEEDS_FORCE;
1464 + reject_reason = REF_STATUS_REJECT_NEEDS_FORCE;
1465 else if (!ref_newer(ref->new_sha1, ref->old_sha1))
1447 - why = REF_STATUS_REJECT_NONFASTFORWARD;
1448 -
1449 - if (!force_ref_update)
1450 - ref->status = why;
1451 - else if (why)
1452 - ref->forced_update = 1;
1466 + reject_reason = REF_STATUS_REJECT_NONFASTFORWARD;
1467 }
1468 +
1469 + /*
1470 + * "--force" will defeat any rejection implemented
1471 + * by the rules above.
1472 + */
1473 + if (!force_ref_update)
1474 + ref->status = reject_reason;
1475 + else if (reject_reason)
1476 + ref->forced_update = 1;
1477 }
1478 }
1479
remote.h
+1
@@ -107,6 +107,7 @@ struct ref {
107 REF_STATUS_REJECT_NODELETE,
108 REF_STATUS_REJECT_FETCH_FIRST,
109 REF_STATUS_REJECT_NEEDS_FORCE,
110 + REF_STATUS_REJECT_STALE,
111 REF_STATUS_UPTODATE,
112 REF_STATUS_REMOTE_REJECT,
113 REF_STATUS_EXPECTING_REPORT
send-pack.c
+1
@@ -227,6 +227,7 @@ int send_pack(struct send_pack_args *args,
227 case REF_STATUS_REJECT_ALREADY_EXISTS:
228 case REF_STATUS_REJECT_FETCH_FIRST:
229 case REF_STATUS_REJECT_NEEDS_FORCE:
230 + case REF_STATUS_REJECT_STALE:
231 case REF_STATUS_UPTODATE:
232 continue;
233 default:
transport-helper.c
+6
@@ -683,6 +683,11 @@ static int push_update_ref_status(struct strbuf *buf,
683 free(msg);
684 msg = NULL;
685 }
686 + else if (!strcmp(msg, "stale info")) {
687 + status = REF_STATUS_REJECT_STALE;
688 + free(msg);
689 + msg = NULL;
690 + }
691 }
692
693 if (*ref)
@@ -756,6 +761,7 @@ static int push_refs_with_push(struct transport *transport,
761 /* Check for statuses set by set_ref_status_for_push() */
762 switch (ref->status) {
763 case REF_STATUS_REJECT_NONFASTFORWARD:
764 + case REF_STATUS_REJECT_STALE:
765 case REF_STATUS_REJECT_ALREADY_EXISTS:
766 case REF_STATUS_UPTODATE:
767 continue;
transport.c
+5
@@ -709,6 +709,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
709 print_ref_status('!', "[rejected]", ref, ref->peer_ref,
710 "needs force", porcelain);
711 break;
712 + case REF_STATUS_REJECT_STALE:
713 + print_ref_status('!', "[rejected]", ref, ref->peer_ref,
714 + "stale info", porcelain);
715 + break;
716 case REF_STATUS_REMOTE_REJECT:
717 print_ref_status('!', "[remote rejected]", ref,
718 ref->deletion ? NULL : ref->peer_ref,
@@ -1078,6 +1082,7 @@ static int run_pre_push_hook(struct transport *transport,
1082 for (r = remote_refs; r; r = r->next) {
1083 if (!r->peer_ref) continue;
1084 if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue;
1085 + if (r->status == REF_STATUS_REJECT_STALE) continue;
1086 if (r->status == REF_STATUS_UPTODATE) continue;
1087
1088 strbuf_reset(&buf);