This teaches the deepest part of the callchain for "git push" (and "git send-pack") to optionally allow "the old value of the ref must be this, otherwise fail this push" we discussed earlier. Nobody sets the new "expect_old_sha1" and "expect_old_no_trackback" bitfields yet, so this is still a no-op. Signed-off-by: Junio C Hamano <gitster@xxxxxxxxx> --- builtin/send-pack.c | 5 +++++ remote.c | 21 +++++++++++++++++++-- remote.h | 4 ++++ send-pack.c | 1 + transport-helper.c | 6 ++++++ transport.c | 5 +++++ 6 files changed, 40 insertions(+), 2 deletions(-) diff --git a/builtin/send-pack.c b/builtin/send-pack.c index e86d3b5..c86c556 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -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"; diff --git a/remote.c b/remote.c index b1ff7a2..81bc876 100644 --- a/remote.c +++ b/remote.c @@ -1416,13 +1416,30 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, } /* + * If we know what the old value of the remote ref + * should be, reject any push, even forced ones, + * if they do not match. + * + * 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 && + (ref->expect_old_no_trackback || + hashcmp(ref->old_sha1, ref->old_sha1_expect))) { + ref->status = REF_STATUS_REJECT_STALE; + continue; + } + + /* * Decide whether an individual refspec A:B can be * pushed. The push will succeed if any of the * following are true: * - * (1) the remote reference B does not exist + * (1) the remote reference B does not exist (i.e. create) * - * (2) the remote reference B is being removed (i.e., + * (2) the remote reference B is being removed (i.e. delete; * pushing :B where no source is specified) * * (3) the destination is not under refs/tags/, and diff --git a/remote.h b/remote.h index a850059..7ad37e6 100644 --- a/remote.h +++ b/remote.h @@ -75,10 +75,13 @@ struct ref { struct ref *next; unsigned char old_sha1[20]; unsigned char new_sha1[20]; + unsigned char old_sha1_expect[20]; /* used by expect-old */ char *symref; unsigned int force:1, forced_update:1, + expect_old_sha1:1, + expect_old_no_trackback:1, deletion:1, matched:1; @@ -102,6 +105,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 diff --git a/send-pack.c b/send-pack.c index 9a9908c..b228d65 100644 --- a/send-pack.c +++ b/send-pack.c @@ -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: diff --git a/transport-helper.c b/transport-helper.c index db9bd18..95d22f8 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -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; diff --git a/transport.c b/transport.c index b84dbf0..98f5270 100644 --- a/transport.c +++ b/transport.c @@ -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); -- 1.8.3.2-875-g76c723c -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html