From: Junio C Hamano Date: Wed, 21 Nov 2018 13:57:59 +0000 (+0900) Subject: Merge branch 'jk/check-everything-connected-is-long-gone' into maint X-Git-Tag: v2.19.2~24 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/75266b4e8bc032edd237e938aae93b67d7638768?ds=inline;hp=-c Merge branch 'jk/check-everything-connected-is-long-gone' into maint Comment fix. * jk/check-everything-connected-is-long-gone: receive-pack: update comment with check_everything_connected --- 75266b4e8bc032edd237e938aae93b67d7638768 diff --combined builtin/receive-pack.c index 6c047b5e28,8744faf897..95eb7002ee --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@@ -1,13 -1,11 +1,13 @@@ #include "builtin.h" +#include "repository.h" +#include "config.h" #include "lockfile.h" #include "pack.h" #include "refs.h" #include "pkt-line.h" #include "sideband.h" #include "run-command.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "commit.h" #include "object.h" #include "remote.h" @@@ -22,11 -20,6 +22,11 @@@ #include "gpg-interface.h" #include "sigchain.h" #include "fsck.h" +#include "tmp-objdir.h" +#include "oidset.h" +#include "packfile.h" +#include "object-store.h" +#include "protocol.h" static const char * const receive_pack_usage[] = { N_("git receive-pack "), @@@ -51,13 -44,10 +51,13 @@@ static struct strbuf fsck_msg_types = S static int receive_unpack_limit = -1; static int transfer_unpack_limit = -1; static int advertise_atomic_push = 1; +static int advertise_push_options; static int unpack_limit = 100; +static off_t max_input_size; static int report_status; static int use_sideband; static int use_atomic; +static int use_push_options; static int quiet; static int prefer_ofs_delta = 1; static int auto_update_server_info; @@@ -71,7 -61,7 +71,7 @@@ static int sent_capabilities static int shallow_update; static const char *alt_shallow_file; static struct strbuf push_cert = STRBUF_INIT; -static unsigned char push_cert_sha1[20]; +static struct object_id push_cert_oid; static struct signature_check sigcheck; static const char *push_cert_nonce; static const char *cert_nonce_seed; @@@ -83,18 -73,9 +83,18 @@@ static const char *NONCE_OK = "OK" static const char *NONCE_SLOP = "SLOP"; static const char *nonce_status; static long nonce_stamp_slop; -static unsigned long nonce_stamp_slop_limit; +static timestamp_t nonce_stamp_slop_limit; static struct ref_transaction *transaction; +static enum { + KEEPALIVE_NEVER = 0, + KEEPALIVE_AFTER_NUL, + KEEPALIVE_ALWAYS +} use_keepalive; +static int keepalive_in_sec = 5; + +static struct tmp_objdir *tmp_objdir; + static enum deny_action parse_deny_action(const char *var, const char *value) { if (value) { @@@ -212,28 -193,13 +212,28 @@@ static int receive_pack_config(const ch return 0; } + if (strcmp(var, "receive.advertisepushoptions") == 0) { + advertise_push_options = git_config_bool(var, value); + return 0; + } + + if (strcmp(var, "receive.keepalive") == 0) { + keepalive_in_sec = git_config_int(var, value); + return 0; + } + + if (strcmp(var, "receive.maxinputsize") == 0) { + max_input_size = git_config_int64(var, value); + return 0; + } + return git_default_config(var, value, cb); } -static void show_ref(const char *path, const unsigned char *sha1) +static void show_ref(const char *path, const struct object_id *oid) { if (sent_capabilities) { - packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); + packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), path); } else { struct strbuf cap = STRBUF_INIT; @@@ -245,20 -211,17 +245,20 @@@ strbuf_addstr(&cap, " ofs-delta"); if (push_cert_nonce) strbuf_addf(&cap, " push-cert=%s", push_cert_nonce); + if (advertise_push_options) + strbuf_addstr(&cap, " push-options"); strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized()); - packet_write(1, "%s %s%c%s\n", - sha1_to_hex(sha1), path, 0, cap.buf); + packet_write_fmt(1, "%s %s%c%s\n", + oid_to_hex(oid), path, 0, cap.buf); strbuf_release(&cap); sent_capabilities = 1; } } static int show_ref_cb(const char *path_full, const struct object_id *oid, - int flag, void *unused) + int flag, void *data) { + struct oidset *seen = data; const char *path = strip_namespace(path_full); if (ref_is_hidden(path, path_full)) @@@ -267,40 -230,38 +267,40 @@@ /* * Advertise refs outside our current namespace as ".have" * refs, so that the client can use them to minimize data - * transfer but will otherwise ignore them. This happens to - * cover ".have" that are thrown in by add_one_alternate_ref() - * to mark histories that are complete in our alternates as - * well. + * transfer but will otherwise ignore them. */ - if (!path) + if (!path) { + if (oidset_insert(seen, oid)) + return 0; path = ".have"; - show_ref(path, oid->hash); + } else { + oidset_insert(seen, oid); + } + show_ref(path, oid); return 0; } -static void show_one_alternate_sha1(const unsigned char sha1[20], void *unused) +static void show_one_alternate_ref(const char *refname, + const struct object_id *oid, + void *data) { - show_ref(".have", sha1); -} + struct oidset *seen = data; -static void collect_one_alternate_ref(const struct ref *ref, void *data) -{ - struct sha1_array *sa = data; - sha1_array_append(sa, ref->old_oid.hash); + if (oidset_insert(seen, oid)) + return; + + show_ref(".have", oid); } static void write_head_info(void) { - struct sha1_array sa = SHA1_ARRAY_INIT; + static struct oidset seen = OIDSET_INIT; - for_each_alternate_ref(collect_one_alternate_ref, &sa); - sha1_array_for_each_unique(&sa, show_one_alternate_sha1, NULL); - sha1_array_clear(&sa); - for_each_ref(show_ref_cb, NULL); + for_each_ref(show_ref_cb, &seen); + for_each_alternate_ref(show_one_alternate_ref, &seen); + oidset_clear(&seen); if (!sent_capabilities) - show_ref("capabilities^{}", null_sha1); + show_ref("capabilities^{}", &null_oid); advertise_shallow_grafts(1); @@@ -314,8 -275,8 +314,8 @@@ struct command unsigned int skip_update:1, did_not_exist:1; int index; - unsigned char old_sha1[20]; - unsigned char new_sha1[20]; + struct object_id old_oid; + struct object_id new_oid; char ref_name[FLEX_ARRAY]; /* more */ }; @@@ -358,60 -319,10 +358,60 @@@ static void rp_error(const char *err, . static int copy_to_sideband(int in, int out, void *arg) { char data[128]; + int keepalive_active = 0; + + if (keepalive_in_sec <= 0) + use_keepalive = KEEPALIVE_NEVER; + if (use_keepalive == KEEPALIVE_ALWAYS) + keepalive_active = 1; + while (1) { - ssize_t sz = xread(in, data, sizeof(data)); + ssize_t sz; + + if (keepalive_active) { + struct pollfd pfd; + int ret; + + pfd.fd = in; + pfd.events = POLLIN; + ret = poll(&pfd, 1, 1000 * keepalive_in_sec); + + if (ret < 0) { + if (errno == EINTR) + continue; + else + break; + } else if (ret == 0) { + /* no data; send a keepalive packet */ + static const char buf[] = "0005\1"; + write_or_die(1, buf, sizeof(buf) - 1); + continue; + } /* else there is actual data to read */ + } + + sz = xread(in, data, sizeof(data)); if (sz <= 0) break; + + if (use_keepalive == KEEPALIVE_AFTER_NUL && !keepalive_active) { + const char *p = memchr(data, '\0', sz); + if (p) { + /* + * The NUL tells us to start sending keepalives. Make + * sure we send any other data we read along + * with it. + */ + keepalive_active = 1; + send_sideband(1, 2, data, p - data, use_sideband); + send_sideband(1, 2, p + 1, sz - (p - data + 1), use_sideband); + continue; + } + } + + /* + * Either we're not looking for a NUL signal, or we didn't see + * it yet; just pass along the data. + */ send_sideband(1, 2, data, sz, use_sideband); } close(in); @@@ -455,21 -366,21 +455,21 @@@ static void hmac_sha1(unsigned char *ou /* RFC 2104 2. (6) & (7) */ git_SHA1_Init(&ctx); git_SHA1_Update(&ctx, k_opad, sizeof(k_opad)); - git_SHA1_Update(&ctx, out, 20); + git_SHA1_Update(&ctx, out, GIT_SHA1_RAWSZ); git_SHA1_Final(out, &ctx); } -static char *prepare_push_cert_nonce(const char *path, unsigned long stamp) +static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp) { struct strbuf buf = STRBUF_INIT; - unsigned char sha1[20]; + unsigned char sha1[GIT_SHA1_RAWSZ]; - strbuf_addf(&buf, "%s:%lu", path, stamp); - hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));; + strbuf_addf(&buf, "%s:%"PRItime, path, stamp); + hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed)); strbuf_release(&buf); /* RFC 2104 5. HMAC-SHA1-80 */ - strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1)); + strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, GIT_SHA1_HEXSZ, sha1_to_hex(sha1)); return strbuf_detach(&buf, NULL); } @@@ -478,8 -389,7 +478,8 @@@ * after dropping "_commit" from its name and possibly moving it out * of commit.c */ -static char *find_header(const char *msg, size_t len, const char *key) +static char *find_header(const char *msg, size_t len, const char *key, + const char **next_line) { int key_len = strlen(key); const char *line = msg; @@@ -492,8 -402,6 +492,8 @@@ if (line + key_len < eol && !memcmp(line, key, key_len) && line[key_len] == ' ') { int offset = key_len + 1; + if (next_line) + *next_line = *eol ? eol + 1 : eol; return xmemdupz(line + offset, (eol - line) - offset); } line = *eol ? eol + 1 : NULL; @@@ -503,8 -411,8 +503,8 @@@ static const char *check_nonce(const char *buf, size_t len) { - char *nonce = find_header(buf, len, "nonce"); - unsigned long stamp, ostamp; + char *nonce = find_header(buf, len, "nonce", NULL); + timestamp_t stamp, ostamp; char *bohmac, *expect = NULL; const char *retval = NONCE_BAD; @@@ -542,7 -450,7 +542,7 @@@ retval = NONCE_BAD; goto leave; } - stamp = strtoul(nonce, &bohmac, 10); + stamp = parse_timestamp(nonce, &bohmac, 10); if (bohmac == nonce || bohmac[0] != '-') { retval = NONCE_BAD; goto leave; @@@ -560,7 -468,7 +560,7 @@@ * would mean it was issued by another server with its clock * skewed in the future. */ - ostamp = strtoul(push_cert_nonce, NULL, 10); + ostamp = parse_timestamp(push_cert_nonce, NULL, 10); nonce_stamp_slop = (long)ostamp - (long)stamp; if (nonce_stamp_slop_limit && @@@ -583,45 -491,6 +583,45 @@@ leave return retval; } +/* + * Return 1 if there is no push_cert or if the push options in push_cert are + * the same as those in the argument; 0 otherwise. + */ +static int check_cert_push_options(const struct string_list *push_options) +{ + const char *buf = push_cert.buf; + int len = push_cert.len; + + char *option; + const char *next_line; + int options_seen = 0; + + int retval = 1; + + if (!len) + return 1; + + while ((option = find_header(buf, len, "push-option", &next_line))) { + len -= (next_line - buf); + buf = next_line; + options_seen++; + if (options_seen > push_options->nr + || strcmp(option, + push_options->items[options_seen - 1].string)) { + retval = 0; + goto leave; + } + free(option); + } + + if (options_seen != push_options->nr) + retval = 0; + +leave: + free(option); + return retval; +} + static void prepare_push_cert_sha1(struct child_process *proc) { static int already_done; @@@ -630,24 -499,36 +630,24 @@@ return; if (!already_done) { - struct strbuf gpg_output = STRBUF_INIT; - struct strbuf gpg_status = STRBUF_INIT; int bogs /* beginning_of_gpg_sig */; already_done = 1; - if (write_sha1_file(push_cert.buf, push_cert.len, "blob", push_cert_sha1)) - hashclr(push_cert_sha1); + if (write_object_file(push_cert.buf, push_cert.len, "blob", + &push_cert_oid)) + oidclr(&push_cert_oid); memset(&sigcheck, '\0', sizeof(sigcheck)); - sigcheck.result = 'N'; bogs = parse_signature(push_cert.buf, push_cert.len); - if (verify_signed_buffer(push_cert.buf, bogs, - push_cert.buf + bogs, push_cert.len - bogs, - &gpg_output, &gpg_status) < 0) { - ; /* error running gpg */ - } else { - sigcheck.payload = push_cert.buf; - sigcheck.gpg_output = gpg_output.buf; - sigcheck.gpg_status = gpg_status.buf; - parse_gpg_output(&sigcheck); - } + check_signature(push_cert.buf, bogs, push_cert.buf + bogs, + push_cert.len - bogs, &sigcheck); - strbuf_release(&gpg_output); - strbuf_release(&gpg_status); nonce_status = check_nonce(push_cert.buf, bogs); } - if (!is_null_sha1(push_cert_sha1)) { + if (!is_null_oid(&push_cert_oid)) { argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT=%s", - sha1_to_hex(push_cert_sha1)); + oid_to_hex(&push_cert_oid)); argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_SIGNER=%s", sigcheck.signer ? sigcheck.signer : ""); argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_KEY=%s", @@@ -669,16 -550,8 +669,16 @@@ } } +struct receive_hook_feed_state { + struct command *cmd; + int skip_broken; + struct strbuf buf; + const struct string_list *push_options; +}; + typedef int (*feed_fn)(void *, const char **, size_t *); -static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_state) +static int run_and_feed_hook(const char *hook_name, feed_fn feed, + struct receive_hook_feed_state *feed_state) { struct child_process proc = CHILD_PROCESS_INIT; struct async muxer; @@@ -694,19 -567,6 +694,19 @@@ proc.argv = argv; proc.in = -1; proc.stdout_to_stderr = 1; + if (feed_state->push_options) { + int i; + for (i = 0; i < feed_state->push_options->nr; i++) + argv_array_pushf(&proc.env_array, + "GIT_PUSH_OPTION_%d=%s", i, + feed_state->push_options->items[i].string); + argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT=%d", + feed_state->push_options->nr); + } else + argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT"); + + if (tmp_objdir) + argv_array_pushv(&proc.env_array, tmp_objdir_env(tmp_objdir)); if (use_sideband) { memset(&muxer, 0, sizeof(muxer)); @@@ -734,7 -594,7 +734,7 @@@ size_t n; if (feed(feed_state, &buf, &n)) break; - if (write_in_full(proc.in, buf, n) != n) + if (write_in_full(proc.in, buf, n) < 0) break; } close(proc.in); @@@ -746,6 -606,12 +746,6 @@@ return finish_command(&proc); } -struct receive_hook_feed_state { - struct command *cmd; - int skip_broken; - struct strbuf buf; -}; - static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep) { struct receive_hook_feed_state *state = state_; @@@ -758,7 -624,7 +758,7 @@@ return -1; /* EOF */ strbuf_reset(&state->buf); strbuf_addf(&state->buf, "%s %s %s\n", - sha1_to_hex(cmd->old_sha1), sha1_to_hex(cmd->new_sha1), + oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid), cmd->ref_name); state->cmd = cmd->next; if (bufp) { @@@ -768,10 -634,8 +768,10 @@@ return 0; } -static int run_receive_hook(struct command *commands, const char *hook_name, - int skip_broken) +static int run_receive_hook(struct command *commands, + const char *hook_name, + int skip_broken, + const struct string_list *push_options) { struct receive_hook_feed_state state; int status; @@@ -782,7 -646,6 +782,7 @@@ if (feed_receive_hook(&state, NULL, NULL)) return 0; state.cmd = commands; + state.push_options = push_options; status = run_and_feed_hook(hook_name, feed_receive_hook, &state); strbuf_release(&state.buf); return status; @@@ -799,8 -662,8 +799,8 @@@ static int run_update_hook(struct comma return 0; argv[1] = cmd->ref_name; - argv[2] = sha1_to_hex(cmd->old_sha1); - argv[3] = sha1_to_hex(cmd->new_sha1); + argv[2] = oid_to_hex(&cmd->old_oid); + argv[3] = oid_to_hex(&cmd->new_oid); argv[4] = NULL; proc.no_stdin = 1; @@@ -826,46 -689,54 +826,46 @@@ static int is_ref_checked_out(const cha return !strcmp(head_name, ref); } -static char *refuse_unconfigured_deny_msg[] = { - "By default, updating the current branch in a non-bare repository", - "is denied, because it will make the index and work tree inconsistent", - "with what you pushed, and will require 'git reset --hard' to match", - "the work tree to HEAD.", - "", - "You can set 'receive.denyCurrentBranch' configuration variable to", - "'ignore' or 'warn' in the remote repository to allow pushing into", - "its current branch; however, this is not recommended unless you", - "arranged to update its work tree to match what you pushed in some", - "other way.", - "", - "To squelch this message and still keep the default behaviour, set", - "'receive.denyCurrentBranch' configuration variable to 'refuse'." -}; +static char *refuse_unconfigured_deny_msg = + N_("By default, updating the current branch in a non-bare repository\n" + "is denied, because it will make the index and work tree inconsistent\n" + "with what you pushed, and will require 'git reset --hard' to match\n" + "the work tree to HEAD.\n" + "\n" + "You can set the 'receive.denyCurrentBranch' configuration variable\n" + "to 'ignore' or 'warn' in the remote repository to allow pushing into\n" + "its current branch; however, this is not recommended unless you\n" + "arranged to update its work tree to match what you pushed in some\n" + "other way.\n" + "\n" + "To squelch this message and still keep the default behaviour, set\n" + "'receive.denyCurrentBranch' configuration variable to 'refuse'."); static void refuse_unconfigured_deny(void) { - int i; - for (i = 0; i < ARRAY_SIZE(refuse_unconfigured_deny_msg); i++) - rp_error("%s", refuse_unconfigured_deny_msg[i]); + rp_error("%s", _(refuse_unconfigured_deny_msg)); } -static char *refuse_unconfigured_deny_delete_current_msg[] = { - "By default, deleting the current branch is denied, because the next", - "'git clone' won't result in any file checked out, causing confusion.", - "", - "You can set 'receive.denyDeleteCurrent' configuration variable to", - "'warn' or 'ignore' in the remote repository to allow deleting the", - "current branch, with or without a warning message.", - "", - "To squelch this message, you can set it to 'refuse'." -}; +static char *refuse_unconfigured_deny_delete_current_msg = + N_("By default, deleting the current branch is denied, because the next\n" + "'git clone' won't result in any file checked out, causing confusion.\n" + "\n" + "You can set 'receive.denyDeleteCurrent' configuration variable to\n" + "'warn' or 'ignore' in the remote repository to allow deleting the\n" + "current branch, with or without a warning message.\n" + "\n" + "To squelch this message, you can set it to 'refuse'."); static void refuse_unconfigured_deny_delete_current(void) { - int i; - for (i = 0; - i < ARRAY_SIZE(refuse_unconfigured_deny_delete_current_msg); - i++) - rp_error("%s", refuse_unconfigured_deny_delete_current_msg[i]); + rp_error("%s", _(refuse_unconfigured_deny_delete_current_msg)); } -static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]); +static int command_singleton_iterator(void *cb_data, struct object_id *oid); static int update_shallow_ref(struct command *cmd, struct shallow_info *si) { - static struct lock_file shallow_lock; - struct sha1_array extra = SHA1_ARRAY_INIT; + struct lock_file shallow_lock = LOCK_INIT; + struct oid_array extra = OID_ARRAY_INIT; struct check_connected_options opt = CHECK_CONNECTED_INIT; uint32_t mask = 1 << (cmd->index % 32); int i; @@@ -876,13 -747,12 +876,13 @@@ if (si->used_shallow[i] && (si->used_shallow[i][cmd->index / 32] & mask) && !delayed_reachability_test(si, i)) - sha1_array_append(&extra, si->shallow->sha1[i]); + oid_array_append(&extra, &si->shallow->oid[i]); + opt.env = tmp_objdir_env(tmp_objdir); setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra); if (check_connected(command_singleton_iterator, cmd, &opt)) { rollback_lock_file(&shallow_lock); - sha1_array_clear(&extra); + oid_array_clear(&extra); return -1; } @@@ -893,10 -763,10 +893,10 @@@ * not lose these new roots.. */ for (i = 0; i < extra.nr; i++) - register_shallow(extra.sha1[i]); + register_shallow(the_repository, &extra.oid[i]); si->shallow_ref[cmd->index] = 0; - sha1_array_clear(&extra); + oid_array_clear(&extra); return 0; } @@@ -911,9 -781,9 +911,9 @@@ */ static int head_has_history(void) { - unsigned char sha1[20]; + struct object_id oid; - return !get_sha1("HEAD", sha1); + return !get_oid("HEAD", &oid); } static const char *push_to_deploy(unsigned char *sha1, @@@ -956,7 -826,7 +956,7 @@@ return "Working directory has unstaged changes"; /* diff-index with either HEAD or an empty tree */ - diff_index[4] = head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX; + diff_index[4] = head_has_history() ? "HEAD" : empty_tree_oid_hex(); child_process_init(&child); child.argv = diff_index; @@@ -1021,11 -891,9 +1021,11 @@@ static const char *update(struct comman { const char *name = cmd->ref_name; struct strbuf namespaced_name_buf = STRBUF_INIT; - const char *namespaced_name, *ret; - unsigned char *old_sha1 = cmd->old_sha1; - unsigned char *new_sha1 = cmd->new_sha1; + static char *namespaced_name; + const char *ret; + struct object_id *old_oid = &cmd->old_oid; + struct object_id *new_oid = &cmd->new_oid; + int do_update_worktree = 0; /* only refs/... are allowed */ if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) { @@@ -1034,7 -902,6 +1034,7 @@@ } strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name); + free(namespaced_name); namespaced_name = strbuf_detach(&namespaced_name_buf, NULL); if (is_ref_checked_out(namespaced_name)) { @@@ -1051,19 -918,20 +1051,19 @@@ refuse_unconfigured_deny(); return "branch is currently checked out"; case DENY_UPDATE_INSTEAD: - ret = update_worktree(new_sha1); - if (ret) - return ret; + /* pass -- let other checks intervene first */ + do_update_worktree = 1; break; } } - if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) { + if (!is_null_oid(new_oid) && !has_object_file(new_oid)) { error("unpack should have generated %s, " - "but I can't find it!", sha1_to_hex(new_sha1)); + "but I can't find it!", oid_to_hex(new_oid)); return "bad pack"; } - if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) { + if (!is_null_oid(old_oid) && is_null_oid(new_oid)) { if (deny_deletes && starts_with(name, "refs/heads/")) { rp_error("denying ref deletion for %s", name); return "deletion prohibited"; @@@ -1089,14 -957,14 +1089,14 @@@ } } - if (deny_non_fast_forwards && !is_null_sha1(new_sha1) && - !is_null_sha1(old_sha1) && + if (deny_non_fast_forwards && !is_null_oid(new_oid) && + !is_null_oid(old_oid) && starts_with(name, "refs/heads/")) { struct object *old_object, *new_object; struct commit *old_commit, *new_commit; - old_object = parse_object(old_sha1); - new_object = parse_object(new_sha1); + old_object = parse_object(the_repository, old_oid); + new_object = parse_object(the_repository, new_oid); if (!old_object || !new_object || old_object->type != OBJ_COMMIT || @@@ -1117,16 -985,10 +1117,16 @@@ return "hook declined"; } - if (is_null_sha1(new_sha1)) { + if (do_update_worktree) { + ret = update_worktree(new_oid->hash); + if (ret) + return ret; + } + + if (is_null_oid(new_oid)) { struct strbuf err = STRBUF_INIT; - if (!parse_object(old_sha1)) { - old_sha1 = NULL; + if (!parse_object(the_repository, old_oid)) { + old_oid = NULL; if (ref_exists(name)) { rp_warning("Allowing deletion of corrupt ref."); } else { @@@ -1136,7 -998,7 +1136,7 @@@ } if (ref_transaction_delete(transaction, namespaced_name, - old_sha1, + old_oid, 0, "push", &err)) { rp_error("%s", err.buf); strbuf_release(&err); @@@ -1153,7 -1015,7 +1153,7 @@@ if (ref_transaction_update(transaction, namespaced_name, - new_sha1, old_sha1, + new_oid, old_oid, 0, "push", &err)) { rp_error("%s", err.buf); @@@ -1170,22 -1032,25 +1170,22 @@@ static void run_update_post_hook(struct command *commands) { struct command *cmd; - int argc; struct child_process proc = CHILD_PROCESS_INIT; const char *hook; hook = find_hook("post-update"); - for (argc = 0, cmd = commands; cmd; cmd = cmd->next) { - if (cmd->error_string || cmd->did_not_exist) - continue; - argc++; - } - if (!argc || !hook) + if (!hook) return; - argv_array_push(&proc.args, hook); for (cmd = commands; cmd; cmd = cmd->next) { if (cmd->error_string || cmd->did_not_exist) continue; + if (!proc.args.argc) + argv_array_push(&proc.args, hook); argv_array_push(&proc.args, cmd->ref_name); } + if (!proc.args.argc) + return; proc.no_stdin = 1; proc.stdout_to_stderr = 1; @@@ -1204,10 -1069,15 +1204,10 @@@ static void check_aliased_update(struc const char *dst_name; struct string_list_item *item; struct command *dst_cmd; - unsigned char sha1[GIT_SHA1_RAWSZ]; - char cmd_oldh[GIT_SHA1_HEXSZ + 1], - cmd_newh[GIT_SHA1_HEXSZ + 1], - dst_oldh[GIT_SHA1_HEXSZ + 1], - dst_newh[GIT_SHA1_HEXSZ + 1]; int flag; strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name); - dst_name = resolve_ref_unsafe(buf.buf, 0, sha1, &flag); + dst_name = resolve_ref_unsafe(buf.buf, 0, NULL, &flag); strbuf_release(&buf); if (!(flag & REF_ISSYMREF)) @@@ -1228,20 -1098,20 +1228,20 @@@ dst_cmd = (struct command *) item->util; - if (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) && - !hashcmp(cmd->new_sha1, dst_cmd->new_sha1)) + if (!oidcmp(&cmd->old_oid, &dst_cmd->old_oid) && + !oidcmp(&cmd->new_oid, &dst_cmd->new_oid)) return; dst_cmd->skip_update = 1; - find_unique_abbrev_r(cmd_oldh, cmd->old_sha1, DEFAULT_ABBREV); - find_unique_abbrev_r(cmd_newh, cmd->new_sha1, DEFAULT_ABBREV); - find_unique_abbrev_r(dst_oldh, dst_cmd->old_sha1, DEFAULT_ABBREV); - find_unique_abbrev_r(dst_newh, dst_cmd->new_sha1, DEFAULT_ABBREV); rp_error("refusing inconsistent update between symref '%s' (%s..%s) and" " its target '%s' (%s..%s)", - cmd->ref_name, cmd_oldh, cmd_newh, - dst_cmd->ref_name, dst_oldh, dst_newh); + cmd->ref_name, + find_unique_abbrev(&cmd->old_oid, DEFAULT_ABBREV), + find_unique_abbrev(&cmd->new_oid, DEFAULT_ABBREV), + dst_cmd->ref_name, + find_unique_abbrev(&dst_cmd->old_oid, DEFAULT_ABBREV), + find_unique_abbrev(&dst_cmd->new_oid, DEFAULT_ABBREV)); cmd->error_string = dst_cmd->error_string = "inconsistent aliased update"; @@@ -1267,15 -1137,15 +1267,15 @@@ static void check_aliased_updates(struc string_list_clear(&ref_list, 0); } -static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]) +static int command_singleton_iterator(void *cb_data, struct object_id *oid) { struct command **cmd_list = cb_data; struct command *cmd = *cmd_list; - if (!cmd || is_null_sha1(cmd->new_sha1)) + if (!cmd || is_null_oid(&cmd->new_oid)) return -1; /* end of list */ *cmd_list = NULL; /* this returns only one */ - hashcpy(sha1, cmd->new_sha1); + oidcpy(oid, &cmd->new_oid); return 0; } @@@ -1286,17 -1156,12 +1286,17 @@@ static void set_connectivity_errors(str for (cmd = commands; cmd; cmd = cmd->next) { struct command *singleton = cmd; + struct check_connected_options opt = CHECK_CONNECTED_INIT; + if (shallow_update && si->shallow_ref[cmd->index]) /* to be checked in update_shallow_ref() */ continue; + + opt.env = tmp_objdir_env(tmp_objdir); if (!check_connected(command_singleton_iterator, &singleton, - NULL)) + &opt)) continue; + cmd->error_string = "missing necessary objects"; } } @@@ -1306,7 -1171,7 +1306,7 @@@ struct iterate_data struct shallow_info *si; }; -static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20]) +static int iterate_receive_command_list(void *cb_data, struct object_id *oid) { struct iterate_data *data = cb_data; struct command **cmd_list = &data->cmds; @@@ -1316,8 -1181,8 +1316,8 @@@ if (shallow_update && data->si->shallow_ref[cmd->index]) /* to be checked in update_shallow_ref() */ continue; - if (!is_null_sha1(cmd->new_sha1) && !cmd->skip_update) { - hashcpy(sha1, cmd->new_sha1); + if (!is_null_oid(&cmd->new_oid) && !cmd->skip_update) { + oidcpy(oid, &cmd->new_oid); *cmd_list = cmd->next; return 0; } @@@ -1344,7 -1209,7 +1344,7 @@@ static void reject_updates_to_hidden(st if (!ref_is_hidden(cmd->ref_name, refname_full.buf)) continue; - if (is_null_sha1(cmd->new_sha1)) + if (is_null_oid(&cmd->new_oid)) cmd->error_string = "deny deleting a hidden ref"; else cmd->error_string = "deny updating a hidden ref"; @@@ -1372,7 -1237,7 +1372,7 @@@ static void warn_if_skipped_connectivit } } if (!checked_connectivity) - die("BUG: connectivity check skipped???"); + BUG("connectivity check skipped???"); } static void execute_commands_non_atomic(struct command *commands, @@@ -1450,14 -1315,11 +1450,14 @@@ cleanup static void execute_commands(struct command *commands, const char *unpacker_error, - struct shallow_info *si) + struct shallow_info *si, + const struct string_list *push_options) { + struct check_connected_options opt = CHECK_CONNECTED_INIT; struct command *cmd; - unsigned char sha1[20]; struct iterate_data data; + struct async muxer; + int err_fd = 0; if (unpacker_error) { for (cmd = commands; cmd; cmd = cmd->next) @@@ -1465,29 -1327,14 +1465,29 @@@ return; } + if (use_sideband) { + memset(&muxer, 0, sizeof(muxer)); + muxer.proc = copy_to_sideband; + muxer.in = -1; + if (!start_async(&muxer)) + err_fd = muxer.in; + /* ...else, continue without relaying sideband */ + } + data.cmds = commands; data.si = si; - if (check_connected(iterate_receive_command_list, &data, NULL)) + opt.err_fd = err_fd; + opt.progress = err_fd && !quiet; + opt.env = tmp_objdir_env(tmp_objdir); + if (check_connected(iterate_receive_command_list, &data, &opt)) set_connectivity_errors(commands, si); + if (use_sideband) + finish_async(&muxer); + reject_updates_to_hidden(commands); - if (run_receive_hook(commands, "pre-receive", 0)) { + if (run_receive_hook(commands, "pre-receive", 0, push_options)) { for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) cmd->error_string = "pre-receive hook declined"; @@@ -1495,23 -1342,10 +1495,23 @@@ return; } + /* + * Now we'll start writing out refs, which means the objects need + * to be in their final positions so that other processes can see them. + */ + if (tmp_objdir_migrate(tmp_objdir) < 0) { + for (cmd = commands; cmd; cmd = cmd->next) { + if (!cmd->error_string) + cmd->error_string = "unable to migrate objects to permanent storage"; + } + return; + } + tmp_objdir = NULL; + check_aliased_updates(commands); free(head_name_to_free); - head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL); + head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL); if (use_atomic) execute_commands_atomic(commands, si); @@@ -1526,23 -1360,25 +1526,23 @@@ static struct command **queue_command(s const char *line, int linelen) { - unsigned char old_sha1[20], new_sha1[20]; + struct object_id old_oid, new_oid; struct command *cmd; const char *refname; int reflen; + const char *p; - if (linelen < 83 || - line[40] != ' ' || - line[81] != ' ' || - get_sha1_hex(line, old_sha1) || - get_sha1_hex(line + 41, new_sha1)) + if (parse_oid_hex(line, &old_oid, &p) || + *p++ != ' ' || + parse_oid_hex(p, &new_oid, &p) || + *p++ != ' ') die("protocol error: expected old/new/ref, got '%s'", line); - refname = line + 82; - reflen = linelen - 82; - cmd = xcalloc(1, st_add3(sizeof(struct command), reflen, 1)); - hashcpy(cmd->old_sha1, old_sha1); - hashcpy(cmd->new_sha1, new_sha1); - memcpy(cmd->ref_name, refname, reflen); - cmd->ref_name[reflen] = '\0'; + refname = p; + reflen = linelen - (p - line); + FLEX_ALLOC_MEM(cmd, ref_name, refname, reflen); + oidcpy(&cmd->old_oid, &old_oid); + oidcpy(&cmd->new_oid, &new_oid); *tail = cmd; return &cmd->next; } @@@ -1564,12 -1400,12 +1564,12 @@@ static void queue_commands_from_cert(st while (boc < eoc) { const char *eol = memchr(boc, '\n', eoc - boc); - tail = queue_command(tail, boc, eol ? eol - boc : eoc - eol); + tail = queue_command(tail, boc, eol ? eol - boc : eoc - boc); boc = eol ? eol + 1 : eoc; } } -static struct command *read_head_info(struct sha1_array *shallow) +static struct command *read_head_info(struct oid_array *shallow) { struct command *commands = NULL; struct command **p = &commands; @@@ -1581,12 -1417,12 +1581,12 @@@ if (!line) break; - if (len == 48 && starts_with(line, "shallow ")) { - unsigned char sha1[20]; - if (get_sha1_hex(line + 8, sha1)) + if (len > 8 && starts_with(line, "shallow ")) { + struct object_id oid; + if (get_oid_hex(line + 8, &oid)) die("protocol error: expected shallow sha, got '%s'", line + 8); - sha1_array_append(shallow, sha1); + oid_array_append(shallow, &oid); continue; } @@@ -1602,9 -1438,6 +1602,9 @@@ if (advertise_atomic_push && parse_feature_request(feature_list, "atomic")) use_atomic = 1; + if (advertise_push_options + && parse_feature_request(feature_list, "push-options")) + use_push_options = 1; } if (!strcmp(line, "push-cert")) { @@@ -1637,21 -1470,6 +1637,21 @@@ return commands; } +static void read_push_options(struct string_list *options) +{ + while (1) { + char *line; + int len; + + line = packet_read_line(0, &len); + + if (!line) + break; + + string_list_append(options, line); + } +} + static const char *parse_pack_header(struct pack_header *hdr) { switch (read_pack_header(0, hdr)) { @@@ -1674,17 -1492,12 +1674,17 @@@ static const char *pack_lockfile; +static void push_header_arg(struct argv_array *args, struct pack_header *hdr) +{ + argv_array_pushf(args, "--pack_header=%"PRIu32",%"PRIu32, + ntohl(hdr->hdr_version), ntohl(hdr->hdr_entries)); +} + static const char *unpack(int err_fd, struct shallow_info *si) { struct pack_header hdr; const char *hdr_err; int status; - char hdr_arg[38]; struct child_process child = CHILD_PROCESS_INIT; int fsck_objects = (receive_fsck_objects >= 0 ? receive_fsck_objects @@@ -1698,6 -1511,9 +1698,6 @@@ close(err_fd); return hdr_err; } - snprintf(hdr_arg, sizeof(hdr_arg), - "--pack_header=%"PRIu32",%"PRIu32, - ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries)); if (si->nr_ours || si->nr_theirs) { alt_shallow_file = setup_temporary_shallow(si->shallow); @@@ -1705,32 -1521,13 +1705,32 @@@ argv_array_push(&child.args, alt_shallow_file); } + tmp_objdir = tmp_objdir_create(); + if (!tmp_objdir) { + if (err_fd > 0) + close(err_fd); + return "unable to create temporary object directory"; + } + child.env = tmp_objdir_env(tmp_objdir); + + /* + * Normally we just pass the tmp_objdir environment to the child + * processes that do the heavy lifting, but we may need to see these + * objects ourselves to set up shallow information. + */ + tmp_objdir_add_as_alternate(tmp_objdir); + if (ntohl(hdr.hdr_entries) < unpack_limit) { - argv_array_pushl(&child.args, "unpack-objects", hdr_arg, NULL); + argv_array_push(&child.args, "unpack-objects"); + push_header_arg(&child.args, &hdr); if (quiet) argv_array_push(&child.args, "-q"); if (fsck_objects) argv_array_pushf(&child.args, "--strict%s", fsck_msg_types.buf); + if (max_input_size) + argv_array_pushf(&child.args, "--max-input-size=%"PRIuMAX, + (uintmax_t)max_input_size); child.no_stdout = 1; child.err = err_fd; child.git_cmd = 1; @@@ -1738,30 -1535,23 +1738,30 @@@ if (status) return "unpack-objects abnormal exit"; } else { - char hostname[256]; + char hostname[HOST_NAME_MAX + 1]; - argv_array_pushl(&child.args, "index-pack", - "--stdin", hdr_arg, NULL); + argv_array_pushl(&child.args, "index-pack", "--stdin", NULL); + push_header_arg(&child.args, &hdr); - if (gethostname(hostname, sizeof(hostname))) + if (xgethostname(hostname, sizeof(hostname))) xsnprintf(hostname, sizeof(hostname), "localhost"); argv_array_pushf(&child.args, "--keep=receive-pack %"PRIuMAX" on %s", (uintmax_t)getpid(), hostname); + if (!quiet && err_fd) + argv_array_push(&child.args, "--show-resolving-progress"); + if (use_sideband) + argv_array_push(&child.args, "--report-end-of-input"); if (fsck_objects) argv_array_pushf(&child.args, "--strict%s", fsck_msg_types.buf); if (!reject_thin) argv_array_push(&child.args, "--fix-thin"); + if (max_input_size) + argv_array_pushf(&child.args, "--max-input-size=%"PRIuMAX, + (uintmax_t)max_input_size); child.out = -1; child.err = err_fd; child.git_cmd = 1; @@@ -1773,7 -1563,7 +1773,7 @@@ status = finish_command(&child); if (status) return "index-pack abnormal exit"; - reprepare_packed_git(); + reprepare_packed_git(the_repository); } return NULL; } @@@ -1786,7 -1576,6 +1786,7 @@@ static const char *unpack_with_sideband if (!use_sideband) return unpack(0, si); + use_keepalive = KEEPALIVE_AFTER_NUL; memset(&muxer, 0, sizeof(muxer)); muxer.proc = copy_to_sideband; muxer.in = -1; @@@ -1802,7 -1591,7 +1802,7 @@@ static void prepare_shallow_update(struct command *commands, struct shallow_info *si) { - int i, j, k, bitmap_size = (si->ref->nr + 31) / 32; + int i, j, k, bitmap_size = DIV_ROUND_UP(si->ref->nr, 32); ALLOC_ARRAY(si->used_shallow, si->shallow->nr); assign_shallow_commits_to_refs(si, si->used_shallow, NULL); @@@ -1839,7 -1628,7 +1839,7 @@@ /* * keep hooks happy by forcing a temporary shallow file via * env variable because we can't add --shallow-file to every - * command. check_everything_connected() will be done with + * command. check_connected() will be done with * true .git/shallow though. */ setenv(GIT_SHALLOW_FILE_ENVIRONMENT, alt_shallow_file, 1); @@@ -1847,7 -1636,7 +1847,7 @@@ static void update_shallow_info(struct command *commands, struct shallow_info *si, - struct sha1_array *ref) + struct oid_array *ref) { struct command *cmd; int *ref_status; @@@ -1858,9 -1647,9 +1858,9 @@@ } for (cmd = commands; cmd; cmd = cmd->next) { - if (is_null_sha1(cmd->new_sha1)) + if (is_null_oid(&cmd->new_oid)) continue; - sha1_array_append(ref, cmd->new_sha1); + oid_array_append(ref, &cmd->new_oid); cmd->index = ref->nr - 1; } si->ref = ref; @@@ -1873,7 -1662,7 +1873,7 @@@ ALLOC_ARRAY(ref_status, ref->nr); assign_shallow_commits_to_refs(si, NULL, ref_status); for (cmd = commands; cmd; cmd = cmd->next) { - if (is_null_sha1(cmd->new_sha1)) + if (is_null_oid(&cmd->new_oid)) continue; if (ref_status[cmd->index]) { cmd->error_string = "shallow update not allowed"; @@@ -1911,7 -1700,7 +1911,7 @@@ static int delete_only(struct command * { struct command *cmd; for (cmd = commands; cmd; cmd = cmd->next) { - if (!is_null_sha1(cmd->new_sha1)) + if (!is_null_oid(&cmd->new_oid)) return 0; } return 1; @@@ -1921,8 -1710,8 +1921,8 @@@ int cmd_receive_pack(int argc, const ch { int advertise_refs = 0; struct command *commands; - struct sha1_array shallow = SHA1_ARRAY_INIT; - struct sha1_array ref = SHA1_ARRAY_INIT; + struct oid_array shallow = OID_ARRAY_INIT; + struct oid_array ref = OID_ARRAY_INIT; struct shallow_info si; struct option options[] = { @@@ -1958,28 -1747,6 +1958,28 @@@ else if (0 <= receive_unpack_limit) unpack_limit = receive_unpack_limit; + switch (determine_protocol_version_server()) { + case protocol_v2: + /* + * push support for protocol v2 has not been implemented yet, + * so ignore the request to use v2 and fallback to using v0. + */ + break; + case protocol_v1: + /* + * v1 is just the original protocol with a version string, + * so just fall through after writing the version string. + */ + if (advertise_refs || !stateless_rpc) + packet_write_fmt(1, "version 1\n"); + + /* fallthrough */ + case protocol_v0: + break; + case protocol_unknown_version: + BUG("unknown protocol version"); + } + if (advertise_refs || !stateless_rpc) { write_head_info(); } @@@ -1988,15 -1755,6 +1988,15 @@@ if ((commands = read_head_info(&shallow)) != NULL) { const char *unpack_status = NULL; + struct string_list push_options = STRING_LIST_INIT_DUP; + + if (use_push_options) + read_push_options(&push_options); + if (!check_cert_push_options(&push_options)) { + struct command *cmd; + for (cmd = commands; cmd; cmd = cmd->next) + cmd->error_string = "inconsistent push options"; + } prepare_shallow_info(&si, &shallow); if (!si.nr_ours && !si.nr_theirs) @@@ -2005,17 -1763,13 +2005,17 @@@ unpack_status = unpack_with_sideband(&si); update_shallow_info(commands, &si, &ref); } - execute_commands(commands, unpack_status, &si); + use_keepalive = KEEPALIVE_ALWAYS; + execute_commands(commands, unpack_status, &si, + &push_options); if (pack_lockfile) unlink_or_warn(pack_lockfile); if (report_status) report(commands, unpack_status); - run_receive_hook(commands, "post-receive", 1); + run_receive_hook(commands, "post-receive", 1, + &push_options); run_update_post_hook(commands); + string_list_clear(&push_options, 0); if (auto_gc) { const char *argv_gc_auto[] = { "gc", "--auto", "--quiet", NULL, @@@ -2028,7 -1782,7 +2028,7 @@@ proc.git_cmd = 1; proc.argv = argv_gc_auto; - close_all_packs(); + close_all_packs(the_repository->objects); if (!start_command(&proc)) { if (use_sideband) copy_to_sideband(proc.err, -1, NULL); @@@ -2041,8 -1795,8 +2041,8 @@@ } if (use_sideband) packet_flush(1); - sha1_array_clear(&shallow); - sha1_array_clear(&ref); + oid_array_clear(&shallow); + oid_array_clear(&ref); free((void *)push_cert_nonce); return 0; }