Skip to content

Commit 6da1f1a

Browse files
steadmongitster
authored andcommitted
protocol: advertise multiple supported versions
Currently the client advertises that it supports the wire protocol version set in the protocol.version config. However, not all services support the same set of protocol versions. For example, git-receive-pack supports v1 and v0, but not v2. If a client connects to git-receive-pack and requests v2, it will instead be downgraded to v0. Other services, such as git-upload-archive, do not do any version negotiation checks. This patch creates a protocol version registry. Individual client and server programs register all the protocol versions they support prior to communicating with a remote instance. Versions should be listed in preference order; the version specified in protocol.version will automatically be moved to the front of the registry. The protocol version registry is passed to remote helpers via the GIT_PROTOCOL environment variable. Clients now advertise the full list of registered versions. Servers select the first allowed version from this advertisement. Additionally, remove special cases around advertising version=0. Previously we avoided adding version advertisements to the client's initial connection request if the client wanted version=0. However, including these advertisements does not change the version negotiation behavior, so it's better to have simpler code. As a side effect, this means that client operations over SSH will always include a "SendEnv=GIT_PROTOCOL" option on the SSH command line. While we're at it, remove unnecessary externs from function declarations in protocol.h. Signed-off-by: Josh Steadmon <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b21ebb6 commit 6da1f1a

21 files changed

+256
-82
lines changed

builtin/archive.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "transport.h"
99
#include "parse-options.h"
1010
#include "pkt-line.h"
11+
#include "protocol.h"
1112
#include "sideband.h"
1213

1314
static void create_output_file(const char *output_file)
@@ -94,6 +95,8 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
9495
OPT_END()
9596
};
9697

98+
register_allowed_protocol_version(protocol_v0);
99+
97100
argc = parse_options(argc, argv, prefix, local_opts, NULL,
98101
PARSE_OPT_KEEP_ALL);
99102

builtin/clone.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
901901
struct refspec rs = REFSPEC_INIT_FETCH;
902902
struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
903903

904+
register_allowed_protocol_version(protocol_v2);
905+
register_allowed_protocol_version(protocol_v1);
906+
register_allowed_protocol_version(protocol_v0);
907+
904908
fetch_if_missing = 0;
905909

906910
packet_trace_identity("clone");

builtin/fetch-pack.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
5858

5959
fetch_if_missing = 0;
6060

61+
register_allowed_protocol_version(protocol_v2);
62+
register_allowed_protocol_version(protocol_v1);
63+
register_allowed_protocol_version(protocol_v0);
64+
6165
packet_trace_identity("fetch-pack");
6266

6367
memset(&args, 0, sizeof(args));

builtin/fetch.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "argv-array.h"
2222
#include "utf8.h"
2323
#include "packfile.h"
24+
#include "protocol.h"
2425
#include "list-objects-filter-options.h"
2526
#include "commit-reach.h"
2627

@@ -1570,6 +1571,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
15701571
int prune_tags_ok = 1;
15711572
struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
15721573

1574+
register_allowed_protocol_version(protocol_v2);
1575+
register_allowed_protocol_version(protocol_v1);
1576+
register_allowed_protocol_version(protocol_v0);
1577+
15731578
packet_trace_identity("fetch");
15741579

15751580
fetch_if_missing = 0;

builtin/ls-remote.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "builtin.h"
22
#include "cache.h"
3+
#include "protocol.h"
34
#include "transport.h"
45
#include "ref-filter.h"
56
#include "remote.h"
@@ -80,6 +81,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
8081

8182
memset(&ref_array, 0, sizeof(ref_array));
8283

84+
register_allowed_protocol_version(protocol_v2);
85+
register_allowed_protocol_version(protocol_v1);
86+
register_allowed_protocol_version(protocol_v0);
87+
8388
argc = parse_options(argc, argv, prefix, options, ls_remote_usage,
8489
PARSE_OPT_STOP_AT_NON_OPTION);
8590
dest = argv[0];

builtin/pull.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "config.h"
1010
#include "builtin.h"
1111
#include "parse-options.h"
12+
#include "protocol.h"
1213
#include "exec-cmd.h"
1314
#include "run-command.h"
1415
#include "sha1-array.h"
@@ -863,6 +864,10 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
863864
struct object_id rebase_fork_point;
864865
int autostash;
865866

867+
register_allowed_protocol_version(protocol_v2);
868+
register_allowed_protocol_version(protocol_v1);
869+
register_allowed_protocol_version(protocol_v0);
870+
866871
if (!getenv("GIT_REFLOG_ACTION"))
867872
set_reflog_message(argc, argv);
868873

builtin/push.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "remote.h"
1111
#include "transport.h"
1212
#include "parse-options.h"
13+
#include "protocol.h"
1314
#include "submodule.h"
1415
#include "submodule-config.h"
1516
#include "send-pack.h"
@@ -587,6 +588,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
587588
OPT_END()
588589
};
589590

591+
register_allowed_protocol_version(protocol_v1);
592+
register_allowed_protocol_version(protocol_v0);
593+
590594
packet_trace_identity("push");
591595
git_config(git_push_config, &flags);
592596
argc = parse_options(argc, argv, prefix, options, push_usage, 0);

builtin/receive-pack.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,6 +1935,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
19351935

19361936
packet_trace_identity("receive-pack");
19371937

1938+
register_allowed_protocol_version(protocol_v1);
1939+
register_allowed_protocol_version(protocol_v0);
1940+
19381941
argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0);
19391942

19401943
if (argc > 1)

builtin/send-pack.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
184184
OPT_END()
185185
};
186186

187+
register_allowed_protocol_version(protocol_v1);
188+
register_allowed_protocol_version(protocol_v0);
189+
187190
git_config(send_pack_config, NULL);
188191
argc = parse_options(argc, argv, prefix, options, send_pack_usage, 0);
189192
if (argc > 0) {

builtin/upload-archive.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "builtin.h"
66
#include "archive.h"
77
#include "pkt-line.h"
8+
#include "protocol.h"
89
#include "sideband.h"
910
#include "run-command.h"
1011
#include "argv-array.h"
@@ -82,6 +83,8 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
8283
if (argc == 2 && !strcmp(argv[1], "-h"))
8384
usage(upload_archive_usage);
8485

86+
register_allowed_protocol_version(protocol_v0);
87+
8588
/*
8689
* Set up sideband subprocess.
8790
*

builtin/upload-pack.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
3333
packet_trace_identity("upload-pack");
3434
read_replace_refs = 0;
3535

36+
register_allowed_protocol_version(protocol_v2);
37+
register_allowed_protocol_version(protocol_v1);
38+
register_allowed_protocol_version(protocol_v0);
39+
3640
argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0);
3741

3842
if (argc != 1)

connect.c

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,7 +1046,7 @@ static enum ssh_variant determine_ssh_variant(const char *ssh_command,
10461046
*/
10471047
static struct child_process *git_connect_git(int fd[2], char *hostandport,
10481048
const char *path, const char *prog,
1049-
enum protocol_version version,
1049+
const struct strbuf *version_advert,
10501050
int flags)
10511051
{
10521052
struct child_process *conn;
@@ -1084,12 +1084,9 @@ static struct child_process *git_connect_git(int fd[2], char *hostandport,
10841084
prog, path, 0,
10851085
target_host, 0);
10861086

1087-
/* If using a new version put that stuff here after a second null byte */
1088-
if (version > 0) {
1089-
strbuf_addch(&request, '\0');
1090-
strbuf_addf(&request, "version=%d%c",
1091-
version, '\0');
1092-
}
1087+
/* Add version fields after a second null byte */
1088+
strbuf_addch(&request, '\0');
1089+
strbuf_addf(&request, "%s%c", version_advert->buf, '\0');
10931090

10941091
packet_write(fd[1], request.buf, request.len);
10951092

@@ -1104,14 +1101,13 @@ static struct child_process *git_connect_git(int fd[2], char *hostandport,
11041101
*/
11051102
static void push_ssh_options(struct argv_array *args, struct argv_array *env,
11061103
enum ssh_variant variant, const char *port,
1107-
enum protocol_version version, int flags)
1104+
const struct strbuf *version_advert, int flags)
11081105
{
1109-
if (variant == VARIANT_SSH &&
1110-
version > 0) {
1106+
if (variant == VARIANT_SSH) {
11111107
argv_array_push(args, "-o");
11121108
argv_array_push(args, "SendEnv=" GIT_PROTOCOL_ENVIRONMENT);
1113-
argv_array_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=version=%d",
1114-
version);
1109+
argv_array_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=%s",
1110+
version_advert->buf);
11151111
}
11161112

11171113
if (flags & CONNECT_IPV4) {
@@ -1164,7 +1160,7 @@ static void push_ssh_options(struct argv_array *args, struct argv_array *env,
11641160

11651161
/* Prepare a child_process for use by Git's SSH-tunneled transport. */
11661162
static void fill_ssh_args(struct child_process *conn, const char *ssh_host,
1167-
const char *port, enum protocol_version version,
1163+
const char *port, const struct strbuf *version_advert,
11681164
int flags)
11691165
{
11701166
const char *ssh;
@@ -1198,15 +1194,16 @@ static void fill_ssh_args(struct child_process *conn, const char *ssh_host,
11981194

11991195
argv_array_push(&detect.args, ssh);
12001196
argv_array_push(&detect.args, "-G");
1201-
push_ssh_options(&detect.args, &detect.env_array,
1202-
VARIANT_SSH, port, version, flags);
1197+
push_ssh_options(&detect.args, &detect.env_array, VARIANT_SSH,
1198+
port, version_advert, flags);
12031199
argv_array_push(&detect.args, ssh_host);
12041200

12051201
variant = run_command(&detect) ? VARIANT_SIMPLE : VARIANT_SSH;
12061202
}
12071203

12081204
argv_array_push(&conn->args, ssh);
1209-
push_ssh_options(&conn->args, &conn->env_array, variant, port, version, flags);
1205+
push_ssh_options(&conn->args, &conn->env_array, variant, port,
1206+
version_advert, flags);
12101207
argv_array_push(&conn->args, ssh_host);
12111208
}
12121209

@@ -1226,16 +1223,10 @@ struct child_process *git_connect(int fd[2], const char *url,
12261223
{
12271224
char *hostandport, *path;
12281225
struct child_process *conn;
1226+
struct strbuf version_advert = STRBUF_INIT;
12291227
enum protocol protocol;
1230-
enum protocol_version version = get_protocol_version_config();
12311228

1232-
/*
1233-
* NEEDSWORK: If we are trying to use protocol v2 and we are planning
1234-
* to perform a push, then fallback to v0 since the client doesn't know
1235-
* how to push yet using v2.
1236-
*/
1237-
if (version == protocol_v2 && !strcmp("git-receive-pack", prog))
1238-
version = protocol_v0;
1229+
get_client_protocol_version_advertisement(&version_advert);
12391230

12401231
/* Without this we cannot rely on waitpid() to tell
12411232
* what happened to our children.
@@ -1250,7 +1241,8 @@ struct child_process *git_connect(int fd[2], const char *url,
12501241
printf("Diag: path=%s\n", path ? path : "NULL");
12511242
conn = NULL;
12521243
} else if (protocol == PROTO_GIT) {
1253-
conn = git_connect_git(fd, hostandport, path, prog, version, flags);
1244+
conn = git_connect_git(fd, hostandport, path, prog,
1245+
&version_advert, flags);
12541246
} else {
12551247
struct strbuf cmd = STRBUF_INIT;
12561248
const char *const *var;
@@ -1293,13 +1285,13 @@ struct child_process *git_connect(int fd[2], const char *url,
12931285
strbuf_release(&cmd);
12941286
return NULL;
12951287
}
1296-
fill_ssh_args(conn, ssh_host, port, version, flags);
1288+
fill_ssh_args(conn, ssh_host, port, &version_advert,
1289+
flags);
12971290
} else {
12981291
transport_check_allowed("file");
1299-
if (version > 0) {
1300-
argv_array_pushf(&conn->env_array, GIT_PROTOCOL_ENVIRONMENT "=version=%d",
1301-
version);
1302-
}
1292+
argv_array_pushf(&conn->env_array,
1293+
GIT_PROTOCOL_ENVIRONMENT "=%s",
1294+
version_advert.buf);
13031295
}
13041296
argv_array_push(&conn->args, cmd.buf);
13051297

0 commit comments

Comments
 (0)