summaryrefslogtreecommitdiff
path: root/remote-curl.c
diff options
context:
space:
mode:
Diffstat (limited to 'remote-curl.c')
-rw-r--r--remote-curl.c156
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);