Skip to content

Commit 631b5ef

Browse files
committed
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 <[email protected]>
1 parent 91048a9 commit 631b5ef

File tree

6 files changed

+54
-13
lines changed

6 files changed

+54
-13
lines changed

builtin/send-pack.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ static void print_helper_status(struct ref *ref)
5555
msg = "needs force";
5656
break;
5757

58+
case REF_STATUS_REJECT_STALE:
59+
res = "error";
60+
msg = "stale info";
61+
break;
62+
5863
case REF_STATUS_REJECT_ALREADY_EXISTS:
5964
res = "error";
6065
msg = "already exists";

remote.c

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,12 +1396,13 @@ int match_push_refs(struct ref *src, struct ref **dst,
13961396
}
13971397

13981398
void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
1399-
int force_update)
1399+
int force_update)
14001400
{
14011401
struct ref *ref;
14021402

14031403
for (ref = remote_refs; ref; ref = ref->next) {
14041404
int force_ref_update = ref->force || force_update;
1405+
int reject_reason = 0;
14051406

14061407
if (ref->peer_ref)
14071408
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,
14161417
}
14171418

14181419
/*
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+
*
14191440
* Decide whether an individual refspec A:B can be
14201441
* pushed. The push will succeed if any of the
14211442
* following are true:
@@ -1433,24 +1454,26 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
14331454
* passing the --force argument
14341455
*/
14351456

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)) {
14391458
if (!prefixcmp(ref->name, "refs/tags/"))
1440-
why = REF_STATUS_REJECT_ALREADY_EXISTS;
1459+
reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
14411460
else if (!has_sha1_file(ref->old_sha1))
1442-
why = REF_STATUS_REJECT_FETCH_FIRST;
1461+
reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
14431462
else if (!lookup_commit_reference_gently(ref->old_sha1, 1) ||
14441463
!lookup_commit_reference_gently(ref->new_sha1, 1))
1445-
why = REF_STATUS_REJECT_NEEDS_FORCE;
1464+
reject_reason = REF_STATUS_REJECT_NEEDS_FORCE;
14461465
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;
14531467
}
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;
14541477
}
14551478
}
14561479

remote.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ struct ref {
107107
REF_STATUS_REJECT_NODELETE,
108108
REF_STATUS_REJECT_FETCH_FIRST,
109109
REF_STATUS_REJECT_NEEDS_FORCE,
110+
REF_STATUS_REJECT_STALE,
110111
REF_STATUS_UPTODATE,
111112
REF_STATUS_REMOTE_REJECT,
112113
REF_STATUS_EXPECTING_REPORT

send-pack.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ int send_pack(struct send_pack_args *args,
227227
case REF_STATUS_REJECT_ALREADY_EXISTS:
228228
case REF_STATUS_REJECT_FETCH_FIRST:
229229
case REF_STATUS_REJECT_NEEDS_FORCE:
230+
case REF_STATUS_REJECT_STALE:
230231
case REF_STATUS_UPTODATE:
231232
continue;
232233
default:

transport-helper.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,11 @@ static int push_update_ref_status(struct strbuf *buf,
683683
free(msg);
684684
msg = NULL;
685685
}
686+
else if (!strcmp(msg, "stale info")) {
687+
status = REF_STATUS_REJECT_STALE;
688+
free(msg);
689+
msg = NULL;
690+
}
686691
}
687692

688693
if (*ref)
@@ -756,6 +761,7 @@ static int push_refs_with_push(struct transport *transport,
756761
/* Check for statuses set by set_ref_status_for_push() */
757762
switch (ref->status) {
758763
case REF_STATUS_REJECT_NONFASTFORWARD:
764+
case REF_STATUS_REJECT_STALE:
759765
case REF_STATUS_REJECT_ALREADY_EXISTS:
760766
case REF_STATUS_UPTODATE:
761767
continue;

transport.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
709709
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
710710
"needs force", porcelain);
711711
break;
712+
case REF_STATUS_REJECT_STALE:
713+
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
714+
"stale info", porcelain);
715+
break;
712716
case REF_STATUS_REMOTE_REJECT:
713717
print_ref_status('!', "[remote rejected]", ref,
714718
ref->deletion ? NULL : ref->peer_ref,
@@ -1078,6 +1082,7 @@ static int run_pre_push_hook(struct transport *transport,
10781082
for (r = remote_refs; r; r = r->next) {
10791083
if (!r->peer_ref) continue;
10801084
if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue;
1085+
if (r->status == REF_STATUS_REJECT_STALE) continue;
10811086
if (r->status == REF_STATUS_UPTODATE) continue;
10821087

10831088
strbuf_reset(&buf);

0 commit comments

Comments
 (0)