From bf3c523c3fd641609adcef67dcec47a43a6abd60 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Date: Wed, 9 Dec 2009 17:26:27 +0200 Subject: [PATCH 1/9] Add remote helper debug mode Remote helpers deadlock easily, so support debug mode which shows the interaction steps. Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- transport-helper.c | 94 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 25 deletions(-) diff --git a/transport-helper.c b/transport-helper.c index 11f3d7ec52..a721dc25be 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -8,6 +8,8 @@ #include "quote.h" #include "remote.h" +static int debug; + struct helper_data { const char *name; @@ -22,6 +24,45 @@ struct helper_data int refspec_nr; }; +static void sendline(struct helper_data *helper, struct strbuf *buffer) +{ + if (debug) + fprintf(stderr, "Debug: Remote helper: -> %s", buffer->buf); + if (write_in_full(helper->helper->in, buffer->buf, buffer->len) + != buffer->len) + die_errno("Full write to remote helper failed"); +} + +static int recvline(struct helper_data *helper, struct strbuf *buffer) +{ + strbuf_reset(buffer); + if (debug) + fprintf(stderr, "Debug: Remote helper: Waiting...\n"); + if (strbuf_getline(buffer, helper->out, '\n') == EOF) { + if (debug) + fprintf(stderr, "Debug: Remote helper quit.\n"); + exit(128); + } + + if (debug) + fprintf(stderr, "Debug: Remote helper: <- %s\n", buffer->buf); + return 0; +} + +static void xchgline(struct helper_data *helper, struct strbuf *buffer) +{ + sendline(helper, buffer); + recvline(helper, buffer); +} + +static void write_constant(int fd, const char *str) +{ + if (debug) + fprintf(stderr, "Debug: Remote helper: -> %s", str); + if (write_in_full(fd, str, strlen(str)) != strlen(str)) + die_errno("Full write to remote helper failed"); +} + static struct child_process *get_helper(struct transport *transport) { struct helper_data *data = transport->data; @@ -48,15 +89,16 @@ static struct child_process *get_helper(struct transport *transport) die("Unable to run helper: git %s", helper->argv[0]); data->helper = helper; - write_str_in_full(helper->in, "capabilities\n"); + write_constant(helper->in, "capabilities\n"); data->out = xfdopen(helper->out, "r"); while (1) { - if (strbuf_getline(&buf, data->out, '\n') == EOF) - exit(128); /* child died, message supplied already */ + recvline(data, &buf); if (!*buf.buf) break; + if (debug) + fprintf(stderr, "Debug: Got cap %s\n", buf.buf); if (!strcmp(buf.buf, "fetch")) data->fetch = 1; if (!strcmp(buf.buf, "option")) @@ -82,14 +124,21 @@ static struct child_process *get_helper(struct transport *transport) free(refspecs); } strbuf_release(&buf); + if (debug) + fprintf(stderr, "Debug: Capabilities complete.\n"); return data->helper; } static int disconnect_helper(struct transport *transport) { struct helper_data *data = transport->data; + struct strbuf buf = STRBUF_INIT; + if (data->helper) { - write_str_in_full(data->helper->in, "\n"); + if (debug) + fprintf(stderr, "Debug: Disconnecting.\n"); + strbuf_addf(&buf, "\n"); + sendline(data, &buf); close(data->helper->in); fclose(data->out); finish_command(data->helper); @@ -117,10 +166,11 @@ static int set_helper_option(struct transport *transport, const char *name, const char *value) { struct helper_data *data = transport->data; - struct child_process *helper = get_helper(transport); struct strbuf buf = STRBUF_INIT; int i, ret, is_bool = 0; + get_helper(transport); + if (!data->option) return 1; @@ -143,12 +193,7 @@ static int set_helper_option(struct transport *transport, quote_c_style(value, &buf, NULL, 0); strbuf_addch(&buf, '\n'); - if (write_in_full(helper->in, buf.buf, buf.len) != buf.len) - die_errno("cannot send option to %s", data->name); - - strbuf_reset(&buf); - if (strbuf_getline(&buf, data->out, '\n') == EOF) - exit(128); /* child died, message supplied already */ + xchgline(data, &buf); if (!strcmp(buf.buf, "ok")) ret = 0; @@ -208,13 +253,10 @@ static int fetch_with_fetch(struct transport *transport, } strbuf_addch(&buf, '\n'); - if (write_in_full(data->helper->in, buf.buf, buf.len) != buf.len) - die_errno("cannot send fetch to %s", data->name); + sendline(data, &buf); while (1) { - strbuf_reset(&buf); - if (strbuf_getline(&buf, data->out, '\n') == EOF) - exit(128); /* child died, message supplied already */ + recvline(data, &buf); if (!prefixcmp(buf.buf, "lock ")) { const char *name = buf.buf + 5; @@ -249,12 +291,13 @@ static int fetch_with_import(struct transport *transport, int nr_heads, struct ref **to_fetch) { struct child_process fastimport; - struct child_process *helper = get_helper(transport); struct helper_data *data = transport->data; int i; struct ref *posn; struct strbuf buf = STRBUF_INIT; + get_helper(transport); + if (get_importer(transport, &fastimport)) die("Couldn't run fast-import"); @@ -264,7 +307,7 @@ static int fetch_with_import(struct transport *transport, continue; strbuf_addf(&buf, "import %s\n", posn->name); - write_in_full(helper->in, buf.buf, buf.len); + sendline(data, &buf); strbuf_reset(&buf); } disconnect_helper(transport); @@ -369,17 +412,14 @@ static int push_refs(struct transport *transport, } strbuf_addch(&buf, '\n'); - if (write_in_full(helper->in, buf.buf, buf.len) != buf.len) - exit(128); + sendline(data, &buf); ref = remote_refs; while (1) { char *refname, *msg; int status; - strbuf_reset(&buf); - if (strbuf_getline(&buf, data->out, '\n') == EOF) - exit(128); /* child died, message supplied already */ + recvline(data, &buf); if (!buf.len) break; @@ -471,8 +511,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push) while (1) { char *eov, *eon; - if (strbuf_getline(&buf, data->out, '\n') == EOF) - exit(128); /* child died, message supplied already */ + recvline(data, &buf); if (!*buf.buf) break; @@ -497,6 +536,8 @@ static struct ref *get_refs_list(struct transport *transport, int for_push) } tail = &((*tail)->next); } + if (debug) + fprintf(stderr, "Debug: Read ref listing.\n"); strbuf_release(&buf); for (posn = ret; posn; posn = posn->next) @@ -510,6 +551,9 @@ int transport_helper_init(struct transport *transport, const char *name) struct helper_data *data = xcalloc(sizeof(*data), 1); data->name = name; + if (getenv("GIT_TRANSPORT_HELPER_DEBUG")) + debug = 1; + transport->data = data; transport->set_option = set_helper_option; transport->get_refs_list = get_refs_list; From 28ed5b3524c1ee5245131691b783d897239f5b03 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Date: Wed, 9 Dec 2009 17:26:28 +0200 Subject: [PATCH 2/9] Support mandatory capabilities Add support for marking capability as mandatory for hosting git version to understand. This is useful for helpers which require various types of assistance from main git binary. Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- Documentation/git-remote-helpers.txt | 5 ++++- transport-helper.c | 25 +++++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt index 5cfdc0cfc5..20a05fe9d8 100644 --- a/Documentation/git-remote-helpers.txt +++ b/Documentation/git-remote-helpers.txt @@ -25,7 +25,10 @@ Commands are given by the caller on the helper's standard input, one per line. 'capabilities':: Lists the capabilities of the helper, one per line, ending - with a blank line. + with a blank line. Each capability may be preceeded with '*'. + This marks them mandatory for git version using the remote + helper to understand (unknown mandatory capability is fatal + error). 'list':: Lists the refs, one per line, in the format "<value> <name> diff --git a/transport-helper.c b/transport-helper.c index a721dc25be..4b17aaa237 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -93,25 +93,38 @@ static struct child_process *get_helper(struct transport *transport) data->out = xfdopen(helper->out, "r"); while (1) { + const char *capname; + int mandatory = 0; recvline(data, &buf); if (!*buf.buf) break; + + if (*buf.buf == '*') { + capname = buf.buf + 1; + mandatory = 1; + } else + capname = buf.buf; + if (debug) - fprintf(stderr, "Debug: Got cap %s\n", buf.buf); - if (!strcmp(buf.buf, "fetch")) + fprintf(stderr, "Debug: Got cap %s\n", capname); + if (!strcmp(capname, "fetch")) data->fetch = 1; - if (!strcmp(buf.buf, "option")) + else if (!strcmp(capname, "option")) data->option = 1; - if (!strcmp(buf.buf, "push")) + else if (!strcmp(capname, "push")) data->push = 1; - if (!strcmp(buf.buf, "import")) + else if (!strcmp(capname, "import")) data->import = 1; - if (!data->refspecs && !prefixcmp(buf.buf, "refspec ")) { + else if (!data->refspecs && !prefixcmp(capname, "refspec ")) { ALLOC_GROW(refspecs, refspec_nr + 1, refspec_alloc); refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec ")); + } else if (mandatory) { + die("Unknown madatory capability %s. This remote " + "helper probably needs newer version of Git.\n", + capname); } } if (refspecs) { From 25d5cc488a75cc232e97af42759812d9aa398713 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Date: Wed, 9 Dec 2009 17:26:29 +0200 Subject: [PATCH 3/9] Pass unknown protocols to external protocol handlers Change URL handling to allow external protocol handlers to implement new protocols without the '::' syntax if helper name does not conflict with any built-in protocol. foo:// now invokes git-remote-foo with foo:// as the URL. Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- transport-helper.c | 12 +++++++- transport.c | 76 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/transport-helper.c b/transport-helper.c index 4b17aaa237..271af345e4 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -63,6 +63,16 @@ static void write_constant(int fd, const char *str) die_errno("Full write to remote helper failed"); } +const char *remove_ext_force(const char *url) +{ + if (url) { + const char *colon = strchr(url, ':'); + if (colon && colon[1] == ':') + return colon + 2; + } + return url; +} + static struct child_process *get_helper(struct transport *transport) { struct helper_data *data = transport->data; @@ -83,7 +93,7 @@ static struct child_process *get_helper(struct transport *transport) strbuf_addf(&buf, "remote-%s", data->name); helper->argv[0] = strbuf_detach(&buf, NULL); helper->argv[1] = transport->remote->name; - helper->argv[2] = transport->url; + helper->argv[2] = remove_ext_force(transport->url); helper->git_cmd = 1; if (start_command(helper)) die("Unable to run helper: git %s", helper->argv[0]); diff --git a/transport.c b/transport.c index 3eea836a33..dea37d09b2 100644 --- a/transport.c +++ b/transport.c @@ -780,6 +780,44 @@ static int is_file(const char *url) return S_ISREG(buf.st_mode); } +static int is_url(const char *url) +{ + const char *url2, *first_slash; + + if (!url) + return 0; + url2 = url; + first_slash = strchr(url, '/'); + + /* Input with no slash at all or slash first can't be URL. */ + if (!first_slash || first_slash == url) + return 0; + /* Character before must be : and next must be /. */ + if (first_slash[-1] != ':' || first_slash[1] != '/') + return 0; + /* There must be something before the :// */ + if (first_slash == url + 1) + return 0; + /* + * Check all characters up to first slash - 1. Only alphanum + * is allowed. + */ + url2 = url; + while (url2 < first_slash - 1) { + if (!isalnum((unsigned char)*url2)) + return 0; + url2++; + } + + /* Valid enough. */ + return 1; +} + +static int external_specification_len(const char *url) +{ + return strchr(url, ':') - url; +} + struct transport *transport_get(struct remote *remote, const char *url) { struct transport *ret = xcalloc(1, sizeof(*ret)); @@ -805,30 +843,23 @@ struct transport *transport_get(struct remote *remote, const char *url) if (remote && remote->foreign_vcs) { transport_helper_init(ret, remote->foreign_vcs); - return ret; - } - - if (!prefixcmp(url, "rsync:")) { + } else if (!prefixcmp(url, "rsync:")) { ret->get_refs_list = get_refs_via_rsync; ret->fetch = fetch_objs_via_rsync; ret->push = rsync_transport_push; - - } else if (!prefixcmp(url, "http://") - || !prefixcmp(url, "https://") - || !prefixcmp(url, "ftp://")) { - transport_helper_init(ret, "curl"); -#ifdef NO_CURL - error("git was compiled without libcurl support."); -#endif - } else if (is_local(url) && is_file(url)) { struct bundle_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; ret->get_refs_list = get_refs_from_bundle; ret->fetch = fetch_refs_from_bundle; ret->disconnect = close_bundle; - - } else { + } else if (!is_url(url) + || !prefixcmp(url, "file://") + || !prefixcmp(url, "git://") + || !prefixcmp(url, "ssh://") + || !prefixcmp(url, "git+ssh://") + || !prefixcmp(url, "ssh+git://")) { + /* These are builtin smart transports. */ struct git_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; ret->set_option = set_git_option; @@ -845,6 +876,21 @@ struct transport *transport_get(struct remote *remote, const char *url) data->receivepack = "git-receive-pack"; if (remote->receivepack) data->receivepack = remote->receivepack; + } else if (!prefixcmp(url, "http://") + || !prefixcmp(url, "https://") + || !prefixcmp(url, "ftp://")) { + /* These three are just plain special. */ + transport_helper_init(ret, "curl"); +#ifdef NO_CURL + error("git was compiled without libcurl support."); +#endif + } else { + /* Unknown protocol in URL. Pass to external handler. */ + int len = external_specification_len(url); + char *handler = xmalloc(len + 1); + handler[len] = 0; + strncpy(handler, url, len); + transport_helper_init(ret, handler); } return ret; From aa5af9749f53f7e44bef36c5c40918295430fb03 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Date: Wed, 9 Dec 2009 17:26:30 +0200 Subject: [PATCH 4/9] Refactor git transport options parsing Refactor the transport options parsing so that protocols that aren't directly smart transports (file://, git://, ssh:// & co) can record the smart transport options for the case if it turns that transport can actually be smart. Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- transport.c | 78 ++++++++++++++++++++++++++++++++--------------------- transport.h | 15 +++++++++++ 2 files changed, 62 insertions(+), 31 deletions(-) diff --git a/transport.c b/transport.c index dea37d09b2..e6eb20ea94 100644 --- a/transport.c +++ b/transport.c @@ -395,41 +395,35 @@ static int close_bundle(struct transport *transport) } struct git_transport_data { - unsigned thin : 1; - unsigned keep : 1; - unsigned followtags : 1; - int depth; + struct git_transport_options options; struct child_process *conn; int fd[2]; - const char *uploadpack; - const char *receivepack; struct extra_have_objects extra_have; }; -static int set_git_option(struct transport *connection, +static int set_git_option(struct git_transport_options *opts, const char *name, const char *value) { - struct git_transport_data *data = connection->data; if (!strcmp(name, TRANS_OPT_UPLOADPACK)) { - data->uploadpack = value; + opts->uploadpack = value; return 0; } else if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) { - data->receivepack = value; + opts->receivepack = value; return 0; } else if (!strcmp(name, TRANS_OPT_THIN)) { - data->thin = !!value; + opts->thin = !!value; return 0; } else if (!strcmp(name, TRANS_OPT_FOLLOWTAGS)) { - data->followtags = !!value; + opts->followtags = !!value; return 0; } else if (!strcmp(name, TRANS_OPT_KEEP)) { - data->keep = !!value; + opts->keep = !!value; return 0; } else if (!strcmp(name, TRANS_OPT_DEPTH)) { if (!value) - data->depth = 0; + opts->depth = 0; else - data->depth = atoi(value); + opts->depth = atoi(value); return 0; } return 1; @@ -439,7 +433,8 @@ static int connect_setup(struct transport *transport, int for_push, int verbose) { struct git_transport_data *data = transport->data; data->conn = git_connect(data->fd, transport->url, - for_push ? data->receivepack : data->uploadpack, + for_push ? data->options.receivepack : + data->options.uploadpack, verbose ? CONNECT_VERBOSE : 0); return 0; } @@ -469,15 +464,15 @@ static int fetch_refs_via_pack(struct transport *transport, struct ref *refs_tmp = NULL; memset(&args, 0, sizeof(args)); - args.uploadpack = data->uploadpack; - args.keep_pack = data->keep; + args.uploadpack = data->options.uploadpack; + args.keep_pack = data->options.keep; args.lock_pack = 1; - args.use_thin_pack = data->thin; - args.include_tag = data->followtags; + args.use_thin_pack = data->options.thin; + args.include_tag = data->options.followtags; args.verbose = (transport->verbose > 0); args.quiet = (transport->verbose < 0); args.no_progress = args.quiet || (!transport->progress && !isatty(1)); - args.depth = data->depth; + args.depth = data->options.depth; for (i = 0; i < nr_heads; i++) origh[i] = heads[i] = xstrdup(to_fetch[i]->name); @@ -734,7 +729,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re memset(&args, 0, sizeof(args)); args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR); args.force_update = !!(flags & TRANSPORT_PUSH_FORCE); - args.use_thin_pack = data->thin; + args.use_thin_pack = data->options.thin; args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE); args.quiet = !!(flags & TRANSPORT_PUSH_QUIET); args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN); @@ -847,12 +842,14 @@ struct transport *transport_get(struct remote *remote, const char *url) ret->get_refs_list = get_refs_via_rsync; ret->fetch = fetch_objs_via_rsync; ret->push = rsync_transport_push; + ret->smart_options = NULL; } else if (is_local(url) && is_file(url)) { struct bundle_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; ret->get_refs_list = get_refs_from_bundle; ret->fetch = fetch_refs_from_bundle; ret->disconnect = close_bundle; + ret->smart_options = NULL; } else if (!is_url(url) || !prefixcmp(url, "file://") || !prefixcmp(url, "git://") @@ -862,20 +859,14 @@ struct transport *transport_get(struct remote *remote, const char *url) /* These are builtin smart transports. */ struct git_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; - ret->set_option = set_git_option; + ret->set_option = NULL; ret->get_refs_list = get_refs_via_connect; ret->fetch = fetch_refs_via_pack; ret->push_refs = git_transport_push; ret->disconnect = disconnect_git; + ret->smart_options = &(data->options); - data->thin = 1; data->conn = NULL; - data->uploadpack = "git-upload-pack"; - if (remote->uploadpack) - data->uploadpack = remote->uploadpack; - data->receivepack = "git-receive-pack"; - if (remote->receivepack) - data->receivepack = remote->receivepack; } else if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://") || !prefixcmp(url, "ftp://")) { @@ -893,14 +884,39 @@ struct transport *transport_get(struct remote *remote, const char *url) transport_helper_init(ret, handler); } + if (ret->smart_options) { + ret->smart_options->thin = 1; + ret->smart_options->uploadpack = "git-upload-pack"; + if (remote->uploadpack) + ret->smart_options->uploadpack = remote->uploadpack; + ret->smart_options->receivepack = "git-receive-pack"; + if (remote->receivepack) + ret->smart_options->receivepack = remote->receivepack; + } + return ret; } int transport_set_option(struct transport *transport, const char *name, const char *value) { + int git_reports = 1, protocol_reports = 1; + + if (transport->smart_options) + git_reports = set_git_option(transport->smart_options, + name, value); + if (transport->set_option) - return transport->set_option(transport, name, value); + protocol_reports = transport->set_option(transport, name, + value); + + /* If either report is 0, report 0 (success). */ + if (!git_reports || !protocol_reports) + return 0; + /* If either reports -1 (invalid value), report -1. */ + if ((git_reports == -1) || (protocol_reports == -1)) + return -1; + /* Otherwise if both report unknown, report unknown. */ return 1; } diff --git a/transport.h b/transport.h index 9e74406fa2..e90c285bbc 100644 --- a/transport.h +++ b/transport.h @@ -4,6 +4,15 @@ #include "cache.h" #include "remote.h" +struct git_transport_options { + unsigned thin : 1; + unsigned keep : 1; + unsigned followtags : 1; + int depth; + const char *uploadpack; + const char *receivepack; +}; + struct transport { struct remote *remote; const char *url; @@ -65,6 +74,12 @@ struct transport { signed verbose : 3; /* Force progress even if the output is not a tty */ unsigned progress : 1; + /* + * If transport is at least potentially smart, this points to + * git_transport_options structure to use in case transport + * actually turns out to be smart. + */ + struct git_transport_options *smart_options; }; #define TRANSPORT_PUSH_ALL 1 From 61b075bd3e69cbdc9010a7f817b2017455dd36a7 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Date: Wed, 9 Dec 2009 17:26:31 +0200 Subject: [PATCH 5/9] Support taking over transports Add support for taking over transports that turn out to be smart. Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- transport-helper.c | 19 ++++++++++++++++- transport.c | 51 +++++++++++++++++++++++++++++++++++++++++----- transport.h | 2 ++ 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/transport-helper.c b/transport-helper.c index 271af345e4..97eed6cbf6 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -22,6 +22,10 @@ struct helper_data /* These go from remote name (as in "list") to private name */ struct refspec *refspecs; int refspec_nr; + /* Transport options for fetch-pack/send-pack (should one of + * those be invoked). + */ + struct git_transport_options transport_options; }; static void sendline(struct helper_data *helper, struct strbuf *buffer) @@ -81,6 +85,7 @@ static struct child_process *get_helper(struct transport *transport) const char **refspecs = NULL; int refspec_nr = 0; int refspec_alloc = 0; + int duped; if (data->helper) return data->helper; @@ -99,9 +104,19 @@ static struct child_process *get_helper(struct transport *transport) die("Unable to run helper: git %s", helper->argv[0]); data->helper = helper; + /* + * Open the output as FILE* so strbuf_getline() 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. + * + */ + duped = dup(helper->out); + if (duped < 0) + die_errno("Can't dup helper output fd"); + data->out = xfdopen(duped, "r"); + write_constant(helper->in, "capabilities\n"); - data->out = xfdopen(helper->out, "r"); while (1) { const char *capname; int mandatory = 0; @@ -163,6 +178,7 @@ static int disconnect_helper(struct transport *transport) strbuf_addf(&buf, "\n"); sendline(data, &buf); close(data->helper->in); + close(data->helper->out); fclose(data->out); finish_command(data->helper); free((char *)data->helper->argv[0]); @@ -583,5 +599,6 @@ int transport_helper_init(struct transport *transport, const char *name) transport->fetch = fetch; transport->push_refs = push_refs; transport->disconnect = release_helper; + transport->smart_options = &(data->transport_options); return 0; } diff --git a/transport.c b/transport.c index e6eb20ea94..ad25b98ae1 100644 --- a/transport.c +++ b/transport.c @@ -398,6 +398,7 @@ struct git_transport_data { struct git_transport_options options; struct child_process *conn; int fd[2]; + unsigned got_remote_heads : 1; struct extra_have_objects extra_have; }; @@ -432,10 +433,15 @@ static int set_git_option(struct git_transport_options *opts, static int connect_setup(struct transport *transport, int for_push, int verbose) { struct git_transport_data *data = transport->data; + + if (data->conn) + return 0; + data->conn = git_connect(data->fd, transport->url, for_push ? data->options.receivepack : data->options.uploadpack, verbose ? CONNECT_VERBOSE : 0); + return 0; } @@ -447,6 +453,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus connect_setup(transport, for_push, 0); get_remote_heads(data->fd[0], &refs, 0, NULL, for_push ? REF_NORMAL : 0, &data->extra_have); + data->got_remote_heads = 1; return refs; } @@ -477,9 +484,10 @@ static int fetch_refs_via_pack(struct transport *transport, for (i = 0; i < nr_heads; i++) origh[i] = heads[i] = xstrdup(to_fetch[i]->name); - if (!data->conn) { + if (!data->got_remote_heads) { connect_setup(transport, 0, 0); get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL); + data->got_remote_heads = 1; } refs = fetch_pack(&args, data->fd, data->conn, @@ -490,6 +498,7 @@ static int fetch_refs_via_pack(struct transport *transport, if (finish_connect(data->conn)) refs = NULL; data->conn = NULL; + data->got_remote_heads = 0; free_refs(refs_tmp); @@ -718,12 +727,13 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re struct send_pack_args args; int ret; - if (!data->conn) { + if (!data->got_remote_heads) { struct ref *tmp_refs; connect_setup(transport, 1, 0); get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL, NULL); + data->got_remote_heads = 1; } memset(&args, 0, sizeof(args)); @@ -741,6 +751,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re close(data->fd[0]); ret |= finish_connect(data->conn); data->conn = NULL; + data->got_remote_heads = 0; return ret; } @@ -749,7 +760,8 @@ static int disconnect_git(struct transport *transport) { struct git_transport_data *data = transport->data; if (data->conn) { - packet_flush(data->fd[1]); + if (data->got_remote_heads) + packet_flush(data->fd[1]); close(data->fd[0]); close(data->fd[1]); finish_connect(data->conn); @@ -759,6 +771,32 @@ static int disconnect_git(struct transport *transport) return 0; } +void transport_take_over(struct transport *transport, + struct child_process *child) +{ + struct git_transport_data *data; + + if (!transport->smart_options) + die("Bug detected: Taking over transport requires non-NULL " + "smart_options field."); + + data = xcalloc(1, sizeof(*data)); + data->options = *transport->smart_options; + data->conn = child; + data->fd[0] = data->conn->out; + data->fd[1] = data->conn->in; + data->got_remote_heads = 0; + transport->data = data; + + transport->set_option = NULL; + transport->get_refs_list = get_refs_via_connect; + transport->fetch = fetch_refs_via_pack; + transport->push = NULL; + transport->push_refs = git_transport_push; + transport->disconnect = disconnect_git; + transport->smart_options = &(data->options); +} + static int is_local(const char *url) { const char *colon = strchr(url, ':'); @@ -867,6 +905,7 @@ struct transport *transport_get(struct remote *remote, const char *url) ret->smart_options = &(data->options); data->conn = NULL; + data->got_remote_heads = 0; } else if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://") || !prefixcmp(url, "ftp://")) { @@ -927,9 +966,9 @@ int transport_push(struct transport *transport, *nonfastforward = 0; verify_remote_names(refspec_nr, refspec); - if (transport->push) + if (transport->push) { return transport->push(transport, refspec_nr, refspec, flags); - if (transport->push_refs) { + } else if (transport->push_refs) { struct ref *remote_refs = transport->get_refs_list(transport, 1); struct ref *local_refs = get_local_heads(); @@ -973,6 +1012,7 @@ const struct ref *transport_get_remote_refs(struct transport *transport) { if (!transport->remote_refs) transport->remote_refs = transport->get_refs_list(transport, 0); + return transport->remote_refs; } @@ -1007,6 +1047,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs) } rc = transport->fetch(transport, nr_heads, heads); + free(heads); return rc; } diff --git a/transport.h b/transport.h index e90c285bbc..781db2ec82 100644 --- a/transport.h +++ b/transport.h @@ -130,6 +130,8 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs); void transport_unlock_pack(struct transport *transport); int transport_disconnect(struct transport *transport); char *transport_anonymize_url(const char *url); +void transport_take_over(struct transport *transport, + struct child_process *child); /* Transport methods defined outside transport.c */ int transport_helper_init(struct transport *transport, const char *name); From fa8c097cc9b416283ae4c1c1507909ff207b7f60 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Date: Wed, 9 Dec 2009 17:26:32 +0200 Subject: [PATCH 6/9] Support remote helpers implementing smart transports Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- Documentation/git-remote-helpers.txt | 25 +++++- transport-helper.c | 124 +++++++++++++++++++++++++-- 2 files changed, 142 insertions(+), 7 deletions(-) diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt index 20a05fe9d8..4685a898f1 100644 --- a/Documentation/git-remote-helpers.txt +++ b/Documentation/git-remote-helpers.txt @@ -93,6 +93,20 @@ Supported if the helper has the "push" capability. + Supported if the helper has the "import" capability. +'connect' <service>:: + Connects to given service. Standard input and standard output + of helper are connected to specified service (git prefix is + included in service name so e.g. fetching uses 'git-upload-pack' + as service) on remote side. Valid replies to this command are + empty line (connection established), 'fallback' (no smart + transport support, fall back to dumb transports) and just + exiting with error message printed (can't connect, don't + bother trying to fall back). After line feed terminating the + positive (empty) response, the output of service starts. After + the connection ends, the remote helper exits. ++ +Supported if the helper has the "connect" capability. + If a fatal error occurs, the program writes the error message to stderr and exits. The caller should expect that a suitable error message has been printed if the child closes the connection without @@ -126,6 +140,9 @@ CAPABILITIES all, it must cover all refs reported by the list command; if it is not used, it is effectively "*:*" +'connect':: + This helper supports the 'connect' command. + REF LIST ATTRIBUTES ------------------- @@ -168,9 +185,15 @@ OPTIONS but don't actually change any repository data. For most helpers this only applies to the 'push', if supported. +'option servpath <c-style-quoted-path>':: + Set service path (--upload-pack, --receive-pack etc.) for + next connect. Remote helper MAY support this option. Remote + helper MUST NOT rely on this option being set before + connect request occurs. + Documentation ------------- -Documentation by Daniel Barkalow. +Documentation by Daniel Barkalow and Ilari Liusvaara GIT --- diff --git a/transport-helper.c b/transport-helper.c index 97eed6cbf6..50b3bac0c4 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -18,7 +18,9 @@ struct helper_data unsigned fetch : 1, import : 1, option : 1, - push : 1; + push : 1, + connect : 1, + no_disconnect_req : 1; /* These go from remote name (as in "list") to private name */ struct refspec *refspecs; int refspec_nr; @@ -37,12 +39,12 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer) die_errno("Full write to remote helper failed"); } -static int recvline(struct helper_data *helper, struct strbuf *buffer) +static int recvline_fh(FILE *helper, struct strbuf *buffer) { strbuf_reset(buffer); if (debug) fprintf(stderr, "Debug: Remote helper: Waiting...\n"); - if (strbuf_getline(buffer, helper->out, '\n') == EOF) { + if (strbuf_getline(buffer, helper, '\n') == EOF) { if (debug) fprintf(stderr, "Debug: Remote helper quit.\n"); exit(128); @@ -53,6 +55,11 @@ static int recvline(struct helper_data *helper, struct strbuf *buffer) return 0; } +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); @@ -77,6 +84,15 @@ const char *remove_ext_force(const char *url) return url; } +static void do_take_over(struct transport *transport) +{ + struct helper_data *data; + data = (struct helper_data *)transport->data; + transport_take_over(transport, data->helper); + fclose(data->out); + free(data); +} + static struct child_process *get_helper(struct transport *transport) { struct helper_data *data = transport->data; @@ -103,12 +119,12 @@ static struct child_process *get_helper(struct transport *transport) if (start_command(helper)) die("Unable to run helper: git %s", helper->argv[0]); data->helper = helper; + data->no_disconnect_req = 0; /* * Open the output as FILE* so strbuf_getline() 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. - * */ duped = dup(helper->out); if (duped < 0) @@ -146,6 +162,8 @@ static struct child_process *get_helper(struct transport *transport) refspec_nr + 1, refspec_alloc); refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec ")); + } else if (!strcmp(capname, "connect")) { + data->connect = 1; } else if (mandatory) { die("Unknown madatory capability %s. This remote " "helper probably needs newer version of Git.\n", @@ -175,8 +193,10 @@ static int disconnect_helper(struct transport *transport) if (data->helper) { if (debug) fprintf(stderr, "Debug: Disconnecting.\n"); - strbuf_addf(&buf, "\n"); - sendline(data, &buf); + if (!data->no_disconnect_req) { + strbuf_addf(&buf, "\n"); + sendline(data, &buf); + } close(data->helper->in); close(data->helper->out); fclose(data->out); @@ -370,12 +390,94 @@ static int fetch_with_import(struct transport *transport, return 0; } +static int process_connect_service(struct transport *transport, + const char *name, const char *exec) +{ + struct helper_data *data = transport->data; + struct strbuf cmdbuf = STRBUF_INIT; + struct child_process *helper; + int r, duped, ret = 0; + FILE *input; + + helper = get_helper(transport); + + /* + * Yes, dup the pipe another time, as we need unbuffered version + * of input pipe as FILE*. fclose() closes the underlying fd and + * stream buffering only can be changed before first I/O operation + * on it. + */ + duped = dup(helper->out); + if (duped < 0) + die_errno("Can't dup helper output fd"); + input = xfdopen(duped, "r"); + setvbuf(input, NULL, _IONBF, 0); + + /* + * Handle --upload-pack and friends. This is fire and forget... + * just warn if it fails. + */ + if (strcmp(name, exec)) { + r = set_helper_option(transport, "servpath", exec); + if (r > 0) + warning("Setting remote service path not supported by protocol."); + else if (r < 0) + warning("Invalid remote service path."); + } + + if (data->connect) + strbuf_addf(&cmdbuf, "connect %s\n", name); + else + goto exit; + + sendline(data, &cmdbuf); + recvline_fh(input, &cmdbuf); + if (!strcmp(cmdbuf.buf, "")) { + data->no_disconnect_req = 1; + if (debug) + fprintf(stderr, "Debug: Smart transport connection " + "ready.\n"); + ret = 1; + } else if (!strcmp(cmdbuf.buf, "fallback")) { + if (debug) + fprintf(stderr, "Debug: Falling back to dumb " + "transport.\n"); + } else + die("Unknown response to connect: %s", + cmdbuf.buf); + +exit: + fclose(input); + return ret; +} + +static int process_connect(struct transport *transport, + int for_push) +{ + struct helper_data *data = transport->data; + const char *name; + const char *exec; + + name = for_push ? "git-receive-pack" : "git-upload-pack"; + if (for_push) + exec = data->transport_options.receivepack; + else + exec = data->transport_options.uploadpack; + + return process_connect_service(transport, name, exec); +} + static int fetch(struct transport *transport, int nr_heads, struct ref **to_fetch) { struct helper_data *data = transport->data; int i, count; + if (process_connect(transport, 0)) { + do_take_over(transport); + return transport->fetch(transport, nr_heads, to_fetch); + } + count = 0; for (i = 0; i < nr_heads; i++) if (!(to_fetch[i]->status & REF_STATUS_UPTODATE)) @@ -403,6 +505,11 @@ static int push_refs(struct transport *transport, struct child_process *helper; struct ref *ref; + if (process_connect(transport, 1)) { + do_take_over(transport); + return transport->push_refs(transport, remote_refs, flags); + } + if (!remote_refs) return 0; @@ -543,6 +650,11 @@ static struct ref *get_refs_list(struct transport *transport, int for_push) helper = get_helper(transport); + if (process_connect(transport, for_push)) { + do_take_over(transport); + return transport->get_refs_list(transport, for_push); + } + if (data->push && for_push) write_str_in_full(helper->in, "list for-push\n"); else From b236752a8722c77b5a9b4ed488a992ee05252843 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Date: Wed, 9 Dec 2009 17:26:33 +0200 Subject: [PATCH 7/9] Support remote archive from all smart transports Previously, remote archive required internal (non remote-helper) smart transport. Extend the remote archive to also support smart transports implemented by remote helpers. Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- builtin-archive.c | 17 ++++++++++------- transport-helper.c | 19 +++++++++++++++++++ transport.c | 21 +++++++++++++++++++++ transport.h | 5 +++++ 4 files changed, 55 insertions(+), 7 deletions(-) diff --git a/builtin-archive.c b/builtin-archive.c index 12351e9dd5..d34b3fd028 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -5,6 +5,7 @@ #include "cache.h" #include "builtin.h" #include "archive.h" +#include "transport.h" #include "parse-options.h" #include "pkt-line.h" #include "sideband.h" @@ -25,12 +26,16 @@ static void create_output_file(const char *output_file) static int run_remote_archiver(int argc, const char **argv, const char *remote, const char *exec) { - char *url, buf[LARGE_PACKET_MAX]; + char buf[LARGE_PACKET_MAX]; int fd[2], i, len, rv; - struct child_process *conn; + struct transport *transport; + struct remote *_remote; - url = xstrdup(remote); - conn = git_connect(fd, url, exec, 0); + _remote = remote_get(remote); + if (!_remote->url[0]) + die("git archive: Remote with no URL"); + transport = transport_get(_remote, _remote->url[0]); + transport_connect(transport, "git-upload-archive", exec, fd); for (i = 1; i < argc; i++) packet_write(fd[1], "argument %s\n", argv[i]); @@ -53,9 +58,7 @@ static int run_remote_archiver(int argc, const char **argv, /* Now, start reading from fd[0] and spit it out to stdout */ rv = recv_sideband("archive", fd[0], 1); - close(fd[0]); - close(fd[1]); - rv |= finish_connect(conn); + rv |= transport_disconnect(transport); return !!rv; } diff --git a/transport-helper.c b/transport-helper.c index 50b3bac0c4..6ece0d9875 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -467,6 +467,24 @@ static int process_connect(struct transport *transport, return process_connect_service(transport, name, exec); } +static int connect_helper(struct transport *transport, const char *name, + const char *exec, int fd[2]) +{ + struct helper_data *data = transport->data; + + /* Get_helper so connect is inited. */ + get_helper(transport); + if (!data->connect) + die("Operation not supported by protocol."); + + if (!process_connect_service(transport, name, exec)) + die("Can't connect to subservice %s.", name); + + fd[0] = data->helper->out; + fd[1] = data->helper->in; + return 0; +} + static int fetch(struct transport *transport, int nr_heads, struct ref **to_fetch) { @@ -711,6 +729,7 @@ int transport_helper_init(struct transport *transport, const char *name) transport->fetch = fetch; transport->push_refs = push_refs; transport->disconnect = release_helper; + transport->connect = connect_helper; transport->smart_options = &(data->transport_options); return 0; } diff --git a/transport.c b/transport.c index ad25b98ae1..a7d67eba83 100644 --- a/transport.c +++ b/transport.c @@ -756,6 +756,17 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re return ret; } +static int connect_git(struct transport *transport, const char *name, + const char *executable, int fd[2]) +{ + struct git_transport_data *data = transport->data; + data->conn = git_connect(data->fd, transport->url, + executable, 0); + fd[0] = data->fd[0]; + fd[1] = data->fd[1]; + return 0; +} + static int disconnect_git(struct transport *transport) { struct git_transport_data *data = transport->data; @@ -901,6 +912,7 @@ struct transport *transport_get(struct remote *remote, const char *url) ret->get_refs_list = get_refs_via_connect; ret->fetch = fetch_refs_via_pack; ret->push_refs = git_transport_push; + ret->connect = connect_git; ret->disconnect = disconnect_git; ret->smart_options = &(data->options); @@ -1061,6 +1073,15 @@ void transport_unlock_pack(struct transport *transport) } } +int transport_connect(struct transport *transport, const char *name, + const char *exec, int fd[2]) +{ + if (transport->connect) + return transport->connect(transport, name, exec, fd); + else + die("Operation not supported by protocol"); +} + int transport_disconnect(struct transport *transport) { int ret = 0; diff --git a/transport.h b/transport.h index 781db2ec82..97ba2519dd 100644 --- a/transport.h +++ b/transport.h @@ -64,6 +64,8 @@ struct transport { **/ int (*push_refs)(struct transport *transport, struct ref *refs, int flags); int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags); + int (*connect)(struct transport *connection, const char *name, + const char *executable, int fd[2]); /** get_refs_list(), fetch(), and push_refs() can keep * resources (such as a connection) reserved for futher @@ -133,6 +135,9 @@ char *transport_anonymize_url(const char *url); void transport_take_over(struct transport *transport, struct child_process *child); +int transport_connect(struct transport *transport, const char *name, + const char *exec, int fd[2]); + /* Transport methods defined outside transport.c */ int transport_helper_init(struct transport *transport, const char *name); From 28ca0c90080ec933d82b0f7d050ea5fde2816c57 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Date: Wed, 9 Dec 2009 17:26:34 +0200 Subject: [PATCH 8/9] Remove special casing of http, https and ftp HTTP, HTTPS and FTP are no longer special to transport code. Also add support for FTPS (curl supports it so it is easy). Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- .gitignore | 4 ++++ Makefile | 27 +++++++++++++++++++++++++-- transport.c | 8 -------- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index ac02a580da..aa7a8ac193 100644 --- a/.gitignore +++ b/.gitignore @@ -107,6 +107,10 @@ /git-relink /git-remote /git-remote-curl +/git-remote-http +/git-remote-https +/git-remote-ftp +/git-remote-ftps /git-repack /git-replace /git-repo-config diff --git a/Makefile b/Makefile index 2ad7e366ef..87fc7ff476 100644 --- a/Makefile +++ b/Makefile @@ -424,6 +424,16 @@ BUILT_INS += git-stage$X BUILT_INS += git-status$X BUILT_INS += git-whatchanged$X +ifdef NO_CURL +REMOTE_CURL_PRIMARY = +REMOTE_CURL_ALIASES = +REMOTE_CURL_NAMES = +else +REMOTE_CURL_PRIMARY = git-remote-http$X +REMOTE_CURL_ALIASES = git-remote-https$X git-remote-ftp$X git-remote-ftps$X +REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES) +endif + # what 'all' will build and 'install' will install in gitexecdir, # excluding programs for built-in commands ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) @@ -1097,7 +1107,7 @@ else else CURL_LIBCURL = -lcurl endif - PROGRAMS += git-remote-curl$X git-http-fetch$X + PROGRAMS += $(REMOTE_CURL_NAMES) git-http-fetch$X curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p) ifeq "$(curl_check)" "070908" ifndef NO_EXPAT @@ -1676,7 +1686,13 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) -git-remote-curl$X: remote-curl.o http.o http-walker.o $(GITLIBS) +$(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY) + $(QUIET_LNCP)$(RM) $@ && \ + ln $< $@ 2>/dev/null || \ + ln -s $< $@ 2>/dev/null || \ + cp $< $@ + +$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) @@ -1852,6 +1868,7 @@ endif ifneq (,$X) $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p' -ef '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p$X' || $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';) endif + bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \ execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \ { test "$$bindir/" = "$$execdir/" || \ @@ -1865,6 +1882,12 @@ endif ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \ cp "$$execdir/git$X" "$$execdir/$$p" || exit; \ done; } && \ + { for p in $(REMOTE_CURL_ALIASES); do \ + $(RM) "$$execdir/$$p" && \ + ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \ + ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \ + cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \ + done; } && \ ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X" install-doc: diff --git a/transport.c b/transport.c index a7d67eba83..652bf0bd83 100644 --- a/transport.c +++ b/transport.c @@ -918,14 +918,6 @@ struct transport *transport_get(struct remote *remote, const char *url) data->conn = NULL; data->got_remote_heads = 0; - } else if (!prefixcmp(url, "http://") - || !prefixcmp(url, "https://") - || !prefixcmp(url, "ftp://")) { - /* These three are just plain special. */ - transport_helper_init(ret, "curl"); -#ifdef NO_CURL - error("git was compiled without libcurl support."); -#endif } else { /* Unknown protocol in URL. Pass to external handler. */ int len = external_specification_len(url); From 27a557a9ff9273ad47aa29c52e9903cf6405f7f7 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Date: Sat, 9 Jan 2010 19:28:12 +0200 Subject: [PATCH 9/9] Reset possible helper before reusing remote structure If one had multiple URLs configured for remote with previous one having forced helper but the subsequent one not, like: url = foo::bar://baz url = ssh://example/example.git Then the subsequent URL is passed to foo helper, which isn't correct. Fix it to be parsed normally by resetting foreign VCS name before parsing the URL protocol. Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- transport.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/transport.c b/transport.c index 652bf0bd83..b5332c018b 100644 --- a/transport.c +++ b/transport.c @@ -875,6 +875,9 @@ struct transport *transport_get(struct remote *remote, const char *url) url = remote->url[0]; ret->url = url; + /* In case previous URL had helper forced, reset it. */ + remote->foreign_vcs = NULL; + /* maybe it is a foreign URL? */ if (url) { const char *p = url;