diff options
Diffstat (limited to 'remote-curl.c')
-rw-r--r-- | remote-curl.c | 156 |
1 files changed, 104 insertions, 52 deletions
diff --git a/remote-curl.c b/remote-curl.c index c9b891adbf..af7b6786dc 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -10,6 +10,7 @@ #include "sideband.h" #include "argv-array.h" #include "credential.h" +#include "sha1-array.h" static struct remote *remote; /* always ends with a trailing slash */ @@ -20,9 +21,12 @@ struct options { unsigned long depth; unsigned progress : 1, check_self_contained_and_connected : 1, + cloning : 1, + update_shallow : 1, followtags : 1, dry_run : 1, - thin : 1; + thin : 1, + push_cert : 1; }; static struct options options; static struct string_list cas_options = STRING_LIST_INIT_DUP; @@ -87,8 +91,31 @@ static int set_option(const char *name, const char *value) string_list_append(&cas_options, val.buf); strbuf_release(&val); return 0; - } - else { + } else if (!strcmp(name, "cloning")) { + if (!strcmp(value, "true")) + options.cloning = 1; + else if (!strcmp(value, "false")) + options.cloning = 0; + else + return -1; + return 0; + } else if (!strcmp(name, "update-shallow")) { + if (!strcmp(value, "true")) + options.update_shallow = 1; + else if (!strcmp(value, "false")) + options.update_shallow = 0; + else + return -1; + return 0; + } else if (!strcmp(name, "pushcert")) { + if (!strcmp(value, "true")) + options.push_cert = 1; + else if (!strcmp(value, "false")) + options.push_cert = 0; + else + return -1; + return 0; + } else { return 1 /* unsupported */; } } @@ -99,6 +126,7 @@ struct discovery { char *buf; size_t len; struct ref *refs; + struct sha1_array shallow; unsigned proto_git : 1; }; static struct discovery *last_discovery; @@ -107,7 +135,7 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push) { struct ref *list = NULL; get_remote_heads(-1, heads->buf, heads->len, &list, - for_push ? REF_NORMAL : 0, NULL); + for_push ? REF_NORMAL : 0, NULL, &heads->shallow); return list; } @@ -168,25 +196,26 @@ static void free_discovery(struct discovery *d) if (d) { if (d == last_discovery) last_discovery = NULL; + free(d->shallow.sha1); free(d->buf_alloc); free_refs(d->refs); free(d); } } -static int show_http_message(struct strbuf *type, struct strbuf *msg) +static int show_http_message(struct strbuf *type, struct strbuf *charset, + struct strbuf *msg) { const char *p, *eol; /* * We only show text/plain parts, as other types are likely * to be ugly to look at on the user's terminal. - * - * TODO should handle "; charset=XXX", and re-encode into - * logoutputencoding */ - if (strcasecmp(type->buf, "text/plain")) + if (strcmp(type->buf, "text/plain")) return -1; + if (charset->len) + strbuf_reencode(msg, charset->buf, get_log_output_encoding()); strbuf_trim(msg); if (!msg->len) @@ -201,10 +230,11 @@ static int show_http_message(struct strbuf *type, struct strbuf *msg) return 0; } -static struct discovery* discover_refs(const char *service, int for_push) +static struct discovery *discover_refs(const char *service, int for_push) { struct strbuf exp = STRBUF_INIT; struct strbuf type = STRBUF_INIT; + struct strbuf charset = STRBUF_INIT; struct strbuf buffer = STRBUF_INIT; struct strbuf refs_url = STRBUF_INIT; struct strbuf effective_url = STRBUF_INIT; @@ -217,7 +247,7 @@ static struct discovery* discover_refs(const char *service, int for_push) free_discovery(last); strbuf_addf(&refs_url, "%sinfo/refs", url.buf); - if ((!prefixcmp(url.buf, "http://") || !prefixcmp(url.buf, "https://")) && + if ((starts_with(url.buf, "http://") || starts_with(url.buf, "https://")) && git_env_bool("GIT_SMART_HTTP", 1)) { maybe_smart = 1; if (!strchr(url.buf, '?')) @@ -229,6 +259,7 @@ static struct discovery* discover_refs(const char *service, int for_push) memset(&options, 0, sizeof(options)); options.content_type = &type; + options.charset = &charset; options.effective_url = &effective_url; options.base_url = &url; options.no_cache = 1; @@ -239,13 +270,13 @@ static struct discovery* discover_refs(const char *service, int for_push) case HTTP_OK: break; case HTTP_MISSING_TARGET: - show_http_message(&type, &buffer); + show_http_message(&type, &charset, &buffer); die("repository '%s' not found", url.buf); case HTTP_NOAUTH: - show_http_message(&type, &buffer); + show_http_message(&type, &charset, &buffer); die("Authentication failed for '%s'", url.buf); default: - show_http_message(&type, &buffer); + show_http_message(&type, &charset, &buffer); die("unable to access '%s': %s", url.buf, curl_errorstr); } @@ -290,6 +321,7 @@ static struct discovery* discover_refs(const char *service, int for_push) strbuf_release(&refs_url); strbuf_release(&exp); strbuf_release(&type); + strbuf_release(&charset); strbuf_release(&effective_url); strbuf_release(&buffer); last_discovery = last; @@ -376,7 +408,7 @@ static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp) rpc->pos = 0; return CURLIOE_OK; } - fprintf(stderr, "Unable to rewind rpc post data - try increasing http.postBuffer\n"); + error("unable to rewind rpc post data - try increasing http.postBuffer"); return CURLIOE_FAILRESTART; default: @@ -394,25 +426,26 @@ static size_t rpc_in(char *ptr, size_t eltsize, return size; } -static int run_slot(struct active_request_slot *slot) +static int run_slot(struct active_request_slot *slot, + struct slot_results *results) { int err; - struct slot_results results; + struct slot_results results_buf; + + if (!results) + results = &results_buf; - slot->results = &results; - slot->curl_result = curl_easy_perform(slot->curl); - finish_active_slot(slot); + err = run_one_slot(slot, results); - err = handle_curl_result(&results); if (err != HTTP_OK && err != HTTP_REAUTH) { error("RPC failed; result=%d, HTTP code = %ld", - results.curl_result, results.http_code); + results->curl_result, results->http_code); } return err; } -static int probe_rpc(struct rpc_state *rpc) +static int probe_rpc(struct rpc_state *rpc, struct slot_results *results) { struct active_request_slot *slot; struct curl_slist *headers = NULL; @@ -434,7 +467,7 @@ static int probe_rpc(struct rpc_state *rpc) curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(slot->curl, CURLOPT_FILE, &buf); - err = run_slot(slot); + err = run_slot(slot, results); curl_slist_free_all(headers); strbuf_release(&buf); @@ -449,6 +482,7 @@ static int post_rpc(struct rpc_state *rpc) char *gzip_body = NULL; size_t gzip_size = 0; int err, large_request = 0; + int needs_100_continue = 0; /* Try to load the entire request, if we can fit it into the * allocated buffer space we can use HTTP/1.0 and avoid the @@ -472,18 +506,24 @@ static int post_rpc(struct rpc_state *rpc) } if (large_request) { + struct slot_results results; + do { - err = probe_rpc(rpc); + err = probe_rpc(rpc, &results); if (err == HTTP_REAUTH) credential_fill(&http_auth); } while (err == HTTP_REAUTH); if (err != HTTP_OK) return -1; + + if (results.auth_avail & CURLAUTH_GSSNEGOTIATE) + needs_100_continue = 1; } headers = curl_slist_append(headers, rpc->hdr_content_type); headers = curl_slist_append(headers, rpc->hdr_accept); - headers = curl_slist_append(headers, "Expect:"); + headers = curl_slist_append(headers, needs_100_continue ? + "Expect: 100-continue" : "Expect:"); retry: slot = get_active_slot(); @@ -527,7 +567,6 @@ retry: git_zstream stream; int ret; - memset(&stream, 0, sizeof(stream)); git_deflate_init_gzip(&stream, Z_BEST_COMPRESSION); gzip_size = git_deflate_bound(&stream, rpc->len); gzip_body = xmalloc(gzip_size); @@ -574,7 +613,7 @@ retry: curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in); curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc); - err = run_slot(slot); + err = run_slot(slot, NULL); if (err == HTTP_REAUTH && !large_request) { credential_fill(&http_auth); goto retry; @@ -592,10 +631,9 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads) const char *svc = rpc->service_name; struct strbuf buf = STRBUF_INIT; struct strbuf *preamble = rpc->stdin_preamble; - struct child_process client; + struct child_process client = CHILD_PROCESS_INIT; int err = 0; - memset(&client, 0, sizeof(client)); client.in = -1; client.out = -1; client.git_cmd = 1; @@ -678,7 +716,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch) free(targets[i]); free(targets); - return ret ? error("Fetch failed.") : 0; + return ret ? error("fetch failed.") : 0; } static int fetch_git(struct discovery *heads, @@ -688,7 +726,7 @@ static int fetch_git(struct discovery *heads, struct strbuf preamble = STRBUF_INIT; char *depth_arg = NULL; int argc = 0, i, err; - const char *argv[16]; + const char *argv[17]; argv[argc++] = "fetch-pack"; argv[argc++] = "--stateless-rpc"; @@ -704,6 +742,10 @@ static int fetch_git(struct discovery *heads, } if (options.check_self_contained_and_connected) argv[argc++] = "--check-self-contained-and-connected"; + if (options.cloning) + argv[argc++] = "--cloning"; + if (options.update_shallow) + argv[argc++] = "--update-shallow"; if (!options.progress) argv[argc++] = "--no-progress"; if (options.depth) { @@ -717,9 +759,10 @@ static int fetch_git(struct discovery *heads, for (i = 0; i < nr_heads; i++) { struct ref *ref = to_fetch[i]; - if (!ref->name || !*ref->name) + if (!*ref->name) die("cannot fetch by sha1 over smart http"); - packet_buf_write(&preamble, "%s\n", ref->name); + packet_buf_write(&preamble, "%s %s\n", + sha1_to_hex(ref->old_sha1), ref->name); } packet_buf_flush(&preamble); @@ -755,9 +798,9 @@ static void parse_fetch(struct strbuf *buf) int alloc_heads = 0, nr_heads = 0; do { - if (!prefixcmp(buf->buf, "fetch ")) { - char *p = buf->buf + strlen("fetch "); - char *name; + const char *p; + if (skip_prefix(buf->buf, "fetch ", &p)) { + const char *name; struct ref *ref; unsigned char old_sha1[20]; @@ -827,6 +870,7 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs) int i, err; struct argv_array args; struct string_list_item *cas_option; + struct strbuf preamble = STRBUF_INIT; argv_array_init(&args); argv_array_pushl(&args, "send-pack", "--stateless-rpc", "--helper-status", @@ -836,6 +880,8 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs) argv_array_push(&args, "--thin"); if (options.dry_run) argv_array_push(&args, "--dry-run"); + if (options.push_cert) + argv_array_push(&args, "--signed"); if (options.verbosity == 0) argv_array_push(&args, "--quiet"); else if (options.verbosity > 1) @@ -844,17 +890,22 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs) for_each_string_list_item(cas_option, &cas_options) argv_array_push(&args, cas_option->string); argv_array_push(&args, url.buf); + + argv_array_push(&args, "--stdin"); for (i = 0; i < nr_spec; i++) - argv_array_push(&args, specs[i]); + packet_buf_write(&preamble, "%s\n", specs[i]); + packet_buf_flush(&preamble); memset(&rpc, 0, sizeof(rpc)); rpc.service_name = "git-receive-pack", rpc.argv = args.argv; + rpc.stdin_preamble = &preamble; err = rpc_service(&rpc, heads); if (rpc.result.len) write_or_die(1, rpc.result.buf, rpc.result.len); strbuf_release(&rpc.result); + strbuf_release(&preamble); argv_array_clear(&args); return err; } @@ -878,7 +929,7 @@ static void parse_push(struct strbuf *buf) int alloc_spec = 0, nr_spec = 0, i, ret; do { - if (!prefixcmp(buf->buf, "push ")) { + if (starts_with(buf->buf, "push ")) { ALLOC_GROW(specs, nr_spec + 1, alloc_spec); specs[nr_spec++] = xstrdup(buf->buf + 5); } @@ -910,10 +961,12 @@ int main(int argc, const char **argv) struct strbuf buf = STRBUF_INIT; int nongit; + git_setup_gettext(); + git_extract_argv0_path(argv[0]); setup_git_directory_gently(&nongit); if (argc < 2) { - fprintf(stderr, "Remote needed\n"); + error("remote-curl: usage: git remote-curl <remote> [<url>]"); return 1; } @@ -932,30 +985,29 @@ int main(int argc, const char **argv) http_init(remote, url.buf, 0); do { + const char *arg; + if (strbuf_getline(&buf, stdin, '\n') == EOF) { if (ferror(stdin)) - fprintf(stderr, "Error reading command stream\n"); - else - fprintf(stderr, "Unexpected end of command stream\n"); + error("remote-curl: error reading command stream from git"); return 1; } if (buf.len == 0) break; - if (!prefixcmp(buf.buf, "fetch ")) { + if (starts_with(buf.buf, "fetch ")) { if (nongit) - die("Fetch attempted without a local repo"); + die("remote-curl: fetch attempted without a local repo"); parse_fetch(&buf); - } else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) { + } else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) { int for_push = !!strstr(buf.buf + 4, "for-push"); output_refs(get_refs(for_push)); - } else if (!prefixcmp(buf.buf, "push ")) { + } else if (starts_with(buf.buf, "push ")) { parse_push(&buf); - } else if (!prefixcmp(buf.buf, "option ")) { - char *name = buf.buf + strlen("option "); - char *value = strchr(name, ' '); + } else if (skip_prefix(buf.buf, "option ", &arg)) { + char *value = strchr(arg, ' '); int result; if (value) @@ -963,7 +1015,7 @@ int main(int argc, const char **argv) else value = "true"; - result = set_option(name, value); + result = set_option(arg, value); if (!result) printf("ok\n"); else if (result < 0) @@ -980,7 +1032,7 @@ int main(int argc, const char **argv) printf("\n"); fflush(stdout); } else { - fprintf(stderr, "Unknown command '%s'\n", buf.buf); + error("remote-curl: unknown command '%s' from git", buf.buf); return 1; } strbuf_reset(&buf); |