On Mon, Nov 3, 2014 at 11:12 AM, Ronnie Sahlberg <sahlberg@xxxxxxxxxx> wrote: > This adds support to send-pack to to negotiate and use atomic pushes /s/to to/to/ > iff the server supports it. Atomic pushes are activated by a new command > line flag --atomic-push. > > In order to do this we also need to change the semantics for send_pack() > slightly. The existing send_pack() function actually don't sent all the > refs back to the server when multiple refs are involved, for example > when using --all. Several of the failure modes for pushes can already be > detected locally in the send_pack client based on the information from the > initial server side list of all the refs as generated by receive-pack. > Any such refs that we thus know would fail to push are thus pruned from > the list of refs we send to the server to update. > > For atomic pushes, we have to deal thus with both failures that are detected > locally as well as failures that are reported back from the server. In order > to do so we treat all local failures as push failures too. > > We introduce a new status code REF_STATUS_ATOMIC_PUSH_FAILED so we can > flag all refs that we would normally have tried to push to the server > but we did not due to local failures. This is to improve the error message > back to the end user to flag that "these refs failed to update since the > atomic push operation failed." > > Signed-off-by: Ronnie Sahlberg <sahlberg@xxxxxxxxxx> > --- > Documentation/git-send-pack.txt | 7 ++++++- > builtin/send-pack.c | 6 +++++- > remote.h | 3 ++- > send-pack.c | 39 ++++++++++++++++++++++++++++++++++----- > send-pack.h | 1 + > transport.c | 4 ++++ > 6 files changed, 52 insertions(+), 8 deletions(-) > > diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt > index 2a0de42..9296587 100644 > --- a/Documentation/git-send-pack.txt > +++ b/Documentation/git-send-pack.txt > @@ -9,7 +9,7 @@ git-send-pack - Push objects over Git protocol to another repository > SYNOPSIS > -------- > [verse] > -'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...] > +'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic-push] [<host>:]<directory> [<ref>...] > > DESCRIPTION > ----------- > @@ -62,6 +62,11 @@ be in a separate packet, and the list must end with a flush packet. > Send a "thin" pack, which records objects in deltified form based > on objects not included in the pack to reduce network traffic. > > +--atomic-push:: > + Use an atomic transaction for updating the refs. If any of the refs > + fails to update then the entire push will fail without changing any > + refs. > + > <host>:: > A remote host to house the repository. When this > part is specified, 'git-receive-pack' is invoked via > diff --git a/builtin/send-pack.c b/builtin/send-pack.c > index b564a77..93cb17c 100644 > --- a/builtin/send-pack.c > +++ b/builtin/send-pack.c > @@ -13,7 +13,7 @@ > #include "sha1-array.h" > > static const char send_pack_usage[] = > -"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n" > +"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic-push] [<host>:]<directory> [<ref>...]\n" > " --all and explicit <ref> specification are mutually exclusive."; > > static struct send_pack_args args; > @@ -170,6 +170,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) > args.use_thin_pack = 1; > continue; > } > + if (!strcmp(arg, "--atomic-push")) { > + args.use_atomic_push = 1; > + continue; > + } > if (!strcmp(arg, "--stateless-rpc")) { > args.stateless_rpc = 1; > continue; > diff --git a/remote.h b/remote.h > index 8b62efd..f346524 100644 > --- a/remote.h > +++ b/remote.h > @@ -115,7 +115,8 @@ struct ref { > REF_STATUS_REJECT_SHALLOW, > REF_STATUS_UPTODATE, > REF_STATUS_REMOTE_REJECT, > - REF_STATUS_EXPECTING_REPORT > + REF_STATUS_EXPECTING_REPORT, > + REF_STATUS_ATOMIC_PUSH_FAILED > } status; > char *remote_status; > struct ref *peer_ref; /* when renaming */ > diff --git a/send-pack.c b/send-pack.c > index 1ccc84c..08602a8 100644 > --- a/send-pack.c > +++ b/send-pack.c > @@ -190,7 +190,7 @@ 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) > +static int ref_update_to_be_sent(const struct ref *ref, const struct send_pack_args *args, int *atomic_push_failed) > { > if (!ref->peer_ref && !args->send_mirror) > return 0; > @@ -203,6 +203,12 @@ 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: > + if (atomic_push_failed) { > + fprintf(stderr, "Atomic push failed for ref %s. " > + "Status:%d\n", ref->name, ref->status); > + *atomic_push_failed = 1; > + } > + /* fallthrough */ > case REF_STATUS_UPTODATE: > return 0; > default: > @@ -250,7 +256,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 (!ref_update_to_be_sent(ref, args, NULL)) > continue; > update_seen = 1; > strbuf_addf(&cert, "%s %s %s\n", > @@ -297,7 +303,7 @@ int send_pack(struct send_pack_args *args, > int atomic_push_supported = 0; > int atomic_push = 0; > unsigned cmds_sent = 0; > - int ret; > + int ret, atomic_push_failed = 0; > struct async demux; > const char *push_cert_nonce = NULL; > > @@ -332,6 +338,11 @@ int send_pack(struct send_pack_args *args, > "Perhaps you should specify a branch such as 'master'.\n"); > return 0; > } > + if (args->use_atomic_push && !atomic_push_supported) { > + fprintf(stderr, "Server does not support atomic-push."); > + return -1; > + } > + atomic_push = atomic_push_supported && args->use_atomic_push; > > if (status_report) > strbuf_addstr(&cap_buf, " report-status"); > @@ -365,7 +376,8 @@ 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)) > + if (!ref_update_to_be_sent(ref, args, > + args->use_atomic_push ? &atomic_push_failed : NULL)) > continue; > > if (!ref->deletion) > @@ -377,6 +389,23 @@ int send_pack(struct send_pack_args *args, > ref->status = REF_STATUS_EXPECTING_REPORT; > } > > + if (atomic_push_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: > + ; /* do nothing */ > + } > + } > + fprintf(stderr, "Atomic push failed."); > + return -1; > + } > + > /* > * Finally, tell the other end! > */ > @@ -386,7 +415,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 (!ref_update_to_be_sent(ref, args, NULL)) > continue; > > old_hex = sha1_to_hex(ref->old_sha1); > diff --git a/send-pack.h b/send-pack.h > index 5635457..7486e65 100644 > --- a/send-pack.h > +++ b/send-pack.h > @@ -11,6 +11,7 @@ struct send_pack_args { > force_update:1, > use_thin_pack:1, > use_ofs_delta:1, > + use_atomic_push:1, > dry_run:1, > push_cert:1, > stateless_rpc:1; > diff --git a/transport.c b/transport.c > index f70d62f..2111986 100644 > --- a/transport.c > +++ b/transport.c > @@ -731,6 +731,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i > ref->deletion ? NULL : ref->peer_ref, > "remote failed to report status", porcelain); > break; > + case REF_STATUS_ATOMIC_PUSH_FAILED: > + print_ref_status('!', "[rejected]", ref, ref->peer_ref, > + "atomic-push-failed", porcelain); > + break; > case REF_STATUS_OK: > print_ok_ref_status(ref, porcelain); > break; > -- > 2.1.0.rc2.206.gedb03e5 > > -- > 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 -- 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