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:
commit
5f8b86db94
@ -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;
|
||||
|
78
fetch-pack.c
78
fetch-pack.c
@ -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 @@ done:
|
||||
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;
|
||||
|
111
pkt-line.c
111
pkt-line.c
@ -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);
|
||||
}
|
||||
|
42
pkt-line.h
42
pkt-line.h
@ -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] == '#') &&
|
||||
!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
|
||||
* pkt-line matches our request.
|
||||
*/
|
||||
line = packet_read_line_buf(&last->buf, &last->len, NULL);
|
||||
if (!line)
|
||||
if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
|
||||
die("invalid server response; expected service, got flush packet");
|
||||
|
||||
strbuf_reset(&exp);
|
||||
strbuf_addf(&exp, "# service=%s", service);
|
||||
if (strcmp(line, exp.buf))
|
||||
die("invalid server response; got '%s'", line);
|
||||
if (strcmp(reader.line, exp.buf))
|
||||
die("invalid server response; got '%s'", reader.line);
|
||||
strbuf_release(&exp);
|
||||
|
||||
/* The header can include additional metadata lines, up
|
||||
* until a packet flush marker. Ignore these now, but
|
||||
* in the future we might start to scan them.
|
||||
*/
|
||||
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;
|
||||
} 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
39
send-pack.c
39
send-pack.c
@ -135,38 +135,36 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc
|
||||
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 (!line)
|
||||
if (packet_reader_read(reader) != PACKET_READ_NORMAL)
|
||||
return error(_("unexpected flush packet while reading remote unpack status"));
|
||||
if (!skip_prefix(line, "unpack ", &line))
|
||||
return error(_("unable to parse remote unpack status: %s"), line);
|
||||
if (strcmp(line, "ok"))
|
||||
return error(_("remote unpack failed: %s"), line);
|
||||
if (!skip_prefix(reader->line, "unpack ", &reader->line))
|
||||
return error(_("unable to parse remote unpack status: %s"), reader->line);
|
||||
if (strcmp(reader->line, "ok"))
|
||||
return error(_("remote unpack failed: %s"), reader->line);
|
||||
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;
|
||||
int ret;
|
||||
|
||||
hint = NULL;
|
||||
ret = receive_unpack_status(in);
|
||||
ret = receive_unpack_status(reader);
|
||||
while (1) {
|
||||
char *refname;
|
||||
const char *refname;
|
||||
char *msg;
|
||||
char *line = packet_read_line(in, NULL);
|
||||
if (!line)
|
||||
if (packet_reader_read(reader) != PACKET_READ_NORMAL)
|
||||
break;
|
||||
if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) {
|
||||
error("invalid ref status from remote: %s", line);
|
||||
if (!starts_with(reader->line, "ok ") && !starts_with(reader->line, "ng ")) {
|
||||
error("invalid ref status from remote: %s", reader->line);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
refname = line + 3;
|
||||
refname = reader->line + 3;
|
||||
msg = strchr(refname, ' ');
|
||||
if (msg)
|
||||
*msg++ = '\0';
|
||||
@ -187,7 +185,7 @@ static int receive_status(int in, struct ref *refs)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line[0] == 'o' && line[1] == 'k')
|
||||
if (reader->line[0] == 'o' && reader->line[1] == 'k')
|
||||
hint->status = REF_STATUS_OK;
|
||||
else {
|
||||
hint->status = REF_STATUS_REMOTE_REJECT;
|
||||
@ -390,6 +388,7 @@ int send_pack(struct send_pack_args *args,
|
||||
int ret;
|
||||
struct async demux;
|
||||
const char *push_cert_nonce = NULL;
|
||||
struct packet_reader reader;
|
||||
|
||||
/* Does the other end support the reporting? */
|
||||
if (server_supports("report-status"))
|
||||
@ -559,6 +558,10 @@ int send_pack(struct send_pack_args *args,
|
||||
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 (pack_objects(out, remote_refs, extra_have, args) < 0) {
|
||||
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.
|
||||
*/
|
||||
if (status_report)
|
||||
receive_unpack_status(in);
|
||||
receive_unpack_status(&reader);
|
||||
|
||||
if (use_sideband) {
|
||||
close(demux.out);
|
||||
@ -590,7 +593,7 @@ int send_pack(struct send_pack_args *args,
|
||||
packet_flush(out);
|
||||
|
||||
if (status_report && cmds_sent)
|
||||
ret = receive_status(in, remote_refs);
|
||||
ret = receive_status(&reader, remote_refs);
|
||||
else
|
||||
ret = 0;
|
||||
if (args->stateless_rpc)
|
||||
|
5
serve.c
5
serve.c
@ -167,7 +167,8 @@ static int process_request(void)
|
||||
|
||||
packet_reader_init(&reader, 0, NULL, 0,
|
||||
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
|
||||
@ -175,7 +176,7 @@ static int process_request(void)
|
||||
*/
|
||||
if (packet_reader_peek(&reader) == PACKET_READ_EOF)
|
||||
return 1;
|
||||
reader.options = PACKET_READ_CHOMP_NEWLINE;
|
||||
reader.options &= ~PACKET_READ_GENTLE_ON_EOF;
|
||||
|
||||
while (state != PROCESS_REQUEST_DONE) {
|
||||
switch (packet_reader_peek(&reader)) {
|
||||
|
182
sideband.c
182
sideband.c
@ -1,7 +1,6 @@
|
||||
#include "cache.h"
|
||||
#include "color.h"
|
||||
#include "config.h"
|
||||
#include "pkt-line.h"
|
||||
#include "sideband.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 ANSI_SUFFIX "\033[K"
|
||||
#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;
|
||||
char buf[LARGE_PACKET_MAX + 1];
|
||||
struct strbuf outbuf = STRBUF_INIT;
|
||||
int retval = 0;
|
||||
static const char *suffix;
|
||||
const char *b, *brk;
|
||||
int band;
|
||||
|
||||
if (isatty(2) && !is_terminal_dumb())
|
||||
suffix = ANSI_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 (!suffix) {
|
||||
if (isatty(2) && !is_terminal_dumb())
|
||||
suffix = ANSI_SUFFIX;
|
||||
else
|
||||
suffix = DUMB_SUFFIX;
|
||||
}
|
||||
|
||||
if (outbuf.len) {
|
||||
strbuf_addch(&outbuf, '\n');
|
||||
xwrite(2, outbuf.buf, outbuf.len);
|
||||
if (len == 0) {
|
||||
*sideband_type = SIDEBAND_FLUSH;
|
||||
goto cleanup;
|
||||
}
|
||||
strbuf_release(&outbuf);
|
||||
return retval;
|
||||
if (len < 1) {
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
25
sideband.h
25
sideband.h
@ -1,10 +1,29 @@
|
||||
#ifndef SIDEBAND_H
|
||||
#define SIDEBAND_H
|
||||
|
||||
#define SIDEBAND_PROTOCOL_ERROR -2
|
||||
#define SIDEBAND_REMOTE_ERROR -1
|
||||
enum sideband_type {
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
5
t/README
5
t/README
@ -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
|
||||
'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
|
||||
------------
|
||||
|
||||
|
@ -78,6 +78,7 @@ PassEnv GNUPGHOME
|
||||
PassEnv ASAN_OPTIONS
|
||||
PassEnv GIT_TRACE
|
||||
PassEnv GIT_CONFIG_NOSYSTEM
|
||||
PassEnv GIT_TEST_SIDEBAND_ALL
|
||||
|
||||
SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0
|
||||
|
||||
|
@ -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^)" \
|
||||
>"$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 &&
|
||||
|
||||
# Ensure that the one-time-sed script was used.
|
||||
|
@ -14,7 +14,7 @@ test_expect_success 'test capability advertisement' '
|
||||
0000
|
||||
EOF
|
||||
|
||||
git serve --advertise-capabilities >out &&
|
||||
GIT_TEST_SIDEBAND_ALL=0 git serve --advertise-capabilities >out &&
|
||||
test-tool pkt-line unpack <out >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
@ -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 \
|
||||
-c protocol.version=2 \
|
||||
fetch "$HTTPD_URL/one_time_sed/http_parent" 2> err &&
|
||||
grep "fetch< acknowledgments" log &&
|
||||
! grep "fetch< ready" log &&
|
||||
grep "fetch< .*acknowledgments" log &&
|
||||
! grep "fetch< .*ready" log &&
|
||||
test_i18ngrep "expected no other sections to be sent after no .ready." err
|
||||
'
|
||||
|
||||
|
@ -208,7 +208,7 @@ test_expect_success 'server is initially ahead - no ref in want' '
|
||||
cp -r "$LOCAL_PRISTINE" local &&
|
||||
inconsistency master 1234567890123456789012345678901234567890 &&
|
||||
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' '
|
||||
@ -254,7 +254,7 @@ test_expect_success 'server loses a ref - ref in want' '
|
||||
echo "s/master/raster/" >"$HTTPD_ROOT_PATH/one-time-sed" &&
|
||||
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
|
||||
|
@ -273,7 +273,8 @@ static struct ref *handshake(struct transport *transport, int for_push,
|
||||
|
||||
packet_reader_init(&reader, data->fd[0], NULL, 0,
|
||||
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);
|
||||
switch (data->version) {
|
||||
|
172
upload-pack.c
172
upload-pack.c
@ -70,6 +70,8 @@ static int allow_filter;
|
||||
static int allow_ref_in_want;
|
||||
static struct list_objects_filter_options filter_options;
|
||||
|
||||
static int allow_sideband_all;
|
||||
|
||||
static void reset_timeout(void)
|
||||
{
|
||||
alarm(timeout);
|
||||
@ -356,7 +358,8 @@ static int ok_to_give_up(const struct object_array *have_obj,
|
||||
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_id oid;
|
||||
@ -368,12 +371,11 @@ static int get_common_commits(struct object_array *have_obj,
|
||||
save_commit_buffer = 0;
|
||||
|
||||
for (;;) {
|
||||
char *line = packet_read_line(0, NULL);
|
||||
const char *arg;
|
||||
|
||||
reset_timeout();
|
||||
|
||||
if (!line) {
|
||||
if (packet_reader_read(reader) != PACKET_READ_NORMAL) {
|
||||
if (multi_ack == 2 && got_common
|
||||
&& !got_other && ok_to_give_up(have_obj, want_obj)) {
|
||||
sent_ready = 1;
|
||||
@ -392,7 +394,7 @@ static int get_common_commits(struct object_array *have_obj,
|
||||
got_other = 0;
|
||||
continue;
|
||||
}
|
||||
if (skip_prefix(line, "have ", &arg)) {
|
||||
if (skip_prefix(reader->line, "have ", &arg)) {
|
||||
switch (got_oid(arg, &oid, have_obj)) {
|
||||
case -1: /* they have what we do not */
|
||||
got_other = 1;
|
||||
@ -418,7 +420,7 @@ static int get_common_commits(struct object_array *have_obj,
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(line, "done")) {
|
||||
if (!strcmp(reader->line, "done")) {
|
||||
if (have_obj->nr > 0) {
|
||||
if (multi_ack)
|
||||
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");
|
||||
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) {
|
||||
struct object *object = &result->item->object;
|
||||
if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {
|
||||
packet_write_fmt(1, "shallow %s",
|
||||
oid_to_hex(&object->oid));
|
||||
packet_writer_write(writer, "shallow %s",
|
||||
oid_to_hex(&object->oid));
|
||||
register_shallow(the_repository, &object->oid);
|
||||
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)
|
||||
{
|
||||
int i;
|
||||
@ -638,8 +642,8 @@ static void send_unshallow(const struct object_array *shallows,
|
||||
struct object *object = shallows->objects[i].item;
|
||||
if (object->flags & NOT_SHALLOW) {
|
||||
struct commit_list *parents;
|
||||
packet_write_fmt(1, "unshallow %s",
|
||||
oid_to_hex(&object->oid));
|
||||
packet_writer_write(writer, "unshallow %s",
|
||||
oid_to_hex(&object->oid));
|
||||
object->flags &= ~CLIENT_SHALLOW;
|
||||
/*
|
||||
* 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,
|
||||
int flag, void *cb_data);
|
||||
|
||||
static void deepen(int depth, int deepen_relative,
|
||||
static void deepen(struct packet_writer *writer, int depth, int deepen_relative,
|
||||
struct object_array *shallows, struct object_array *want_obj)
|
||||
{
|
||||
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,
|
||||
depth + 1,
|
||||
SHALLOW, NOT_SHALLOW);
|
||||
send_shallow(result);
|
||||
send_shallow(writer, result);
|
||||
free_commit_list(result);
|
||||
object_array_clear(&reachable_shallows);
|
||||
} else {
|
||||
@ -700,14 +703,15 @@ static void deepen(int depth, int deepen_relative,
|
||||
|
||||
result = get_shallow_commits(want_obj, depth,
|
||||
SHALLOW, NOT_SHALLOW);
|
||||
send_shallow(result);
|
||||
send_shallow(writer, 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 *want_obj)
|
||||
{
|
||||
@ -715,13 +719,14 @@ static void deepen_by_rev_list(int ac, const char **av,
|
||||
|
||||
close_commit_graph(the_repository);
|
||||
result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
|
||||
send_shallow(result);
|
||||
send_shallow(writer, 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 */
|
||||
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,
|
||||
struct string_list *deepen_not,
|
||||
int deepen_relative,
|
||||
@ -733,7 +738,7 @@ static int send_shallow_list(int depth, int deepen_rev_list,
|
||||
if (depth > 0 && deepen_rev_list)
|
||||
die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together");
|
||||
if (depth > 0) {
|
||||
deepen(depth, deepen_relative, shallows, want_obj);
|
||||
deepen(writer, depth, deepen_relative, shallows, want_obj);
|
||||
ret = 1;
|
||||
} else if (deepen_rev_list) {
|
||||
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;
|
||||
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);
|
||||
ret = 1;
|
||||
} else {
|
||||
@ -839,7 +844,7 @@ static int process_deepen_not(const char *line, struct string_list *deepen_not,
|
||||
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 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;
|
||||
int deepen_rev_list = 0;
|
||||
int deepen_relative = 0;
|
||||
struct packet_writer writer;
|
||||
|
||||
shallow_nr = 0;
|
||||
packet_writer_init(&writer, 1);
|
||||
for (;;) {
|
||||
struct object *o;
|
||||
const char *features;
|
||||
struct object_id oid_buf;
|
||||
char *line = packet_read_line(0, NULL);
|
||||
const char *arg;
|
||||
|
||||
reset_timeout();
|
||||
if (!line)
|
||||
if (packet_reader_read(reader) != PACKET_READ_NORMAL)
|
||||
break;
|
||||
|
||||
if (process_shallow(line, &shallows))
|
||||
if (process_shallow(reader->line, &shallows))
|
||||
continue;
|
||||
if (process_deepen(line, &depth))
|
||||
if (process_deepen(reader->line, &depth))
|
||||
continue;
|
||||
if (process_deepen_since(line, &deepen_since, &deepen_rev_list))
|
||||
if (process_deepen_since(reader->line, &deepen_since, &deepen_rev_list))
|
||||
continue;
|
||||
if (process_deepen_not(line, &deepen_not, &deepen_rev_list))
|
||||
if (process_deepen_not(reader->line, &deepen_not, &deepen_rev_list))
|
||||
continue;
|
||||
|
||||
if (skip_prefix(line, "filter ", &arg)) {
|
||||
if (skip_prefix(reader->line, "filter ", &arg)) {
|
||||
if (!filter_capability_requested)
|
||||
die("git upload-pack: filtering capability not negotiated");
|
||||
parse_list_objects_filter(&filter_options, arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!skip_prefix(line, "want ", &arg) ||
|
||||
if (!skip_prefix(reader->line, "want ", &arg) ||
|
||||
parse_oid_hex(arg, &oid_buf, &features))
|
||||
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"))
|
||||
deepen_relative = 1;
|
||||
@ -907,9 +913,9 @@ static void receive_needs(struct object_array *want_obj)
|
||||
|
||||
o = parse_object(the_repository, &oid_buf);
|
||||
if (!o) {
|
||||
packet_write_fmt(1,
|
||||
"ERR upload-pack: not our ref %s",
|
||||
oid_to_hex(&oid_buf));
|
||||
packet_writer_error(&writer,
|
||||
"upload-pack: not our ref %s",
|
||||
oid_to_hex(&oid_buf));
|
||||
die("git upload-pack: not our ref %s",
|
||||
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)
|
||||
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,
|
||||
want_obj))
|
||||
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);
|
||||
} else if (!strcmp("uploadpack.allowrefinwant", var)) {
|
||||
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) {
|
||||
@ -1070,6 +1078,7 @@ void upload_pack(struct upload_pack_options *options)
|
||||
{
|
||||
struct string_list symref = STRING_LIST_INIT_DUP;
|
||||
struct object_array want_obj = OBJECT_ARRAY_INIT;
|
||||
struct packet_reader reader;
|
||||
|
||||
stateless_rpc = options->stateless_rpc;
|
||||
timeout = options->timeout;
|
||||
@ -1093,10 +1102,14 @@ void upload_pack(struct upload_pack_options *options)
|
||||
if (options->advertise_refs)
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1113,6 +1126,8 @@ struct upload_pack_data {
|
||||
int deepen_rev_list;
|
||||
int deepen_relative;
|
||||
|
||||
struct packet_writer writer;
|
||||
|
||||
unsigned stateless_rpc : 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->shallows = shallows;
|
||||
data->deepen_not = deepen_not;
|
||||
packet_writer_init(&data->writer, 1);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
if (!o) {
|
||||
packet_write_fmt(1,
|
||||
"ERR upload-pack: not our ref %s",
|
||||
oid_to_hex(&oid));
|
||||
packet_writer_error(writer,
|
||||
"upload-pack: not our ref %s",
|
||||
oid_to_hex(&oid));
|
||||
die("git upload-pack: not our ref %s",
|
||||
oid_to_hex(&oid));
|
||||
}
|
||||
@ -1178,7 +1195,8 @@ static int parse_want(const char *line, struct object_array *want_obj)
|
||||
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)
|
||||
{
|
||||
const char *arg;
|
||||
@ -1188,7 +1206,7 @@ static int parse_want_ref(const char *line, struct string_list *wanted_refs,
|
||||
struct object *o;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1231,10 +1249,11 @@ static void process_args(struct packet_reader *request,
|
||||
const char *p;
|
||||
|
||||
/* process want */
|
||||
if (parse_want(arg, want_obj))
|
||||
if (parse_want(&data->writer, arg, want_obj))
|
||||
continue;
|
||||
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;
|
||||
/* process have line */
|
||||
if (parse_have(arg, &data->haves))
|
||||
@ -1283,6 +1302,13 @@ static void process_args(struct packet_reader *request,
|
||||
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? */
|
||||
die("unexpected line: '%s'", arg);
|
||||
}
|
||||
@ -1328,26 +1354,26 @@ static int process_haves(struct oid_array *haves, struct oid_array *common,
|
||||
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,
|
||||
struct object_array *want_obj)
|
||||
{
|
||||
int i;
|
||||
|
||||
packet_buf_write(response, "acknowledgments\n");
|
||||
packet_writer_write(writer, "acknowledgments\n");
|
||||
|
||||
/* Send Acks */
|
||||
if (!acks->nr)
|
||||
packet_buf_write(response, "NAK\n");
|
||||
packet_writer_write(writer, "NAK\n");
|
||||
|
||||
for (i = 0; i < acks->nr; i++) {
|
||||
packet_buf_write(response, "ACK %s\n",
|
||||
oid_to_hex(&acks->oid[i]));
|
||||
packet_writer_write(writer, "ACK %s\n",
|
||||
oid_to_hex(&acks->oid[i]));
|
||||
}
|
||||
|
||||
if (ok_to_give_up(have_obj, want_obj)) {
|
||||
/* Send Ready */
|
||||
packet_buf_write(response, "ready\n");
|
||||
packet_writer_write(writer, "ready\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1359,25 +1385,20 @@ static int process_haves_and_send_acks(struct upload_pack_data *data,
|
||||
struct object_array *want_obj)
|
||||
{
|
||||
struct oid_array common = OID_ARRAY_INIT;
|
||||
struct strbuf response = STRBUF_INIT;
|
||||
int ret = 0;
|
||||
|
||||
process_haves(&data->haves, &common, have_obj);
|
||||
if (data->done) {
|
||||
ret = 1;
|
||||
} else if (send_acks(&common, &response, have_obj, want_obj)) {
|
||||
packet_buf_delim(&response);
|
||||
} else if (send_acks(&data->writer, &common, have_obj, want_obj)) {
|
||||
packet_writer_delim(&data->writer);
|
||||
ret = 1;
|
||||
} else {
|
||||
/* Add Flush */
|
||||
packet_buf_flush(&response);
|
||||
packet_writer_flush(&data->writer);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
/* Send response */
|
||||
write_or_die(1, response.buf, response.len);
|
||||
strbuf_release(&response);
|
||||
|
||||
oid_array_clear(&data->haves);
|
||||
oid_array_clear(&common);
|
||||
return ret;
|
||||
@ -1390,15 +1411,15 @@ static void send_wanted_ref_info(struct upload_pack_data *data)
|
||||
if (!data->wanted_refs.nr)
|
||||
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) {
|
||||
packet_write_fmt(1, "%s %s\n",
|
||||
oid_to_hex(item->util),
|
||||
item->string);
|
||||
packet_writer_write(&data->writer, "%s %s\n",
|
||||
oid_to_hex(item->util),
|
||||
item->string);
|
||||
}
|
||||
|
||||
packet_delim(1);
|
||||
packet_writer_delim(&data->writer);
|
||||
}
|
||||
|
||||
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))
|
||||
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_relative,
|
||||
&data->shallows, want_obj) &&
|
||||
is_repository_shallow(the_repository))
|
||||
deepen(INFINITE_DEPTH, data->deepen_relative, &data->shallows,
|
||||
want_obj);
|
||||
deepen(&data->writer, INFINITE_DEPTH, data->deepen_relative,
|
||||
&data->shallows, want_obj);
|
||||
|
||||
packet_delim(1);
|
||||
}
|
||||
@ -1479,7 +1501,7 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,
|
||||
send_wanted_ref_info(&data);
|
||||
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);
|
||||
state = FETCH_DONE;
|
||||
break;
|
||||
@ -1500,6 +1522,7 @@ int upload_pack_advertise(struct repository *r,
|
||||
if (value) {
|
||||
int allow_filter_value;
|
||||
int allow_ref_in_want;
|
||||
int allow_sideband_all_value;
|
||||
|
||||
strbuf_addstr(value, "shallow");
|
||||
|
||||
@ -1514,6 +1537,13 @@ int upload_pack_advertise(struct repository *r,
|
||||
&allow_ref_in_want) &&
|
||||
allow_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;
|
||||
|
Loading…
Reference in New Issue
Block a user