@@ -168,6 +168,9 @@ Supported commands: 'list', 'import'.
Can guarantee that when a clone is requested, the received
pack is self contained and is connected.
+'get'::
+ Can use the 'get' command to download a file from a given URI.
+
If a helper advertises 'connect', Git will use it if possible and
fall back to another capability if the helper requests so when
connecting (see the 'connect' command under COMMANDS).
@@ -418,6 +421,12 @@ Supported if the helper has the "connect" capability.
+
Supported if the helper has the "stateless-connect" capability.
+'get' <uri> <path>::
+ Downloads the file from the given `<uri>` to the given `<path>`. If
+ `<path>.temp` exists, then Git assumes that the `.temp` file is a
+ partial download from a previous attempt and will resume the
+ download from that position.
+
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
@@ -1276,6 +1276,34 @@ static void parse_fetch(struct strbuf *buf)
strbuf_reset(buf);
}
+static void parse_get(struct strbuf *buf)
+{
+ struct http_get_options opts = { 0 };
+ struct strbuf url = STRBUF_INIT;
+ struct strbuf path = STRBUF_INIT;
+ const char *p, *space;
+
+ if (!skip_prefix(buf->buf, "get ", &p))
+ die(_("http transport does not support %s"), buf->buf);
+
+ space = strchr(p, ' ');
+
+ if (!space)
+ die(_("protocol error: expected '<url> <path>', missing space"));
+
+ strbuf_add(&url, p, space - p);
+ strbuf_addstr(&path, space + 1);
+
+ if (http_get_file(url.buf, path.buf, &opts))
+ die(_("failed to download file at URL '%s'"), url.buf);
+
+ strbuf_release(&url);
+ strbuf_release(&path);
+ printf("\n");
+ fflush(stdout);
+ strbuf_reset(buf);
+}
+
static int push_dav(int nr_spec, const char **specs)
{
struct child_process child = CHILD_PROCESS_INIT;
@@ -1549,9 +1577,14 @@ int cmd_main(int argc, const char **argv)
printf("unsupported\n");
fflush(stdout);
+ } else if (skip_prefix(buf.buf, "get ", &arg)) {
+ parse_get(&buf);
+ fflush(stdout);
+
} else if (!strcmp(buf.buf, "capabilities")) {
printf("stateless-connect\n");
printf("fetch\n");
+ printf("get\n");
printf("option\n");
printf("push\n");
printf("check-connectivity\n");
new file mode 100755
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='test downloading a file by URL'
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'get by URL: 404' '
+ url="$HTTPD_URL/none.txt" &&
+ cat >input <<-EOF &&
+ capabilities
+ get $url file1
+ EOF
+
+ test_must_fail git remote-http $url $url <input 2>err &&
+ test_path_is_missing file1 &&
+ grep "failed to download file at URL" err &&
+ rm file1.temp
+'
+
+test_expect_success 'get by URL: 200' '
+ echo data >"$HTTPD_DOCUMENT_ROOT_PATH/exists.txt" &&
+
+ url="$HTTPD_URL/exists.txt" &&
+ cat >input <<-EOF &&
+ capabilities
+ get $url file2
+
+ EOF
+
+ GIT_TRACE2_PERF=1 git remote-http $url $url <input &&
+ test_cmp "$HTTPD_DOCUMENT_ROOT_PATH/exists.txt" file2
+'
+
+test_done
@@ -33,7 +33,8 @@ struct helper_data {
check_connectivity : 1,
no_disconnect_req : 1,
no_private_update : 1,
- object_format : 1;
+ object_format : 1,
+ get : 1;
/*
* As an optimization, the transport code may invoke fetch before
@@ -210,6 +211,8 @@ static struct child_process *get_helper(struct transport *transport)
data->no_private_update = 1;
} else if (starts_with(capname, "object-format")) {
data->object_format = 1;
+ } else if (!strcmp(capname, "get")) {
+ data->get = 1;
} else if (mandatory) {
die(_("unknown mandatory capability %s; this remote "
"helper probably needs newer version of Git"),