diff options
Diffstat (limited to 'send-pack.c')
-rw-r--r-- | send-pack.c | 842 |
1 files changed, 495 insertions, 347 deletions
diff --git a/send-pack.c b/send-pack.c index 512b660e99..c6a4030738 100644 --- a/send-pack.c +++ b/send-pack.c @@ -1,430 +1,578 @@ -#include "cache.h" +#include "builtin.h" #include "commit.h" -#include "tag.h" #include "refs.h" #include "pkt-line.h" -#include "exec_cmd.h" +#include "sideband.h" +#include "run-command.h" +#include "remote.h" +#include "connect.h" +#include "send-pack.h" +#include "quote.h" +#include "transport.h" +#include "version.h" +#include "sha1-array.h" +#include "gpg-interface.h" +#include "cache.h" -static const char send_pack_usage[] = -"git-send-pack [--all] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n" -" --all and explicit <ref> specification are mutually exclusive."; -static const char *receivepack = "git-receive-pack"; -static int verbose; -static int send_all; -static int force_update; -static int use_thin_pack; +int option_parse_push_signed(const struct option *opt, + const char *arg, int unset) +{ + if (unset) { + *(int *)(opt->value) = SEND_PACK_PUSH_CERT_NEVER; + return 0; + } + switch (git_parse_maybe_bool(arg)) { + case 1: + *(int *)(opt->value) = SEND_PACK_PUSH_CERT_ALWAYS; + return 0; + case 0: + *(int *)(opt->value) = SEND_PACK_PUSH_CERT_NEVER; + return 0; + } + if (!strcasecmp("if-asked", arg)) { + *(int *)(opt->value) = SEND_PACK_PUSH_CERT_IF_ASKED; + return 0; + } + die("bad %s argument: %s", opt->long_name, arg); +} + +static int feed_object(const unsigned char *sha1, int fd, int negative) +{ + char buf[42]; + + if (negative && !has_sha1_file(sha1)) + return 1; + + memcpy(buf + negative, sha1_to_hex(sha1), 40); + if (negative) + buf[0] = '^'; + buf[40 + negative] = '\n'; + return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs"); +} /* * Make a pack stream and spit it out into file descriptor fd */ -static int pack_objects(int fd, struct ref *refs) +static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, struct send_pack_args *args) { - int pipe_fd[2]; - pid_t pid; - - if (pipe(pipe_fd) < 0) - return error("send-pack: pipe failed"); - pid = fork(); - if (pid < 0) - return error("send-pack: unable to fork git-pack-objects"); - if (!pid) { - /* - * The child becomes pack-objects --revs; we feed - * the revision parameters to it via its stdin and - * let its stdout go back to the other end. - */ - static const char *args[] = { - "pack-objects", - "--all-progress", - "--revs", - "--stdout", - NULL, - NULL, - }; - if (use_thin_pack) - args[4] = "--thin"; - dup2(pipe_fd[0], 0); - dup2(fd, 1); - close(pipe_fd[0]); - close(pipe_fd[1]); - close(fd); - execv_git_cmd(args); - die("git-pack-objects exec failed (%s)", strerror(errno)); - } + /* + * The child becomes pack-objects --revs; we feed + * the revision parameters to it via its stdin and + * let its stdout go back to the other end. + */ + const char *argv[] = { + "pack-objects", + "--all-progress-implied", + "--revs", + "--stdout", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + }; + struct child_process po = CHILD_PROCESS_INIT; + int i; + + i = 4; + if (args->use_thin_pack) + argv[i++] = "--thin"; + if (args->use_ofs_delta) + argv[i++] = "--delta-base-offset"; + if (args->quiet || !args->progress) + 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; + po.git_cmd = 1; + if (start_command(&po)) + die_errno("git pack-objects failed"); /* * We feed the pack-objects we just spawned with revision * parameters by writing to the pipe. */ - close(pipe_fd[0]); - close(fd); + for (i = 0; i < extra->nr; i++) + if (!feed_object(extra->sha1[i], po.in, 1)) + break; while (refs) { - char buf[42]; - if (!is_null_sha1(refs->old_sha1) && - has_sha1_file(refs->old_sha1)) { - memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40); - buf[0] = '^'; - buf[41] = '\n'; - if (!write_or_whine(pipe_fd[1], buf, 42, - "send-pack: send refs")) - break; - } - if (!is_null_sha1(refs->new_sha1)) { - memcpy(buf, sha1_to_hex(refs->new_sha1), 40); - buf[40] = '\n'; - if (!write_or_whine(pipe_fd[1], buf, 41, - "send-pack: send refs")) - break; - } + !feed_object(refs->old_sha1, po.in, 1)) + break; + if (!is_null_sha1(refs->new_sha1) && + !feed_object(refs->new_sha1, po.in, 0)) + break; refs = refs->next; } - close(pipe_fd[1]); - for (;;) { - int status, code; - pid_t waiting = waitpid(pid, &status, 0); + close(po.in); - if (waiting < 0) { - if (errno == EINTR) - continue; - return error("waitpid failed (%s)", strerror(errno)); + if (args->stateless_rpc) { + char *buf = xmalloc(LARGE_PACKET_MAX); + while (1) { + ssize_t n = xread(po.out, buf, LARGE_PACKET_MAX); + if (n <= 0) + break; + send_sideband(fd, -1, buf, n, LARGE_PACKET_MAX); } - if ((waiting != pid) || WIFSIGNALED(status) || - !WIFEXITED(status)) - return error("pack-objects died with strange error"); - code = WEXITSTATUS(status); - if (code) - return -code; - return 0; + free(buf); + close(po.out); + po.out = -1; } -} -static void unmark_and_free(struct commit_list *list, unsigned int mark) -{ - while (list) { - struct commit_list *temp = list; - temp->item->object.flags &= ~mark; - list = temp->next; - free(temp); - } + if (finish_command(&po)) + return -1; + return 0; } -static int ref_newer(const unsigned char *new_sha1, - const unsigned char *old_sha1) +static int receive_status(int in, struct ref *refs) { - struct object *o; - struct commit *old, *new; - struct commit_list *list, *used; - int found = 0; - - /* Both new and old must be commit-ish and new is descendant of - * old. Otherwise we require --force. - */ - o = deref_tag(parse_object(old_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - old = (struct commit *) o; - - o = deref_tag(parse_object(new_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - new = (struct commit *) o; + struct ref *hint; + int ret = 0; + char *line = packet_read_line(in, NULL); + if (!starts_with(line, "unpack ")) + return error("did not receive remote status"); + if (strcmp(line, "unpack ok")) { + error("unpack failed: %s", line + 7); + ret = -1; + } + hint = NULL; + while (1) { + char *refname; + char *msg; + line = packet_read_line(in, NULL); + if (!line) + break; + if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) { + error("invalid ref status from remote: %s", line); + ret = -1; + break; + } - if (parse_commit(new) < 0) - return 0; + refname = line + 3; + msg = strchr(refname, ' '); + if (msg) + *msg++ = '\0'; + + /* first try searching at our hint, falling back to all refs */ + if (hint) + hint = find_ref_by_name(hint, refname); + if (!hint) + hint = find_ref_by_name(refs, refname); + if (!hint) { + warning("remote reported status on unknown ref: %s", + refname); + continue; + } + if (hint->status != REF_STATUS_EXPECTING_REPORT) { + warning("remote reported status on unexpected ref: %s", + refname); + continue; + } - used = list = NULL; - commit_list_insert(new, &list); - while (list) { - new = pop_most_recent_commit(&list, 1); - commit_list_insert(new, &used); - if (new == old) { - found = 1; - break; + if (line[0] == 'o' && line[1] == 'k') + hint->status = REF_STATUS_OK; + else { + hint->status = REF_STATUS_REMOTE_REJECT; + ret = -1; } + if (msg) + hint->remote_status = xstrdup(msg); + /* start our next search from the next ref */ + hint = hint->next; } - unmark_and_free(list, 1); - unmark_and_free(used, 1); - return found; + return ret; } -static struct ref *local_refs, **local_tail; -static struct ref *remote_refs, **remote_tail; +static int sideband_demux(int in, int out, void *data) +{ + int *fd = data, ret; +#ifdef NO_PTHREADS + close(fd[1]); +#endif + ret = recv_sideband("send-pack", fd[0], out); + close(out); + return ret; +} -static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) +static int advertise_shallow_grafts_cb(const struct commit_graft *graft, void *cb) { - struct ref *ref; - int len = strlen(refname) + 1; - ref = xcalloc(1, sizeof(*ref) + len); - hashcpy(ref->new_sha1, sha1); - memcpy(ref->name, refname, len); - *local_tail = ref; - local_tail = &ref->next; + struct strbuf *sb = cb; + if (graft->nr_parent == -1) + packet_buf_write(sb, "shallow %s\n", oid_to_hex(&graft->oid)); return 0; } -static void get_local_heads(void) +static void advertise_shallow_grafts_buf(struct strbuf *sb) { - local_tail = &local_refs; - for_each_ref(one_local_ref, NULL); + if (!is_repository_shallow()) + return; + for_each_commit_graft(advertise_shallow_grafts_cb, sb); } -static int receive_status(int in) +#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) { - char line[1000]; - int ret = 0; - int len = packet_read_line(in, line, sizeof(line)); - if (len < 10 || memcmp(line, "unpack ", 7)) { - fprintf(stderr, "did not receive status back\n"); - return -1; + if (!ref->peer_ref && !args->send_mirror) + return CHECK_REF_NO_PUSH; + + /* Check for statuses set by set_ref_status_for_push() */ + switch (ref->status) { + case REF_STATUS_REJECT_NONFASTFORWARD: + case REF_STATUS_REJECT_ALREADY_EXISTS: + case REF_STATUS_REJECT_FETCH_FIRST: + 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 CHECK_REF_UPTODATE; + default: + return 0; } - if (memcmp(line, "unpack ok\n", 10)) { - fputs(line, stderr); - ret = -1; +} + +/* + * the beginning of the next line, or the end of buffer. + * + * NEEDSWORK: perhaps move this to git-compat-util.h or somewhere and + * convert many similar uses found by "git grep -A4 memchr". + */ +static const char *next_line(const char *line, size_t len) +{ + const char *nl = memchr(line, '\n', len); + if (!nl) + return line + len; /* incomplete line */ + return nl + 1; +} + +static int generate_push_cert(struct strbuf *req_buf, + const struct ref *remote_refs, + struct send_pack_args *args, + const char *cap_string, + const char *push_cert_nonce) +{ + const struct ref *ref; + char *signing_key = xstrdup(get_signing_key()); + const char *cp, *np; + struct strbuf cert = STRBUF_INIT; + int update_seen = 0; + + strbuf_addf(&cert, "certificate version 0.1\n"); + strbuf_addf(&cert, "pusher %s ", signing_key); + datestamp(&cert); + strbuf_addch(&cert, '\n'); + if (args->url && *args->url) { + char *anon_url = transport_anonymize_url(args->url); + strbuf_addf(&cert, "pushee %s\n", anon_url); + free(anon_url); } - while (1) { - len = packet_read_line(in, line, sizeof(line)); - if (!len) - break; - if (len < 3 || - (memcmp(line, "ok", 2) && memcmp(line, "ng", 2))) { - fprintf(stderr, "protocol error: %s\n", line); - ret = -1; - break; + if (push_cert_nonce[0]) + strbuf_addf(&cert, "nonce %s\n", push_cert_nonce); + strbuf_addstr(&cert, "\n"); + + for (ref = remote_refs; ref; ref = ref->next) { + if (check_to_send_update(ref, args) < 0) + continue; + update_seen = 1; + strbuf_addf(&cert, "%s %s %s\n", + sha1_to_hex(ref->old_sha1), + sha1_to_hex(ref->new_sha1), + ref->name); + } + if (!update_seen) + goto free_return; + + if (sign_buffer(&cert, &cert, signing_key)) + die(_("failed to sign the push certificate")); + + packet_buf_write(req_buf, "push-cert%c%s", 0, cap_string); + for (cp = cert.buf; cp < cert.buf + cert.len; cp = np) { + np = next_line(cp, cert.buf + cert.len - cp); + packet_buf_write(req_buf, + "%.*s", (int)(np - cp), cp); + } + packet_buf_write(req_buf, "push-cert-end\n"); + +free_return: + free(signing_key); + strbuf_release(&cert); + 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 */ } - if (!memcmp(line, "ok", 2)) + } + return error("atomic push failed for ref %s. status: %d\n", + failing_ref->name, failing_ref->status); +} + +#define NONCE_LEN_LIMIT 256 + +static void reject_invalid_nonce(const char *nonce, int len) +{ + int i = 0; + + if (NONCE_LEN_LIMIT <= len) + die("the receiving end asked to sign an invalid nonce <%.*s>", + len, nonce); + + for (i = 0; i < len; i++) { + int ch = nonce[i] & 0xFF; + if (isalnum(ch) || + ch == '-' || ch == '.' || + ch == '/' || ch == '+' || + ch == '=' || ch == '_') continue; - fputs(line, stderr); - ret = -1; + die("the receiving end asked to sign an invalid nonce <%.*s>", + len, nonce); } - return ret; } -static int send_pack(int in, int out, int nr_refspec, char **refspec) +int send_pack(struct send_pack_args *args, + int fd[], struct child_process *conn, + struct ref *remote_refs, + struct sha1_array *extra_have) { + int in = fd[0]; + int out = fd[1]; + struct strbuf req_buf = STRBUF_INIT; + struct strbuf cap_buf = STRBUF_INIT; struct ref *ref; - int new_refs; - int ret = 0; - int ask_for_status_report = 0; + int need_pack_data = 0; int allow_deleting_refs = 0; - int expect_status_report = 0; - - /* No funny business with the matcher */ - remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL); - get_local_heads(); + int status_report = 0; + 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; + const char *push_cert_nonce = NULL; /* Does the other end support the reporting? */ if (server_supports("report-status")) - ask_for_status_report = 1; + status_report = 1; if (server_supports("delete-refs")) allow_deleting_refs = 1; - - /* match them up */ - if (!remote_tail) - remote_tail = &remote_refs; - if (match_refs(local_refs, remote_refs, &remote_tail, - nr_refspec, refspec, send_all)) - return -1; + if (server_supports("ofs-delta")) + args->use_ofs_delta = 1; + if (server_supports("side-band-64k")) + use_sideband = 1; + if (server_supports("quiet")) + quiet_supported = 1; + if (server_supports("agent")) + agent_supported = 1; + if (server_supports("no-thin")) + args->use_thin_pack = 0; + if (server_supports("atomic")) + atomic_supported = 1; + + if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) { + int len; + push_cert_nonce = server_feature_value("push-cert", &len); + if (push_cert_nonce) { + reject_invalid_nonce(push_cert_nonce, len); + push_cert_nonce = xmemdupz(push_cert_nonce, len); + } else if (args->push_cert == SEND_PACK_PUSH_CERT_ALWAYS) { + die(_("the receiving end does not support --signed push")); + } else if (args->push_cert == SEND_PACK_PUSH_CERT_IF_ASKED) { + warning(_("not sending a push certificate since the" + " receiving end does not support --signed" + " push")); + } + } if (!remote_refs) { - fprintf(stderr, "No refs in common and none specified; doing nothing.\n"); + fprintf(stderr, "No refs in common and none specified; doing nothing.\n" + "Perhaps you should specify a branch such as 'master'.\n"); return 0; } + if (args->atomic && !atomic_supported) + die(_("the receiving end does not support --atomic push")); + + use_atomic = atomic_supported && args->atomic; + + if (status_report) + strbuf_addstr(&cap_buf, " report-status"); + if (use_sideband) + 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()); /* - * Finally, tell the other end! + * NEEDSWORK: why does delete-refs have to be so specific to + * send-pack machinery that set_ref_status_for_push() cannot + * set this bit for us??? */ - new_refs = 0; - for (ref = remote_refs; ref; ref = ref->next) { - char old_hex[60], *new_hex; - int delete_ref; + for (ref = remote_refs; ref; ref = ref->next) + if (ref->deletion && !allow_deleting_refs) + ref->status = REF_STATUS_REJECT_NODELETE; - if (!ref->peer_ref) - continue; + if (!args->dry_run) + advertise_shallow_grafts_buf(&req_buf); - delete_ref = is_null_sha1(ref->peer_ref->new_sha1); - if (delete_ref && !allow_deleting_refs) { - error("remote does not support deleting refs"); - ret = -2; - continue; - } - if (!delete_ref && - !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) { - if (verbose) - fprintf(stderr, "'%s': up-to-date\n", ref->name); - continue; - } + if (!args->dry_run && push_cert_nonce) + cmds_sent = generate_push_cert(&req_buf, remote_refs, args, + cap_buf.buf, push_cert_nonce); - /* This part determines what can overwrite what. - * The rules are: - * - * (0) you can always use --force or +A:B notation to - * selectively force individual ref pairs. - * - * (1) if the old thing does not exist, it is OK. - * - * (2) if you do not have the old thing, you are not allowed - * to overwrite it; you would not know what you are losing - * otherwise. - * - * (3) if both new and old are commit-ish, and new is a - * descendant of old, it is OK. - * - * (4) regardless of all of the above, removing :B is - * always allowed. - */ - - if (!force_update && - !delete_ref && - !is_null_sha1(ref->old_sha1) && - !ref->force) { - if (!has_sha1_file(ref->old_sha1) || - !ref_newer(ref->peer_ref->new_sha1, - ref->old_sha1)) { - /* We do not have the remote ref, or - * we know that the remote ref is not - * an ancestor of what we are trying to - * push. Either way this can be losing - * commits at the remote end and likely - * we were not up to date to begin with. - */ - error("remote '%s' is not a strict " - "subset of local ref '%s'. " - "maybe you are not up-to-date and " - "need to pull first?", - ref->name, - ref->peer_ref->name); - ret = -2; - continue; - } + /* + * Clear the status for each ref and see if we need to send + * the pack data. + */ + for (ref = remote_refs; ref; ref = ref->next) { + 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; } - hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); - if (!delete_ref) - new_refs++; - strcpy(old_hex, sha1_to_hex(ref->old_sha1)); - new_hex = sha1_to_hex(ref->new_sha1); + if (!ref->deletion) + need_pack_data = 1; - if (ask_for_status_report) { - packet_write(out, "%s %s %s%c%s", - old_hex, new_hex, ref->name, 0, - "report-status"); - ask_for_status_report = 0; - expect_status_report = 1; - } + if (args->dry_run || !status_report) + ref->status = REF_STATUS_OK; else - packet_write(out, "%s %s %s", - old_hex, new_hex, ref->name); - if (delete_ref) - fprintf(stderr, "deleting '%s'\n", ref->name); - else { - fprintf(stderr, "updating '%s'", ref->name); - if (strcmp(ref->name, ref->peer_ref->name)) - fprintf(stderr, " using '%s'", - ref->peer_ref->name); - fprintf(stderr, "\n from %s\n to %s\n", - old_hex, new_hex); - } + ref->status = REF_STATUS_EXPECTING_REPORT; } - packet_flush(out); - if (new_refs) - ret = pack_objects(out, remote_refs); - close(out); - - if (expect_status_report) { - if (receive_status(in)) - ret = -4; - } + /* + * Finally, tell the other end! + */ + for (ref = remote_refs; ref; ref = ref->next) { + char *old_hex, *new_hex; - if (!new_refs && ret == 0) - fprintf(stderr, "Everything up-to-date\n"); - return ret; -} + if (args->dry_run || push_cert_nonce) + continue; -static void verify_remote_names(int nr_heads, char **heads) -{ - int i; + if (check_to_send_update(ref, args) < 0) + continue; - for (i = 0; i < nr_heads; i++) { - const char *remote = strchr(heads[i], ':'); + old_hex = sha1_to_hex(ref->old_sha1); + new_hex = sha1_to_hex(ref->new_sha1); + if (!cmds_sent) { + packet_buf_write(&req_buf, + "%s %s %s%c%s", + old_hex, new_hex, ref->name, 0, + cap_buf.buf); + cmds_sent = 1; + } else { + packet_buf_write(&req_buf, "%s %s %s", + old_hex, new_hex, ref->name); + } + } - remote = remote ? (remote + 1) : heads[i]; - switch (check_ref_format(remote)) { - case 0: /* ok */ - case -2: /* ok but a single level -- that is fine for - * a match pattern. - */ - continue; + if (args->stateless_rpc) { + if (!args->dry_run && (cmds_sent || is_repository_shallow())) { + packet_buf_flush(&req_buf); + send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX); } - die("remote part of refspec is not a valid name in %s", - heads[i]); + } else { + write_or_die(out, req_buf.buf, req_buf.len); + packet_flush(out); + } + strbuf_release(&req_buf); + strbuf_release(&cap_buf); + + if (use_sideband && cmds_sent) { + memset(&demux, 0, sizeof(demux)); + demux.proc = sideband_demux; + demux.data = fd; + demux.out = -1; + if (start_async(&demux)) + die("send-pack: unable to fork off sideband demultiplexer"); + in = demux.out; } -} -int main(int argc, char **argv) -{ - int i, nr_heads = 0; - char *dest = NULL; - char **heads = NULL; - int fd[2], ret; - pid_t pid; - - setup_git_directory(); - git_config(git_default_config); - - argv++; - for (i = 1; i < argc; i++, argv++) { - char *arg = *argv; - - if (*arg == '-') { - if (!prefixcmp(arg, "--receive-pack=")) { - receivepack = arg + 15; - continue; - } - if (!prefixcmp(arg, "--exec=")) { - receivepack = arg + 7; - continue; - } - if (!strcmp(arg, "--all")) { - send_all = 1; - continue; - } - if (!strcmp(arg, "--force")) { - force_update = 1; - continue; - } - if (!strcmp(arg, "--verbose")) { - verbose = 1; - continue; - } - if (!strcmp(arg, "--thin")) { - use_thin_pack = 1; - continue; - } - usage(send_pack_usage); + if (need_pack_data && cmds_sent) { + if (pack_objects(out, remote_refs, extra_have, args) < 0) { + for (ref = remote_refs; ref; ref = ref->next) + ref->status = REF_STATUS_NONE; + if (args->stateless_rpc) + close(out); + if (git_connection_is_socket(conn)) + shutdown(fd[0], SHUT_WR); + if (use_sideband) + finish_async(&demux); + fd[1] = -1; + return -1; } - if (!dest) { - dest = arg; - continue; + if (!args->stateless_rpc) + /* Closed by pack_objects() via start_command() */ + fd[1] = -1; + } + if (args->stateless_rpc && cmds_sent) + packet_flush(out); + + if (status_report && cmds_sent) + ret = receive_status(in, remote_refs); + else + ret = 0; + if (args->stateless_rpc) + packet_flush(out); + + if (use_sideband && cmds_sent) { + if (finish_async(&demux)) { + error("error in sideband demultiplexer"); + ret = -1; } - heads = argv; - nr_heads = argc - i; - break; + close(demux.out); } - if (!dest) - usage(send_pack_usage); - if (heads && send_all) - usage(send_pack_usage); - verify_remote_names(nr_heads, heads); - - pid = git_connect(fd, dest, receivepack); - if (pid < 0) - return 1; - ret = send_pack(fd[0], fd[1], nr_heads, heads); - close(fd[0]); - close(fd[1]); - ret |= finish_connect(pid); - return !!ret; + + if (ret < 0) + return ret; + + if (args->porcelain) + return 0; + + for (ref = remote_refs; ref; ref = ref->next) { + switch (ref->status) { + case REF_STATUS_NONE: + case REF_STATUS_UPTODATE: + case REF_STATUS_OK: + break; + default: + return -1; + } + } + return 0; } |