Commit 5f8b86db authored by Junio C Hamano's avatar Junio C Hamano

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
parents 33bea735 07c3c2aa
......@@ -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
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
----------
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" |
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
-------------
......@@ -398,12 +401,11 @@ from the client).
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_status = "continue" / "common" / "ready"
ack = PKT-LINE("ACK" SP obj-id)
nak = PKT-LINE("NAK")
error-line = PKT-LINE("ERR" SP explanation-text)
----
A simple clone may look like this (with no 'have' lines):
......
......@@ -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
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
delimiter packets (0001), with each section beginning with its section
header.
......
......@@ -27,10 +27,10 @@ static int run_remote_archiver(int argc, const char **argv,
const char *remote, const char *exec,
const char *name_hint)
{
char *buf;
int fd[2], i, rv;
struct transport *transport;
struct remote *_remote;
struct packet_reader reader;
_remote = remote_get(remote);
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_flush(fd[1]);
buf = packet_read_line(fd[0], NULL);
if (!buf)
packet_reader_init(&reader, fd[0], NULL, 0,
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"));
if (strcmp(buf, "ACK")) {
if (starts_with(buf, "NACK "))
die(_("git archive: NACK %s"), buf + 5);
if (starts_with(buf, "ERR "))
die(_("remote error: %s"), buf + 4);
if (strcmp(reader.line, "ACK")) {
if (starts_with(reader.line, "NACK "))
die(_("git archive: NACK %s"), reader.line + 5);
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"));
/* Now, start reading from fd[0] and spit it out to stdout */
......
......@@ -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_READ_CHOMP_NEWLINE |
PACKET_READ_GENTLE_ON_EOF);
PACKET_READ_GENTLE_ON_EOF |
PACKET_READ_DIE_ON_ERR_PACKET);
version = discover_version(&reader);
switch (version) {
......
......@@ -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 **p = &commands;
for (;;) {
char *line;
int len, linelen;
int linelen;
line = packet_read_line(0, &len);
if (!line)
if (packet_reader_read(reader) != PACKET_READ_NORMAL)
break;
if (len > 8 && starts_with(line, "shallow ")) {
if (reader->pktlen > 8 && starts_with(reader->line, "shallow ")) {
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'",
line + 8);
reader->line + 8);
oid_array_append(shallow, &oid);
continue;
}
linelen = strlen(line);
if (linelen < len) {
const char *feature_list = line + linelen + 1;
linelen = strlen(reader->line);
if (linelen < reader->pktlen) {
const char *feature_list = reader->line + linelen + 1;
if (parse_feature_request(feature_list, "report-status"))
report_status = 1;
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;
}
if (!strcmp(line, "push-cert")) {
if (!strcmp(reader->line, "push-cert")) {
int true_flush = 0;
char certbuf[1024];
int saved_options = reader->options;
reader->options &= ~PACKET_READ_CHOMP_NEWLINE;
for (;;) {
len = packet_read(0, NULL, NULL,
certbuf, sizeof(certbuf), 0);
if (!len) {
packet_reader_read(reader);
if (reader->status == PACKET_READ_FLUSH) {
true_flush = 1;
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 */
strbuf_addstr(&push_cert, certbuf);
strbuf_addstr(&push_cert, reader->line);
}
reader->options = saved_options;
if (true_flush)
break;
continue;
}
p = queue_command(p, line, linelen);
p = queue_command(p, reader->line, linelen);
}
if (push_cert.len)
......@@ -1637,18 +1640,14 @@ static struct command *read_head_info(struct oid_array *shallow)
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) {
char *line;
int len;
line = packet_read_line(0, &len);
if (!line)
if (packet_reader_read(reader) != PACKET_READ_NORMAL)
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 ref = OID_ARRAY_INIT;
struct shallow_info si;
struct packet_reader reader;
struct option options[] = {
OPT__QUIET(&quiet, N_("quiet")),
......@@ -1986,12 +1986,16 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
if (advertise_refs)
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;
struct string_list push_options = STRING_LIST_INIT_DUP;
if (use_push_options)
read_push_options(&push_options);
read_push_options(&reader, &push_options);
if (!check_cert_push_options(&push_options)) {
struct command *cmd;
for (cmd = commands; cmd; cmd = cmd->next)
......
......@@ -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_READ_CHOMP_NEWLINE |
PACKET_READ_GENTLE_ON_EOF);
PACKET_READ_GENTLE_ON_EOF |
PACKET_READ_DIE_ON_ERR_PACKET);
switch (discover_version(&reader)) {
case protocol_v2:
......
......@@ -296,7 +296,6 @@ struct ref **get_remote_heads(struct packet_reader *reader,
struct ref **orig_list = list;
int len = 0;
enum get_remote_heads_state state = EXPECTING_FIRST_REF;
const char *arg;
*list = NULL;
......@@ -306,8 +305,6 @@ struct ref **get_remote_heads(struct packet_reader *reader,
die_initial_contact(1);
case PACKET_READ_NORMAL:
len = reader->pktlen;
if (len > 4 && skip_prefix(reader->line, "ERR ", &arg))
die(_("remote error: %s"), arg);
break;
case PACKET_READ_FLUSH:
state = EXPECTING_DONE;
......
......@@ -135,38 +135,42 @@ enum ack_type {
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 we sent a depth we will get back "duplicate"
* shallow and unshallow commands every time there
* is a block of have lines exchanged.
*/
char *line;
while ((line = packet_read_line(fd, NULL))) {
if (starts_with(line, "shallow "))
while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
if (starts_with(reader->line, "shallow "))
continue;
if (starts_with(line, "unshallow "))
if (starts_with(reader->line, "unshallow "))
continue;
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;
char *line = packet_read_line(fd, &len);
const char *arg;
if (!line)
if (packet_reader_read(reader) != PACKET_READ_NORMAL)
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;
if (skip_prefix(line, "ACK ", &arg)) {
if (skip_prefix(reader->line, "ACK ", &arg)) {
if (!get_oid_hex(arg, result_oid)) {
arg += 40;
len -= arg - line;
len -= arg - reader->line;
if (len < 1)
return ACK;
if (strstr(arg, "continue"))
......@@ -178,9 +182,7 @@ static enum ack_type get_ack(int fd, struct object_id *result_oid)
return ACK;
}
}
if (skip_prefix(line, "ERR ", &arg))
die(_("remote error: %s"), arg);
die(_("git fetch-pack: expected ACK/NAK, got '%s'"), line);
die(_("git fetch-pack: expected ACK/NAK, got '%s'"), reader->line);
}
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;
struct strbuf req_buf = STRBUF_INIT;
size_t state_len = 0;
struct packet_reader reader;
if (args->stateless_rpc && multi_ack == 1)
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) {
mark_tips(negotiator, args->negotiation_tips);
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;
if (args->deepen) {
char *line;
const char *arg;
struct object_id oid;
send_request(args, fd[1], &req_buf);
while ((line = packet_read_line(fd[0], NULL))) {
if (skip_prefix(line, "shallow ", &arg)) {
while (packet_reader_read(&reader) == PACKET_READ_NORMAL) {
if (skip_prefix(reader.line, "shallow ", &arg)) {
if (get_oid_hex(arg, &oid))
die(_("invalid shallow line: %s"), line);
die(_("invalid shallow line: %s"), reader.line);
register_shallow(the_repository, &oid);
continue;
}
if (skip_prefix(line, "unshallow ", &arg)) {
if (skip_prefix(reader.line, "unshallow ", &arg)) {
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))
die(_("object not found: %s"), line);
die(_("object not found: %s"), reader.line);
/* make sure that it is parsed as shallow */
if (!parse_object(the_repository, &oid))
die(_("error in object: %s"), line);
die(_("error in object: %s"), reader.line);
if (unregister_shallow(&oid))
die(_("no shallow found: %s"), line);
die(_("no shallow found: %s"), reader.line);
continue;
}
die(_("expected shallow/unshallow, got %s"), line);
die(_("expected shallow/unshallow, got %s"), reader.line);
}
} else if (!args->stateless_rpc)
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)
continue;
consume_shallow_list(args, fd[0]);
consume_shallow_list(args, &reader);
do {
ack = get_ack(fd[0], result_oid);
ack = get_ack(&reader, result_oid);
if (ack)
print_verbose(args, _("got %s %d %s"), "ack",
ack, oid_to_hex(result_oid));
......@@ -474,9 +480,9 @@ static int find_common(struct fetch_negotiator *negotiator,
strbuf_release(&req_buf);
if (!got_ready || !no_done)
consume_shallow_list(args, fd[0]);
consume_shallow_list(args, &reader);
while (flushes || multi_ack) {
int ack = get_ack(fd[0], result_oid);
int ack = get_ack(&reader, result_oid);
if (ack) {
print_verbose(args, _("got %s (%d) %s"), "ack",
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,
const struct fetch_pack_args *args,
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;
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");
if (prefer_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 */
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;
fetch_negotiator_init(&negotiator, negotiation_algorithm);
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) {
switch (state) {
......@@ -1368,7 +1383,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
case FETCH_SEND_REQUEST:
if (send_fetch_request(&negotiator, fd[1], args, ref,
&common,
&haves_to_send, &in_vain))
&haves_to_send, &in_vain,
reader.use_sideband))
state = FETCH_GET_PACK;
else
state = FETCH_PROCESS_ACKS;
......
......@@ -129,12 +129,14 @@ static void set_packet_header(char *buf, const int size)
#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;
orig_len = out->len;
strbuf_addstr(out, "0000");
strbuf_addstr(out, prefix);
strbuf_vaddf(out, fmt, args);
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);
}
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)
{
static struct strbuf buf = STRBUF_INIT;
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 (!gently) {
check_pipe(errno);
......@@ -168,7 +170,7 @@ void packet_write_fmt(int fd, const char *fmt, ...)
va_list args;
va_start(args, fmt);
packet_write_fmt_1(fd, 0, fmt, args);
packet_write_fmt_1(fd, 0, "", fmt, args);
va_end(args);
}
......@@ -178,7 +180,7 @@ int packet_write_fmt_gently(int fd, const char *fmt, ...)
va_list args;
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);
return status;
}
......@@ -211,7 +213,7 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
va_list args;
va_start(args, fmt);
format_packet(buf, fmt, args);
format_packet(buf, "", fmt, 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;
}
if ((options & PACKET_READ_DIE_ON_ERR_PACKET) &&
starts_with(buffer, "ERR "))
die(_("remote error: %s"), buffer + 4);
if ((options & PACKET_READ_CHOMP_NEWLINE) &&
len && buffer[len-1] == '\n')
len--;
......@@ -433,6 +439,29 @@ ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out)
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 */
void packet_reader_init(struct packet_reader *reader, int fd,
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_size = sizeof(packet_buffer);
reader->options = options;
reader->me = "git";
}
enum packet_read_status packet_reader_read(struct packet_reader *reader)
{
struct strbuf scratch = STRBUF_INIT;
if (reader->line_peeked) {
reader->line_peeked = 0;
return reader->status;
}
reader->status = packet_read_with_status(reader->fd,
&reader->src_buffer,
&reader->src_len,
reader->buffer,
reader->buffer_size,
&reader->pktlen,
reader->options);
/*
* Consume all progress packets until a primary payload packet is
* received
*/
while (1) {
enum sideband_type sideband_type;
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)
reader->line = reader->buffer;
/* Skip the sideband designator if sideband is used */
reader->line = reader->use_sideband ?
reader->buffer + 1 : reader->buffer;
else
reader->line = NULL;
......@@ -482,3 +529,39 @@ enum packet_read_status packet_reader_peek(struct packet_reader *reader)
reader->line_peeked = 1;
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);
}
......@@ -3,6 +3,7 @@
#include "git-compat-util.h"
#include "strbuf.h"
#include "sideband.h"
/*
* 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
* 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_CHOMP_NEWLINE (1u<<1)
#define PACKET_READ_GENTLE_ON_EOF (1u<<0)
#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
*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);
/*
* 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 {
/* source file descriptor */
int fd;
......@@ -142,6 +162,9 @@ struct packet_reader {
/* indicates if a line has been 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)
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
......@@ -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_READ_CHOMP_NEWLINE |
PACKET_READ_GENTLE_ON_EOF);
PACKET_READ_GENTLE_ON_EOF |
PACKET_READ_DIE_ON_ERR_PACKET);
heads->version = discover_version(&reader);
switch (heads->version) {
......@@ -408,28 +409,37 @@ static struct discovery *discover_refs(const char *service, int for_push)
if (maybe_smart &&
(5 <= last->len && last->buf[4] == '#') &&