diff options
author | Junio C Hamano <gitster@pobox.com> | 2015-02-11 13:43:50 -0800 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2015-02-11 13:43:51 -0800 |
commit | 39fa6112ec8492f9300765e1f71e7c8ecfafc713 (patch) | |
tree | aaf52d72ae2010c4a99d0f0c3ccd1c3f40cdcb91 /send-pack.c | |
parent | 4d5c4e498a14af732f3b0b690f8b551d18610bfc (diff) | |
parent | 04b39f195baf95b79c9c28a096011e9fe0a08303 (diff) | |
download | git-39fa6112ec8492f9300765e1f71e7c8ecfafc713.tar.gz |
Merge branch 'sb/atomic-push'
"git push" has been taught a "--atomic" option that makes push to
update more than one ref an "all-or-none" affair.
* sb/atomic-push:
Document receive.advertiseatomic
t5543-atomic-push.sh: add basic tests for atomic pushes
push.c: add an --atomic argument
send-pack.c: add --atomic command line argument
send-pack: rename ref_update_to_be_sent to check_to_send_update
receive-pack.c: negotiate atomic push support
receive-pack.c: add execute_commands_atomic function
receive-pack.c: move transaction handling in a central place
receive-pack.c: move iterating over all commands outside execute_commands
receive-pack.c: die instead of error in case of possible future bug
receive-pack.c: shorten the execute_commands loop over all commands
Diffstat (limited to 'send-pack.c')
-rw-r--r-- | send-pack.c | 65 |
1 files changed, 57 insertions, 8 deletions
diff --git a/send-pack.c b/send-pack.c index 25947d7df9..9d2b0c52ed 100644 --- a/send-pack.c +++ b/send-pack.c @@ -193,10 +193,13 @@ static void advertise_shallow_grafts_buf(struct strbuf *sb) for_each_commit_graft(advertise_shallow_grafts_cb, sb); } -static int ref_update_to_be_sent(const struct ref *ref, const struct send_pack_args *args) +#define CHECK_REF_NO_PUSH -1 +#define CHECK_REF_STATUS_REJECTED -2 +#define CHECK_REF_UPTODATE -3 +static int check_to_send_update(const struct ref *ref, const struct send_pack_args *args) { if (!ref->peer_ref && !args->send_mirror) - return 0; + return CHECK_REF_NO_PUSH; /* Check for statuses set by set_ref_status_for_push() */ switch (ref->status) { @@ -206,10 +209,11 @@ static int ref_update_to_be_sent(const struct ref *ref, const struct send_pack_a case REF_STATUS_REJECT_NEEDS_FORCE: case REF_STATUS_REJECT_STALE: case REF_STATUS_REJECT_NODELETE: + return CHECK_REF_STATUS_REJECTED; case REF_STATUS_UPTODATE: - return 0; + return CHECK_REF_UPTODATE; default: - return 1; + return 0; } } @@ -253,7 +257,7 @@ static int generate_push_cert(struct strbuf *req_buf, strbuf_addstr(&cert, "\n"); for (ref = remote_refs; ref; ref = ref->next) { - if (!ref_update_to_be_sent(ref, args)) + if (check_to_send_update(ref, args) < 0) continue; update_seen = 1; strbuf_addf(&cert, "%s %s %s\n", @@ -281,6 +285,29 @@ free_return: return update_seen; } + +static int atomic_push_failure(struct send_pack_args *args, + struct ref *remote_refs, + struct ref *failing_ref) +{ + struct ref *ref; + /* Mark other refs as failed */ + for (ref = remote_refs; ref; ref = ref->next) { + if (!ref->peer_ref && !args->send_mirror) + continue; + + switch (ref->status) { + case REF_STATUS_EXPECTING_REPORT: + ref->status = REF_STATUS_ATOMIC_PUSH_FAILED; + continue; + default: + break; /* do nothing */ + } + } + return error("atomic push failed for ref %s. status: %d\n", + failing_ref->name, failing_ref->status); +} + int send_pack(struct send_pack_args *args, int fd[], struct child_process *conn, struct ref *remote_refs, @@ -297,6 +324,8 @@ int send_pack(struct send_pack_args *args, int use_sideband = 0; int quiet_supported = 0; int agent_supported = 0; + int use_atomic = 0; + int atomic_supported = 0; unsigned cmds_sent = 0; int ret; struct async demux; @@ -317,6 +346,8 @@ int send_pack(struct send_pack_args *args, agent_supported = 1; if (server_supports("no-thin")) args->use_thin_pack = 0; + if (server_supports("atomic")) + atomic_supported = 1; if (args->push_cert) { int len; @@ -331,6 +362,10 @@ int send_pack(struct send_pack_args *args, "Perhaps you should specify a branch such as 'master'.\n"); return 0; } + if (args->atomic && !atomic_supported) + die(_("server does not support --atomic push")); + + use_atomic = atomic_supported && args->atomic; if (status_report) strbuf_addstr(&cap_buf, " report-status"); @@ -338,6 +373,8 @@ int send_pack(struct send_pack_args *args, strbuf_addstr(&cap_buf, " side-band-64k"); if (quiet_supported && (args->quiet || !args->progress)) strbuf_addstr(&cap_buf, " quiet"); + if (use_atomic) + strbuf_addstr(&cap_buf, " atomic"); if (agent_supported) strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized()); @@ -362,9 +399,21 @@ int send_pack(struct send_pack_args *args, * the pack data. */ for (ref = remote_refs; ref; ref = ref->next) { - if (!ref_update_to_be_sent(ref, args)) + switch (check_to_send_update(ref, args)) { + case 0: /* no error */ + break; + case CHECK_REF_STATUS_REJECTED: + /* + * When we know the server would reject a ref update if + * we were to send it and we're trying to send the refs + * atomically, abort the whole operation. + */ + if (use_atomic) + return atomic_push_failure(args, remote_refs, ref); + /* Fallthrough for non atomic case. */ + default: continue; - + } if (!ref->deletion) need_pack_data = 1; @@ -383,7 +432,7 @@ int send_pack(struct send_pack_args *args, if (args->dry_run || args->push_cert) continue; - if (!ref_update_to_be_sent(ref, args)) + if (check_to_send_update(ref, args) < 0) continue; old_hex = sha1_to_hex(ref->old_sha1); |