int err = 0, complete_refs_before_fetch = 1;
int submodule_progress;
- struct refspec rs = REFSPEC_INIT_FETCH;
struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
+ register_allowed_protocol_version(protocol_v2);
+ register_allowed_protocol_version(protocol_v1);
+ register_allowed_protocol_version(protocol_v0);
+
fetch_if_missing = 0;
packet_trace_identity("clone");
#include "argv-array.h"
#include "utf8.h"
#include "packfile.h"
+ #include "protocol.h"
#include "list-objects-filter-options.h"
#include "commit-reach.h"
+#include "branch.h"
+#include "promisor-remote.h"
+#include "commit-graph.h"
+
+#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
static const char * const builtin_fetch_usage[] = {
N_("git fetch [<options>] [<repository> [<refspec>...]]"),
packet_trace_identity("upload-pack");
read_replace_refs = 0;
- argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0);
+ register_allowed_protocol_version(protocol_v2);
+ register_allowed_protocol_version(protocol_v1);
+ register_allowed_protocol_version(protocol_v0);
+
+ argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
if (argc != 1)
usage_with_options(upload_pack_usage, options);
printf("Diag: path=%s\n", path ? path : "NULL");
conn = NULL;
} else if (protocol == PROTO_GIT) {
- conn = git_connect_git(fd, hostandport, path, prog, version, flags);
+ conn = git_connect_git(fd, hostandport, path, prog,
+ &version_advert, flags);
+ conn->trace2_child_class = "transport/git";
} else {
struct strbuf cmd = STRBUF_INIT;
const char *const *var;
strbuf_release(&cmd);
return NULL;
}
- fill_ssh_args(conn, ssh_host, port, version, flags);
+ conn->trace2_child_class = "transport/ssh";
+ fill_ssh_args(conn, ssh_host, port, &version_advert,
+ flags);
} else {
transport_check_allowed("file");
- if (version > 0) {
- argv_array_pushf(&conn->env_array, GIT_PROTOCOL_ENVIRONMENT "=version=%d",
- version);
- }
+ conn->trace2_child_class = "transport/file";
+ argv_array_pushf(&conn->env_array,
+ GIT_PROTOCOL_ENVIRONMENT "=%s",
+ version_advert.buf);
}
argv_array_push(&conn->args, cmd.buf);
die("unknown value for config 'protocol.version': %s",
value);
- return version;
+ retval = version;
+ }
+
+ if (git_test_v && *git_test_v) {
+ enum protocol_version env = parse_protocol_version(git_test_v);
+
+ if (env == protocol_unknown_version)
+ die("unknown value for %s: %s", git_test_k, git_test_v);
+ if (retval < env)
+ retval = env;
}
- return protocol_v0;
+ return retval;
}
+ void register_allowed_protocol_version(enum protocol_version version)
+ {
+ if (version_registration_locked)
+ BUG("late attempt to register an allowed protocol version");
+
+ ALLOC_GROW(allowed_versions, nr_allowed_versions + 1,
+ alloc_allowed_versions);
+ allowed_versions[nr_allowed_versions++] = version;
+ }
+
+ void register_allowed_protocol_versions_from_env(void)
+ {
+ const char *git_protocol = getenv(GIT_PROTOCOL_ENVIRONMENT);
+ const char *version_str;
+ struct string_list version_list = STRING_LIST_INIT_DUP;
+ struct string_list_item *version;
+
+ if (!git_protocol)
+ return;
+
+ string_list_split(&version_list, git_protocol, ':', -1);
+ for_each_string_list_item(version, &version_list) {
+ if (skip_prefix(version->string, "version=", &version_str))
+ register_allowed_protocol_version(
+ parse_protocol_version(version_str));
+ }
+ string_list_clear(&version_list, 0);
+ }
+
+ static int is_allowed_protocol_version(enum protocol_version version)
+ {
+ int i;
+ version_registration_locked = 1;
+ for (i = 0; i < nr_allowed_versions; i++)
+ if (version == allowed_versions[i])
+ return 1;
+ return 0;
+ }
+
+ void get_client_protocol_version_advertisement(struct strbuf *advert)
+ {
+ int i, tmp_nr = nr_allowed_versions;
+ enum protocol_version *tmp_allowed_versions, config_version;
+ strbuf_reset(advert);
+
+ version_registration_locked = 1;
+
+ config_version = get_protocol_version_config();
+ if (config_version == protocol_v0) {
+ strbuf_addstr(advert, "version=0");
+ return;
+ }
+
+ if (tmp_nr > 0) {
+ ALLOC_ARRAY(tmp_allowed_versions, tmp_nr);
+ copy_array(tmp_allowed_versions, allowed_versions, tmp_nr,
+ sizeof(enum protocol_version));
+ } else {
+ ALLOC_ARRAY(tmp_allowed_versions, 1);
+ tmp_nr = 1;
+ tmp_allowed_versions[0] = config_version;
+ }
+
+ if (tmp_allowed_versions[0] != config_version)
+ for (i = 1; i < nr_allowed_versions; i++)
+ if (tmp_allowed_versions[i] == config_version) {
+ SWAP(tmp_allowed_versions[0],
+ tmp_allowed_versions[i]);
+ }
+
+ strbuf_addf(advert, "version=%s",
+ format_protocol_version(tmp_allowed_versions[0]));
+ for (i = 1; i < tmp_nr; i++)
+ strbuf_addf(advert, ":version=%s",
+ format_protocol_version(tmp_allowed_versions[i]));
+
+ free(tmp_allowed_versions);
+ }
+
enum protocol_version determine_protocol_version_server(void)
{
const char *git_protocol = getenv(GIT_PROTOCOL_ENVIRONMENT);
return 0;
}
+ static int get_client_protocol_http_header(const struct strbuf *version_advert,
+ struct strbuf *header)
+ {
+ if (version_advert->len > 0) {
+ strbuf_addf(header, GIT_PROTOCOL_HEADER ": %s",
+ version_advert->buf);
+
+ return 1;
+ }
+
+ return 0;
+ }
+
+static void check_smart_http(struct discovery *d, const char *service,
+ struct strbuf *type)
+{
+ const char *p;
+ struct packet_reader reader;
+
+ /*
+ * If we don't see x-$service-advertisement, then it's not smart-http.
+ * But once we do, we commit to it and assume any other protocol
+ * violations are hard errors.
+ */
+ if (!skip_prefix(type->buf, "application/x-", &p) ||
+ !skip_prefix(p, service, &p) ||
+ strcmp(p, "-advertisement"))
+ return;
+
+ packet_reader_init(&reader, -1, d->buf, d->len,
+ PACKET_READ_CHOMP_NEWLINE |
+ PACKET_READ_DIE_ON_ERR_PACKET);
+ if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
+ die(_("invalid server response; expected service, got flush packet"));
+
+ if (skip_prefix(reader.line, "# service=", &p) && !strcmp(p, service)) {
+ /*
+ * The header can include additional metadata lines, up
+ * until a packet flush marker. Ignore these now, but
+ * in the future we might start to scan them.
+ */
+ for (;;) {
+ packet_reader_read(&reader);
+ if (reader.pktlen <= 0) {
+ break;
+ }
+ }
+
+ /*
+ * v0 smart http; callers expect us to soak up the
+ * service and header packets
+ */
+ d->buf = reader.src_buffer;
+ d->len = reader.src_len;
+ d->proto_git = 1;
+
+ } else if (!strcmp(reader.line, "version 2")) {
+ /*
+ * v2 smart http; do not consume version packet, which will
+ * be handled elsewhere.
+ */
+ d->proto_git = 1;
+
+ } else {
+ die(_("invalid server response; got '%s'"), reader.line);
+ }
+}
+
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 buf = STRBUF_INIT;
int nongit;
+ register_allowed_protocol_versions_from_env();
+
setup_git_directory_gently(&nongit);
if (argc < 2) {
- error("remote-curl: usage: git remote-curl <remote> [<url>]");
+ error(_("remote-curl: usage: git remote-curl <remote> [<url>]"));
return 1;
}
}
test_expect_success 'clone myhost:src uses ssh' '
- git clone myhost:src ssh-clone &&
+ GIT_TEST_PROTOCOL_VERSION=0 git clone myhost:src ssh-clone &&
- expect_ssh myhost src
+ expect_ssh "-o SendEnv=GIT_PROTOCOL" myhost src
'
test_expect_success !MINGW,!CYGWIN 'clone local path foo:bar' '
'
test_expect_success 'bracketed hostnames are still ssh' '
- git clone "[myhost:123]:src" ssh-bracket-clone &&
+ GIT_TEST_PROTOCOL_VERSION=0 git clone "[myhost:123]:src" ssh-bracket-clone &&
- expect_ssh "-p 123" myhost src
+ expect_ssh "-o SendEnv=GIT_PROTOCOL -p 123" myhost src
'
test_expect_success 'OpenSSH variant passes -4' '
- git clone -4 "[myhost:123]:src" ssh-ipv4-clone &&
+ GIT_TEST_PROTOCOL_VERSION=0 git clone -4 "[myhost:123]:src" ssh-ipv4-clone &&
- expect_ssh "-4 -p 123" myhost src
+ expect_ssh "-o SendEnv=GIT_PROTOCOL -4 -p 123" myhost src
'
test_expect_success 'variant can be overridden' '
test_when_finished "rm -f \"\$TRASH_DIRECTORY/uplink\"" &&
GIT_SSH="$TRASH_DIRECTORY/uplink" &&
test_when_finished "GIT_SSH=\"\$TRASH_DIRECTORY/ssh\$X\"" &&
- git clone "[myhost:123]:src" ssh-bracket-clone-sshlike-uplink &&
+ GIT_TEST_PROTOCOL_VERSION=0 git clone "[myhost:123]:src" ssh-bracket-clone-sshlike-uplink &&
- expect_ssh "-p 123" myhost src
+ expect_ssh "-o SendEnv=GIT_PROTOCOL -p 123" myhost src
'
test_expect_success 'plink is treated specially (as putty)' '
test_expect_success 'GIT_SSH_VARIANT overrides plink detection' '
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
- GIT_SSH_VARIANT=ssh \
+ GIT_TEST_PROTOCOL_VERSION=0 GIT_SSH_VARIANT=ssh \
- git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 &&
- expect_ssh "-p 123" myhost src
+ git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 &&
+ expect_ssh "-o SendEnv=GIT_PROTOCOL -p 123" myhost src
'
test_expect_success 'ssh.variant overrides plink detection' '
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
- git -c ssh.variant=ssh \
+ GIT_TEST_PROTOCOL_VERSION=0 git -c ssh.variant=ssh \
clone "[myhost:123]:src" ssh-bracket-clone-variant-2 &&
- expect_ssh "-p 123" myhost src
+ expect_ssh "-o SendEnv=GIT_PROTOCOL -p 123" myhost src
'
test_expect_success 'GIT_SSH_VARIANT overrides plink detection to plink' '
expect_ssh "$@"
}
-test_expect_success !MINGW 'clone c:temp is ssl' '
+test_expect_success !MINGW,!CYGWIN 'clone c:temp is ssl' '
- test_clone_url c:temp c temp
+ test_clone_url c:temp "-o SendEnv=GIT_PROTOCOL" c temp
'
test_expect_success MINGW 'clone c:temp is dos drive' '
argv_array_pushf(&helper->env_array, "%s=%s",
GIT_DIR_ENVIRONMENT, get_git_dir());
+ get_client_protocol_version_advertisement(&version_advert);
+ if (version_advert.len > 0)
+ argv_array_pushf(&helper->env_array, "%s=%s",
+ GIT_PROTOCOL_ENVIRONMENT, version_advert.buf);
+
+ helper->trace2_child_class = helper->args.argv[0]; /* "remote-<name>" */
+
code = start_command(helper);
if (code < 0 && errno == ENOENT)
die(_("unable to find remote helper for '%s'"), data->name);