1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-05-06 06:26:12 +02:00

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>
This commit is contained in:
Junio C Hamano 2013-07-08 14:42:40 -07:00
parent 91048a9537
commit 631b5ef219
6 changed files with 54 additions and 13 deletions

View File

@ -55,6 +55,11 @@ static void print_helper_status(struct ref *ref)
msg = "needs force";
break;
case REF_STATUS_REJECT_STALE:
res = "error";
msg = "stale info";
break;
case REF_STATUS_REJECT_ALREADY_EXISTS:
res = "error";
msg = "already exists";

View File

@ -1396,12 +1396,13 @@ int match_push_refs(struct ref *src, struct ref **dst,
}
void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
int force_update)
int force_update)
{
struct ref *ref;
for (ref = remote_refs; ref; ref = ref->next) {
int force_ref_update = ref->force || force_update;
int reject_reason = 0;
if (ref->peer_ref)
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,
}
/*
* Bypass the usual "must fast-forward" check but
* replace it with a weaker "the old value must be
* this value we observed". If the remote ref has
* moved and is now different from what we expect,
* reject any push.
*
* It also is an error if the user told us to check
* with the remote-tracking branch to find the value
* to expect, but we did not have such a tracking
* branch.
*/
if (ref->expect_old_sha1) {
if (ref->expect_old_no_trackback ||
hashcmp(ref->old_sha1, ref->old_sha1_expect))
reject_reason = REF_STATUS_REJECT_STALE;
}
/*
* The usual "must fast-forward" rules.
*
* Decide whether an individual refspec A:B can be
* pushed. The push will succeed if any of the
* following are true:
@ -1433,24 +1454,26 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
* passing the --force argument
*/
if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
int why = 0; /* why would this push require --force? */
else if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
if (!prefixcmp(ref->name, "refs/tags/"))
why = REF_STATUS_REJECT_ALREADY_EXISTS;
reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
else if (!has_sha1_file(ref->old_sha1))
why = REF_STATUS_REJECT_FETCH_FIRST;
reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
else if (!lookup_commit_reference_gently(ref->old_sha1, 1) ||
!lookup_commit_reference_gently(ref->new_sha1, 1))
why = REF_STATUS_REJECT_NEEDS_FORCE;
reject_reason = REF_STATUS_REJECT_NEEDS_FORCE;
else if (!ref_newer(ref->new_sha1, ref->old_sha1))
why = REF_STATUS_REJECT_NONFASTFORWARD;
if (!force_ref_update)
ref->status = why;
else if (why)
ref->forced_update = 1;
reject_reason = REF_STATUS_REJECT_NONFASTFORWARD;
}
/*
* "--force" will defeat any rejection implemented
* by the rules above.
*/
if (!force_ref_update)
ref->status = reject_reason;
else if (reject_reason)
ref->forced_update = 1;
}
}

View File

@ -107,6 +107,7 @@ struct ref {
REF_STATUS_REJECT_NODELETE,
REF_STATUS_REJECT_FETCH_FIRST,
REF_STATUS_REJECT_NEEDS_FORCE,
REF_STATUS_REJECT_STALE,
REF_STATUS_UPTODATE,
REF_STATUS_REMOTE_REJECT,
REF_STATUS_EXPECTING_REPORT

View File

@ -227,6 +227,7 @@ int send_pack(struct send_pack_args *args,
case REF_STATUS_REJECT_ALREADY_EXISTS:
case REF_STATUS_REJECT_FETCH_FIRST:
case REF_STATUS_REJECT_NEEDS_FORCE:
case REF_STATUS_REJECT_STALE:
case REF_STATUS_UPTODATE:
continue;
default:

View File

@ -683,6 +683,11 @@ static int push_update_ref_status(struct strbuf *buf,
free(msg);
msg = NULL;
}
else if (!strcmp(msg, "stale info")) {
status = REF_STATUS_REJECT_STALE;
free(msg);
msg = NULL;
}
}
if (*ref)
@ -756,6 +761,7 @@ static int push_refs_with_push(struct transport *transport,
/* Check for statuses set by set_ref_status_for_push() */
switch (ref->status) {
case REF_STATUS_REJECT_NONFASTFORWARD:
case REF_STATUS_REJECT_STALE:
case REF_STATUS_REJECT_ALREADY_EXISTS:
case REF_STATUS_UPTODATE:
continue;

View File

@ -709,6 +709,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
"needs force", porcelain);
break;
case REF_STATUS_REJECT_STALE:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
"stale info", porcelain);
break;
case REF_STATUS_REMOTE_REJECT:
print_ref_status('!', "[remote rejected]", ref,
ref->deletion ? NULL : ref->peer_ref,
@ -1078,6 +1082,7 @@ static int run_pre_push_hook(struct transport *transport,
for (r = remote_refs; r; r = r->next) {
if (!r->peer_ref) continue;
if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue;
if (r->status == REF_STATUS_REJECT_STALE) continue;
if (r->status == REF_STATUS_UPTODATE) continue;
strbuf_reset(&buf);