From: Junio C Hamano Date: Mon, 9 Feb 2015 22:37:17 +0000 (-0800) Subject: Merge branch 'sb/atomic-push' into mh/ref-trans-value-check X-Git-Tag: v2.4.0-rc0~51^2~7 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/507d6aa5bf2fb9f56d0c7f70d4ff3b8177da9d8e?ds=inline;hp=-c Merge branch 'sb/atomic-push' into mh/ref-trans-value-check * 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 --- 507d6aa5bf2fb9f56d0c7f70d4ff3b8177da9d8e diff --combined Documentation/config.txt index 04e2a71687,4f8f4989fe..1a54eae8f8 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@@ -246,17 -246,6 +246,17 @@@ core.precomposeunicode: When false, file names are handled fully transparent by Git, which is backward compatible with older versions of Git. +core.protectHFS:: + If set to true, do not allow checkout of paths that would + be considered equivalent to `.git` on an HFS+ filesystem. + Defaults to `true` on Mac OS, and `false` elsewhere. + +core.protectNTFS:: + If set to true, do not allow checkout of paths that would + cause problems with the NTFS filesystem, e.g. conflict with + 8.3 "short" names. + Defaults to `true` on Windows, and `false` elsewhere. + core.trustctime:: If false, the ctime differences between the index and the working tree are ignored; useful when the inode change time @@@ -375,19 -364,14 +375,19 @@@ This is useful for excluding servers in proxy use, while defaulting to a common proxy for external domains. core.ignoreStat:: - If true, commands which modify both the working tree and the index - will mark the updated paths with the "assume unchanged" bit in the - index. These marked files are then assumed to stay unchanged in the - working tree, until you mark them otherwise manually - Git will not - detect the file changes by lstat() calls. This is useful on systems - where those are very slow, such as Microsoft Windows. - See linkgit:git-update-index[1]. - False by default. + If true, Git will avoid using lstat() calls to detect if files have + changed by setting the "assume-unchanged" bit for those tracked files + which it has updated identically in both the index and working tree. ++ +When files are modified outside of Git, the user will need to stage +the modified files explicitly (e.g. see 'Examples' section in +linkgit:git-update-index[1]). +Git will not normally detect changes to those files. ++ +This is useful on systems where lstat() calls are very slow, such as +CIFS/Microsoft Windows. ++ +False by default. core.preferSymlinkRefs:: Instead of the default "symref" format for HEAD @@@ -854,13 -838,7 +854,13 @@@ accepted are `normal`, `black`, `red`, `magenta`, `cyan` and `white`; the attributes are `bold`, `dim`, `ul`, `blink` and `reverse`. The first color given is the foreground; the second is the background. The position of the attribute, if any, -doesn't matter. +doesn't matter. Attributes may be turned off specifically by prefixing +them with `no` (e.g., `noreverse`, `noul`, etc). ++ +Colors (foreground and background) may also be given as numbers between +0 and 255; these use ANSI 256-color mode (but note that not all +terminals may support this). If your terminal supports it, you may also +specify 24-bit RGB values as hex, like `#ff0ab3`. color.diff:: Whether to use ANSI escape sequences to add color to patches. @@@ -2094,6 -2072,11 +2094,11 @@@ rebase.autostash: successful rebase might result in non-trivial conflicts. Defaults to false. + receive.advertiseatomic:: + By default, git-receive-pack will advertise the atomic push + capability to its clients. If you don't want to this capability + to be advertised, set this variable to false. + receive.autogc:: By default, git-receive-pack will run "git-gc --auto" after receiving data from git-push and updating refs. You can stop @@@ -2151,13 -2134,6 +2156,13 @@@ receive.denyCurrentBranch: print a warning of such a push to stderr, but allow the push to proceed. If set to false or "ignore", allow such pushes with no message. Defaults to "refuse". ++ +Another option is "updateInstead" which will update the working +directory (must be clean) if pushing into the current branch. This option is +intended for synchronizing working directories when one side is not easily +accessible via interactive ssh (e.g. a live web site, hence the requirement +that the working directory be clean). This mode also comes in handy when +developing inside a VM to test and fix code on different Operating Systems. receive.denyNonFastForwards:: If set to true, git-receive-pack will deny a ref update which is @@@ -2332,9 -2308,7 +2337,9 @@@ sendemail.smtpserverport: sendemail.smtpserveroption:: sendemail.smtpuser:: sendemail.thread:: +sendemail.transferencoding:: sendemail.validate:: +sendemail.xmailer:: See linkgit:git-send-email[1] for description. sendemail.signedoffcc:: diff --combined Documentation/git-push.txt index b17283ab7a,4764fcfa75..ea9757692a --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@@ -9,7 -9,7 +9,7 @@@ git-push - Update remote refs along wit SYNOPSIS -------- [verse] - 'git push' [--all | --mirror | --tags] [--follow-tags] [-n | --dry-run] [--receive-pack=] + 'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=] [--repo=] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream] [--signed] [--force-with-lease[=[:]]] @@@ -34,7 -34,7 +34,7 @@@ When the command line does not specify arguments or `--all`, `--mirror`, `--tags` options, the command finds the default `` by consulting `remote.*.push` configuration, and if it is not found, honors `push.default` configuration to decide -what to push (See gitlink:git-config[1] for the meaning of `push.default`). +what to push (See linkgit:git-config[1] for the meaning of `push.default`). OPTIONS[[OPTIONS]] @@@ -136,6 -136,11 +136,11 @@@ already exists on the remote side logged. See linkgit:git-receive-pack[1] for the details on the receiving end. + --[no-]atomic:: + Use an atomic transaction on the remote side if available. + Either all refs are updated, or on error, no refs are updated. + If the server does not support atomic pushes the push will fail. + --receive-pack=:: --exec=:: Path to the 'git-receive-pack' program on the remote diff --combined builtin/push.c index 12f5e69393,8f1d945d1a..fc771a9f6f --- a/builtin/push.c +++ b/builtin/push.c @@@ -161,7 -161,7 +161,7 @@@ static const char message_detached_head " git push %s HEAD:\n"); static void setup_push_upstream(struct remote *remote, struct branch *branch, - int triangular) + int triangular, int simple) { struct strbuf refspec = STRBUF_INIT; @@@ -184,7 -184,7 +184,7 @@@ "to update which remote branch."), remote->name, branch->name); - if (push_default == PUSH_DEFAULT_SIMPLE) { + if (simple) { /* Additional safety */ if (strcmp(branch->refname, branch->merge[0]->src)) die_push_simple(branch, remote); @@@ -257,11 -257,11 +257,11 @@@ static void setup_default_push_refspecs if (triangular) setup_push_current(remote, branch); else - setup_push_upstream(remote, branch, triangular); + setup_push_upstream(remote, branch, triangular, 1); break; case PUSH_DEFAULT_UPSTREAM: - setup_push_upstream(remote, branch, triangular); + setup_push_upstream(remote, branch, triangular, 0); break; case PUSH_DEFAULT_CURRENT: @@@ -487,6 -487,7 +487,7 @@@ int cmd_push(int argc, const char **arg int flags = 0; int tags = 0; int rc; + int atomic = 0; const char *repo = NULL; /* default repository */ struct option options[] = { OPT__VERBOSITY(&verbosity), @@@ -503,7 -504,7 +504,7 @@@ 0, CAS_OPT_NAME, &cas, N_("refname>: 0) { repo = argv[0]; set_refspecs(argv + 1, argc - 1, repo); diff --combined builtin/receive-pack.c index 8266c1fccf,4c069c53e9..4e85e25d0f --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@@ -26,8 -26,7 +26,8 @@@ enum deny_action DENY_UNCONFIGURED, DENY_IGNORE, DENY_WARN, - DENY_REFUSE + DENY_REFUSE, + DENY_UPDATE_INSTEAD }; static int deny_deletes; @@@ -38,9 -37,11 +38,11 @@@ static int receive_fsck_objects = -1 static int transfer_fsck_objects = -1; static int receive_unpack_limit = -1; static int transfer_unpack_limit = -1; + static int advertise_atomic_push = 1; static int unpack_limit = 100; static int report_status; static int use_sideband; + static int use_atomic; static int quiet; static int prefer_ofs_delta = 1; static int auto_update_server_info; @@@ -67,6 -68,7 +69,7 @@@ static const char *NONCE_SLOP = "SLOP" static const char *nonce_status; static long nonce_stamp_slop; static unsigned long nonce_stamp_slop_limit; + static struct ref_transaction *transaction; static enum deny_action parse_deny_action(const char *var, const char *value) { @@@ -77,8 -79,6 +80,8 @@@ return DENY_WARN; if (!strcasecmp(value, "refuse")) return DENY_REFUSE; + if (!strcasecmp(value, "updateinstead")) + return DENY_UPDATE_INSTEAD; } if (git_config_bool(var, value)) return DENY_REFUSE; @@@ -160,6 -160,11 +163,11 @@@ static int receive_pack_config(const ch return 0; } + if (strcmp(var, "receive.advertiseatomic") == 0) { + advertise_atomic_push = git_config_bool(var, value); + return 0; + } + return git_default_config(var, value, cb); } @@@ -175,6 -180,8 +183,8 @@@ static void show_ref(const char *path, strbuf_addstr(&cap, "report-status delete-refs side-band-64k quiet"); + if (advertise_atomic_push) + strbuf_addstr(&cap, " atomic"); if (prefer_ofs_delta) strbuf_addstr(&cap, " ofs-delta"); if (push_cert_nonce) @@@ -434,7 -441,7 +444,7 @@@ static const char *check_nonce(const ch nonce_stamp_slop = (long)ostamp - (long)stamp; if (nonce_stamp_slop_limit && - abs(nonce_stamp_slop) <= nonce_stamp_slop_limit) { + labs(nonce_stamp_slop) <= nonce_stamp_slop_limit) { /* * Pretend as if the received nonce (which passes the * HMAC check, so it is not a forged by third-party) @@@ -733,89 -740,11 +743,89 @@@ static int update_shallow_ref(struct co return 0; } +static const char *update_worktree(unsigned char *sha1) +{ + const char *update_refresh[] = { + "update-index", "-q", "--ignore-submodules", "--refresh", NULL + }; + const char *diff_files[] = { + "diff-files", "--quiet", "--ignore-submodules", "--", NULL + }; + const char *diff_index[] = { + "diff-index", "--quiet", "--cached", "--ignore-submodules", + "HEAD", "--", NULL + }; + const char *read_tree[] = { + "read-tree", "-u", "-m", NULL, NULL + }; + const char *work_tree = git_work_tree_cfg ? git_work_tree_cfg : ".."; + struct argv_array env = ARGV_ARRAY_INIT; + struct child_process child = CHILD_PROCESS_INIT; + + if (is_bare_repository()) + return "denyCurrentBranch = updateInstead needs a worktree"; + + argv_array_pushf(&env, "GIT_DIR=%s", absolute_path(get_git_dir())); + + child.argv = update_refresh; + child.env = env.argv; + child.dir = work_tree; + child.no_stdin = 1; + child.stdout_to_stderr = 1; + child.git_cmd = 1; + if (run_command(&child)) { + argv_array_clear(&env); + return "Up-to-date check failed"; + } + + /* run_command() does not clean up completely; reinitialize */ + child_process_init(&child); + child.argv = diff_files; + child.env = env.argv; + child.dir = work_tree; + child.no_stdin = 1; + child.stdout_to_stderr = 1; + child.git_cmd = 1; + if (run_command(&child)) { + argv_array_clear(&env); + return "Working directory has unstaged changes"; + } + + child_process_init(&child); + child.argv = diff_index; + child.env = env.argv; + child.no_stdin = 1; + child.no_stdout = 1; + child.stdout_to_stderr = 0; + child.git_cmd = 1; + if (run_command(&child)) { + argv_array_clear(&env); + return "Working directory has staged changes"; + } + + read_tree[3] = sha1_to_hex(sha1); + child_process_init(&child); + child.argv = read_tree; + child.env = env.argv; + child.dir = work_tree; + child.no_stdin = 1; + child.no_stdout = 1; + child.stdout_to_stderr = 0; + child.git_cmd = 1; + if (run_command(&child)) { + argv_array_clear(&env); + return "Could not update working tree to new HEAD"; + } + + argv_array_clear(&env); + return NULL; +} + static const char *update(struct command *cmd, struct shallow_info *si) { const char *name = cmd->ref_name; struct strbuf namespaced_name_buf = STRBUF_INIT; - const char *namespaced_name; + const char *namespaced_name, *ret; unsigned char *old_sha1 = cmd->old_sha1; unsigned char *new_sha1 = cmd->new_sha1; @@@ -841,11 -770,6 +851,11 @@@ if (deny_current_branch == DENY_UNCONFIGURED) refuse_unconfigured_deny(); return "branch is currently checked out"; + case DENY_UPDATE_INSTEAD: + ret = update_worktree(new_sha1); + if (ret) + return ret; + break; } } @@@ -870,13 -794,10 +880,13 @@@ break; case DENY_REFUSE: case DENY_UNCONFIGURED: + case DENY_UPDATE_INSTEAD: if (deny_delete_current == DENY_UNCONFIGURED) refuse_unconfigured_deny_delete_current(); rp_error("refusing to delete the current branch: %s", name); return "deletion of the current branch prohibited"; + default: + return "Invalid denyDeleteCurrent setting"; } } } @@@ -910,6 -831,7 +920,7 @@@ } if (is_null_sha1(new_sha1)) { + struct strbuf err = STRBUF_INIT; if (!parse_object(old_sha1)) { old_sha1 = NULL; if (ref_exists(name)) { @@@ -919,35 -841,36 +930,36 @@@ cmd->did_not_exist = 1; } } - if (delete_ref(namespaced_name, old_sha1, 0)) { - rp_error("failed to delete %s", name); + if (ref_transaction_delete(transaction, + namespaced_name, + old_sha1, + 0, old_sha1 != NULL, + "push", &err)) { + rp_error("%s", err.buf); + strbuf_release(&err); return "failed to delete"; } + strbuf_release(&err); return NULL; /* good */ } else { struct strbuf err = STRBUF_INIT; - struct ref_transaction *transaction; - if (shallow_update && si->shallow_ref[cmd->index] && update_shallow_ref(cmd, si)) return "shallow error"; - transaction = ref_transaction_begin(&err); - if (!transaction || - ref_transaction_update(transaction, namespaced_name, - new_sha1, old_sha1, 0, 1, "push", - &err) || - ref_transaction_commit(transaction, &err)) { - ref_transaction_free(transaction); - + if (ref_transaction_update(transaction, + namespaced_name, + new_sha1, old_sha1, + 0, 1, "push", + &err)) { rp_error("%s", err.buf); strbuf_release(&err); + return "failed to update ref"; } - - ref_transaction_free(transaction); strbuf_release(&err); + return NULL; /* good */ } } @@@ -1053,7 -976,7 +1065,7 @@@ static void check_aliased_updates(struc string_list_append(&ref_list, cmd->ref_name); item->util = (void *)cmd; } - sort_string_list(&ref_list); + string_list_sort(&ref_list); for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) @@@ -1131,11 -1054,105 +1143,105 @@@ static void reject_updates_to_hidden(st } } + static int should_process_cmd(struct command *cmd) + { + return !cmd->error_string && !cmd->skip_update; + } + + static void warn_if_skipped_connectivity_check(struct command *commands, + struct shallow_info *si) + { + struct command *cmd; + int checked_connectivity = 1; + + for (cmd = commands; cmd; cmd = cmd->next) { + if (should_process_cmd(cmd) && si->shallow_ref[cmd->index]) { + error("BUG: connectivity check has not been run on ref %s", + cmd->ref_name); + checked_connectivity = 0; + } + } + if (!checked_connectivity) + die("BUG: connectivity check skipped???"); + } + + static void execute_commands_non_atomic(struct command *commands, + struct shallow_info *si) + { + struct command *cmd; + struct strbuf err = STRBUF_INIT; + + for (cmd = commands; cmd; cmd = cmd->next) { + if (!should_process_cmd(cmd)) + continue; + + transaction = ref_transaction_begin(&err); + if (!transaction) { + rp_error("%s", err.buf); + strbuf_reset(&err); + cmd->error_string = "transaction failed to start"; + continue; + } + + cmd->error_string = update(cmd, si); + + if (!cmd->error_string + && ref_transaction_commit(transaction, &err)) { + rp_error("%s", err.buf); + strbuf_reset(&err); + cmd->error_string = "failed to update ref"; + } + ref_transaction_free(transaction); + } + strbuf_release(&err); + } + + static void execute_commands_atomic(struct command *commands, + struct shallow_info *si) + { + struct command *cmd; + struct strbuf err = STRBUF_INIT; + const char *reported_error = "atomic push failure"; + + transaction = ref_transaction_begin(&err); + if (!transaction) { + rp_error("%s", err.buf); + strbuf_reset(&err); + reported_error = "transaction failed to start"; + goto failure; + } + + for (cmd = commands; cmd; cmd = cmd->next) { + if (!should_process_cmd(cmd)) + continue; + + cmd->error_string = update(cmd, si); + + if (cmd->error_string) + goto failure; + } + + if (ref_transaction_commit(transaction, &err)) { + rp_error("%s", err.buf); + reported_error = "atomic transaction failed"; + goto failure; + } + goto cleanup; + + failure: + for (cmd = commands; cmd; cmd = cmd->next) + if (!cmd->error_string) + cmd->error_string = reported_error; + + cleanup: + ref_transaction_free(transaction); + strbuf_release(&err); + } + static void execute_commands(struct command *commands, const char *unpacker_error, struct shallow_info *si) { - int checked_connectivity; struct command *cmd; unsigned char sha1[20]; struct iterate_data data; @@@ -1166,27 -1183,13 +1272,13 @@@ free(head_name_to_free); head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL); - checked_connectivity = 1; - for (cmd = commands; cmd; cmd = cmd->next) { - if (cmd->error_string) - continue; - - if (cmd->skip_update) - continue; - - cmd->error_string = update(cmd, si); - if (shallow_update && !cmd->error_string && - si->shallow_ref[cmd->index]) { - error("BUG: connectivity check has not been run on ref %s", - cmd->ref_name); - checked_connectivity = 0; - } - } + if (use_atomic) + execute_commands_atomic(commands, si); + else + execute_commands_non_atomic(commands, si); - if (shallow_update && !checked_connectivity) - error("BUG: run 'git fsck' for safety.\n" - "If there are errors, try to remove " - "the reported refs above"); + if (shallow_update) + warn_if_skipped_connectivity_check(commands, si); } static struct command **queue_command(struct command **tail, @@@ -1268,6 -1271,9 +1360,9 @@@ static struct command *read_head_info(s use_sideband = LARGE_PACKET_MAX; if (parse_feature_request(feature_list, "quiet")) quiet = 1; + if (advertise_atomic_push + && parse_feature_request(feature_list, "atomic")) + use_atomic = 1; } if (!strcmp(line, "push-cert")) { diff --combined send-pack.c index 25947d7df9,266f37f060..9d2b0c52ed --- a/send-pack.c +++ b/send-pack.c @@@ -47,7 -47,6 +47,7 @@@ static int pack_objects(int fd, struct NULL, NULL, NULL, + NULL, }; struct child_process po = CHILD_PROCESS_INIT; int i; @@@ -61,8 -60,6 +61,8 @@@ argv[i++] = "-q"; if (args->progress) argv[i++] = "--progress"; + if (is_repository_shallow()) + argv[i++] = "--shallow"; po.argv = argv; po.in = -1; po.out = args->stateless_rpc ? -1 : fd; @@@ -193,10 -190,13 +193,13 @@@ static void advertise_shallow_grafts_bu 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 -206,11 +209,11 @@@ 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 -254,7 +257,7 @@@ static int generate_push_cert(struct st 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 -282,29 +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 -321,8 +324,8 @@@ 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 -343,8 +346,8 @@@ 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 -359,10 +362,10 @@@ "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 -370,8 +373,8 @@@ 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 -396,21 +399,21 @@@ * 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 -429,7 +432,7 @@@ 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); diff --combined transport.c index 08bcd3a4eb,1373152a93..0694a7cf3e --- a/transport.c +++ b/transport.c @@@ -728,6 -728,10 +728,10 @@@ static int print_one_push_status(struc 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; @@@ -826,6 -830,7 +830,7 @@@ static int git_transport_push(struct tr args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN); args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN); args.push_cert = !!(flags & TRANSPORT_PUSH_CERT); + args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC); args.url = transport->url; ret = send_pack(&args, data->fd, data->conn, remote_refs, @@@ -971,7 -976,9 +976,7 @@@ struct transport *transport_get(struct } else { /* Unknown protocol in URL. Pass to external handler. */ int len = external_specification_len(url); - char *handler = xmalloc(len + 1); - handler[len] = 0; - strncpy(handler, url, len); + char *handler = xmemdupz(url, len); transport_helper_init(ret, handler); }