{"thread":{"id":"37378","subject":"[PATCH 0/5] ref-transactions-send-pack","startedAt":"2014-08-19T16:24:46Z","lastAt":"2014-08-19T16:24:51Z","messageCount":6,"participants":["Ronnie Sahlberg"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"247919","messageId":"1408465491-25488-1-git-send-email-sahlberg@google.com","threadId":"37378","inReplyTo":null,"subject":"[PATCH 0/5] ref-transactions-send-pack","fromName":"Ronnie Sahlberg","fromEmail":"sahlberg@google.com","sentAt":"2014-08-19T16:24:46Z","receivedAt":"2014-08-19T16:24:46Z","isPatch":true,"sender":{"key":"sahlberg@google.com","avatar":"https://avatars.githubusercontent.com/u/7320636?v=4"},"body":"List,\n\nThis small patch series adds atomic-push support for pushes.\nBy default git will use the old style non-atomic updates for pushes,\nas not to cause disruption in client scripts that may depend on that\nbehaviour.\n\nCommand line arguments are introduced to allow the client side to request/\nnegotiate atomic pushes if the remote repo supports it.\nThere is also a new configuration variable where a repo can set that it\nwants all pushes to become atomic whether the client requests it or not.\n\nThis patch series is called ref-transactions-send-pack and depends on/is built\nontop of the series called ref-transactions-req-strbuf-err\n\nRonnie Sahlberg (5):\n  receive-pack.c: add protocol support to negotiate atomic-push\n  send-pack.c: add an --atomic-push command line argument\n  receive-pack.c: use a single transaction when atomic-push is\n    negotiated\n  receive-pack.c: add receive.atomicpush configuration option\n  push.c: add an --atomic-push argument\n\n Documentation/config.txt                          |  5 ++\n Documentation/git-push.txt                        |  7 ++-\n Documentation/git-send-pack.txt                   |  7 ++-\n Documentation/technical/protocol-capabilities.txt |  7 +++\n builtin/push.c                                    |  2 +\n builtin/receive-pack.c                            | 66 ++++++++++++++++++-----\n builtin/send-pack.c                               |  6 ++-\n send-pack.c                                       | 18 +++++--\n send-pack.h                                       |  1 +\n transport.c                                       |  1 +\n transport.h                                       |  1 +\n 11 files changed, 103 insertions(+), 18 deletions(-)\n\n-- \n2.0.1.556.ge8f7cba.dirty\n"},{"id":"247924","messageId":"1408465491-25488-2-git-send-email-sahlberg@google.com","threadId":"37378","inReplyTo":"1408465491-25488-1-git-send-email-sahlberg@google.com","subject":"[PATCH 1/5] receive-pack.c: add protocol support to negotiate atomic-push","fromName":"Ronnie Sahlberg","fromEmail":"sahlberg@google.com","sentAt":"2014-08-19T16:24:47Z","receivedAt":"2014-08-19T16:24:47Z","isPatch":true,"sender":{"key":"sahlberg@google.com","avatar":"https://avatars.githubusercontent.com/u/7320636?v=4"},"body":"Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>\n---\n builtin/receive-pack.c |  6 +++++-\n send-pack.c            | 12 +++++++++---\n 2 files changed, 14 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 0565b94..f6b20cb 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -36,6 +36,7 @@ static int transfer_unpack_limit = -1;\n static int unpack_limit = 100;\n static int report_status;\n static int use_sideband;\n+static int use_atomic_push;\n static int quiet;\n static int prefer_ofs_delta = 1;\n static int auto_update_server_info;\n@@ -142,7 +143,8 @@ static void show_ref(const char *path, const unsigned char *sha1)\n \telse\n \t\tpacket_write(1, \"%s %s%c%s%s agent=%s\\n\",\n \t\t\t     sha1_to_hex(sha1), path, 0,\n-\t\t\t     \" report-status delete-refs side-band-64k quiet\",\n+\t\t\t     \" report-status delete-refs side-band-64k quiet\"\n+\t\t\t     \" atomic-push\",\n \t\t\t     prefer_ofs_delta ? \" ofs-delta\" : \"\",\n \t\t\t     git_user_agent_sanitized());\n \tsent_capabilities = 1;\n@@ -892,6 +894,8 @@ static struct command *read_head_info(struct sha1_array *shallow)\n \t\t\t\tuse_sideband = LARGE_PACKET_MAX;\n \t\t\tif (parse_feature_request(feature_list, \"quiet\"))\n \t\t\t\tquiet = 1;\n+\t\t\tif (parse_feature_request(feature_list, \"atomic-push\"))\n+\t\t\t\tuse_atomic_push = 1;\n \t\t}\n \t\tcmd = xcalloc(1, sizeof(struct command) + len - 80);\n \t\thashcpy(cmd->old_sha1, old_sha1);\ndiff --git a/send-pack.c b/send-pack.c\nindex 6129b0f..f91b8d9 100644\n--- a/send-pack.c\n+++ b/send-pack.c\n@@ -205,6 +205,7 @@ int send_pack(struct send_pack_args *args,\n \tint use_sideband = 0;\n \tint quiet_supported = 0;\n \tint agent_supported = 0;\n+\tint atomic_push_supported = 0;\n \tunsigned cmds_sent = 0;\n \tint ret;\n \tstruct async demux;\n@@ -224,6 +225,8 @@ int send_pack(struct send_pack_args *args,\n \t\tagent_supported = 1;\n \tif (server_supports(\"no-thin\"))\n \t\targs->use_thin_pack = 0;\n+\tif (server_supports(\"atomic-push\"))\n+\t\tatomic_push_supported = 1;\n \n \tif (!remote_refs) {\n \t\tfprintf(stderr, \"No refs in common and none specified; doing nothing.\\n\"\n@@ -269,17 +272,20 @@ int send_pack(struct send_pack_args *args,\n \t\t\tchar *old_hex = sha1_to_hex(ref->old_sha1);\n \t\t\tchar *new_hex = sha1_to_hex(ref->new_sha1);\n \t\t\tint quiet = quiet_supported && (args->quiet || !args->progress);\n+\t\t\tint atomic_push = atomic_push_supported;\n \n \t\t\tif (!cmds_sent && (status_report || use_sideband ||\n-\t\t\t\t\t   quiet || agent_supported)) {\n+\t\t\t\t\t   quiet || agent_supported ||\n+\t\t\t\t\t   atomic_push)) {\n \t\t\t\tpacket_buf_write(&req_buf,\n-\t\t\t\t\t\t \"%s %s %s%c%s%s%s%s%s\",\n+\t\t\t\t\t\t \"%s %s %s%c%s%s%s%s%s%s\",\n \t\t\t\t\t\t old_hex, new_hex, ref->name, 0,\n \t\t\t\t\t\t status_report ? \" report-status\" : \"\",\n \t\t\t\t\t\t use_sideband ? \" side-band-64k\" : \"\",\n \t\t\t\t\t\t quiet ? \" quiet\" : \"\",\n \t\t\t\t\t\t agent_supported ? \" agent=\" : \"\",\n-\t\t\t\t\t\t agent_supported ? git_user_agent_sanitized() : \"\"\n+\t\t\t\t\t\t agent_supported ? git_user_agent_sanitized() : \"\",\n+\t\t\t\t\t\t atomic_push ? \" atomic-push\" : \"\"\n \t\t\t\t\t\t);\n \t\t\t}\n \t\t\telse\n-- \n2.0.1.556.ge8f7cba.dirty\n"},{"id":"247922","messageId":"1408465491-25488-3-git-send-email-sahlberg@google.com","threadId":"37378","inReplyTo":"1408465491-25488-1-git-send-email-sahlberg@google.com","subject":"[PATCH 2/5] send-pack.c: add an --atomic-push command line argument","fromName":"Ronnie Sahlberg","fromEmail":"sahlberg@google.com","sentAt":"2014-08-19T16:24:48Z","receivedAt":"2014-08-19T16:24:48Z","isPatch":true,"sender":{"key":"sahlberg@google.com","avatar":"https://avatars.githubusercontent.com/u/7320636?v=4"},"body":"Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>\n---\n Documentation/git-send-pack.txt | 7 ++++++-\n builtin/send-pack.c             | 6 +++++-\n send-pack.c                     | 8 +++++++-\n send-pack.h                     | 1 +\n 4 files changed, 19 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt\nindex dc3a568..4ee2ca1 100644\n--- a/Documentation/git-send-pack.txt\n+++ b/Documentation/git-send-pack.txt\n@@ -9,7 +9,7 @@ git-send-pack - Push objects over Git protocol to another repository\n SYNOPSIS\n --------\n [verse]\n-'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n+'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic-push] [<host>:]<directory> [<ref>...]\n \n DESCRIPTION\n -----------\n@@ -52,6 +52,11 @@ OPTIONS\n \tSend a \"thin\" pack, which records objects in deltified form based\n \ton objects not included in the pack to reduce network traffic.\n \n+--atomic-push::\n+\tWith atomic-push all refs are updated in one single atomic transaction.\n+\tThis means that if any of the refs fails then the entire push will\n+\tfail without changing any refs.\n+\n <host>::\n \tA remote host to house the repository.  When this\n \tpart is specified, 'git-receive-pack' is invoked via\ndiff --git a/builtin/send-pack.c b/builtin/send-pack.c\nindex f420b74..78e7d8f 100644\n--- a/builtin/send-pack.c\n+++ b/builtin/send-pack.c\n@@ -13,7 +13,7 @@\n #include \"sha1-array.h\"\n \n static const char send_pack_usage[] =\n-\"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\\n\"\n+\"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic-push] [<host>:]<directory> [<ref>...]\\n\"\n \"  --all and explicit <ref> specification are mutually exclusive.\";\n \n static struct send_pack_args args;\n@@ -165,6 +165,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \t\t\t\targs.use_thin_pack = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!strcmp(arg, \"--atomic-push\")) {\n+\t\t\t\targs.use_atomic_push = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!strcmp(arg, \"--stateless-rpc\")) {\n \t\t\t\targs.stateless_rpc = 1;\n \t\t\t\tcontinue;\ndiff --git a/send-pack.c b/send-pack.c\nindex f91b8d9..66f3724 100644\n--- a/send-pack.c\n+++ b/send-pack.c\n@@ -228,6 +228,11 @@ int send_pack(struct send_pack_args *args,\n \tif (server_supports(\"atomic-push\"))\n \t\tatomic_push_supported = 1;\n \n+\tif (args->use_atomic_push && !atomic_push_supported) {\n+\t\tfprintf(stderr, \"Server does not support atomic-push.\");\n+\t\treturn -1;\n+\t}\n+\n \tif (!remote_refs) {\n \t\tfprintf(stderr, \"No refs in common and none specified; doing nothing.\\n\"\n \t\t\t\"Perhaps you should specify a branch such as 'master'.\\n\");\n@@ -272,7 +277,8 @@ int send_pack(struct send_pack_args *args,\n \t\t\tchar *old_hex = sha1_to_hex(ref->old_sha1);\n \t\t\tchar *new_hex = sha1_to_hex(ref->new_sha1);\n \t\t\tint quiet = quiet_supported && (args->quiet || !args->progress);\n-\t\t\tint atomic_push = atomic_push_supported;\n+\t\t\tint atomic_push = atomic_push_supported &&\n+\t\t\t\targs->use_atomic_push;\n \n \t\t\tif (!cmds_sent && (status_report || use_sideband ||\n \t\t\t\t\t   quiet || agent_supported ||\ndiff --git a/send-pack.h b/send-pack.h\nindex 8e84392..0374ed8 100644\n--- a/send-pack.h\n+++ b/send-pack.h\n@@ -10,6 +10,7 @@ struct send_pack_args {\n \t\tforce_update:1,\n \t\tuse_thin_pack:1,\n \t\tuse_ofs_delta:1,\n+\t\tuse_atomic_push:1,\n \t\tdry_run:1,\n \t\tstateless_rpc:1;\n };\n-- \n2.0.1.556.ge8f7cba.dirty\n"},{"id":"247921","messageId":"1408465491-25488-4-git-send-email-sahlberg@google.com","threadId":"37378","inReplyTo":"1408465491-25488-1-git-send-email-sahlberg@google.com","subject":"[PATCH 3/5] receive-pack.c: use a single transaction when atomic-push is negotiated","fromName":"Ronnie Sahlberg","fromEmail":"sahlberg@google.com","sentAt":"2014-08-19T16:24:49Z","receivedAt":"2014-08-19T16:24:49Z","isPatch":true,"sender":{"key":"sahlberg@google.com","avatar":"https://avatars.githubusercontent.com/u/7320636?v=4"},"body":"Update receive-pack to use an atomic transaction IFF the client negotiated\nthat it wanted atomic-push.\nThis leaves the default behaviour to be the old non-atomic one ref at a\ntime update. This is to cause as little disruption as possible to existing\nclients. It is unknown if there are client scripts that depend on the old\nnon-atomic behaviour so we make it opt-in for now.\n\nLater patch in this series also adds a configuration variable where you can\noverride the atomic push behaviour on the receiving repo and force it\nto use atomic updates always.\n\nIf it turns out over time that there are no client scripts that depend on the\nold behaviour we can change git to default to use atomic pushes and instead\noffer an opt-out argument for people that do not want atomic pushes.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\n---\n Documentation/technical/protocol-capabilities.txt |  7 +++\n builtin/receive-pack.c                            | 55 ++++++++++++++++++-----\n 2 files changed, 51 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt\nindex e174343..93d99c5 100644\n--- a/Documentation/technical/protocol-capabilities.txt\n+++ b/Documentation/technical/protocol-capabilities.txt\n@@ -250,3 +250,10 @@ allow-tip-sha1-in-want\n If the upload-pack server advertises this capability, fetch-pack may\n send \"want\" lines with SHA-1s that exist at the server but are not\n advertised by upload-pack.\n+\n+atomic-push\n+-----------\n+\n+If the receive-pack server advertises the 'atomic-push' capability, it means\n+that it is capable of accepting atomic pushes. An atomic push is a push\n+where the ref updates are done as a single atomic transaction.\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex f6b20cb..47f778d 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -47,6 +47,8 @@ static void *head_name_to_free;\n static int sent_capabilities;\n static int shallow_update;\n static const char *alt_shallow_file;\n+struct strbuf err = STRBUF_INIT;\n+struct ref_transaction *transaction;\n \n static enum deny_action parse_deny_action(const char *var, const char *value)\n {\n@@ -577,26 +579,38 @@ static char *update(struct command *cmd, struct shallow_info *si)\n \t\treturn NULL; /* good */\n \t}\n \telse {\n-\t\tstruct strbuf err = STRBUF_INIT;\n-\t\tstruct ref_transaction *transaction;\n-\n \t\tif (shallow_update && si->shallow_ref[cmd->index] &&\n \t\t    update_shallow_ref(cmd, si))\n \t\t\treturn xstrdup(\"shallow error\");\n-\n-\t\ttransaction = transaction_begin(&err);\n-\t\tif (!transaction ||\n-\t\t    transaction_update_sha1(transaction, namespaced_name,\n+\t\tif (!use_atomic_push) {\n+\t\t\ttransaction = transaction_begin(&err);\n+\t\t\tif (!transaction) {\n+\t\t\t\tchar *str = xstrdup(err.buf);\n+\n+\t\t\t\tstrbuf_release(&err);\n+\t\t\t\ttransaction_free(transaction);\n+\t\t\t\trp_error(\"%s\", str);\n+\t\t\t\treturn str;\n+\t\t\t}\n+\t\t}\n+\t\tif (transaction_update_sha1(transaction, namespaced_name,\n \t\t\t\t\t    new_sha1, old_sha1, 0, 1, \"push\",\n-\t\t\t\t\t    &err) ||\n-\t\t    transaction_commit(transaction, &err)) {\n-\t\t\tchar *str = strbuf_detach(&err, NULL);\n-\t\t\ttransaction_free(transaction);\n+\t\t\t\t\t    &err)) {\n+\t\t\tchar *str = xstrdup(err.buf);\n \n+\t\t\tstrbuf_release(&err);\n+\t\t\ttransaction_free(transaction);\n \t\t\trp_error(\"%s\", str);\n \t\t\treturn str;\n \t\t}\n+\t\tif (!use_atomic_push && transaction_commit(transaction, &err)) {\n+\t\t\tchar *str = xstrdup(err.buf);\n \n+\t\t\tstrbuf_release(&err);\n+\t\t\ttransaction_free(transaction);\n+\t\t\trp_error(\"%s\", str);\n+\t\t\treturn str;\n+\t\t}\n \t\ttransaction_free(transaction);\n \t\tstrbuf_release(&err);\n \t\treturn NULL; /* good */\n@@ -810,6 +824,16 @@ static void execute_commands(struct command *commands,\n \t\treturn;\n \t}\n \n+\tif (use_atomic_push) {\n+\t\ttransaction = transaction_begin(&err);\n+\t\tif (!transaction) {\n+\t\t\terror(\"%s\", err.buf);\n+\t\t\tstrbuf_release(&err);\n+\t\t\tfor (cmd = commands; cmd; cmd = cmd->next)\n+\t\t\t\tcmd->error_string = \"transaction error\";\n+\t\t\treturn;\n+\t\t}\n+\t}\n \tdata.cmds = commands;\n \tdata.si = si;\n \tif (check_everything_connected(iterate_receive_command_list, 0, &data))\n@@ -848,6 +872,14 @@ static void execute_commands(struct command *commands,\n \t\t}\n \t}\n \n+\tif (use_atomic_push) {\n+\t\tif (transaction_commit(transaction, &err)) {\n+\t\t\trp_error(\"%s\", err.buf);\n+\t\t\tfor (cmd = commands; cmd; cmd = cmd->next)\n+\t\t\t\tcmd->error_string = err.buf;\n+\t\t}\n+\t\ttransaction_free(transaction);\n+\t}\n \tif (shallow_update && !checked_connectivity)\n \t\terror(\"BUG: run 'git fsck' for safety.\\n\"\n \t\t      \"If there are errors, try to remove \"\n@@ -1250,5 +1282,6 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)\n \tsha1_array_clear(&shallow);\n \tsha1_array_clear(&ref);\n \tfree_commands(commands);\n+\tstrbuf_release(&err);\n \treturn 0;\n }\n-- \n2.0.1.556.ge8f7cba.dirty\n"},{"id":"247923","messageId":"1408465491-25488-5-git-send-email-sahlberg@google.com","threadId":"37378","inReplyTo":"1408465491-25488-1-git-send-email-sahlberg@google.com","subject":"[PATCH 4/5] receive-pack.c: add receive.atomicpush configuration option","fromName":"Ronnie Sahlberg","fromEmail":"sahlberg@google.com","sentAt":"2014-08-19T16:24:50Z","receivedAt":"2014-08-19T16:24:50Z","isPatch":true,"sender":{"key":"sahlberg@google.com","avatar":"https://avatars.githubusercontent.com/u/7320636?v=4"},"body":"Add a configuration argument to the receive side to force atomic pushes\nfor all pushes to the repo.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\n---\n Documentation/config.txt | 5 +++++\n builtin/receive-pack.c   | 5 +++++\n 2 files changed, 10 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 1d718bd..75ce157 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2038,6 +2038,11 @@ rebase.autostash::\n \tsuccessful rebase might result in non-trivial conflicts.\n \tDefaults to false.\n \n+receive.atomicpush::\n+\tBy default, git-receive-pack will only use atomic ref transactions\n+\tif the client negotiates it. When set to true, git-receive-pack\n+\twill force atomic ref updates for all client pushes.\n+\n receive.autogc::\n \tBy default, git-receive-pack will run \"git-gc --auto\" after\n \treceiving data from git-push and updating refs.  You can stop\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 47f778d..9fa637a 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -132,6 +132,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (strcmp(var, \"receive.atomicpush\") == 0) {\n+\t\tuse_atomic_push = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\n \treturn git_default_config(var, value, cb);\n }\n \n-- \n2.0.1.556.ge8f7cba.dirty\n"},{"id":"247920","messageId":"1408465491-25488-6-git-send-email-sahlberg@google.com","threadId":"37378","inReplyTo":"1408465491-25488-1-git-send-email-sahlberg@google.com","subject":"[PATCH 5/5] push.c: add an --atomic-push argument","fromName":"Ronnie Sahlberg","fromEmail":"sahlberg@google.com","sentAt":"2014-08-19T16:24:51Z","receivedAt":"2014-08-19T16:24:51Z","isPatch":true,"sender":{"key":"sahlberg@google.com","avatar":"https://avatars.githubusercontent.com/u/7320636?v=4"},"body":"Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>\n---\n Documentation/git-push.txt | 7 ++++++-\n builtin/push.c             | 2 ++\n transport.c                | 1 +\n transport.h                | 1 +\n 4 files changed, 10 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 21cd455..b80b0ac 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -9,7 +9,7 @@ git-push - Update remote refs along with associated objects\n SYNOPSIS\n --------\n [verse]\n-'git push' [--all | --mirror | --tags] [--follow-tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]\n+'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic-push] [-n | --dry-run] [--receive-pack=<git-receive-pack>]\n \t   [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream]\n \t   [--force-with-lease[=<refname>[:<expect>]]]\n \t   [--no-verify] [<repository> [<refspec>...]]\n@@ -129,6 +129,11 @@ already exists on the remote side.\n \tfrom the remote but are pointing at commit-ish that are\n \treachable from the refs being pushed.\n \n+--atomic-push::\n+\tTry using atomic push. If atomic push is negotiated with the server\n+\tthen any push covering multiple refs will be atomic. Either all\n+\trefs are updated, or on error, no refs are updated.\n+\n --receive-pack=<git-receive-pack>::\n --exec=<git-receive-pack>::\n \tPath to the 'git-receive-pack' program on the remote\ndiff --git a/builtin/push.c b/builtin/push.c\nindex f8dfea4..f37390c 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -507,6 +507,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tOPT_BIT(0, \"no-verify\", &flags, N_(\"bypass pre-push hook\"), TRANSPORT_PUSH_NO_HOOK),\n \t\tOPT_BIT(0, \"follow-tags\", &flags, N_(\"push missing but relevant tags\"),\n \t\t\tTRANSPORT_PUSH_FOLLOW_TAGS),\n+\t\tOPT_BIT(0, \"atomic-push\", &flags, N_(\"use atomic push, if available\"),\n+\t\t\tTRANSPORT_ATOMIC_PUSH),\n \t\tOPT_END()\n \t};\n \ndiff --git a/transport.c b/transport.c\nindex a3b7f48..ab5f553 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -837,6 +837,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re\n \targs.progress = transport->progress;\n \targs.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);\n \targs.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);\n+\targs.use_atomic_push = !!(flags & TRANSPORT_ATOMIC_PUSH);\n \n \tret = send_pack(&args, data->fd, data->conn, remote_refs,\n \t\t\t&data->extra_have);\ndiff --git a/transport.h b/transport.h\nindex 02ea248..407d641 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -123,6 +123,7 @@ struct transport {\n #define TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND 256\n #define TRANSPORT_PUSH_NO_HOOK 512\n #define TRANSPORT_PUSH_FOLLOW_TAGS 1024\n+#define TRANSPORT_ATOMIC_PUSH 2048\n \n #define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)\n #define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)\n-- \n2.0.1.556.ge8f7cba.dirty\n"}]}