зеркало из https://github.com/microsoft/git.git
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:
Родитель
91048a9537
Коммит
631b5ef219
|
@ -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";
|
||||
|
|
49
remote.c
49
remote.c
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
1
remote.h
1
remote.h
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче