diff options
Diffstat (limited to 'transport-helper.c')
-rw-r--r-- | transport-helper.c | 425 |
1 files changed, 300 insertions, 125 deletions
diff --git a/transport-helper.c b/transport-helper.c index 6f227e253b..a6bff8b308 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -5,10 +5,12 @@ #include "commit.h" #include "diff.h" #include "revision.h" -#include "quote.h" #include "remote.h" #include "string-list.h" #include "thread-utils.h" +#include "sigchain.h" +#include "argv-array.h" +#include "refs.h" static int debug; @@ -18,11 +20,15 @@ struct helper_data { FILE *out; unsigned fetch : 1, import : 1, + bidi_import : 1, export : 1, option : 1, push : 1, connect : 1, - no_disconnect_req : 1; + signed_tags : 1, + check_connectivity : 1, + no_disconnect_req : 1, + no_private_update : 1; char *export_marks; char *import_marks; /* These go from remote name (as in "list") to private name */ @@ -43,15 +49,15 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer) die_errno("Full write to remote helper failed"); } -static int recvline_fh(FILE *helper, struct strbuf *buffer) +static int recvline_fh(FILE *helper, struct strbuf *buffer, const char *name) { strbuf_reset(buffer); if (debug) fprintf(stderr, "Debug: Remote helper: Waiting...\n"); - if (strbuf_getline(buffer, helper, '\n') == EOF) { + if (strbuf_getline(buffer, helper) == EOF) { if (debug) fprintf(stderr, "Debug: Remote helper quit.\n"); - exit(128); + return 1; } if (debug) @@ -61,13 +67,7 @@ static int recvline_fh(FILE *helper, struct strbuf *buffer) static int recvline(struct helper_data *helper, struct strbuf *buffer) { - return recvline_fh(helper->out, buffer); -} - -static void xchgline(struct helper_data *helper, struct strbuf *buffer) -{ - sendline(helper, buffer); - recvline(helper, buffer); + return recvline_fh(helper->out, buffer, helper->name); } static void write_constant(int fd, const char *str) @@ -97,6 +97,8 @@ static void do_take_over(struct transport *transport) free(data); } +static void standard_options(struct transport *t); + static struct child_process *get_helper(struct transport *transport) { struct helper_data *data = transport->data; @@ -107,30 +109,23 @@ static struct child_process *get_helper(struct transport *transport) int refspec_alloc = 0; int duped; int code; - char git_dir_buf[sizeof(GIT_DIR_ENVIRONMENT) + PATH_MAX + 1]; - const char *helper_env[] = { - git_dir_buf, - NULL - }; - if (data->helper) return data->helper; - helper = xcalloc(1, sizeof(*helper)); + helper = xmalloc(sizeof(*helper)); + child_process_init(helper); helper->in = -1; helper->out = -1; helper->err = 0; - helper->argv = xcalloc(4, sizeof(*helper->argv)); - strbuf_addf(&buf, "git-remote-%s", data->name); - helper->argv[0] = strbuf_detach(&buf, NULL); - helper->argv[1] = transport->remote->name; - helper->argv[2] = remove_ext_force(transport->url); + argv_array_pushf(&helper->args, "git-remote-%s", data->name); + argv_array_push(&helper->args, transport->remote->name); + argv_array_push(&helper->args, remove_ext_force(transport->url)); helper->git_cmd = 0; helper->silent_exec_failure = 1; - snprintf(git_dir_buf, sizeof(git_dir_buf), "%s=%s", GIT_DIR_ENVIRONMENT, get_git_dir()); - helper->env = helper_env; + argv_array_pushf(&helper->env_array, "%s=%s", GIT_DIR_ENVIRONMENT, + get_git_dir()); code = start_command(helper); if (code < 0 && errno == ENOENT) @@ -142,7 +137,8 @@ static struct child_process *get_helper(struct transport *transport) data->no_disconnect_req = 0; /* - * Open the output as FILE* so strbuf_getline() can be used. + * Open the output as FILE* so strbuf_getline_*() family of + * functions can be used. * Do this with duped fd because fclose() will close the fd, * and stuff like taking over will require the fd to remain. */ @@ -154,9 +150,10 @@ static struct child_process *get_helper(struct transport *transport) write_constant(helper->in, "capabilities\n"); while (1) { - const char *capname; + const char *capname, *arg; int mandatory = 0; - recvline(data, &buf); + if (recvline(data, &buf)) + exit(128); if (!*buf.buf) break; @@ -177,28 +174,30 @@ static struct child_process *get_helper(struct transport *transport) data->push = 1; else if (!strcmp(capname, "import")) data->import = 1; + else if (!strcmp(capname, "bidi-import")) + data->bidi_import = 1; else if (!strcmp(capname, "export")) data->export = 1; - else if (!data->refspecs && !prefixcmp(capname, "refspec ")) { + else if (!strcmp(capname, "check-connectivity")) + data->check_connectivity = 1; + else if (!data->refspecs && skip_prefix(capname, "refspec ", &arg)) { ALLOC_GROW(refspecs, refspec_nr + 1, refspec_alloc); - refspecs[refspec_nr++] = xstrdup(capname + strlen("refspec ")); + refspecs[refspec_nr++] = xstrdup(arg); } else if (!strcmp(capname, "connect")) { data->connect = 1; - } else if (!prefixcmp(capname, "export-marks ")) { - struct strbuf arg = STRBUF_INIT; - strbuf_addstr(&arg, "--export-marks="); - strbuf_addstr(&arg, capname + strlen("export-marks ")); - data->export_marks = strbuf_detach(&arg, NULL); - } else if (!prefixcmp(capname, "import-marks")) { - struct strbuf arg = STRBUF_INIT; - strbuf_addstr(&arg, "--import-marks="); - strbuf_addstr(&arg, capname + strlen("import-marks ")); - data->import_marks = strbuf_detach(&arg, NULL); + } else if (!strcmp(capname, "signed-tags")) { + data->signed_tags = 1; + } else if (skip_prefix(capname, "export-marks ", &arg)) { + data->export_marks = xstrdup(arg); + } else if (skip_prefix(capname, "import-marks ", &arg)) { + data->import_marks = xstrdup(arg); + } else if (starts_with(capname, "no-private-update")) { + data->no_private_update = 1; } else if (mandatory) { die("Unknown mandatory capability %s. This remote " - "helper probably needs newer version of Git.\n", + "helper probably needs newer version of Git.", capname); } } @@ -206,36 +205,42 @@ static struct child_process *get_helper(struct transport *transport) int i; data->refspec_nr = refspec_nr; data->refspecs = parse_fetch_refspec(refspec_nr, refspecs); - for (i = 0; i < refspec_nr; i++) { + for (i = 0; i < refspec_nr; i++) free((char *)refspecs[i]); - } free(refspecs); + } else if (data->import || data->bidi_import || data->export) { + warning("This remote helper should implement refspec capability."); } strbuf_release(&buf); if (debug) fprintf(stderr, "Debug: Capabilities complete.\n"); + standard_options(transport); return data->helper; } static int disconnect_helper(struct transport *transport) { struct helper_data *data = transport->data; - struct strbuf buf = STRBUF_INIT; int res = 0; if (data->helper) { if (debug) fprintf(stderr, "Debug: Disconnecting.\n"); if (!data->no_disconnect_req) { - strbuf_addf(&buf, "\n"); - sendline(data, &buf); + /* + * Ignore write errors; there's nothing we can do, + * since we're about to close the pipe anyway. And the + * most likely error is EPIPE due to the helper dying + * to report an error itself. + */ + sigchain_push(SIGPIPE, SIG_IGN); + xwrite(data->helper->in, "\n", 1); + sigchain_pop(SIGPIPE); } close(data->helper->in); close(data->helper->out); fclose(data->out); res = finish_command(data->helper); - free((char *)data->helper->argv[0]); - free(data->helper->argv); free(data->helper); data->helper = NULL; } @@ -248,10 +253,11 @@ static const char *unsupported_options[] = { TRANS_OPT_THIN, TRANS_OPT_KEEP }; + static const char *boolean_options[] = { TRANS_OPT_THIN, TRANS_OPT_KEEP, - TRANS_OPT_FOLLOWTAGS + TRANS_OPT_FOLLOWTAGS, }; static int set_helper_option(struct transport *transport, @@ -285,11 +291,13 @@ static int set_helper_option(struct transport *transport, quote_c_style(value, &buf, NULL, 0); strbuf_addch(&buf, '\n'); - xchgline(data, &buf); + sendline(data, &buf); + if (recvline(data, &buf)) + exit(128); if (!strcmp(buf.buf, "ok")) ret = 0; - else if (!prefixcmp(buf.buf, "error")) { + else if (starts_with(buf.buf, "error")) { ret = -1; } else if (!strcmp(buf.buf, "unsupported")) ret = 1; @@ -333,30 +341,34 @@ static int fetch_with_fetch(struct transport *transport, int i; struct strbuf buf = STRBUF_INIT; - standard_options(transport); - for (i = 0; i < nr_heads; i++) { const struct ref *posn = to_fetch[i]; if (posn->status & REF_STATUS_UPTODATE) continue; strbuf_addf(&buf, "fetch %s %s\n", - sha1_to_hex(posn->old_sha1), posn->name); + oid_to_hex(&posn->old_oid), + posn->symref ? posn->symref : posn->name); } strbuf_addch(&buf, '\n'); sendline(data, &buf); while (1) { - recvline(data, &buf); + if (recvline(data, &buf)) + exit(128); - if (!prefixcmp(buf.buf, "lock ")) { + if (starts_with(buf.buf, "lock ")) { const char *name = buf.buf + 5; if (transport->pack_lockfile) warning("%s also locked %s", data->name, name); else transport->pack_lockfile = xstrdup(name); } + else if (data->check_connectivity && + data->transport_options.check_self_contained_and_connected && + !strcmp(buf.buf, "connectivity-ok")) + data->transport_options.self_contained_and_connected = 1; else if (!buf.len) break; else @@ -369,14 +381,21 @@ static int fetch_with_fetch(struct transport *transport, static int get_importer(struct transport *transport, struct child_process *fastimport) { struct child_process *helper = get_helper(transport); - memset(fastimport, 0, sizeof(*fastimport)); + struct helper_data *data = transport->data; + int cat_blob_fd, code; + child_process_init(fastimport); fastimport->in = helper->out; - fastimport->argv = xcalloc(5, sizeof(*fastimport->argv)); - fastimport->argv[0] = "fast-import"; - fastimport->argv[1] = "--quiet"; + argv_array_push(&fastimport->args, "fast-import"); + argv_array_push(&fastimport->args, debug ? "--stats" : "--quiet"); + if (data->bidi_import) { + cat_blob_fd = xdup(helper->in); + argv_array_pushf(&fastimport->args, "--cat-blob-fd=%d", cat_blob_fd); + } fastimport->git_cmd = 1; - return start_command(fastimport); + + code = start_command(fastimport); + return code; } static int get_exporter(struct transport *transport, @@ -385,22 +404,24 @@ static int get_exporter(struct transport *transport, { struct helper_data *data = transport->data; struct child_process *helper = get_helper(transport); - int argc = 0, i; - memset(fastexport, 0, sizeof(*fastexport)); + int i; + + child_process_init(fastexport); /* we need to duplicate helper->in because we want to use it after * fastexport is done with it. */ fastexport->out = dup(helper->in); - fastexport->argv = xcalloc(5 + revlist_args->nr, sizeof(*fastexport->argv)); - fastexport->argv[argc++] = "fast-export"; - fastexport->argv[argc++] = "--use-done-feature"; + argv_array_push(&fastexport->args, "fast-export"); + argv_array_push(&fastexport->args, "--use-done-feature"); + argv_array_push(&fastexport->args, data->signed_tags ? + "--signed-tags=verbatim" : "--signed-tags=warn-strip"); if (data->export_marks) - fastexport->argv[argc++] = data->export_marks; + argv_array_pushf(&fastexport->args, "--export-marks=%s.tmp", data->export_marks); if (data->import_marks) - fastexport->argv[argc++] = data->import_marks; + argv_array_pushf(&fastexport->args, "--import-marks=%s", data->import_marks); for (i = 0; i < revlist_args->nr; i++) - fastexport->argv[argc++] = revlist_args->items[i].string; + argv_array_push(&fastexport->args, revlist_args->items[i].string); fastexport->git_cmd = 1; return start_command(fastexport); @@ -425,29 +446,52 @@ static int fetch_with_import(struct transport *transport, if (posn->status & REF_STATUS_UPTODATE) continue; - strbuf_addf(&buf, "import %s\n", posn->name); + strbuf_addf(&buf, "import %s\n", + posn->symref ? posn->symref : posn->name); sendline(data, &buf); strbuf_reset(&buf); } write_constant(data->helper->in, "\n"); + /* + * remote-helpers that advertise the bidi-import capability are required to + * buffer the complete batch of import commands until this newline before + * sending data to fast-import. + * These helpers read back data from fast-import on their stdin, which could + * be mixed with import commands, otherwise. + */ if (finish_command(&fastimport)) die("Error while running fast-import"); - free(fastimport.argv); - fastimport.argv = NULL; + /* + * The fast-import stream of a remote helper that advertises + * the "refspec" capability writes to the refs named after the + * right hand side of the first refspec matching each ref we + * were fetching. + * + * (If no "refspec" capability was specified, for historical + * reasons we default to the equivalent of *:*.) + * + * Store the result in to_fetch[i].old_sha1. Callers such + * as "git fetch" can use the value to write feedback to the + * terminal, populate FETCH_HEAD, and determine what new value + * should be written to peer_ref if the update is a + * fast-forward or this is a forced update. + */ for (i = 0; i < nr_heads; i++) { - char *private; + char *private, *name; posn = to_fetch[i]; if (posn->status & REF_STATUS_UPTODATE) continue; + name = posn->symref ? posn->symref : posn->name; if (data->refspecs) - private = apply_refspecs(data->refspecs, data->refspec_nr, posn->name); + private = apply_refspecs(data->refspecs, data->refspec_nr, name); else - private = xstrdup(posn->name); + private = xstrdup(name); if (private) { - read_ref(private, posn->old_sha1); + if (read_ref(private, posn->old_oid.hash) < 0) + die("Could not read ref %s", private); free(private); } } @@ -496,7 +540,9 @@ static int process_connect_service(struct transport *transport, goto exit; sendline(data, &cmdbuf); - recvline_fh(input, &cmdbuf); + if (recvline_fh(input, &cmdbuf, name)) + exit(128); + if (!strcmp(cmdbuf.buf, "")) { data->no_disconnect_req = 1; if (debug) @@ -569,6 +615,16 @@ static int fetch(struct transport *transport, if (!count) return 0; + if (data->check_connectivity && + data->transport_options.check_self_contained_and_connected) + set_helper_option(transport, "check-connectivity", "true"); + + if (transport->cloning) + set_helper_option(transport, "cloning", "true"); + + if (data->transport_options.update_shallow) + set_helper_option(transport, "update-shallow", "true"); + if (data->fetch) return fetch_with_fetch(transport, nr_heads, to_fetch); @@ -578,21 +634,21 @@ static int fetch(struct transport *transport, return -1; } -static void push_update_ref_status(struct strbuf *buf, +static int push_update_ref_status(struct strbuf *buf, struct ref **ref, struct ref *remote_refs) { char *refname, *msg; - int status; + int status, forced = 0; - if (!prefixcmp(buf->buf, "ok ")) { + if (starts_with(buf->buf, "ok ")) { status = REF_STATUS_OK; refname = buf->buf + 3; - } else if (!prefixcmp(buf->buf, "error ")) { + } else if (starts_with(buf->buf, "error ")) { status = REF_STATUS_REMOTE_REJECT; refname = buf->buf + 6; } else - die("expected ok/error, helper said '%s'\n", buf->buf); + die("expected ok/error, helper said '%s'", buf->buf); msg = strchr(refname, ' '); if (msg) { @@ -621,6 +677,31 @@ static void push_update_ref_status(struct strbuf *buf, free(msg); msg = NULL; } + else if (!strcmp(msg, "already exists")) { + status = REF_STATUS_REJECT_ALREADY_EXISTS; + free(msg); + msg = NULL; + } + else if (!strcmp(msg, "fetch first")) { + status = REF_STATUS_REJECT_FETCH_FIRST; + free(msg); + msg = NULL; + } + else if (!strcmp(msg, "needs force")) { + status = REF_STATUS_REJECT_NEEDS_FORCE; + free(msg); + msg = NULL; + } + else if (!strcmp(msg, "stale info")) { + status = REF_STATUS_REJECT_STALE; + free(msg); + msg = NULL; + } + else if (!strcmp(msg, "forced update")) { + forced = 1; + free(msg); + msg = NULL; + } } if (*ref) @@ -629,7 +710,7 @@ static void push_update_ref_status(struct strbuf *buf, *ref = find_ref_by_name(remote_refs, refname); if (!*ref) { warning("helper reported unexpected status of %s", refname); - return; + return 1; } if ((*ref)->status != REF_STATUS_NONE) { @@ -638,36 +719,76 @@ static void push_update_ref_status(struct strbuf *buf, * status reported by the remote helper if the latter is 'no match'. */ if (status == REF_STATUS_NONE) - return; + return 1; } (*ref)->status = status; + (*ref)->forced_update |= forced; (*ref)->remote_status = msg; + return !(status == REF_STATUS_OK); } -static void push_update_refs_status(struct helper_data *data, - struct ref *remote_refs) +static int push_update_refs_status(struct helper_data *data, + struct ref *remote_refs, + int flags) { struct strbuf buf = STRBUF_INIT; struct ref *ref = remote_refs; + int ret = 0; + for (;;) { - recvline(data, &buf); + char *private; + + if (recvline(data, &buf)) { + ret = 1; + break; + } + if (!buf.len) break; - push_update_ref_status(&buf, &ref, remote_refs); + if (push_update_ref_status(&buf, &ref, remote_refs)) + continue; + + if (flags & TRANSPORT_PUSH_DRY_RUN || !data->refspecs || data->no_private_update) + continue; + + /* propagate back the update to the remote namespace */ + private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name); + if (!private) + continue; + update_ref("update by helper", private, ref->new_oid.hash, NULL, 0, 0); + free(private); } strbuf_release(&buf); + return ret; +} + +static void set_common_push_options(struct transport *transport, + const char *name, int flags) +{ + if (flags & TRANSPORT_PUSH_DRY_RUN) { + if (set_helper_option(transport, "dry-run", "true") != 0) + die("helper %s does not support dry-run", name); + } else if (flags & TRANSPORT_PUSH_CERT_ALWAYS) { + if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "true") != 0) + die("helper %s does not support --signed", name); + } else if (flags & TRANSPORT_PUSH_CERT_IF_ASKED) { + if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "if-asked") != 0) + die("helper %s does not support --signed=if-asked", name); + } } static int push_refs_with_push(struct transport *transport, - struct ref *remote_refs, int flags) + struct ref *remote_refs, int flags) { int force_all = flags & TRANSPORT_PUSH_FORCE; int mirror = flags & TRANSPORT_PUSH_MIRROR; struct helper_data *data = transport->data; struct strbuf buf = STRBUF_INIT; struct ref *ref; + struct string_list cas_options = STRING_LIST_INIT_DUP; + struct string_list_item *cas_option; get_helper(transport); if (!data->push) @@ -680,6 +801,8 @@ static int push_refs_with_push(struct transport *transport, /* Check for statuses set by set_ref_status_for_push() */ switch (ref->status) { case REF_STATUS_REJECT_NONFASTFORWARD: + case REF_STATUS_REJECT_STALE: + case REF_STATUS_REJECT_ALREADY_EXISTS: case REF_STATUS_UPTODATE: continue; default: @@ -696,28 +819,40 @@ static int push_refs_with_push(struct transport *transport, if (ref->peer_ref) strbuf_addstr(&buf, ref->peer_ref->name); else - strbuf_addstr(&buf, sha1_to_hex(ref->new_sha1)); + strbuf_addstr(&buf, oid_to_hex(&ref->new_oid)); } strbuf_addch(&buf, ':'); strbuf_addstr(&buf, ref->name); strbuf_addch(&buf, '\n'); + + /* + * The "--force-with-lease" options without explicit + * values to expect have already been expanded into + * the ref->old_oid_expect[] field; we can ignore + * transport->smart_options->cas altogether and instead + * can enumerate them from the refs. + */ + if (ref->expect_old_sha1) { + struct strbuf cas = STRBUF_INIT; + strbuf_addf(&cas, "%s:%s", + ref->name, oid_to_hex(&ref->old_oid_expect)); + string_list_append(&cas_options, strbuf_detach(&cas, NULL)); + } } - if (buf.len == 0) + if (buf.len == 0) { + string_list_clear(&cas_options, 0); return 0; - - standard_options(transport); - - if (flags & TRANSPORT_PUSH_DRY_RUN) { - if (set_helper_option(transport, "dry-run", "true") != 0) - die("helper %s does not support dry-run", data->name); } + for_each_string_list_item(cas_option, &cas_options) + set_helper_option(transport, "cas", cas_option->string); + set_common_push_options(transport, data->name, flags); + strbuf_addch(&buf, '\n'); sendline(data, &buf); strbuf_release(&buf); - push_update_refs_status(data, remote_refs); - return 0; + return push_update_refs_status(data, remote_refs, flags); } static int push_refs_with_export(struct transport *transport, @@ -726,43 +861,77 @@ static int push_refs_with_export(struct transport *transport, struct ref *ref; struct child_process *helper, exporter; struct helper_data *data = transport->data; - struct string_list revlist_args = STRING_LIST_INIT_NODUP; + struct string_list revlist_args = STRING_LIST_INIT_DUP; struct strbuf buf = STRBUF_INIT; + if (!data->refspecs) + die("remote-helper doesn't support push; refspec needed"); + + set_common_push_options(transport, data->name, flags); + if (flags & TRANSPORT_PUSH_FORCE) { + if (set_helper_option(transport, "force", "true") != 0) + warning("helper %s does not support 'force'", data->name); + } + helper = get_helper(transport); write_constant(helper->in, "export\n"); - strbuf_reset(&buf); - for (ref = remote_refs; ref; ref = ref->next) { char *private; - unsigned char sha1[20]; + struct object_id oid; - if (!data->refspecs) - continue; private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name); - if (private && !get_sha1(private, sha1)) { + if (private && !get_sha1(private, oid.hash)) { strbuf_addf(&buf, "^%s", private); string_list_append(&revlist_args, strbuf_detach(&buf, NULL)); + oidcpy(&ref->old_oid, &oid); } free(private); - if (ref->deletion) { - die("remote-helpers do not support ref deletion"); + if (ref->peer_ref) { + if (strcmp(ref->name, ref->peer_ref->name)) { + if (!ref->deletion) { + const char *name; + int flag; + + /* Follow symbolic refs (mainly for HEAD). */ + name = resolve_ref_unsafe( + ref->peer_ref->name, + RESOLVE_REF_READING, + oid.hash, &flag); + if (!name || !(flag & REF_ISSYMREF)) + name = ref->peer_ref->name; + + strbuf_addf(&buf, "%s:%s", name, ref->name); + } else + strbuf_addf(&buf, ":%s", ref->name); + + string_list_append(&revlist_args, "--refspec"); + string_list_append(&revlist_args, buf.buf); + strbuf_release(&buf); + } + if (!ref->deletion) + string_list_append(&revlist_args, ref->peer_ref->name); } - - if (ref->peer_ref) - string_list_append(&revlist_args, ref->peer_ref->name); - } if (get_exporter(transport, &exporter, &revlist_args)) die("Couldn't run fast-export"); + string_list_clear(&revlist_args, 1); + if (finish_command(&exporter)) die("Error while running fast-export"); - push_update_refs_status(data, remote_refs); + if (push_update_refs_status(data, remote_refs, flags)) + return 1; + + if (data->export_marks) { + strbuf_addf(&buf, "%s.tmp", data->export_marks); + rename(buf.buf, data->export_marks); + strbuf_release(&buf); + } + return 0; } @@ -831,7 +1000,8 @@ static struct ref *get_refs_list(struct transport *transport, int for_push) while (1) { char *eov, *eon; - recvline(data, &buf); + if (recvline(data, &buf)) + exit(128); if (!*buf.buf) break; @@ -847,11 +1017,14 @@ static struct ref *get_refs_list(struct transport *transport, int for_push) if (buf.buf[0] == '@') (*tail)->symref = xstrdup(buf.buf + 1); else if (buf.buf[0] != '?') - get_sha1_hex(buf.buf, (*tail)->old_sha1); + get_oid_hex(buf.buf, &(*tail)->old_oid); if (eon) { if (has_attribute(eon + 1, "unchanged")) { (*tail)->status |= REF_STATUS_UPTODATE; - read_ref((*tail)->name, (*tail)->old_sha1); + if (read_ref((*tail)->name, + (*tail)->old_oid.hash) < 0) + die(N_("Could not read ref %s"), + (*tail)->name); } } tail = &((*tail)->next); @@ -868,9 +1041,11 @@ static struct ref *get_refs_list(struct transport *transport, int for_push) int transport_helper_init(struct transport *transport, const char *name) { - struct helper_data *data = xcalloc(sizeof(*data), 1); + struct helper_data *data = xcalloc(1, sizeof(*data)); data->name = name; + transport_check_allowed(name); + if (getenv("GIT_TRANSPORT_HELPER_DEBUG")) debug = 1; @@ -894,6 +1069,7 @@ int transport_helper_init(struct transport *transport, const char *name) #define PBUFFERSIZE 8192 /* Print bidirectional transfer loop debug message. */ +__attribute__((format (printf, 1, 2))) static void transfer_debug(const char *fmt, ...) { va_list args; @@ -935,7 +1111,7 @@ struct unidirectional_transfer { int src_is_sock; /* Is destination socket? */ int dest_is_sock; - /* Transfer state (TRANSFERING/FLUSHING/FINISHED) */ + /* Transfer state (TRANSFERRING/FLUSHING/FINISHED) */ int state; /* Buffer. */ char buf[BUFFERSIZE]; @@ -979,7 +1155,7 @@ static int udt_do_read(struct unidirectional_transfer *t) return -1; } else if (bytes == 0) { transfer_debug("%s EOF (with %i bytes in buffer)", - t->src_name, t->bufuse); + t->src_name, (int)t->bufuse); t->state = SSTATE_FLUSHING; } else if (bytes > 0) { t->bufuse += bytes; @@ -1000,9 +1176,8 @@ static int udt_do_write(struct unidirectional_transfer *t) return 0; /* Nothing to write. */ transfer_debug("%s is writable", t->dest_name); - bytes = write(t->dest, t->buf, t->bufuse); - if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN && - errno != EINTR) { + bytes = xwrite(t->dest, t->buf, t->bufuse); + if (bytes < 0 && errno != EWOULDBLOCK) { error("write(%s) failed: %s", t->dest_name, strerror(errno)); return -1; } else if (bytes > 0) { @@ -1043,7 +1218,7 @@ static void *udt_copy_task_routine(void *udt) #ifndef NO_PTHREADS /* - * Join thread, with apporiate errors on failure. Name is name for the + * Join thread, with appropriate errors on failure. Name is name for the * thread (for error messages). Returns 0 on success, 1 on failure. */ static int tloop_join(pthread_t thread, const char *name) @@ -1109,7 +1284,7 @@ static void udt_kill_transfer(struct unidirectional_transfer *t) } /* - * Join process, with apporiate errors on failure. Name is name for the + * Join process, with appropriate errors on failure. Name is name for the * process (for error messages). Returns 0 on success, 1 on failure. */ static int tloop_join(pid_t pid, const char *name) |