Merge branch 'jt/fetch-v2-sideband'

"git fetch" and "git upload-pack" learned to send all exchange over
the sideband channel while talking the v2 protocol.

* jt/fetch-v2-sideband:
  tests: define GIT_TEST_SIDEBAND_ALL
  {fetch,upload}-pack: sideband v2 fetch response
  sideband: reverse its dependency on pkt-line
  pkt-line: introduce struct packet_writer
  pack-protocol.txt: accept error packets in any context
  Use packet_reader instead of packet_read_line
This commit is contained in:
Junio C Hamano 2019-02-05 14:26:11 -08:00
commit 5f8b86db94
23 changed files with 522 additions and 303 deletions

View File

@ -22,6 +22,16 @@ protocol-common.txt. When the grammar indicate `PKT-LINE(...)`, unless
otherwise noted the usual pkt-line LF rules apply: the sender SHOULD otherwise noted the usual pkt-line LF rules apply: the sender SHOULD
include a LF, but the receiver MUST NOT complain if it is not present. include a LF, but the receiver MUST NOT complain if it is not present.
An error packet is a special pkt-line that contains an error string.
----
error-line = PKT-LINE("ERR" SP explanation-text)
----
Throughout the protocol, where `PKT-LINE(...)` is expected, an error packet MAY
be sent. Once this packet is sent by a client or a server, the data transfer
process defined in this protocol is terminated.
Transports Transports
---------- ----------
There are three transports over which the packfile protocol is There are three transports over which the packfile protocol is
@ -89,13 +99,6 @@ process on the server side over the Git protocol is this:
"0039git-upload-pack /schacon/gitbook.git\0host=example.com\0" | "0039git-upload-pack /schacon/gitbook.git\0host=example.com\0" |
nc -v example.com 9418 nc -v example.com 9418
If the server refuses the request for some reasons, it could abort
gracefully with an error message.
----
error-line = PKT-LINE("ERR" SP explanation-text)
----
SSH Transport SSH Transport
------------- -------------
@ -398,12 +401,11 @@ from the client).
Then the server will start sending its packfile data. Then the server will start sending its packfile data.
---- ----
server-response = *ack_multi ack / nak / error-line server-response = *ack_multi ack / nak
ack_multi = PKT-LINE("ACK" SP obj-id ack_status) ack_multi = PKT-LINE("ACK" SP obj-id ack_status)
ack_status = "continue" / "common" / "ready" ack_status = "continue" / "common" / "ready"
ack = PKT-LINE("ACK" SP obj-id) ack = PKT-LINE("ACK" SP obj-id)
nak = PKT-LINE("NAK") nak = PKT-LINE("NAK")
error-line = PKT-LINE("ERR" SP explanation-text)
---- ----
A simple clone may look like this (with no 'have' lines): A simple clone may look like this (with no 'have' lines):

View File

@ -313,6 +313,16 @@ the 'wanted-refs' section in the server's response as explained below.
particular ref, where <ref> is the full name of a ref on the particular ref, where <ref> is the full name of a ref on the
server. server.
If the 'sideband-all' feature is advertised, the following argument can be
included in the client's request:
sideband-all
Instruct the server to send the whole response multiplexed, not just
the packfile section. All non-flush and non-delim PKT-LINE in the
response (not only in the packfile section) will then start with a byte
indicating its sideband (1, 2, or 3), and the server may send "0005\2"
(a PKT-LINE of sideband 2 with no payload) as a keepalive packet.
The response of `fetch` is broken into a number of sections separated by The response of `fetch` is broken into a number of sections separated by
delimiter packets (0001), with each section beginning with its section delimiter packets (0001), with each section beginning with its section
header. header.

View File

@ -27,10 +27,10 @@ static int run_remote_archiver(int argc, const char **argv,
const char *remote, const char *exec, const char *remote, const char *exec,
const char *name_hint) const char *name_hint)
{ {
char *buf;
int fd[2], i, rv; int fd[2], i, rv;
struct transport *transport; struct transport *transport;
struct remote *_remote; struct remote *_remote;
struct packet_reader reader;
_remote = remote_get(remote); _remote = remote_get(remote);
if (!_remote->url[0]) if (!_remote->url[0])
@ -53,18 +53,19 @@ static int run_remote_archiver(int argc, const char **argv,
packet_write_fmt(fd[1], "argument %s\n", argv[i]); packet_write_fmt(fd[1], "argument %s\n", argv[i]);
packet_flush(fd[1]); packet_flush(fd[1]);
buf = packet_read_line(fd[0], NULL); packet_reader_init(&reader, fd[0], NULL, 0,
if (!buf) PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);
if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
die(_("git archive: expected ACK/NAK, got a flush packet")); die(_("git archive: expected ACK/NAK, got a flush packet"));
if (strcmp(buf, "ACK")) { if (strcmp(reader.line, "ACK")) {
if (starts_with(buf, "NACK ")) if (starts_with(reader.line, "NACK "))
die(_("git archive: NACK %s"), buf + 5); die(_("git archive: NACK %s"), reader.line + 5);
if (starts_with(buf, "ERR "))
die(_("remote error: %s"), buf + 4);
die(_("git archive: protocol error")); die(_("git archive: protocol error"));
} }
if (packet_read_line(fd[0], NULL)) if (packet_reader_read(&reader) != PACKET_READ_FLUSH)
die(_("git archive: expected a flush")); die(_("git archive: expected a flush"));
/* Now, start reading from fd[0] and spit it out to stdout */ /* Now, start reading from fd[0] and spit it out to stdout */

View File

@ -218,7 +218,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
packet_reader_init(&reader, fd[0], NULL, 0, packet_reader_init(&reader, fd[0], NULL, 0,
PACKET_READ_CHOMP_NEWLINE | PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_GENTLE_ON_EOF); PACKET_READ_GENTLE_ON_EOF |
PACKET_READ_DIE_ON_ERR_PACKET);
version = discover_version(&reader); version = discover_version(&reader);
switch (version) { switch (version) {

View File

@ -1569,30 +1569,29 @@ static void queue_commands_from_cert(struct command **tail,
} }
} }
static struct command *read_head_info(struct oid_array *shallow) static struct command *read_head_info(struct packet_reader *reader,
struct oid_array *shallow)
{ {
struct command *commands = NULL; struct command *commands = NULL;
struct command **p = &commands; struct command **p = &commands;
for (;;) { for (;;) {
char *line; int linelen;
int len, linelen;
line = packet_read_line(0, &len); if (packet_reader_read(reader) != PACKET_READ_NORMAL)
if (!line)
break; break;
if (len > 8 && starts_with(line, "shallow ")) { if (reader->pktlen > 8 && starts_with(reader->line, "shallow ")) {
struct object_id oid; struct object_id oid;
if (get_oid_hex(line + 8, &oid)) if (get_oid_hex(reader->line + 8, &oid))
die("protocol error: expected shallow sha, got '%s'", die("protocol error: expected shallow sha, got '%s'",
line + 8); reader->line + 8);
oid_array_append(shallow, &oid); oid_array_append(shallow, &oid);
continue; continue;
} }
linelen = strlen(line); linelen = strlen(reader->line);
if (linelen < len) { if (linelen < reader->pktlen) {
const char *feature_list = line + linelen + 1; const char *feature_list = reader->line + linelen + 1;
if (parse_feature_request(feature_list, "report-status")) if (parse_feature_request(feature_list, "report-status"))
report_status = 1; report_status = 1;
if (parse_feature_request(feature_list, "side-band-64k")) if (parse_feature_request(feature_list, "side-band-64k"))
@ -1607,28 +1606,32 @@ static struct command *read_head_info(struct oid_array *shallow)
use_push_options = 1; use_push_options = 1;
} }
if (!strcmp(line, "push-cert")) { if (!strcmp(reader->line, "push-cert")) {
int true_flush = 0; int true_flush = 0;
char certbuf[1024]; int saved_options = reader->options;
reader->options &= ~PACKET_READ_CHOMP_NEWLINE;
for (;;) { for (;;) {
len = packet_read(0, NULL, NULL, packet_reader_read(reader);
certbuf, sizeof(certbuf), 0); if (reader->status == PACKET_READ_FLUSH) {
if (!len) {
true_flush = 1; true_flush = 1;
break; break;
} }
if (!strcmp(certbuf, "push-cert-end\n")) if (reader->status != PACKET_READ_NORMAL) {
die("protocol error: got an unexpected packet");
}
if (!strcmp(reader->line, "push-cert-end\n"))
break; /* end of cert */ break; /* end of cert */
strbuf_addstr(&push_cert, certbuf); strbuf_addstr(&push_cert, reader->line);
} }
reader->options = saved_options;
if (true_flush) if (true_flush)
break; break;
continue; continue;
} }
p = queue_command(p, line, linelen); p = queue_command(p, reader->line, linelen);
} }
if (push_cert.len) if (push_cert.len)
@ -1637,18 +1640,14 @@ static struct command *read_head_info(struct oid_array *shallow)
return commands; return commands;
} }
static void read_push_options(struct string_list *options) static void read_push_options(struct packet_reader *reader,
struct string_list *options)
{ {
while (1) { while (1) {
char *line; if (packet_reader_read(reader) != PACKET_READ_NORMAL)
int len;
line = packet_read_line(0, &len);
if (!line)
break; break;
string_list_append(options, line); string_list_append(options, reader->line);
} }
} }
@ -1924,6 +1923,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
struct oid_array shallow = OID_ARRAY_INIT; struct oid_array shallow = OID_ARRAY_INIT;
struct oid_array ref = OID_ARRAY_INIT; struct oid_array ref = OID_ARRAY_INIT;
struct shallow_info si; struct shallow_info si;
struct packet_reader reader;
struct option options[] = { struct option options[] = {
OPT__QUIET(&quiet, N_("quiet")), OPT__QUIET(&quiet, N_("quiet")),
@ -1986,12 +1986,16 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
if (advertise_refs) if (advertise_refs)
return 0; return 0;
if ((commands = read_head_info(&shallow)) != NULL) { packet_reader_init(&reader, 0, NULL, 0,
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);
if ((commands = read_head_info(&reader, &shallow)) != NULL) {
const char *unpack_status = NULL; const char *unpack_status = NULL;
struct string_list push_options = STRING_LIST_INIT_DUP; struct string_list push_options = STRING_LIST_INIT_DUP;
if (use_push_options) if (use_push_options)
read_push_options(&push_options); read_push_options(&reader, &push_options);
if (!check_cert_push_options(&push_options)) { if (!check_cert_push_options(&push_options)) {
struct command *cmd; struct command *cmd;
for (cmd = commands; cmd; cmd = cmd->next) for (cmd = commands; cmd; cmd = cmd->next)

View File

@ -250,7 +250,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
packet_reader_init(&reader, fd[0], NULL, 0, packet_reader_init(&reader, fd[0], NULL, 0,
PACKET_READ_CHOMP_NEWLINE | PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_GENTLE_ON_EOF); PACKET_READ_GENTLE_ON_EOF |
PACKET_READ_DIE_ON_ERR_PACKET);
switch (discover_version(&reader)) { switch (discover_version(&reader)) {
case protocol_v2: case protocol_v2:

View File

@ -296,7 +296,6 @@ struct ref **get_remote_heads(struct packet_reader *reader,
struct ref **orig_list = list; struct ref **orig_list = list;
int len = 0; int len = 0;
enum get_remote_heads_state state = EXPECTING_FIRST_REF; enum get_remote_heads_state state = EXPECTING_FIRST_REF;
const char *arg;
*list = NULL; *list = NULL;
@ -306,8 +305,6 @@ struct ref **get_remote_heads(struct packet_reader *reader,
die_initial_contact(1); die_initial_contact(1);
case PACKET_READ_NORMAL: case PACKET_READ_NORMAL:
len = reader->pktlen; len = reader->pktlen;
if (len > 4 && skip_prefix(reader->line, "ERR ", &arg))
die(_("remote error: %s"), arg);
break; break;
case PACKET_READ_FLUSH: case PACKET_READ_FLUSH:
state = EXPECTING_DONE; state = EXPECTING_DONE;

View File

@ -135,38 +135,42 @@ enum ack_type {
ACK_ready ACK_ready
}; };
static void consume_shallow_list(struct fetch_pack_args *args, int fd) static void consume_shallow_list(struct fetch_pack_args *args,
struct packet_reader *reader)
{ {
if (args->stateless_rpc && args->deepen) { if (args->stateless_rpc && args->deepen) {
/* If we sent a depth we will get back "duplicate" /* If we sent a depth we will get back "duplicate"
* shallow and unshallow commands every time there * shallow and unshallow commands every time there
* is a block of have lines exchanged. * is a block of have lines exchanged.
*/ */
char *line; while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
while ((line = packet_read_line(fd, NULL))) { if (starts_with(reader->line, "shallow "))
if (starts_with(line, "shallow "))
continue; continue;
if (starts_with(line, "unshallow ")) if (starts_with(reader->line, "unshallow "))
continue; continue;
die(_("git fetch-pack: expected shallow list")); die(_("git fetch-pack: expected shallow list"));
} }
if (reader->status != PACKET_READ_FLUSH)
die(_("git fetch-pack: expected a flush packet after shallow list"));
} }
} }
static enum ack_type get_ack(int fd, struct object_id *result_oid) static enum ack_type get_ack(struct packet_reader *reader,
struct object_id *result_oid)
{ {
int len; int len;
char *line = packet_read_line(fd, &len);
const char *arg; const char *arg;
if (!line) if (packet_reader_read(reader) != PACKET_READ_NORMAL)
die(_("git fetch-pack: expected ACK/NAK, got a flush packet")); die(_("git fetch-pack: expected ACK/NAK, got a flush packet"));
if (!strcmp(line, "NAK")) len = reader->pktlen;
if (!strcmp(reader->line, "NAK"))
return NAK; return NAK;
if (skip_prefix(line, "ACK ", &arg)) { if (skip_prefix(reader->line, "ACK ", &arg)) {
if (!get_oid_hex(arg, result_oid)) { if (!get_oid_hex(arg, result_oid)) {
arg += 40; arg += 40;
len -= arg - line; len -= arg - reader->line;
if (len < 1) if (len < 1)
return ACK; return ACK;
if (strstr(arg, "continue")) if (strstr(arg, "continue"))
@ -178,9 +182,7 @@ static enum ack_type get_ack(int fd, struct object_id *result_oid)
return ACK; return ACK;
} }
} }
if (skip_prefix(line, "ERR ", &arg)) die(_("git fetch-pack: expected ACK/NAK, got '%s'"), reader->line);
die(_("remote error: %s"), arg);
die(_("git fetch-pack: expected ACK/NAK, got '%s'"), line);
} }
static void send_request(struct fetch_pack_args *args, static void send_request(struct fetch_pack_args *args,
@ -248,10 +250,15 @@ static int find_common(struct fetch_negotiator *negotiator,
int got_ready = 0; int got_ready = 0;
struct strbuf req_buf = STRBUF_INIT; struct strbuf req_buf = STRBUF_INIT;
size_t state_len = 0; size_t state_len = 0;
struct packet_reader reader;
if (args->stateless_rpc && multi_ack == 1) if (args->stateless_rpc && multi_ack == 1)
die(_("--stateless-rpc requires multi_ack_detailed")); die(_("--stateless-rpc requires multi_ack_detailed"));
packet_reader_init(&reader, fd[0], NULL, 0,
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);
if (!args->no_dependents) { if (!args->no_dependents) {
mark_tips(negotiator, args->negotiation_tips); mark_tips(negotiator, args->negotiation_tips);
for_each_cached_alternate(negotiator, insert_one_alternate_object); for_each_cached_alternate(negotiator, insert_one_alternate_object);
@ -341,31 +348,30 @@ static int find_common(struct fetch_negotiator *negotiator,
state_len = req_buf.len; state_len = req_buf.len;
if (args->deepen) { if (args->deepen) {
char *line;
const char *arg; const char *arg;
struct object_id oid; struct object_id oid;
send_request(args, fd[1], &req_buf); send_request(args, fd[1], &req_buf);
while ((line = packet_read_line(fd[0], NULL))) { while (packet_reader_read(&reader) == PACKET_READ_NORMAL) {
if (skip_prefix(line, "shallow ", &arg)) { if (skip_prefix(reader.line, "shallow ", &arg)) {
if (get_oid_hex(arg, &oid)) if (get_oid_hex(arg, &oid))
die(_("invalid shallow line: %s"), line); die(_("invalid shallow line: %s"), reader.line);
register_shallow(the_repository, &oid); register_shallow(the_repository, &oid);
continue; continue;
} }
if (skip_prefix(line, "unshallow ", &arg)) { if (skip_prefix(reader.line, "unshallow ", &arg)) {
if (get_oid_hex(arg, &oid)) if (get_oid_hex(arg, &oid))
die(_("invalid unshallow line: %s"), line); die(_("invalid unshallow line: %s"), reader.line);
if (!lookup_object(the_repository, oid.hash)) if (!lookup_object(the_repository, oid.hash))
die(_("object not found: %s"), line); die(_("object not found: %s"), reader.line);
/* make sure that it is parsed as shallow */ /* make sure that it is parsed as shallow */
if (!parse_object(the_repository, &oid)) if (!parse_object(the_repository, &oid))
die(_("error in object: %s"), line); die(_("error in object: %s"), reader.line);
if (unregister_shallow(&oid)) if (unregister_shallow(&oid))
die(_("no shallow found: %s"), line); die(_("no shallow found: %s"), reader.line);
continue; continue;
} }
die(_("expected shallow/unshallow, got %s"), line); die(_("expected shallow/unshallow, got %s"), reader.line);
} }
} else if (!args->stateless_rpc) } else if (!args->stateless_rpc)
send_request(args, fd[1], &req_buf); send_request(args, fd[1], &req_buf);
@ -402,9 +408,9 @@ static int find_common(struct fetch_negotiator *negotiator,
if (!args->stateless_rpc && count == INITIAL_FLUSH) if (!args->stateless_rpc && count == INITIAL_FLUSH)
continue; continue;
consume_shallow_list(args, fd[0]); consume_shallow_list(args, &reader);
do { do {
ack = get_ack(fd[0], result_oid); ack = get_ack(&reader, result_oid);
if (ack) if (ack)
print_verbose(args, _("got %s %d %s"), "ack", print_verbose(args, _("got %s %d %s"), "ack",
ack, oid_to_hex(result_oid)); ack, oid_to_hex(result_oid));
@ -474,9 +480,9 @@ done:
strbuf_release(&req_buf); strbuf_release(&req_buf);
if (!got_ready || !no_done) if (!got_ready || !no_done)
consume_shallow_list(args, fd[0]); consume_shallow_list(args, &reader);
while (flushes || multi_ack) { while (flushes || multi_ack) {
int ack = get_ack(fd[0], result_oid); int ack = get_ack(&reader, result_oid);
if (ack) { if (ack) {
print_verbose(args, _("got %s (%d) %s"), "ack", print_verbose(args, _("got %s (%d) %s"), "ack",
ack, oid_to_hex(result_oid)); ack, oid_to_hex(result_oid));
@ -1091,7 +1097,8 @@ static int add_haves(struct fetch_negotiator *negotiator,
static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out, static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
const struct fetch_pack_args *args, const struct fetch_pack_args *args,
const struct ref *wants, struct oidset *common, const struct ref *wants, struct oidset *common,
int *haves_to_send, int *in_vain) int *haves_to_send, int *in_vain,
int sideband_all)
{ {
int ret = 0; int ret = 0;
struct strbuf req_buf = STRBUF_INIT; struct strbuf req_buf = STRBUF_INIT;
@ -1117,6 +1124,8 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
packet_buf_write(&req_buf, "include-tag"); packet_buf_write(&req_buf, "include-tag");
if (prefer_ofs_delta) if (prefer_ofs_delta)
packet_buf_write(&req_buf, "ofs-delta"); packet_buf_write(&req_buf, "ofs-delta");
if (sideband_all)
packet_buf_write(&req_buf, "sideband-all");
/* Add shallow-info and deepen request */ /* Add shallow-info and deepen request */
if (server_supports_feature("fetch", "shallow", 0)) if (server_supports_feature("fetch", "shallow", 0))
@ -1334,7 +1343,13 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
struct fetch_negotiator negotiator; struct fetch_negotiator negotiator;
fetch_negotiator_init(&negotiator, negotiation_algorithm); fetch_negotiator_init(&negotiator, negotiation_algorithm);
packet_reader_init(&reader, fd[0], NULL, 0, packet_reader_init(&reader, fd[0], NULL, 0,
PACKET_READ_CHOMP_NEWLINE); PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);
if (git_env_bool("GIT_TEST_SIDEBAND_ALL", 1) &&
server_supports_feature("fetch", "sideband-all", 0)) {
reader.use_sideband = 1;
reader.me = "fetch-pack";
}
while (state != FETCH_DONE) { while (state != FETCH_DONE) {
switch (state) { switch (state) {
@ -1368,7 +1383,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
case FETCH_SEND_REQUEST: case FETCH_SEND_REQUEST:
if (send_fetch_request(&negotiator, fd[1], args, ref, if (send_fetch_request(&negotiator, fd[1], args, ref,
&common, &common,
&haves_to_send, &in_vain)) &haves_to_send, &in_vain,
reader.use_sideband))
state = FETCH_GET_PACK; state = FETCH_GET_PACK;
else else
state = FETCH_PROCESS_ACKS; state = FETCH_PROCESS_ACKS;

View File

@ -129,12 +129,14 @@ static void set_packet_header(char *buf, const int size)
#undef hex #undef hex
} }
static void format_packet(struct strbuf *out, const char *fmt, va_list args) static void format_packet(struct strbuf *out, const char *prefix,
const char *fmt, va_list args)
{ {
size_t orig_len, n; size_t orig_len, n;
orig_len = out->len; orig_len = out->len;
strbuf_addstr(out, "0000"); strbuf_addstr(out, "0000");
strbuf_addstr(out, prefix);
strbuf_vaddf(out, fmt, args); strbuf_vaddf(out, fmt, args);
n = out->len - orig_len; n = out->len - orig_len;
@ -145,13 +147,13 @@ static void format_packet(struct strbuf *out, const char *fmt, va_list args)
packet_trace(out->buf + orig_len + 4, n - 4, 1); packet_trace(out->buf + orig_len + 4, n - 4, 1);
} }
static int packet_write_fmt_1(int fd, int gently, static int packet_write_fmt_1(int fd, int gently, const char *prefix,
const char *fmt, va_list args) const char *fmt, va_list args)
{ {
static struct strbuf buf = STRBUF_INIT; static struct strbuf buf = STRBUF_INIT;
strbuf_reset(&buf); strbuf_reset(&buf);
format_packet(&buf, fmt, args); format_packet(&buf, prefix, fmt, args);
if (write_in_full(fd, buf.buf, buf.len) < 0) { if (write_in_full(fd, buf.buf, buf.len) < 0) {
if (!gently) { if (!gently) {
check_pipe(errno); check_pipe(errno);
@ -168,7 +170,7 @@ void packet_write_fmt(int fd, const char *fmt, ...)
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
packet_write_fmt_1(fd, 0, fmt, args); packet_write_fmt_1(fd, 0, "", fmt, args);
va_end(args); va_end(args);
} }
@ -178,7 +180,7 @@ int packet_write_fmt_gently(int fd, const char *fmt, ...)
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
status = packet_write_fmt_1(fd, 1, fmt, args); status = packet_write_fmt_1(fd, 1, "", fmt, args);
va_end(args); va_end(args);
return status; return status;
} }
@ -211,7 +213,7 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
format_packet(buf, fmt, args); format_packet(buf, "", fmt, args);
va_end(args); va_end(args);
} }
@ -346,6 +348,10 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer,
return PACKET_READ_EOF; return PACKET_READ_EOF;
} }
if ((options & PACKET_READ_DIE_ON_ERR_PACKET) &&
starts_with(buffer, "ERR "))
die(_("remote error: %s"), buffer + 4);
if ((options & PACKET_READ_CHOMP_NEWLINE) && if ((options & PACKET_READ_CHOMP_NEWLINE) &&
len && buffer[len-1] == '\n') len && buffer[len-1] == '\n')
len--; len--;
@ -433,6 +439,29 @@ ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out)
return sb_out->len - orig_len; return sb_out->len - orig_len;
} }
int recv_sideband(const char *me, int in_stream, int out)
{
char buf[LARGE_PACKET_MAX + 1];
int len;
struct strbuf scratch = STRBUF_INIT;
enum sideband_type sideband_type;
while (1) {
len = packet_read(in_stream, NULL, NULL, buf, LARGE_PACKET_MAX,
0);
if (!demultiplex_sideband(me, buf, len, 0, &scratch,
&sideband_type))
continue;
switch (sideband_type) {
case SIDEBAND_PRIMARY:
write_or_die(out, buf + 1, len - 1);
break;
default: /* errors: message already written */
return sideband_type;
}
}
}
/* Packet Reader Functions */ /* Packet Reader Functions */
void packet_reader_init(struct packet_reader *reader, int fd, void packet_reader_init(struct packet_reader *reader, int fd,
char *src_buffer, size_t src_len, char *src_buffer, size_t src_len,
@ -446,25 +475,43 @@ void packet_reader_init(struct packet_reader *reader, int fd,
reader->buffer = packet_buffer; reader->buffer = packet_buffer;
reader->buffer_size = sizeof(packet_buffer); reader->buffer_size = sizeof(packet_buffer);
reader->options = options; reader->options = options;
reader->me = "git";
} }
enum packet_read_status packet_reader_read(struct packet_reader *reader) enum packet_read_status packet_reader_read(struct packet_reader *reader)
{ {
struct strbuf scratch = STRBUF_INIT;
if (reader->line_peeked) { if (reader->line_peeked) {
reader->line_peeked = 0; reader->line_peeked = 0;
return reader->status; return reader->status;
} }
reader->status = packet_read_with_status(reader->fd, /*
&reader->src_buffer, * Consume all progress packets until a primary payload packet is
&reader->src_len, * received
reader->buffer, */
reader->buffer_size, while (1) {
&reader->pktlen, enum sideband_type sideband_type;
reader->options); reader->status = packet_read_with_status(reader->fd,
&reader->src_buffer,
&reader->src_len,
reader->buffer,
reader->buffer_size,
&reader->pktlen,
reader->options);
if (!reader->use_sideband)
break;
if (demultiplex_sideband(reader->me, reader->buffer,
reader->pktlen, 1, &scratch,
&sideband_type))
break;
}
if (reader->status == PACKET_READ_NORMAL) if (reader->status == PACKET_READ_NORMAL)
reader->line = reader->buffer; /* Skip the sideband designator if sideband is used */
reader->line = reader->use_sideband ?
reader->buffer + 1 : reader->buffer;
else else
reader->line = NULL; reader->line = NULL;
@ -482,3 +529,39 @@ enum packet_read_status packet_reader_peek(struct packet_reader *reader)
reader->line_peeked = 1; reader->line_peeked = 1;
return reader->status; return reader->status;
} }
void packet_writer_init(struct packet_writer *writer, int dest_fd)
{
writer->dest_fd = dest_fd;
writer->use_sideband = 0;
}
void packet_writer_write(struct packet_writer *writer, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
packet_write_fmt_1(writer->dest_fd, 0,
writer->use_sideband ? "\001" : "", fmt, args);
va_end(args);
}
void packet_writer_error(struct packet_writer *writer, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
packet_write_fmt_1(writer->dest_fd, 0,
writer->use_sideband ? "\003" : "ERR ", fmt, args);
va_end(args);
}
void packet_writer_delim(struct packet_writer *writer)
{
packet_delim(writer->dest_fd);
}
void packet_writer_flush(struct packet_writer *writer)
{
packet_flush(writer->dest_fd);
}

View File

@ -3,6 +3,7 @@
#include "git-compat-util.h" #include "git-compat-util.h"
#include "strbuf.h" #include "strbuf.h"
#include "sideband.h"
/* /*
* Write a packetized stream, where each line is preceded by * Write a packetized stream, where each line is preceded by
@ -62,9 +63,13 @@ int write_packetized_from_buf(const char *src_in, size_t len, int fd_out);
* *
* If options contains PACKET_READ_CHOMP_NEWLINE, a trailing newline (if * If options contains PACKET_READ_CHOMP_NEWLINE, a trailing newline (if
* present) is removed from the buffer before returning. * present) is removed from the buffer before returning.
*
* If options contains PACKET_READ_DIE_ON_ERR_PACKET, it dies when it sees an
* ERR packet.
*/ */
#define PACKET_READ_GENTLE_ON_EOF (1u<<0) #define PACKET_READ_GENTLE_ON_EOF (1u<<0)
#define PACKET_READ_CHOMP_NEWLINE (1u<<1) #define PACKET_READ_CHOMP_NEWLINE (1u<<1)
#define PACKET_READ_DIE_ON_ERR_PACKET (1u<<2)
int packet_read(int fd, char **src_buffer, size_t *src_len, char int packet_read(int fd, char **src_buffer, size_t *src_len, char
*buffer, unsigned size, int options); *buffer, unsigned size, int options);
@ -116,6 +121,21 @@ char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size);
*/ */
ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out); ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out);
/*
* Receive multiplexed output stream over git native protocol.
* in_stream is the input stream from the remote, which carries data
* in pkt_line format with band designator. Demultiplex it into out
* and err and return error appropriately. Band #1 carries the
* primary payload. Things coming over band #2 is not necessarily
* error; they are usually informative message on the standard error
* stream, aka "verbose"). A message over band #3 is a signal that
* the remote died unexpectedly. A flush() concludes the stream.
*
* Returns SIDEBAND_FLUSH upon a normal conclusion, and SIDEBAND_PROTOCOL_ERROR
* or SIDEBAND_REMOTE_ERROR if an error occurred.
*/
int recv_sideband(const char *me, int in_stream, int out);
struct packet_reader { struct packet_reader {
/* source file descriptor */ /* source file descriptor */
int fd; int fd;
@ -142,6 +162,9 @@ struct packet_reader {
/* indicates if a line has been peeked */ /* indicates if a line has been peeked */
int line_peeked; int line_peeked;
unsigned use_sideband : 1;
const char *me;
}; };
/* /*
@ -179,4 +202,19 @@ extern enum packet_read_status packet_reader_peek(struct packet_reader *reader);
#define LARGE_PACKET_DATA_MAX (LARGE_PACKET_MAX - 4) #define LARGE_PACKET_DATA_MAX (LARGE_PACKET_MAX - 4)
extern char packet_buffer[LARGE_PACKET_MAX]; extern char packet_buffer[LARGE_PACKET_MAX];
struct packet_writer {
int dest_fd;
unsigned use_sideband : 1;
};
void packet_writer_init(struct packet_writer *writer, int dest_fd);
/* These functions die upon failure. */
__attribute__((format (printf, 2, 3)))
void packet_writer_write(struct packet_writer *writer, const char *fmt, ...);
__attribute__((format (printf, 2, 3)))
void packet_writer_error(struct packet_writer *writer, const char *fmt, ...);
void packet_writer_delim(struct packet_writer *writer);
void packet_writer_flush(struct packet_writer *writer);
#endif #endif

View File

@ -204,7 +204,8 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
packet_reader_init(&reader, -1, heads->buf, heads->len, packet_reader_init(&reader, -1, heads->buf, heads->len,
PACKET_READ_CHOMP_NEWLINE | PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_GENTLE_ON_EOF); PACKET_READ_GENTLE_ON_EOF |
PACKET_READ_DIE_ON_ERR_PACKET);
heads->version = discover_version(&reader); heads->version = discover_version(&reader);
switch (heads->version) { switch (heads->version) {
@ -408,28 +409,37 @@ static struct discovery *discover_refs(const char *service, int for_push)
if (maybe_smart && if (maybe_smart &&
(5 <= last->len && last->buf[4] == '#') && (5 <= last->len && last->buf[4] == '#') &&
!strbuf_cmp(&exp, &type)) { !strbuf_cmp(&exp, &type)) {
char *line; struct packet_reader reader;
packet_reader_init(&reader, -1, last->buf, last->len,
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);
/* /*
* smart HTTP response; validate that the service * smart HTTP response; validate that the service
* pkt-line matches our request. * pkt-line matches our request.
*/ */
line = packet_read_line_buf(&last->buf, &last->len, NULL); if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
if (!line)
die("invalid server response; expected service, got flush packet"); die("invalid server response; expected service, got flush packet");
strbuf_reset(&exp); strbuf_reset(&exp);
strbuf_addf(&exp, "# service=%s", service); strbuf_addf(&exp, "# service=%s", service);
if (strcmp(line, exp.buf)) if (strcmp(reader.line, exp.buf))
die("invalid server response; got '%s'", line); die("invalid server response; got '%s'", reader.line);
strbuf_release(&exp); strbuf_release(&exp);
/* The header can include additional metadata lines, up /* The header can include additional metadata lines, up
* until a packet flush marker. Ignore these now, but * until a packet flush marker. Ignore these now, but
* in the future we might start to scan them. * in the future we might start to scan them.
*/ */
while (packet_read_line_buf(&last->buf, &last->len, NULL)) for (;;) {
; packet_reader_read(&reader);
if (reader.pktlen <= 0) {
break;
}
}
last->buf = reader.src_buffer;
last->len = reader.src_len;
last->proto_git = 1; last->proto_git = 1;
} else if (maybe_smart && } else if (maybe_smart &&
@ -1194,7 +1204,8 @@ static void proxy_state_init(struct proxy_state *p, const char *service_name,
p->headers = curl_slist_append(p->headers, buf.buf); p->headers = curl_slist_append(p->headers, buf.buf);
packet_reader_init(&p->reader, p->in, NULL, 0, packet_reader_init(&p->reader, p->in, NULL, 0,
PACKET_READ_GENTLE_ON_EOF); PACKET_READ_GENTLE_ON_EOF |
PACKET_READ_DIE_ON_ERR_PACKET);
strbuf_release(&buf); strbuf_release(&buf);
} }

View File

@ -135,38 +135,36 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc
return 0; return 0;
} }
static int receive_unpack_status(int in) static int receive_unpack_status(struct packet_reader *reader)
{ {
const char *line = packet_read_line(in, NULL); if (packet_reader_read(reader) != PACKET_READ_NORMAL)
if (!line)
return error(_("unexpected flush packet while reading remote unpack status")); return error(_("unexpected flush packet while reading remote unpack status"));
if (!skip_prefix(line, "unpack ", &line)) if (!skip_prefix(reader->line, "unpack ", &reader->line))
return error(_("unable to parse remote unpack status: %s"), line); return error(_("unable to parse remote unpack status: %s"), reader->line);
if (strcmp(line, "ok")) if (strcmp(reader->line, "ok"))
return error(_("remote unpack failed: %s"), line); return error(_("remote unpack failed: %s"), reader->line);
return 0; return 0;
} }
static int receive_status(int in, struct ref *refs) static int receive_status(struct packet_reader *reader, struct ref *refs)
{ {
struct ref *hint; struct ref *hint;
int ret; int ret;
hint = NULL; hint = NULL;
ret = receive_unpack_status(in); ret = receive_unpack_status(reader);
while (1) { while (1) {
char *refname; const char *refname;
char *msg; char *msg;
char *line = packet_read_line(in, NULL); if (packet_reader_read(reader) != PACKET_READ_NORMAL)
if (!line)
break; break;
if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) { if (!starts_with(reader->line, "ok ") && !starts_with(reader->line, "ng ")) {
error("invalid ref status from remote: %s", line); error("invalid ref status from remote: %s", reader->line);
ret = -1; ret = -1;
break; break;
} }
refname = line + 3; refname = reader->line + 3;
msg = strchr(refname, ' '); msg = strchr(refname, ' ');
if (msg) if (msg)
*msg++ = '\0'; *msg++ = '\0';
@ -187,7 +185,7 @@ static int receive_status(int in, struct ref *refs)
continue; continue;
} }
if (line[0] == 'o' && line[1] == 'k') if (reader->line[0] == 'o' && reader->line[1] == 'k')
hint->status = REF_STATUS_OK; hint->status = REF_STATUS_OK;
else { else {
hint->status = REF_STATUS_REMOTE_REJECT; hint->status = REF_STATUS_REMOTE_REJECT;
@ -390,6 +388,7 @@ int send_pack(struct send_pack_args *args,
int ret; int ret;
struct async demux; struct async demux;
const char *push_cert_nonce = NULL; const char *push_cert_nonce = NULL;
struct packet_reader reader;
/* Does the other end support the reporting? */ /* Does the other end support the reporting? */
if (server_supports("report-status")) if (server_supports("report-status"))
@ -559,6 +558,10 @@ int send_pack(struct send_pack_args *args,
in = demux.out; in = demux.out;
} }
packet_reader_init(&reader, in, NULL, 0,
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);
if (need_pack_data && cmds_sent) { if (need_pack_data && cmds_sent) {
if (pack_objects(out, remote_refs, extra_have, args) < 0) { if (pack_objects(out, remote_refs, extra_have, args) < 0) {
for (ref = remote_refs; ref; ref = ref->next) for (ref = remote_refs; ref; ref = ref->next)
@ -573,7 +576,7 @@ int send_pack(struct send_pack_args *args,
* are failing, and just want the error() side effects. * are failing, and just want the error() side effects.
*/ */
if (status_report) if (status_report)
receive_unpack_status(in); receive_unpack_status(&reader);
if (use_sideband) { if (use_sideband) {
close(demux.out); close(demux.out);
@ -590,7 +593,7 @@ int send_pack(struct send_pack_args *args,
packet_flush(out); packet_flush(out);
if (status_report && cmds_sent) if (status_report && cmds_sent)
ret = receive_status(in, remote_refs); ret = receive_status(&reader, remote_refs);
else else
ret = 0; ret = 0;
if (args->stateless_rpc) if (args->stateless_rpc)

View File

@ -167,7 +167,8 @@ static int process_request(void)
packet_reader_init(&reader, 0, NULL, 0, packet_reader_init(&reader, 0, NULL, 0,
PACKET_READ_CHOMP_NEWLINE | PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_GENTLE_ON_EOF); PACKET_READ_GENTLE_ON_EOF |
PACKET_READ_DIE_ON_ERR_PACKET);
/* /*
* Check to see if the client closed their end before sending another * Check to see if the client closed their end before sending another
@ -175,7 +176,7 @@ static int process_request(void)
*/ */
if (packet_reader_peek(&reader) == PACKET_READ_EOF) if (packet_reader_peek(&reader) == PACKET_READ_EOF)
return 1; return 1;
reader.options = PACKET_READ_CHOMP_NEWLINE; reader.options &= ~PACKET_READ_GENTLE_ON_EOF;
while (state != PROCESS_REQUEST_DONE) { while (state != PROCESS_REQUEST_DONE) {
switch (packet_reader_peek(&reader)) { switch (packet_reader_peek(&reader)) {

View File

@ -1,7 +1,6 @@
#include "cache.h" #include "cache.h"
#include "color.h" #include "color.h"
#include "config.h" #include "config.h"
#include "pkt-line.h"
#include "sideband.h" #include "sideband.h"
#include "help.h" #include "help.h"
@ -110,109 +109,104 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
} }
/*
* Receive multiplexed output stream over git native protocol.
* in_stream is the input stream from the remote, which carries data
* in pkt_line format with band designator. Demultiplex it into out
* and err and return error appropriately. Band #1 carries the
* primary payload. Things coming over band #2 is not necessarily
* error; they are usually informative message on the standard error
* stream, aka "verbose"). A message over band #3 is a signal that
* the remote died unexpectedly. A flush() concludes the stream.
*/
#define DISPLAY_PREFIX "remote: " #define DISPLAY_PREFIX "remote: "
#define ANSI_SUFFIX "\033[K" #define ANSI_SUFFIX "\033[K"
#define DUMB_SUFFIX " " #define DUMB_SUFFIX " "
int recv_sideband(const char *me, int in_stream, int out) int demultiplex_sideband(const char *me, char *buf, int len,
int die_on_error,
struct strbuf *scratch,
enum sideband_type *sideband_type)
{ {
const char *suffix; static const char *suffix;
char buf[LARGE_PACKET_MAX + 1]; const char *b, *brk;
struct strbuf outbuf = STRBUF_INIT; int band;
int retval = 0;
if (isatty(2) && !is_terminal_dumb()) if (!suffix) {
suffix = ANSI_SUFFIX; if (isatty(2) && !is_terminal_dumb())
else suffix = ANSI_SUFFIX;
suffix = DUMB_SUFFIX; else
suffix = DUMB_SUFFIX;
while (!retval) {
const char *b, *brk;
int band, len;
len = packet_read(in_stream, NULL, NULL, buf, LARGE_PACKET_MAX, 0);
if (len == 0)
break;
if (len < 1) {
strbuf_addf(&outbuf,
"%s%s: protocol error: no band designator",
outbuf.len ? "\n" : "", me);
retval = SIDEBAND_PROTOCOL_ERROR;
break;
}
band = buf[0] & 0xff;
buf[len] = '\0';
len--;
switch (band) {
case 3:
strbuf_addf(&outbuf, "%s%s", outbuf.len ? "\n" : "",
DISPLAY_PREFIX);
maybe_colorize_sideband(&outbuf, buf + 1, len);
retval = SIDEBAND_REMOTE_ERROR;
break;
case 2:
b = buf + 1;
/*
* Append a suffix to each nonempty line to clear the
* end of the screen line.
*
* The output is accumulated in a buffer and
* each line is printed to stderr using
* write(2) to ensure inter-process atomicity.
*/
while ((brk = strpbrk(b, "\n\r"))) {
int linelen = brk - b;
if (!outbuf.len)
strbuf_addstr(&outbuf, DISPLAY_PREFIX);
if (linelen > 0) {
maybe_colorize_sideband(&outbuf, b, linelen);
strbuf_addstr(&outbuf, suffix);
}
strbuf_addch(&outbuf, *brk);
xwrite(2, outbuf.buf, outbuf.len);
strbuf_reset(&outbuf);
b = brk + 1;
}
if (*b) {
strbuf_addstr(&outbuf, outbuf.len ?
"" : DISPLAY_PREFIX);
maybe_colorize_sideband(&outbuf, b, strlen(b));
}
break;
case 1:
write_or_die(out, buf + 1, len);
break;
default:
strbuf_addf(&outbuf, "%s%s: protocol error: bad band #%d",
outbuf.len ? "\n" : "", me, band);
retval = SIDEBAND_PROTOCOL_ERROR;
break;
}
} }
if (outbuf.len) { if (len == 0) {
strbuf_addch(&outbuf, '\n'); *sideband_type = SIDEBAND_FLUSH;
xwrite(2, outbuf.buf, outbuf.len); goto cleanup;
} }
strbuf_release(&outbuf); if (len < 1) {
return retval; strbuf_addf(scratch,
"%s%s: protocol error: no band designator",
scratch->len ? "\n" : "", me);
*sideband_type = SIDEBAND_PROTOCOL_ERROR;
goto cleanup;
}
band = buf[0] & 0xff;
buf[len] = '\0';
len--;
switch (band) {
case 3:
if (die_on_error)
die("remote error: %s", buf + 1);
strbuf_addf(scratch, "%s%s", scratch->len ? "\n" : "",
DISPLAY_PREFIX);
maybe_colorize_sideband(scratch, buf + 1, len);
*sideband_type = SIDEBAND_REMOTE_ERROR;
break;
case 2:
b = buf + 1;
/*
* Append a suffix to each nonempty line to clear the
* end of the screen line.
*
* The output is accumulated in a buffer and
* each line is printed to stderr using
* write(2) to ensure inter-process atomicity.
*/
while ((brk = strpbrk(b, "\n\r"))) {
int linelen = brk - b;
if (!scratch->len)
strbuf_addstr(scratch, DISPLAY_PREFIX);
if (linelen > 0) {
maybe_colorize_sideband(scratch, b, linelen);
strbuf_addstr(scratch, suffix);
}
strbuf_addch(scratch, *brk);
xwrite(2, scratch->buf, scratch->len);
strbuf_reset(scratch);
b = brk + 1;
}
if (*b) {
strbuf_addstr(scratch, scratch->len ?
"" : DISPLAY_PREFIX);
maybe_colorize_sideband(scratch, b, strlen(b));
}
return 0;
case 1:
*sideband_type = SIDEBAND_PRIMARY;
break;
default:
strbuf_addf(scratch, "%s%s: protocol error: bad band #%d",
scratch->len ? "\n" : "", me, band);
*sideband_type = SIDEBAND_PROTOCOL_ERROR;
break;
}
cleanup:
if (die_on_error && *sideband_type == SIDEBAND_PROTOCOL_ERROR)
die("%s", scratch->buf);
if (scratch->len) {
strbuf_addch(scratch, '\n');
xwrite(2, scratch->buf, scratch->len);
}
strbuf_release(scratch);
return 1;
} }
/* /*

View File

@ -1,10 +1,29 @@
#ifndef SIDEBAND_H #ifndef SIDEBAND_H
#define SIDEBAND_H #define SIDEBAND_H
#define SIDEBAND_PROTOCOL_ERROR -2 enum sideband_type {
#define SIDEBAND_REMOTE_ERROR -1 SIDEBAND_PROTOCOL_ERROR = -2,
SIDEBAND_REMOTE_ERROR = -1,
SIDEBAND_FLUSH = 0,
SIDEBAND_PRIMARY = 1
};
/*
* Inspects a multiplexed packet read from the remote. If this packet is a
* progress packet and thus should not be processed by the caller, returns 0.
* Otherwise, returns 1, releases scratch, and sets sideband_type.
*
* If this packet is SIDEBAND_PROTOCOL_ERROR, SIDEBAND_REMOTE_ERROR, or a
* progress packet, also prints a message to stderr.
*
* scratch must be a struct strbuf allocated by the caller. It is used to store
* progress messages split across multiple packets.
*/
int demultiplex_sideband(const char *me, char *buf, int len,
int die_on_error,
struct strbuf *scratch,
enum sideband_type *sideband_type);
int recv_sideband(const char *me, int in_stream, int out);
void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max); void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
#endif #endif

View File

@ -374,6 +374,11 @@ GIT_TEST_MULTI_PACK_INDEX=<boolean>, when true, forces the multi-pack-
index to be written after every 'git repack' command, and overrides the index to be written after every 'git repack' command, and overrides the
'core.multiPackIndex' setting to true. 'core.multiPackIndex' setting to true.
GIT_TEST_SIDEBAND_ALL=<boolean>, when true, overrides the
'uploadpack.allowSidebandAll' setting to true, and when false, forces
fetch-pack to not request sideband-all (even if the server advertises
sideband-all).
Naming Tests Naming Tests
------------ ------------

View File

@ -78,6 +78,7 @@ PassEnv GNUPGHOME
PassEnv ASAN_OPTIONS PassEnv ASAN_OPTIONS
PassEnv GIT_TRACE PassEnv GIT_TRACE
PassEnv GIT_CONFIG_NOSYSTEM PassEnv GIT_CONFIG_NOSYSTEM
PassEnv GIT_TEST_SIDEBAND_ALL
SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0 SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0

View File

@ -243,7 +243,8 @@ test_expect_success 'shallow fetches check connectivity before writing shallow f
"$(git -C "$REPO" rev-parse HEAD)" \ "$(git -C "$REPO" rev-parse HEAD)" \
"$(git -C "$REPO" rev-parse HEAD^)" \ "$(git -C "$REPO" rev-parse HEAD^)" \
>"$HTTPD_ROOT_PATH/one-time-sed" && >"$HTTPD_ROOT_PATH/one-time-sed" &&
test_must_fail git -C client fetch --depth=1 "$HTTPD_URL/one_time_sed/repo" \ test_must_fail env GIT_TEST_SIDEBAND_ALL=0 git -C client \
fetch --depth=1 "$HTTPD_URL/one_time_sed/repo" \
master:a_branch && master:a_branch &&
# Ensure that the one-time-sed script was used. # Ensure that the one-time-sed script was used.

View File

@ -14,7 +14,7 @@ test_expect_success 'test capability advertisement' '
0000 0000
EOF EOF
git serve --advertise-capabilities >out && GIT_TEST_SIDEBAND_ALL=0 git serve --advertise-capabilities >out &&
test-tool pkt-line unpack <out >actual && test-tool pkt-line unpack <out >actual &&
test_cmp expect actual test_cmp expect actual
' '

View File

@ -630,8 +630,8 @@ test_expect_success 'when server does not send "ready", expect FLUSH' '
test_must_fail env GIT_TRACE_PACKET="$(pwd)/log" git -C http_child \ test_must_fail env GIT_TRACE_PACKET="$(pwd)/log" git -C http_child \
-c protocol.version=2 \ -c protocol.version=2 \
fetch "$HTTPD_URL/one_time_sed/http_parent" 2> err && fetch "$HTTPD_URL/one_time_sed/http_parent" 2> err &&
grep "fetch< acknowledgments" log && grep "fetch< .*acknowledgments" log &&
! grep "fetch< ready" log && ! grep "fetch< .*ready" log &&
test_i18ngrep "expected no other sections to be sent after no .ready." err test_i18ngrep "expected no other sections to be sent after no .ready." err
' '

View File

@ -208,7 +208,7 @@ test_expect_success 'server is initially ahead - no ref in want' '
cp -r "$LOCAL_PRISTINE" local && cp -r "$LOCAL_PRISTINE" local &&
inconsistency master 1234567890123456789012345678901234567890 && inconsistency master 1234567890123456789012345678901234567890 &&
test_must_fail git -C local fetch 2>err && test_must_fail git -C local fetch 2>err &&
test_i18ngrep "ERR upload-pack: not our ref" err test_i18ngrep "fatal: remote error: upload-pack: not our ref" err
' '
test_expect_success 'server is initially ahead - ref in want' ' test_expect_success 'server is initially ahead - ref in want' '
@ -254,7 +254,7 @@ test_expect_success 'server loses a ref - ref in want' '
echo "s/master/raster/" >"$HTTPD_ROOT_PATH/one-time-sed" && echo "s/master/raster/" >"$HTTPD_ROOT_PATH/one-time-sed" &&
test_must_fail git -C local fetch 2>err && test_must_fail git -C local fetch 2>err &&
test_i18ngrep "ERR unknown ref refs/heads/raster" err test_i18ngrep "fatal: remote error: unknown ref refs/heads/raster" err
' '
stop_httpd stop_httpd

View File

@ -273,7 +273,8 @@ static struct ref *handshake(struct transport *transport, int for_push,
packet_reader_init(&reader, data->fd[0], NULL, 0, packet_reader_init(&reader, data->fd[0], NULL, 0,
PACKET_READ_CHOMP_NEWLINE | PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_GENTLE_ON_EOF); PACKET_READ_GENTLE_ON_EOF |
PACKET_READ_DIE_ON_ERR_PACKET);
data->version = discover_version(&reader); data->version = discover_version(&reader);
switch (data->version) { switch (data->version) {

View File

@ -70,6 +70,8 @@ static int allow_filter;
static int allow_ref_in_want; static int allow_ref_in_want;
static struct list_objects_filter_options filter_options; static struct list_objects_filter_options filter_options;
static int allow_sideband_all;
static void reset_timeout(void) static void reset_timeout(void)
{ {
alarm(timeout); alarm(timeout);
@ -356,7 +358,8 @@ static int ok_to_give_up(const struct object_array *have_obj,
min_generation); min_generation);
} }
static int get_common_commits(struct object_array *have_obj, static int get_common_commits(struct packet_reader *reader,
struct object_array *have_obj,
struct object_array *want_obj) struct object_array *want_obj)
{ {
struct object_id oid; struct object_id oid;
@ -368,12 +371,11 @@ static int get_common_commits(struct object_array *have_obj,
save_commit_buffer = 0; save_commit_buffer = 0;
for (;;) { for (;;) {
char *line = packet_read_line(0, NULL);
const char *arg; const char *arg;
reset_timeout(); reset_timeout();
if (!line) { if (packet_reader_read(reader) != PACKET_READ_NORMAL) {
if (multi_ack == 2 && got_common if (multi_ack == 2 && got_common
&& !got_other && ok_to_give_up(have_obj, want_obj)) { && !got_other && ok_to_give_up(have_obj, want_obj)) {
sent_ready = 1; sent_ready = 1;
@ -392,7 +394,7 @@ static int get_common_commits(struct object_array *have_obj,
got_other = 0; got_other = 0;
continue; continue;
} }
if (skip_prefix(line, "have ", &arg)) { if (skip_prefix(reader->line, "have ", &arg)) {
switch (got_oid(arg, &oid, have_obj)) { switch (got_oid(arg, &oid, have_obj)) {
case -1: /* they have what we do not */ case -1: /* they have what we do not */
got_other = 1; got_other = 1;
@ -418,7 +420,7 @@ static int get_common_commits(struct object_array *have_obj,
} }
continue; continue;
} }
if (!strcmp(line, "done")) { if (!strcmp(reader->line, "done")) {
if (have_obj->nr > 0) { if (have_obj->nr > 0) {
if (multi_ack) if (multi_ack)
packet_write_fmt(1, "ACK %s\n", last_hex); packet_write_fmt(1, "ACK %s\n", last_hex);
@ -427,7 +429,7 @@ static int get_common_commits(struct object_array *have_obj,
packet_write_fmt(1, "NAK\n"); packet_write_fmt(1, "NAK\n");
return -1; return -1;
} }
die("git upload-pack: expected SHA1 list, got '%s'", line); die("git upload-pack: expected SHA1 list, got '%s'", reader->line);
} }
} }
@ -615,13 +617,14 @@ error:
} }
} }
static void send_shallow(struct commit_list *result) static void send_shallow(struct packet_writer *writer,
struct commit_list *result)
{ {
while (result) { while (result) {
struct object *object = &result->item->object; struct object *object = &result->item->object;
if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) { if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {
packet_write_fmt(1, "shallow %s", packet_writer_write(writer, "shallow %s",
oid_to_hex(&object->oid)); oid_to_hex(&object->oid));
register_shallow(the_repository, &object->oid); register_shallow(the_repository, &object->oid);
shallow_nr++; shallow_nr++;
} }
@ -629,7 +632,8 @@ static void send_shallow(struct commit_list *result)
} }
} }
static void send_unshallow(const struct object_array *shallows, static void send_unshallow(struct packet_writer *writer,
const struct object_array *shallows,
struct object_array *want_obj) struct object_array *want_obj)
{ {
int i; int i;
@ -638,8 +642,8 @@ static void send_unshallow(const struct object_array *shallows,
struct object *object = shallows->objects[i].item; struct object *object = shallows->objects[i].item;
if (object->flags & NOT_SHALLOW) { if (object->flags & NOT_SHALLOW) {
struct commit_list *parents; struct commit_list *parents;
packet_write_fmt(1, "unshallow %s", packet_writer_write(writer, "unshallow %s",
oid_to_hex(&object->oid)); oid_to_hex(&object->oid));
object->flags &= ~CLIENT_SHALLOW; object->flags &= ~CLIENT_SHALLOW;
/* /*
* We want to _register_ "object" as shallow, but we * We want to _register_ "object" as shallow, but we
@ -666,8 +670,7 @@ static void send_unshallow(const struct object_array *shallows,
static int check_ref(const char *refname_full, const struct object_id *oid, static int check_ref(const char *refname_full, const struct object_id *oid,
int flag, void *cb_data); int flag, void *cb_data);
static void deepen(struct packet_writer *writer, int depth, int deepen_relative,
static void deepen(int depth, int deepen_relative,
struct object_array *shallows, struct object_array *want_obj) struct object_array *shallows, struct object_array *want_obj)
{ {
if (depth == INFINITE_DEPTH && !is_repository_shallow(the_repository)) { if (depth == INFINITE_DEPTH && !is_repository_shallow(the_repository)) {
@ -692,7 +695,7 @@ static void deepen(int depth, int deepen_relative,
result = get_shallow_commits(&reachable_shallows, result = get_shallow_commits(&reachable_shallows,
depth + 1, depth + 1,
SHALLOW, NOT_SHALLOW); SHALLOW, NOT_SHALLOW);
send_shallow(result); send_shallow(writer, result);
free_commit_list(result); free_commit_list(result);
object_array_clear(&reachable_shallows); object_array_clear(&reachable_shallows);
} else { } else {
@ -700,14 +703,15 @@ static void deepen(int depth, int deepen_relative,
result = get_shallow_commits(want_obj, depth, result = get_shallow_commits(want_obj, depth,
SHALLOW, NOT_SHALLOW); SHALLOW, NOT_SHALLOW);
send_shallow(result); send_shallow(writer, result);
free_commit_list(result); free_commit_list(result);
} }
send_unshallow(shallows, want_obj); send_unshallow(writer, shallows, want_obj);
} }
static void deepen_by_rev_list(int ac, const char **av, static void deepen_by_rev_list(struct packet_writer *writer, int ac,
const char **av,
struct object_array *shallows, struct object_array *shallows,
struct object_array *want_obj) struct object_array *want_obj)
{ {
@ -715,13 +719,14 @@ static void deepen_by_rev_list(int ac, const char **av,
close_commit_graph(the_repository); close_commit_graph(the_repository);
result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW); result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
send_shallow(result); send_shallow(writer, result);
free_commit_list(result); free_commit_list(result);
send_unshallow(shallows, want_obj); send_unshallow(writer, shallows, want_obj);
} }
/* Returns 1 if a shallow list is sent or 0 otherwise */ /* Returns 1 if a shallow list is sent or 0 otherwise */
static int send_shallow_list(int depth, int deepen_rev_list, static int send_shallow_list(struct packet_writer *writer,
int depth, int deepen_rev_list,
timestamp_t deepen_since, timestamp_t deepen_since,
struct string_list *deepen_not, struct string_list *deepen_not,
int deepen_relative, int deepen_relative,
@ -733,7 +738,7 @@ static int send_shallow_list(int depth, int deepen_rev_list,
if (depth > 0 && deepen_rev_list) if (depth > 0 && deepen_rev_list)
die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together"); die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together");
if (depth > 0) { if (depth > 0) {
deepen(depth, deepen_relative, shallows, want_obj); deepen(writer, depth, deepen_relative, shallows, want_obj);
ret = 1; ret = 1;
} else if (deepen_rev_list) { } else if (deepen_rev_list) {
struct argv_array av = ARGV_ARRAY_INIT; struct argv_array av = ARGV_ARRAY_INIT;
@ -754,7 +759,7 @@ static int send_shallow_list(int depth, int deepen_rev_list,
struct object *o = want_obj->objects[i].item; struct object *o = want_obj->objects[i].item;
argv_array_push(&av, oid_to_hex(&o->oid)); argv_array_push(&av, oid_to_hex(&o->oid));
} }
deepen_by_rev_list(av.argc, av.argv, shallows, want_obj); deepen_by_rev_list(writer, av.argc, av.argv, shallows, want_obj);
argv_array_clear(&av); argv_array_clear(&av);
ret = 1; ret = 1;
} else { } else {
@ -839,7 +844,7 @@ static int process_deepen_not(const char *line, struct string_list *deepen_not,
return 0; return 0;
} }
static void receive_needs(struct object_array *want_obj) static void receive_needs(struct packet_reader *reader, struct object_array *want_obj)
{ {
struct object_array shallows = OBJECT_ARRAY_INIT; struct object_array shallows = OBJECT_ARRAY_INIT;
struct string_list deepen_not = STRING_LIST_INIT_DUP; struct string_list deepen_not = STRING_LIST_INIT_DUP;
@ -848,39 +853,40 @@ static void receive_needs(struct object_array *want_obj)
timestamp_t deepen_since = 0; timestamp_t deepen_since = 0;
int deepen_rev_list = 0; int deepen_rev_list = 0;
int deepen_relative = 0; int deepen_relative = 0;
struct packet_writer writer;
shallow_nr = 0; shallow_nr = 0;
packet_writer_init(&writer, 1);
for (;;) { for (;;) {
struct object *o; struct object *o;
const char *features; const char *features;
struct object_id oid_buf; struct object_id oid_buf;
char *line = packet_read_line(0, NULL);
const char *arg; const char *arg;
reset_timeout(); reset_timeout();
if (!line) if (packet_reader_read(reader) != PACKET_READ_NORMAL)
break; break;
if (process_shallow(line, &shallows)) if (process_shallow(reader->line, &shallows))
continue; continue;
if (process_deepen(line, &depth)) if (process_deepen(reader->line, &depth))
continue; continue;
if (process_deepen_since(line, &deepen_since, &deepen_rev_list)) if (process_deepen_since(reader->line, &deepen_since, &deepen_rev_list))
continue; continue;
if (process_deepen_not(line, &deepen_not, &deepen_rev_list)) if (process_deepen_not(reader->line, &deepen_not, &deepen_rev_list))
continue; continue;
if (skip_prefix(line, "filter ", &arg)) { if (skip_prefix(reader->line, "filter ", &arg)) {
if (!filter_capability_requested) if (!filter_capability_requested)
die("git upload-pack: filtering capability not negotiated"); die("git upload-pack: filtering capability not negotiated");
parse_list_objects_filter(&filter_options, arg); parse_list_objects_filter(&filter_options, arg);
continue; continue;
} }
if (!skip_prefix(line, "want ", &arg) || if (!skip_prefix(reader->line, "want ", &arg) ||
parse_oid_hex(arg, &oid_buf, &features)) parse_oid_hex(arg, &oid_buf, &features))
die("git upload-pack: protocol error, " die("git upload-pack: protocol error, "
"expected to get object ID, not '%s'", line); "expected to get object ID, not '%s'", reader->line);
if (parse_feature_request(features, "deepen-relative")) if (parse_feature_request(features, "deepen-relative"))
deepen_relative = 1; deepen_relative = 1;
@ -907,9 +913,9 @@ static void receive_needs(struct object_array *want_obj)
o = parse_object(the_repository, &oid_buf); o = parse_object(the_repository, &oid_buf);
if (!o) { if (!o) {
packet_write_fmt(1, packet_writer_error(&writer,
"ERR upload-pack: not our ref %s", "upload-pack: not our ref %s",
oid_to_hex(&oid_buf)); oid_to_hex(&oid_buf));
die("git upload-pack: not our ref %s", die("git upload-pack: not our ref %s",
oid_to_hex(&oid_buf)); oid_to_hex(&oid_buf));
} }
@ -938,7 +944,7 @@ static void receive_needs(struct object_array *want_obj)
if (depth == 0 && !deepen_rev_list && shallows.nr == 0) if (depth == 0 && !deepen_rev_list && shallows.nr == 0)
return; return;
if (send_shallow_list(depth, deepen_rev_list, deepen_since, if (send_shallow_list(&writer, depth, deepen_rev_list, deepen_since,
&deepen_not, deepen_relative, &shallows, &deepen_not, deepen_relative, &shallows,
want_obj)) want_obj))
packet_flush(1); packet_flush(1);
@ -1056,6 +1062,8 @@ static int upload_pack_config(const char *var, const char *value, void *unused)
allow_filter = git_config_bool(var, value); allow_filter = git_config_bool(var, value);
} else if (!strcmp("uploadpack.allowrefinwant", var)) { } else if (!strcmp("uploadpack.allowrefinwant", var)) {
allow_ref_in_want = git_config_bool(var, value); allow_ref_in_want = git_config_bool(var, value);
} else if (!strcmp("uploadpack.allowsidebandall", var)) {
allow_sideband_all = git_config_bool(var, value);
} }
if (current_config_scope() != CONFIG_SCOPE_REPO) { if (current_config_scope() != CONFIG_SCOPE_REPO) {
@ -1070,6 +1078,7 @@ void upload_pack(struct upload_pack_options *options)
{ {
struct string_list symref = STRING_LIST_INIT_DUP; struct string_list symref = STRING_LIST_INIT_DUP;
struct object_array want_obj = OBJECT_ARRAY_INIT; struct object_array want_obj = OBJECT_ARRAY_INIT;
struct packet_reader reader;
stateless_rpc = options->stateless_rpc; stateless_rpc = options->stateless_rpc;
timeout = options->timeout; timeout = options->timeout;
@ -1093,10 +1102,14 @@ void upload_pack(struct upload_pack_options *options)
if (options->advertise_refs) if (options->advertise_refs)
return; return;
receive_needs(&want_obj); packet_reader_init(&reader, 0, NULL, 0,
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);
receive_needs(&reader, &want_obj);
if (want_obj.nr) { if (want_obj.nr) {
struct object_array have_obj = OBJECT_ARRAY_INIT; struct object_array have_obj = OBJECT_ARRAY_INIT;
get_common_commits(&have_obj, &want_obj); get_common_commits(&reader, &have_obj, &want_obj);
create_pack_file(&have_obj, &want_obj); create_pack_file(&have_obj, &want_obj);
} }
} }
@ -1113,6 +1126,8 @@ struct upload_pack_data {
int deepen_rev_list; int deepen_rev_list;
int deepen_relative; int deepen_relative;
struct packet_writer writer;
unsigned stateless_rpc : 1; unsigned stateless_rpc : 1;
unsigned use_thin_pack : 1; unsigned use_thin_pack : 1;
@ -1136,6 +1151,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
data->haves = haves; data->haves = haves;
data->shallows = shallows; data->shallows = shallows;
data->deepen_not = deepen_not; data->deepen_not = deepen_not;
packet_writer_init(&data->writer, 1);
} }
static void upload_pack_data_clear(struct upload_pack_data *data) static void upload_pack_data_clear(struct upload_pack_data *data)
@ -1147,7 +1163,8 @@ static void upload_pack_data_clear(struct upload_pack_data *data)
string_list_clear(&data->deepen_not, 0); string_list_clear(&data->deepen_not, 0);
} }
static int parse_want(const char *line, struct object_array *want_obj) static int parse_want(struct packet_writer *writer, const char *line,
struct object_array *want_obj)
{ {
const char *arg; const char *arg;
if (skip_prefix(line, "want ", &arg)) { if (skip_prefix(line, "want ", &arg)) {
@ -1160,9 +1177,9 @@ static int parse_want(const char *line, struct object_array *want_obj)
o = parse_object(the_repository, &oid); o = parse_object(the_repository, &oid);
if (!o) { if (!o) {
packet_write_fmt(1, packet_writer_error(writer,
"ERR upload-pack: not our ref %s", "upload-pack: not our ref %s",
oid_to_hex(&oid)); oid_to_hex(&oid));
die("git upload-pack: not our ref %s", die("git upload-pack: not our ref %s",
oid_to_hex(&oid)); oid_to_hex(&oid));
} }
@ -1178,7 +1195,8 @@ static int parse_want(const char *line, struct object_array *want_obj)
return 0; return 0;
} }
static int parse_want_ref(const char *line, struct string_list *wanted_refs, static int parse_want_ref(struct packet_writer *writer, const char *line,
struct string_list *wanted_refs,
struct object_array *want_obj) struct object_array *want_obj)
{ {
const char *arg; const char *arg;
@ -1188,7 +1206,7 @@ static int parse_want_ref(const char *line, struct string_list *wanted_refs,
struct object *o; struct object *o;
if (read_ref(arg, &oid)) { if (read_ref(arg, &oid)) {
packet_write_fmt(1, "ERR unknown ref %s", arg); packet_writer_error(writer, "unknown ref %s", arg);
die("unknown ref %s", arg); die("unknown ref %s", arg);
} }
@ -1231,10 +1249,11 @@ static void process_args(struct packet_reader *request,
const char *p; const char *p;
/* process want */ /* process want */
if (parse_want(arg, want_obj)) if (parse_want(&data->writer, arg, want_obj))
continue; continue;
if (allow_ref_in_want && if (allow_ref_in_want &&
parse_want_ref(arg, &data->wanted_refs, want_obj)) parse_want_ref(&data->writer, arg, &data->wanted_refs,
want_obj))
continue; continue;
/* process have line */ /* process have line */
if (parse_have(arg, &data->haves)) if (parse_have(arg, &data->haves))
@ -1283,6 +1302,13 @@ static void process_args(struct packet_reader *request,
continue; continue;
} }
if ((git_env_bool("GIT_TEST_SIDEBAND_ALL", 0) ||
allow_sideband_all) &&
!strcmp(arg, "sideband-all")) {
data->writer.use_sideband = 1;
continue;
}
/* ignore unknown lines maybe? */ /* ignore unknown lines maybe? */
die("unexpected line: '%s'", arg); die("unexpected line: '%s'", arg);
} }
@ -1328,26 +1354,26 @@ static int process_haves(struct oid_array *haves, struct oid_array *common,
return 0; return 0;
} }
static int send_acks(struct oid_array *acks, struct strbuf *response, static int send_acks(struct packet_writer *writer, struct oid_array *acks,
const struct object_array *have_obj, const struct object_array *have_obj,
struct object_array *want_obj) struct object_array *want_obj)
{ {
int i; int i;
packet_buf_write(response, "acknowledgments\n"); packet_writer_write(writer, "acknowledgments\n");
/* Send Acks */ /* Send Acks */
if (!acks->nr) if (!acks->nr)
packet_buf_write(response, "NAK\n"); packet_writer_write(writer, "NAK\n");
for (i = 0; i < acks->nr; i++) { for (i = 0; i < acks->nr; i++) {
packet_buf_write(response, "ACK %s\n", packet_writer_write(writer, "ACK %s\n",
oid_to_hex(&acks->oid[i])); oid_to_hex(&acks->oid[i]));
} }
if (ok_to_give_up(have_obj, want_obj)) { if (ok_to_give_up(have_obj, want_obj)) {
/* Send Ready */ /* Send Ready */
packet_buf_write(response, "ready\n"); packet_writer_write(writer, "ready\n");
return 1; return 1;
} }
@ -1359,25 +1385,20 @@ static int process_haves_and_send_acks(struct upload_pack_data *data,
struct object_array *want_obj) struct object_array *want_obj)
{ {
struct oid_array common = OID_ARRAY_INIT; struct oid_array common = OID_ARRAY_INIT;
struct strbuf response = STRBUF_INIT;
int ret = 0; int ret = 0;
process_haves(&data->haves, &common, have_obj); process_haves(&data->haves, &common, have_obj);
if (data->done) { if (data->done) {
ret = 1; ret = 1;
} else if (send_acks(&common, &response, have_obj, want_obj)) { } else if (send_acks(&data->writer, &common, have_obj, want_obj)) {
packet_buf_delim(&response); packet_writer_delim(&data->writer);
ret = 1; ret = 1;
} else { } else {
/* Add Flush */ /* Add Flush */
packet_buf_flush(&response); packet_writer_flush(&data->writer);
ret = 0; ret = 0;
} }
/* Send response */
write_or_die(1, response.buf, response.len);
strbuf_release(&response);
oid_array_clear(&data->haves); oid_array_clear(&data->haves);
oid_array_clear(&common); oid_array_clear(&common);
return ret; return ret;
@ -1390,15 +1411,15 @@ static void send_wanted_ref_info(struct upload_pack_data *data)
if (!data->wanted_refs.nr) if (!data->wanted_refs.nr)
return; return;
packet_write_fmt(1, "wanted-refs\n"); packet_writer_write(&data->writer, "wanted-refs\n");
for_each_string_list_item(item, &data->wanted_refs) { for_each_string_list_item(item, &data->wanted_refs) {
packet_write_fmt(1, "%s %s\n", packet_writer_write(&data->writer, "%s %s\n",
oid_to_hex(item->util), oid_to_hex(item->util),
item->string); item->string);
} }
packet_delim(1); packet_writer_delim(&data->writer);
} }
static void send_shallow_info(struct upload_pack_data *data, static void send_shallow_info(struct upload_pack_data *data,
@ -1409,15 +1430,16 @@ static void send_shallow_info(struct upload_pack_data *data,
!is_repository_shallow(the_repository)) !is_repository_shallow(the_repository))
return; return;
packet_write_fmt(1, "shallow-info\n"); packet_writer_write(&data->writer, "shallow-info\n");
if (!send_shallow_list(data->depth, data->deepen_rev_list, if (!send_shallow_list(&data->writer, data->depth,
data->deepen_rev_list,
data->deepen_since, &data->deepen_not, data->deepen_since, &data->deepen_not,
data->deepen_relative, data->deepen_relative,
&data->shallows, want_obj) && &data->shallows, want_obj) &&
is_repository_shallow(the_repository)) is_repository_shallow(the_repository))
deepen(INFINITE_DEPTH, data->deepen_relative, &data->shallows, deepen(&data->writer, INFINITE_DEPTH, data->deepen_relative,
want_obj); &data->shallows, want_obj);
packet_delim(1); packet_delim(1);
} }
@ -1479,7 +1501,7 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,
send_wanted_ref_info(&data); send_wanted_ref_info(&data);
send_shallow_info(&data, &want_obj); send_shallow_info(&data, &want_obj);
packet_write_fmt(1, "packfile\n"); packet_writer_write(&data.writer, "packfile\n");
create_pack_file(&have_obj, &want_obj); create_pack_file(&have_obj, &want_obj);
state = FETCH_DONE; state = FETCH_DONE;
break; break;
@ -1500,6 +1522,7 @@ int upload_pack_advertise(struct repository *r,
if (value) { if (value) {
int allow_filter_value; int allow_filter_value;
int allow_ref_in_want; int allow_ref_in_want;
int allow_sideband_all_value;
strbuf_addstr(value, "shallow"); strbuf_addstr(value, "shallow");
@ -1514,6 +1537,13 @@ int upload_pack_advertise(struct repository *r,
&allow_ref_in_want) && &allow_ref_in_want) &&
allow_ref_in_want) allow_ref_in_want)
strbuf_addstr(value, " ref-in-want"); strbuf_addstr(value, " ref-in-want");
if (git_env_bool("GIT_TEST_SIDEBAND_ALL", 0) ||
(!repo_config_get_bool(the_repository,
"uploadpack.allowsidebandall",
&allow_sideband_all_value) &&
allow_sideband_all_value))
strbuf_addstr(value, " sideband-all");
} }
return 1; return 1;