On Mon, Nov 3, 2014 at 11:12 AM, Ronnie Sahlberg <sahlb...@google.com> 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 <sahlb...@google.com>
> ---
>  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 majord...@vger.kernel.org
> 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to