зеркало из https://github.com/microsoft/git.git
Merge branch 'jk/pkt-line-cleanup'
Clean up pkt-line API, implementation and its callers to make them more robust. * jk/pkt-line-cleanup: do not use GIT_TRACE_PACKET=3 in tests remote-curl: always parse incoming refs remote-curl: move ref-parsing code up in file remote-curl: pass buffer straight to get_remote_heads teach get_remote_heads to read from a memory buffer pkt-line: share buffer/descriptor reading implementation pkt-line: provide a LARGE_PACKET_MAX static buffer pkt-line: move LARGE_PACKET_MAX definition from sideband pkt-line: teach packet_read_line to chomp newlines pkt-line: provide a generic reading function with options pkt-line: drop safe_write function pkt-line: move a misplaced comment write_or_die: raise SIGPIPE when we get EPIPE upload-archive: use argv_array to store client arguments upload-archive: do not copy repo name send-pack: prefer prefixcmp over memcmp in receive_status fetch-pack: fix out-of-bounds buffer offset in get_ack upload-pack: remove packet debugging harness upload-pack: do not add duplicate objects to shallow list upload-pack: use get_sha1_hex to parse "shallow" lines
This commit is contained in:
Коммит
e013bdab0f
|
@ -27,8 +27,8 @@ static int run_remote_archiver(int argc, const char **argv,
|
|||
const char *remote, const char *exec,
|
||||
const char *name_hint)
|
||||
{
|
||||
char buf[LARGE_PACKET_MAX];
|
||||
int fd[2], i, len, rv;
|
||||
char *buf;
|
||||
int fd[2], i, rv;
|
||||
struct transport *transport;
|
||||
struct remote *_remote;
|
||||
|
||||
|
@ -53,21 +53,18 @@ static int run_remote_archiver(int argc, const char **argv,
|
|||
packet_write(fd[1], "argument %s\n", argv[i]);
|
||||
packet_flush(fd[1]);
|
||||
|
||||
len = packet_read_line(fd[0], buf, sizeof(buf));
|
||||
if (!len)
|
||||
buf = packet_read_line(fd[0], NULL);
|
||||
if (!buf)
|
||||
die(_("git archive: expected ACK/NAK, got EOF"));
|
||||
if (buf[len-1] == '\n')
|
||||
buf[--len] = 0;
|
||||
if (strcmp(buf, "ACK")) {
|
||||
if (len > 5 && !prefixcmp(buf, "NACK "))
|
||||
if (!prefixcmp(buf, "NACK "))
|
||||
die(_("git archive: NACK %s"), buf + 5);
|
||||
if (len > 4 && !prefixcmp(buf, "ERR "))
|
||||
if (!prefixcmp(buf, "ERR "))
|
||||
die(_("remote error: %s"), buf + 4);
|
||||
die(_("git archive: protocol error"));
|
||||
}
|
||||
|
||||
len = packet_read_line(fd[0], buf, sizeof(buf));
|
||||
if (len)
|
||||
if (packet_read_line(fd[0], NULL))
|
||||
die(_("git archive: expected a flush"));
|
||||
|
||||
/* Now, start reading from fd[0] and spit it out to stdout */
|
||||
|
|
|
@ -119,14 +119,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
|||
/* in stateless RPC mode we use pkt-line to read
|
||||
* from stdin, until we get a flush packet
|
||||
*/
|
||||
static char line[1000];
|
||||
for (;;) {
|
||||
int n = packet_read_line(0, line, sizeof(line));
|
||||
if (!n)
|
||||
char *line = packet_read_line(0, NULL);
|
||||
if (!line)
|
||||
break;
|
||||
if (line[n-1] == '\n')
|
||||
n--;
|
||||
add_sought_entry_mem(&sought, &nr_sought, &alloc_sought, line, n);
|
||||
add_sought_entry(&sought, &nr_sought, &alloc_sought, line);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -147,7 +144,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
|||
args.verbose ? CONNECT_VERBOSE : 0);
|
||||
}
|
||||
|
||||
get_remote_heads(fd[0], &ref, 0, NULL);
|
||||
get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL);
|
||||
|
||||
ref = fetch_pack(&args, fd, conn, ref, dest,
|
||||
sought, nr_sought, pack_lockfile_ptr);
|
||||
|
|
|
@ -754,17 +754,15 @@ static struct command *read_head_info(void)
|
|||
struct command *commands = NULL;
|
||||
struct command **p = &commands;
|
||||
for (;;) {
|
||||
static char line[1000];
|
||||
char *line;
|
||||
unsigned char old_sha1[20], new_sha1[20];
|
||||
struct command *cmd;
|
||||
char *refname;
|
||||
int len, reflen;
|
||||
|
||||
len = packet_read_line(0, line, sizeof(line));
|
||||
if (!len)
|
||||
line = packet_read_line(0, &len);
|
||||
if (!line)
|
||||
break;
|
||||
if (line[len-1] == '\n')
|
||||
line[--len] = 0;
|
||||
if (len < 83 ||
|
||||
line[40] != ' ' ||
|
||||
line[81] != ' ' ||
|
||||
|
@ -932,7 +930,7 @@ static void report(struct command *commands, const char *unpack_status)
|
|||
if (use_sideband)
|
||||
send_sideband(1, 1, buf.buf, buf.len, use_sideband);
|
||||
else
|
||||
safe_write(1, buf.buf, buf.len);
|
||||
write_or_die(1, buf.buf, buf.len);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ static void print_helper_status(struct ref *ref)
|
|||
}
|
||||
strbuf_addch(&buf, '\n');
|
||||
|
||||
safe_write(1, buf.buf, buf.len);
|
||||
write_or_die(1, buf.buf, buf.len);
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
|
|||
|
||||
memset(&extra_have, 0, sizeof(extra_have));
|
||||
|
||||
get_remote_heads(fd[0], &remote_refs, REF_NORMAL, &extra_have);
|
||||
get_remote_heads(fd[0], NULL, 0, &remote_refs, REF_NORMAL, &extra_have);
|
||||
|
||||
transport_verify_remote_names(nr_refspecs, refspecs);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "pkt-line.h"
|
||||
#include "sideband.h"
|
||||
#include "run-command.h"
|
||||
#include "argv-array.h"
|
||||
|
||||
static const char upload_archive_usage[] =
|
||||
"git upload-archive <repo>";
|
||||
|
@ -18,51 +19,31 @@ static const char deadchild[] =
|
|||
|
||||
int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
const char *sent_argv[MAX_ARGS];
|
||||
struct argv_array sent_argv = ARGV_ARRAY_INIT;
|
||||
const char *arg_cmd = "argument ";
|
||||
char *p, buf[4096];
|
||||
int sent_argc;
|
||||
int len;
|
||||
|
||||
if (argc != 2)
|
||||
usage(upload_archive_usage);
|
||||
|
||||
if (strlen(argv[1]) + 1 > sizeof(buf))
|
||||
die("insanely long repository name");
|
||||
|
||||
strcpy(buf, argv[1]); /* enter-repo smudges its argument */
|
||||
|
||||
if (!enter_repo(buf, 0))
|
||||
die("'%s' does not appear to be a git repository", buf);
|
||||
if (!enter_repo(argv[1], 0))
|
||||
die("'%s' does not appear to be a git repository", argv[1]);
|
||||
|
||||
/* put received options in sent_argv[] */
|
||||
sent_argc = 1;
|
||||
sent_argv[0] = "git-upload-archive";
|
||||
for (p = buf;;) {
|
||||
/* This will die if not enough free space in buf */
|
||||
len = packet_read_line(0, p, (buf + sizeof buf) - p);
|
||||
if (len == 0)
|
||||
argv_array_push(&sent_argv, "git-upload-archive");
|
||||
for (;;) {
|
||||
char *buf = packet_read_line(0, NULL);
|
||||
if (!buf)
|
||||
break; /* got a flush */
|
||||
if (sent_argc > MAX_ARGS - 2)
|
||||
die("Too many options (>%d)", MAX_ARGS - 2);
|
||||
if (sent_argv.argc > MAX_ARGS)
|
||||
die("Too many options (>%d)", MAX_ARGS - 1);
|
||||
|
||||
if (p[len-1] == '\n') {
|
||||
p[--len] = 0;
|
||||
}
|
||||
if (len < strlen(arg_cmd) ||
|
||||
strncmp(arg_cmd, p, strlen(arg_cmd)))
|
||||
if (prefixcmp(buf, arg_cmd))
|
||||
die("'argument' token or flush expected");
|
||||
|
||||
len -= strlen(arg_cmd);
|
||||
memmove(p, p + strlen(arg_cmd), len);
|
||||
sent_argv[sent_argc++] = p;
|
||||
p += len;
|
||||
*p++ = 0;
|
||||
argv_array_push(&sent_argv, buf + strlen(arg_cmd));
|
||||
}
|
||||
sent_argv[sent_argc] = NULL;
|
||||
|
||||
/* parse all options sent by the client */
|
||||
return write_archive(sent_argc, sent_argv, prefix, 0, NULL, 1);
|
||||
return write_archive(sent_argv.argc, sent_argv.argv, prefix, 0, NULL, 1);
|
||||
}
|
||||
|
||||
__attribute__((format (printf, 1, 2)))
|
||||
|
|
4
cache.h
4
cache.h
|
@ -1064,7 +1064,9 @@ struct extra_have_objects {
|
|||
int nr, alloc;
|
||||
unsigned char (*array)[20];
|
||||
};
|
||||
extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
|
||||
extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
|
||||
struct ref **list, unsigned int flags,
|
||||
struct extra_have_objects *);
|
||||
extern int server_supports(const char *feature);
|
||||
extern int parse_feature_request(const char *features, const char *feature);
|
||||
extern const char *server_feature_value(const char *feature, int *len_ret);
|
||||
|
|
13
connect.c
13
connect.c
|
@ -62,8 +62,8 @@ static void die_initial_contact(int got_at_least_one_head)
|
|||
/*
|
||||
* Read all the refs from the other end
|
||||
*/
|
||||
struct ref **get_remote_heads(int in, struct ref **list,
|
||||
unsigned int flags,
|
||||
struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
|
||||
struct ref **list, unsigned int flags,
|
||||
struct extra_have_objects *extra_have)
|
||||
{
|
||||
int got_at_least_one_head = 0;
|
||||
|
@ -72,18 +72,19 @@ struct ref **get_remote_heads(int in, struct ref **list,
|
|||
for (;;) {
|
||||
struct ref *ref;
|
||||
unsigned char old_sha1[20];
|
||||
static char buffer[1000];
|
||||
char *name;
|
||||
int len, name_len;
|
||||
char *buffer = packet_buffer;
|
||||
|
||||
len = packet_read(in, buffer, sizeof(buffer));
|
||||
len = packet_read(in, &src_buf, &src_len,
|
||||
packet_buffer, sizeof(packet_buffer),
|
||||
PACKET_READ_GENTLE_ON_EOF |
|
||||
PACKET_READ_CHOMP_NEWLINE);
|
||||
if (len < 0)
|
||||
die_initial_contact(got_at_least_one_head);
|
||||
|
||||
if (!len)
|
||||
break;
|
||||
if (buffer[len-1] == '\n')
|
||||
buffer[--len] = 0;
|
||||
|
||||
if (len > 4 && !prefixcmp(buffer, "ERR "))
|
||||
die("remote error: %s", buffer + 4);
|
||||
|
|
4
daemon.c
4
daemon.c
|
@ -600,7 +600,7 @@ static void parse_host_arg(char *extra_args, int buflen)
|
|||
|
||||
static int execute(void)
|
||||
{
|
||||
static char line[1000];
|
||||
char *line = packet_buffer;
|
||||
int pktlen, len, i;
|
||||
char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
|
||||
|
||||
|
@ -608,7 +608,7 @@ static int execute(void)
|
|||
loginfo("Connection from %s:%s", addr, port);
|
||||
|
||||
alarm(init_timeout ? init_timeout : timeout);
|
||||
pktlen = packet_read_line(0, line, sizeof(line));
|
||||
pktlen = packet_read(0, NULL, NULL, packet_buffer, sizeof(packet_buffer), 0);
|
||||
alarm(0);
|
||||
|
||||
len = strlen(line);
|
||||
|
|
18
fetch-pack.c
18
fetch-pack.c
|
@ -172,8 +172,8 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd)
|
|||
* shallow and unshallow commands every time there
|
||||
* is a block of have lines exchanged.
|
||||
*/
|
||||
char line[1000];
|
||||
while (packet_read_line(fd, line, sizeof(line))) {
|
||||
char *line;
|
||||
while ((line = packet_read_line(fd, NULL))) {
|
||||
if (!prefixcmp(line, "shallow "))
|
||||
continue;
|
||||
if (!prefixcmp(line, "unshallow "))
|
||||
|
@ -215,17 +215,17 @@ static int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
|
|||
|
||||
static enum ack_type get_ack(int fd, unsigned char *result_sha1)
|
||||
{
|
||||
static char line[1000];
|
||||
int len = packet_read_line(fd, line, sizeof(line));
|
||||
int len;
|
||||
char *line = packet_read_line(fd, &len);
|
||||
|
||||
if (!len)
|
||||
die("git fetch-pack: expected ACK/NAK, got EOF");
|
||||
if (line[len-1] == '\n')
|
||||
line[--len] = 0;
|
||||
if (!strcmp(line, "NAK"))
|
||||
return NAK;
|
||||
if (!prefixcmp(line, "ACK ")) {
|
||||
if (!get_sha1_hex(line+4, result_sha1)) {
|
||||
if (len < 45)
|
||||
return ACK;
|
||||
if (strstr(line+45, "continue"))
|
||||
return ACK_continue;
|
||||
if (strstr(line+45, "common"))
|
||||
|
@ -245,7 +245,7 @@ static void send_request(struct fetch_pack_args *args,
|
|||
send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
|
||||
packet_flush(fd);
|
||||
} else
|
||||
safe_write(fd, buf->buf, buf->len);
|
||||
write_or_die(fd, buf->buf, buf->len);
|
||||
}
|
||||
|
||||
static void insert_one_alternate_ref(const struct ref *ref, void *unused)
|
||||
|
@ -346,11 +346,11 @@ static int find_common(struct fetch_pack_args *args,
|
|||
state_len = req_buf.len;
|
||||
|
||||
if (args->depth > 0) {
|
||||
char line[1024];
|
||||
char *line;
|
||||
unsigned char sha1[20];
|
||||
|
||||
send_request(args, fd[1], &req_buf);
|
||||
while (packet_read_line(fd[0], line, sizeof(line))) {
|
||||
while ((line = packet_read_line(fd[0], NULL))) {
|
||||
if (!prefixcmp(line, "shallow ")) {
|
||||
if (get_sha1_hex(line + 8, sha1))
|
||||
die("invalid shallow line: %s", line);
|
||||
|
|
|
@ -70,7 +70,7 @@ static void format_write(int fd, const char *fmt, ...)
|
|||
if (n >= sizeof(buffer))
|
||||
die("protocol error: impossibly long line");
|
||||
|
||||
safe_write(fd, buffer, n);
|
||||
write_or_die(fd, buffer, n);
|
||||
}
|
||||
|
||||
static void http_status(unsigned code, const char *msg)
|
||||
|
@ -111,7 +111,7 @@ static void hdr_cache_forever(void)
|
|||
|
||||
static void end_headers(void)
|
||||
{
|
||||
safe_write(1, "\r\n", 2);
|
||||
write_or_die(1, "\r\n", 2);
|
||||
}
|
||||
|
||||
__attribute__((format (printf, 1, 2)))
|
||||
|
@ -157,7 +157,7 @@ static void send_strbuf(const char *type, struct strbuf *buf)
|
|||
hdr_int(content_length, buf->len);
|
||||
hdr_str(content_type, type);
|
||||
end_headers();
|
||||
safe_write(1, buf->buf, buf->len);
|
||||
write_or_die(1, buf->buf, buf->len);
|
||||
}
|
||||
|
||||
static void send_local_file(const char *the_type, const char *name)
|
||||
|
@ -185,7 +185,7 @@ static void send_local_file(const char *the_type, const char *name)
|
|||
die_errno("Cannot read '%s'", p);
|
||||
if (!n)
|
||||
break;
|
||||
safe_write(1, buf, n);
|
||||
write_or_die(1, buf, n);
|
||||
}
|
||||
close(fd);
|
||||
free(buf);
|
||||
|
|
1
http.c
1
http.c
|
@ -5,6 +5,7 @@
|
|||
#include "url.h"
|
||||
#include "credential.h"
|
||||
#include "version.h"
|
||||
#include "pkt-line.h"
|
||||
|
||||
int active_requests;
|
||||
int http_is_verbose;
|
||||
|
|
125
pkt-line.c
125
pkt-line.c
|
@ -1,6 +1,7 @@
|
|||
#include "cache.h"
|
||||
#include "pkt-line.h"
|
||||
|
||||
char packet_buffer[LARGE_PACKET_MAX];
|
||||
static const char *packet_trace_prefix = "git";
|
||||
static const char trace_key[] = "GIT_TRACE_PACKET";
|
||||
|
||||
|
@ -46,38 +47,6 @@ static void packet_trace(const char *buf, unsigned int len, int write)
|
|||
strbuf_release(&out);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a packetized stream, where each line is preceded by
|
||||
* its length (including the header) as a 4-byte hex number.
|
||||
* A length of 'zero' means end of stream (and a length of 1-3
|
||||
* would be an error).
|
||||
*
|
||||
* This is all pretty stupid, but we use this packetized line
|
||||
* format to make a streaming format possible without ever
|
||||
* over-running the read buffers. That way we'll never read
|
||||
* into what might be the pack data (which should go to another
|
||||
* process entirely).
|
||||
*
|
||||
* The writing side could use stdio, but since the reading
|
||||
* side can't, we stay with pure read/write interfaces.
|
||||
*/
|
||||
ssize_t safe_write(int fd, const void *buf, ssize_t n)
|
||||
{
|
||||
ssize_t nn = n;
|
||||
while (n) {
|
||||
int ret = xwrite(fd, buf, n);
|
||||
if (ret > 0) {
|
||||
buf = (char *) buf + ret;
|
||||
n -= ret;
|
||||
continue;
|
||||
}
|
||||
if (!ret)
|
||||
die("write error (disk full?)");
|
||||
die_errno("write error");
|
||||
}
|
||||
return nn;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we buffered things up above (we don't, but we should),
|
||||
* we'd flush it here
|
||||
|
@ -85,7 +54,7 @@ ssize_t safe_write(int fd, const void *buf, ssize_t n)
|
|||
void packet_flush(int fd)
|
||||
{
|
||||
packet_trace("0000", 4, 1);
|
||||
safe_write(fd, "0000", 4);
|
||||
write_or_die(fd, "0000", 4);
|
||||
}
|
||||
|
||||
void packet_buf_flush(struct strbuf *buf)
|
||||
|
@ -121,7 +90,7 @@ void packet_write(int fd, const char *fmt, ...)
|
|||
va_start(args, fmt);
|
||||
n = format_packet(fmt, args);
|
||||
va_end(args);
|
||||
safe_write(fd, buffer, n);
|
||||
write_or_die(fd, buffer, n);
|
||||
}
|
||||
|
||||
void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
|
||||
|
@ -135,13 +104,29 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
|
|||
strbuf_add(buf, buffer, n);
|
||||
}
|
||||
|
||||
static int safe_read(int fd, void *buffer, unsigned size, int return_line_fail)
|
||||
static int get_packet_data(int fd, char **src_buf, size_t *src_size,
|
||||
void *dst, unsigned size, int options)
|
||||
{
|
||||
ssize_t ret = read_in_full(fd, buffer, size);
|
||||
if (ret < 0)
|
||||
die_errno("read error");
|
||||
else if (ret < size) {
|
||||
if (return_line_fail)
|
||||
ssize_t ret;
|
||||
|
||||
if (fd >= 0 && src_buf && *src_buf)
|
||||
die("BUG: multiple sources given to packet_read");
|
||||
|
||||
/* Read up to "size" bytes from our source, whatever it is. */
|
||||
if (src_buf && *src_buf) {
|
||||
ret = size < *src_size ? size : *src_size;
|
||||
memcpy(dst, *src_buf, ret);
|
||||
*src_buf += ret;
|
||||
*src_size -= ret;
|
||||
} else {
|
||||
ret = read_in_full(fd, dst, size);
|
||||
if (ret < 0)
|
||||
die_errno("read error");
|
||||
}
|
||||
|
||||
/* And complain if we didn't get enough bytes to satisfy the read. */
|
||||
if (ret < size) {
|
||||
if (options & PACKET_READ_GENTLE_ON_EOF)
|
||||
return -1;
|
||||
|
||||
die("The remote end hung up unexpectedly");
|
||||
|
@ -175,13 +160,14 @@ static int packet_length(const char *linelen)
|
|||
return len;
|
||||
}
|
||||
|
||||
static int packet_read_internal(int fd, char *buffer, unsigned size, int return_line_fail)
|
||||
int packet_read(int fd, char **src_buf, size_t *src_len,
|
||||
char *buffer, unsigned size, int options)
|
||||
{
|
||||
int len, ret;
|
||||
char linelen[4];
|
||||
|
||||
ret = safe_read(fd, linelen, 4, return_line_fail);
|
||||
if (return_line_fail && ret < 0)
|
||||
ret = get_packet_data(fd, src_buf, src_len, linelen, 4, options);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
len = packet_length(linelen);
|
||||
if (len < 0)
|
||||
|
@ -193,50 +179,37 @@ static int packet_read_internal(int fd, char *buffer, unsigned size, int return_
|
|||
len -= 4;
|
||||
if (len >= size)
|
||||
die("protocol error: bad line length %d", len);
|
||||
ret = safe_read(fd, buffer, len, return_line_fail);
|
||||
if (return_line_fail && ret < 0)
|
||||
ret = get_packet_data(fd, src_buf, src_len, buffer, len, options);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((options & PACKET_READ_CHOMP_NEWLINE) &&
|
||||
len && buffer[len-1] == '\n')
|
||||
len--;
|
||||
|
||||
buffer[len] = 0;
|
||||
packet_trace(buffer, len, 0);
|
||||
return len;
|
||||
}
|
||||
|
||||
int packet_read(int fd, char *buffer, unsigned size)
|
||||
static char *packet_read_line_generic(int fd,
|
||||
char **src, size_t *src_len,
|
||||
int *dst_len)
|
||||
{
|
||||
return packet_read_internal(fd, buffer, size, 1);
|
||||
int len = packet_read(fd, src, src_len,
|
||||
packet_buffer, sizeof(packet_buffer),
|
||||
PACKET_READ_CHOMP_NEWLINE);
|
||||
if (dst_len)
|
||||
*dst_len = len;
|
||||
return len ? packet_buffer : NULL;
|
||||
}
|
||||
|
||||
int packet_read_line(int fd, char *buffer, unsigned size)
|
||||
char *packet_read_line(int fd, int *len_p)
|
||||
{
|
||||
return packet_read_internal(fd, buffer, size, 0);
|
||||
return packet_read_line_generic(fd, NULL, NULL, len_p);
|
||||
}
|
||||
|
||||
int packet_get_line(struct strbuf *out,
|
||||
char **src_buf, size_t *src_len)
|
||||
char *packet_read_line_buf(char **src, size_t *src_len, int *dst_len)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (*src_len < 4)
|
||||
return -1;
|
||||
len = packet_length(*src_buf);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
if (!len) {
|
||||
*src_buf += 4;
|
||||
*src_len -= 4;
|
||||
packet_trace("0000", 4, 0);
|
||||
return 0;
|
||||
}
|
||||
if (*src_len < len)
|
||||
return -2;
|
||||
|
||||
*src_buf += 4;
|
||||
*src_len -= 4;
|
||||
len -= 4;
|
||||
|
||||
strbuf_add(out, *src_buf, len);
|
||||
*src_buf += len;
|
||||
*src_len -= len;
|
||||
packet_trace(out->buf, out->len, 0);
|
||||
return len;
|
||||
return packet_read_line_generic(-1, src, src_len, dst_len);
|
||||
}
|
||||
|
|
72
pkt-line.h
72
pkt-line.h
|
@ -5,16 +5,78 @@
|
|||
#include "strbuf.h"
|
||||
|
||||
/*
|
||||
* Silly packetized line writing interface
|
||||
* Write a packetized stream, where each line is preceded by
|
||||
* its length (including the header) as a 4-byte hex number.
|
||||
* A length of 'zero' means end of stream (and a length of 1-3
|
||||
* would be an error).
|
||||
*
|
||||
* This is all pretty stupid, but we use this packetized line
|
||||
* format to make a streaming format possible without ever
|
||||
* over-running the read buffers. That way we'll never read
|
||||
* into what might be the pack data (which should go to another
|
||||
* process entirely).
|
||||
*
|
||||
* The writing side could use stdio, but since the reading
|
||||
* side can't, we stay with pure read/write interfaces.
|
||||
*/
|
||||
void packet_flush(int fd);
|
||||
void packet_write(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
|
||||
void packet_buf_flush(struct strbuf *buf);
|
||||
void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
|
||||
|
||||
int packet_read_line(int fd, char *buffer, unsigned size);
|
||||
int packet_read(int fd, char *buffer, unsigned size);
|
||||
int packet_get_line(struct strbuf *out, char **src_buf, size_t *src_len);
|
||||
ssize_t safe_write(int, const void *, ssize_t);
|
||||
/*
|
||||
* Read a packetized line into the buffer, which must be at least size bytes
|
||||
* long. The return value specifies the number of bytes read into the buffer.
|
||||
*
|
||||
* If src_buffer is not NULL (and nor is *src_buffer), it should point to a
|
||||
* buffer containing the packet data to parse, of at least *src_len bytes.
|
||||
* After the function returns, src_buf will be incremented and src_len
|
||||
* decremented by the number of bytes consumed.
|
||||
*
|
||||
* If src_buffer (or *src_buffer) is NULL, then data is read from the
|
||||
* descriptor "fd".
|
||||
*
|
||||
* If options does not contain PACKET_READ_GENTLE_ON_EOF, we will die under any
|
||||
* of the following conditions:
|
||||
*
|
||||
* 1. Read error from descriptor.
|
||||
*
|
||||
* 2. Protocol error from the remote (e.g., bogus length characters).
|
||||
*
|
||||
* 3. Receiving a packet larger than "size" bytes.
|
||||
*
|
||||
* 4. Truncated output from the remote (e.g., we expected a packet but got
|
||||
* EOF, or we got a partial packet followed by EOF).
|
||||
*
|
||||
* If options does contain PACKET_READ_GENTLE_ON_EOF, we will not die on
|
||||
* condition 4 (truncated input), but instead return -1. However, we will still
|
||||
* die for the other 3 conditions.
|
||||
*
|
||||
* If options contains PACKET_READ_CHOMP_NEWLINE, a trailing newline (if
|
||||
* present) is removed from the buffer before returning.
|
||||
*/
|
||||
#define PACKET_READ_GENTLE_ON_EOF (1u<<0)
|
||||
#define PACKET_READ_CHOMP_NEWLINE (1u<<1)
|
||||
int packet_read(int fd, char **src_buffer, size_t *src_len, char
|
||||
*buffer, unsigned size, int options);
|
||||
|
||||
/*
|
||||
* Convenience wrapper for packet_read that is not gentle, and sets the
|
||||
* CHOMP_NEWLINE option. The return value is NULL for a flush packet,
|
||||
* and otherwise points to a static buffer (that may be overwritten by
|
||||
* subsequent calls). If the size parameter is not NULL, the length of the
|
||||
* packet is written to it.
|
||||
*/
|
||||
char *packet_read_line(int fd, int *size);
|
||||
|
||||
/*
|
||||
* Same as packet_read_line, but read from a buf rather than a descriptor;
|
||||
* see packet_read for details on how src_* is used.
|
||||
*/
|
||||
char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size);
|
||||
|
||||
#define DEFAULT_PACKET_MAX 1000
|
||||
#define LARGE_PACKET_MAX 65520
|
||||
extern char packet_buffer[LARGE_PACKET_MAX];
|
||||
|
||||
#endif
|
||||
|
|
238
remote-curl.c
238
remote-curl.c
|
@ -76,129 +76,16 @@ struct discovery {
|
|||
char *buf_alloc;
|
||||
char *buf;
|
||||
size_t len;
|
||||
struct ref *refs;
|
||||
unsigned proto_git : 1;
|
||||
};
|
||||
static struct discovery *last_discovery;
|
||||
|
||||
static void free_discovery(struct discovery *d)
|
||||
{
|
||||
if (d) {
|
||||
if (d == last_discovery)
|
||||
last_discovery = NULL;
|
||||
free(d->buf_alloc);
|
||||
free(d);
|
||||
}
|
||||
}
|
||||
|
||||
static struct discovery* discover_refs(const char *service)
|
||||
{
|
||||
struct strbuf exp = STRBUF_INIT;
|
||||
struct strbuf type = STRBUF_INIT;
|
||||
struct strbuf buffer = STRBUF_INIT;
|
||||
struct discovery *last = last_discovery;
|
||||
char *refs_url;
|
||||
int http_ret, maybe_smart = 0;
|
||||
|
||||
if (last && !strcmp(service, last->service))
|
||||
return last;
|
||||
free_discovery(last);
|
||||
|
||||
strbuf_addf(&buffer, "%sinfo/refs", url);
|
||||
if ((!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) &&
|
||||
git_env_bool("GIT_SMART_HTTP", 1)) {
|
||||
maybe_smart = 1;
|
||||
if (!strchr(url, '?'))
|
||||
strbuf_addch(&buffer, '?');
|
||||
else
|
||||
strbuf_addch(&buffer, '&');
|
||||
strbuf_addf(&buffer, "service=%s", service);
|
||||
}
|
||||
refs_url = strbuf_detach(&buffer, NULL);
|
||||
|
||||
http_ret = http_get_strbuf(refs_url, &type, &buffer, HTTP_NO_CACHE);
|
||||
switch (http_ret) {
|
||||
case HTTP_OK:
|
||||
break;
|
||||
case HTTP_MISSING_TARGET:
|
||||
die("%s not found: did you run git update-server-info on the"
|
||||
" server?", refs_url);
|
||||
case HTTP_NOAUTH:
|
||||
die("Authentication failed");
|
||||
default:
|
||||
http_error(refs_url, http_ret);
|
||||
die("HTTP request failed");
|
||||
}
|
||||
|
||||
last= xcalloc(1, sizeof(*last_discovery));
|
||||
last->service = service;
|
||||
last->buf_alloc = strbuf_detach(&buffer, &last->len);
|
||||
last->buf = last->buf_alloc;
|
||||
|
||||
strbuf_addf(&exp, "application/x-%s-advertisement", service);
|
||||
if (maybe_smart &&
|
||||
(5 <= last->len && last->buf[4] == '#') &&
|
||||
!strbuf_cmp(&exp, &type)) {
|
||||
/*
|
||||
* smart HTTP response; validate that the service
|
||||
* pkt-line matches our request.
|
||||
*/
|
||||
if (packet_get_line(&buffer, &last->buf, &last->len) <= 0)
|
||||
die("%s has invalid packet header", refs_url);
|
||||
if (buffer.len && buffer.buf[buffer.len - 1] == '\n')
|
||||
strbuf_setlen(&buffer, buffer.len - 1);
|
||||
|
||||
strbuf_reset(&exp);
|
||||
strbuf_addf(&exp, "# service=%s", service);
|
||||
if (strbuf_cmp(&exp, &buffer))
|
||||
die("invalid server response; got '%s'", buffer.buf);
|
||||
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.
|
||||
*/
|
||||
strbuf_reset(&buffer);
|
||||
while (packet_get_line(&buffer, &last->buf, &last->len) > 0)
|
||||
strbuf_reset(&buffer);
|
||||
|
||||
last->proto_git = 1;
|
||||
}
|
||||
|
||||
free(refs_url);
|
||||
strbuf_release(&exp);
|
||||
strbuf_release(&type);
|
||||
strbuf_release(&buffer);
|
||||
last_discovery = last;
|
||||
return last;
|
||||
}
|
||||
|
||||
static int write_discovery(int in, int out, void *data)
|
||||
{
|
||||
struct discovery *heads = data;
|
||||
int err = 0;
|
||||
if (write_in_full(out, heads->buf, heads->len) != heads->len)
|
||||
err = 1;
|
||||
close(out);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct ref *parse_git_refs(struct discovery *heads, int for_push)
|
||||
{
|
||||
struct ref *list = NULL;
|
||||
struct async async;
|
||||
|
||||
memset(&async, 0, sizeof(async));
|
||||
async.proc = write_discovery;
|
||||
async.data = heads;
|
||||
async.out = -1;
|
||||
|
||||
if (start_async(&async))
|
||||
die("cannot start thread to parse advertised refs");
|
||||
get_remote_heads(async.out, &list,
|
||||
for_push ? REF_NORMAL : 0, NULL);
|
||||
close(async.out);
|
||||
if (finish_async(&async))
|
||||
die("ref parsing thread failed");
|
||||
get_remote_heads(-1, heads->buf, heads->len, &list,
|
||||
for_push ? REF_NORMAL : 0, NULL);
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -253,18 +140,112 @@ static struct ref *parse_info_refs(struct discovery *heads)
|
|||
return refs;
|
||||
}
|
||||
|
||||
static void free_discovery(struct discovery *d)
|
||||
{
|
||||
if (d) {
|
||||
if (d == last_discovery)
|
||||
last_discovery = NULL;
|
||||
free(d->buf_alloc);
|
||||
free_refs(d->refs);
|
||||
free(d);
|
||||
}
|
||||
}
|
||||
|
||||
static struct discovery* discover_refs(const char *service, int for_push)
|
||||
{
|
||||
struct strbuf exp = STRBUF_INIT;
|
||||
struct strbuf type = STRBUF_INIT;
|
||||
struct strbuf buffer = STRBUF_INIT;
|
||||
struct discovery *last = last_discovery;
|
||||
char *refs_url;
|
||||
int http_ret, maybe_smart = 0;
|
||||
|
||||
if (last && !strcmp(service, last->service))
|
||||
return last;
|
||||
free_discovery(last);
|
||||
|
||||
strbuf_addf(&buffer, "%sinfo/refs", url);
|
||||
if ((!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) &&
|
||||
git_env_bool("GIT_SMART_HTTP", 1)) {
|
||||
maybe_smart = 1;
|
||||
if (!strchr(url, '?'))
|
||||
strbuf_addch(&buffer, '?');
|
||||
else
|
||||
strbuf_addch(&buffer, '&');
|
||||
strbuf_addf(&buffer, "service=%s", service);
|
||||
}
|
||||
refs_url = strbuf_detach(&buffer, NULL);
|
||||
|
||||
http_ret = http_get_strbuf(refs_url, &type, &buffer, HTTP_NO_CACHE);
|
||||
switch (http_ret) {
|
||||
case HTTP_OK:
|
||||
break;
|
||||
case HTTP_MISSING_TARGET:
|
||||
die("%s not found: did you run git update-server-info on the"
|
||||
" server?", refs_url);
|
||||
case HTTP_NOAUTH:
|
||||
die("Authentication failed");
|
||||
default:
|
||||
http_error(refs_url, http_ret);
|
||||
die("HTTP request failed");
|
||||
}
|
||||
|
||||
last= xcalloc(1, sizeof(*last_discovery));
|
||||
last->service = service;
|
||||
last->buf_alloc = strbuf_detach(&buffer, &last->len);
|
||||
last->buf = last->buf_alloc;
|
||||
|
||||
strbuf_addf(&exp, "application/x-%s-advertisement", service);
|
||||
if (maybe_smart &&
|
||||
(5 <= last->len && last->buf[4] == '#') &&
|
||||
!strbuf_cmp(&exp, &type)) {
|
||||
char *line;
|
||||
|
||||
/*
|
||||
* smart HTTP response; validate that the service
|
||||
* pkt-line matches our request.
|
||||
*/
|
||||
line = packet_read_line_buf(&last->buf, &last->len, NULL);
|
||||
|
||||
strbuf_reset(&exp);
|
||||
strbuf_addf(&exp, "# service=%s", service);
|
||||
if (strcmp(line, exp.buf))
|
||||
die("invalid server response; got '%s'", 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))
|
||||
;
|
||||
|
||||
last->proto_git = 1;
|
||||
}
|
||||
|
||||
if (last->proto_git)
|
||||
last->refs = parse_git_refs(last, for_push);
|
||||
else
|
||||
last->refs = parse_info_refs(last);
|
||||
|
||||
free(refs_url);
|
||||
strbuf_release(&exp);
|
||||
strbuf_release(&type);
|
||||
strbuf_release(&buffer);
|
||||
last_discovery = last;
|
||||
return last;
|
||||
}
|
||||
|
||||
static struct ref *get_refs(int for_push)
|
||||
{
|
||||
struct discovery *heads;
|
||||
|
||||
if (for_push)
|
||||
heads = discover_refs("git-receive-pack");
|
||||
heads = discover_refs("git-receive-pack", for_push);
|
||||
else
|
||||
heads = discover_refs("git-upload-pack");
|
||||
heads = discover_refs("git-upload-pack", for_push);
|
||||
|
||||
if (heads->proto_git)
|
||||
return parse_git_refs(heads, for_push);
|
||||
return parse_info_refs(heads);
|
||||
return heads->refs;
|
||||
}
|
||||
|
||||
static void output_refs(struct ref *refs)
|
||||
|
@ -278,7 +259,6 @@ static void output_refs(struct ref *refs)
|
|||
}
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
free_refs(refs);
|
||||
}
|
||||
|
||||
struct rpc_state {
|
||||
|
@ -308,7 +288,7 @@ static size_t rpc_out(void *ptr, size_t eltsize,
|
|||
|
||||
if (!avail) {
|
||||
rpc->initial_buffer = 0;
|
||||
avail = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
|
||||
avail = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
|
||||
if (!avail)
|
||||
return 0;
|
||||
rpc->pos = 0;
|
||||
|
@ -425,7 +405,7 @@ static int post_rpc(struct rpc_state *rpc)
|
|||
break;
|
||||
}
|
||||
|
||||
n = packet_read_line(rpc->out, buf, left);
|
||||
n = packet_read(rpc->out, NULL, NULL, buf, left, 0);
|
||||
if (!n)
|
||||
break;
|
||||
rpc->len += n;
|
||||
|
@ -579,7 +559,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
|
|||
rpc->hdr_accept = strbuf_detach(&buf, NULL);
|
||||
|
||||
while (!err) {
|
||||
int n = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
|
||||
int n = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
|
||||
if (!n)
|
||||
break;
|
||||
rpc->pos = 0;
|
||||
|
@ -685,7 +665,7 @@ static int fetch_git(struct discovery *heads,
|
|||
|
||||
err = rpc_service(&rpc, heads);
|
||||
if (rpc.result.len)
|
||||
safe_write(1, rpc.result.buf, rpc.result.len);
|
||||
write_or_die(1, rpc.result.buf, rpc.result.len);
|
||||
strbuf_release(&rpc.result);
|
||||
strbuf_release(&preamble);
|
||||
free(depth_arg);
|
||||
|
@ -694,7 +674,7 @@ static int fetch_git(struct discovery *heads,
|
|||
|
||||
static int fetch(int nr_heads, struct ref **to_fetch)
|
||||
{
|
||||
struct discovery *d = discover_refs("git-upload-pack");
|
||||
struct discovery *d = discover_refs("git-upload-pack", 0);
|
||||
if (d->proto_git)
|
||||
return fetch_git(d, nr_heads, to_fetch);
|
||||
else
|
||||
|
@ -805,7 +785,7 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
|
|||
|
||||
err = rpc_service(&rpc, heads);
|
||||
if (rpc.result.len)
|
||||
safe_write(1, rpc.result.buf, rpc.result.len);
|
||||
write_or_die(1, rpc.result.buf, rpc.result.len);
|
||||
strbuf_release(&rpc.result);
|
||||
free(argv);
|
||||
return err;
|
||||
|
@ -813,7 +793,7 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
|
|||
|
||||
static int push(int nr_spec, char **specs)
|
||||
{
|
||||
struct discovery *heads = discover_refs("git-receive-pack");
|
||||
struct discovery *heads = discover_refs("git-receive-pack", 1);
|
||||
int ret;
|
||||
|
||||
if (heads->proto_git)
|
||||
|
|
22
send-pack.c
22
send-pack.c
|
@ -106,15 +106,11 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
|
|||
static int receive_status(int in, struct ref *refs)
|
||||
{
|
||||
struct ref *hint;
|
||||
char line[1000];
|
||||
int ret = 0;
|
||||
int len = packet_read_line(in, line, sizeof(line));
|
||||
if (len < 10 || memcmp(line, "unpack ", 7))
|
||||
char *line = packet_read_line(in, NULL);
|
||||
if (prefixcmp(line, "unpack "))
|
||||
return error("did not receive remote status");
|
||||
if (memcmp(line, "unpack ok\n", 10)) {
|
||||
char *p = line + strlen(line) - 1;
|
||||
if (*p == '\n')
|
||||
*p = '\0';
|
||||
if (strcmp(line, "unpack ok")) {
|
||||
error("unpack failed: %s", line + 7);
|
||||
ret = -1;
|
||||
}
|
||||
|
@ -122,17 +118,15 @@ static int receive_status(int in, struct ref *refs)
|
|||
while (1) {
|
||||
char *refname;
|
||||
char *msg;
|
||||
len = packet_read_line(in, line, sizeof(line));
|
||||
if (!len)
|
||||
line = packet_read_line(in, NULL);
|
||||
if (!line)
|
||||
break;
|
||||
if (len < 3 ||
|
||||
(memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) {
|
||||
fprintf(stderr, "protocol error: %s\n", line);
|
||||
if (prefixcmp(line, "ok ") && prefixcmp(line, "ng ")) {
|
||||
error("invalid ref status from remote: %s", line);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
line[strlen(line)-1] = '\0';
|
||||
refname = line + 3;
|
||||
msg = strchr(refname, ' ');
|
||||
if (msg)
|
||||
|
@ -281,7 +275,7 @@ int send_pack(struct send_pack_args *args,
|
|||
send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
|
||||
}
|
||||
} else {
|
||||
safe_write(out, req_buf.buf, req_buf.len);
|
||||
write_or_die(out, req_buf.buf, req_buf.len);
|
||||
packet_flush(out);
|
||||
}
|
||||
strbuf_release(&req_buf);
|
||||
|
|
11
sideband.c
11
sideband.c
|
@ -1,3 +1,4 @@
|
|||
#include "cache.h"
|
||||
#include "pkt-line.h"
|
||||
#include "sideband.h"
|
||||
|
||||
|
@ -37,7 +38,7 @@ int recv_sideband(const char *me, int in_stream, int out)
|
|||
|
||||
while (1) {
|
||||
int band, len;
|
||||
len = packet_read_line(in_stream, buf + pf, LARGE_PACKET_MAX);
|
||||
len = packet_read(in_stream, NULL, NULL, buf + pf, LARGE_PACKET_MAX, 0);
|
||||
if (len == 0)
|
||||
break;
|
||||
if (len < 1) {
|
||||
|
@ -108,7 +109,7 @@ int recv_sideband(const char *me, int in_stream, int out)
|
|||
} while (len);
|
||||
continue;
|
||||
case 1:
|
||||
safe_write(out, buf + pf+1, len);
|
||||
write_or_die(out, buf + pf+1, len);
|
||||
continue;
|
||||
default:
|
||||
fprintf(stderr, "%s: protocol error: bad band #%d\n",
|
||||
|
@ -138,12 +139,12 @@ ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet
|
|||
if (0 <= band) {
|
||||
sprintf(hdr, "%04x", n + 5);
|
||||
hdr[4] = band;
|
||||
safe_write(fd, hdr, 5);
|
||||
write_or_die(fd, hdr, 5);
|
||||
} else {
|
||||
sprintf(hdr, "%04x", n + 4);
|
||||
safe_write(fd, hdr, 4);
|
||||
write_or_die(fd, hdr, 4);
|
||||
}
|
||||
safe_write(fd, p, n);
|
||||
write_or_die(fd, p, n);
|
||||
p += n;
|
||||
sz -= n;
|
||||
}
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
#define SIDEBAND_PROTOCOL_ERROR -2
|
||||
#define SIDEBAND_REMOTE_ERROR -1
|
||||
|
||||
#define DEFAULT_PACKET_MAX 1000
|
||||
#define LARGE_PACKET_MAX 65520
|
||||
|
||||
int recv_sideband(const char *me, int in_stream, int out);
|
||||
ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
|
||||
|
||||
|
|
|
@ -4,10 +4,6 @@ test_description='test automatic tag following'
|
|||
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq NOT_MINGW; then
|
||||
say "GIT_DEBUG_SEND_PACK not supported - skipping tests"
|
||||
fi
|
||||
|
||||
# End state of the repository:
|
||||
#
|
||||
# T - tag1 S - tag2
|
||||
|
@ -17,7 +13,7 @@ fi
|
|||
# \ C - origin/cat \
|
||||
# origin/master master
|
||||
|
||||
test_expect_success NOT_MINGW setup '
|
||||
test_expect_success setup '
|
||||
test_tick &&
|
||||
echo ichi >file &&
|
||||
git add file &&
|
||||
|
@ -39,28 +35,35 @@ test_expect_success NOT_MINGW setup '
|
|||
'
|
||||
|
||||
U=UPLOAD_LOG
|
||||
UPATH="$(pwd)/$U"
|
||||
|
||||
test_expect_success NOT_MINGW 'setup expect' '
|
||||
test_expect_success 'setup expect' '
|
||||
cat - <<EOF >expect
|
||||
#S
|
||||
want $A
|
||||
#E
|
||||
EOF
|
||||
'
|
||||
|
||||
test_expect_success NOT_MINGW 'fetch A (new commit : 1 connection)' '
|
||||
get_needs () {
|
||||
test -s "$1" &&
|
||||
perl -alne '
|
||||
next unless $F[1] eq "upload-pack<";
|
||||
last if $F[2] eq "0000";
|
||||
print $F[2], " ", $F[3];
|
||||
' "$1"
|
||||
}
|
||||
|
||||
test_expect_success 'fetch A (new commit : 1 connection)' '
|
||||
rm -f $U &&
|
||||
(
|
||||
cd cloned &&
|
||||
GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
|
||||
GIT_TRACE_PACKET=$UPATH git fetch &&
|
||||
test $A = $(git rev-parse --verify origin/master)
|
||||
) &&
|
||||
test -s $U &&
|
||||
cut -d" " -f1,2 $U >actual &&
|
||||
get_needs $U >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success NOT_MINGW "create tag T on A, create C on branch cat" '
|
||||
test_expect_success "create tag T on A, create C on branch cat" '
|
||||
git tag -a -m tag1 tag1 $A &&
|
||||
T=$(git rev-parse --verify tag1) &&
|
||||
|
||||
|
@ -72,30 +75,27 @@ test_expect_success NOT_MINGW "create tag T on A, create C on branch cat" '
|
|||
git checkout master
|
||||
'
|
||||
|
||||
test_expect_success NOT_MINGW 'setup expect' '
|
||||
test_expect_success 'setup expect' '
|
||||
cat - <<EOF >expect
|
||||
#S
|
||||
want $C
|
||||
want $T
|
||||
#E
|
||||
EOF
|
||||
'
|
||||
|
||||
test_expect_success NOT_MINGW 'fetch C, T (new branch, tag : 1 connection)' '
|
||||
test_expect_success 'fetch C, T (new branch, tag : 1 connection)' '
|
||||
rm -f $U &&
|
||||
(
|
||||
cd cloned &&
|
||||
GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
|
||||
GIT_TRACE_PACKET=$UPATH git fetch &&
|
||||
test $C = $(git rev-parse --verify origin/cat) &&
|
||||
test $T = $(git rev-parse --verify tag1) &&
|
||||
test $A = $(git rev-parse --verify tag1^0)
|
||||
) &&
|
||||
test -s $U &&
|
||||
cut -d" " -f1,2 $U >actual &&
|
||||
get_needs $U >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success NOT_MINGW "create commits O, B, tag S on B" '
|
||||
test_expect_success "create commits O, B, tag S on B" '
|
||||
test_tick &&
|
||||
echo O >file &&
|
||||
git add file &&
|
||||
|
@ -111,39 +111,34 @@ test_expect_success NOT_MINGW "create commits O, B, tag S on B" '
|
|||
S=$(git rev-parse --verify tag2)
|
||||
'
|
||||
|
||||
test_expect_success NOT_MINGW 'setup expect' '
|
||||
test_expect_success 'setup expect' '
|
||||
cat - <<EOF >expect
|
||||
#S
|
||||
want $B
|
||||
want $S
|
||||
#E
|
||||
EOF
|
||||
'
|
||||
|
||||
test_expect_success NOT_MINGW 'fetch B, S (commit and tag : 1 connection)' '
|
||||
test_expect_success 'fetch B, S (commit and tag : 1 connection)' '
|
||||
rm -f $U &&
|
||||
(
|
||||
cd cloned &&
|
||||
GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
|
||||
GIT_TRACE_PACKET=$UPATH git fetch &&
|
||||
test $B = $(git rev-parse --verify origin/master) &&
|
||||
test $B = $(git rev-parse --verify tag2^0) &&
|
||||
test $S = $(git rev-parse --verify tag2)
|
||||
) &&
|
||||
test -s $U &&
|
||||
cut -d" " -f1,2 $U >actual &&
|
||||
get_needs $U >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success NOT_MINGW 'setup expect' '
|
||||
test_expect_success 'setup expect' '
|
||||
cat - <<EOF >expect
|
||||
#S
|
||||
want $B
|
||||
want $S
|
||||
#E
|
||||
EOF
|
||||
'
|
||||
|
||||
test_expect_success NOT_MINGW 'new clone fetch master and tags' '
|
||||
test_expect_success 'new clone fetch master and tags' '
|
||||
git branch -D cat
|
||||
rm -f $U
|
||||
(
|
||||
|
@ -151,15 +146,14 @@ test_expect_success NOT_MINGW 'new clone fetch master and tags' '
|
|||
cd clone2 &&
|
||||
git init &&
|
||||
git remote add origin .. &&
|
||||
GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
|
||||
GIT_TRACE_PACKET=$UPATH git fetch &&
|
||||
test $B = $(git rev-parse --verify origin/master) &&
|
||||
test $S = $(git rev-parse --verify tag2) &&
|
||||
test $B = $(git rev-parse --verify tag2^0) &&
|
||||
test $T = $(git rev-parse --verify tag1) &&
|
||||
test $A = $(git rev-parse --verify tag1^0)
|
||||
) &&
|
||||
test -s $U &&
|
||||
cut -d" " -f1,2 $U >actual &&
|
||||
get_needs $U >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
|
|
|
@ -54,11 +54,14 @@ cd "$base_dir"
|
|||
|
||||
rm -f "$U.D"
|
||||
|
||||
test_expect_success 'cloning with reference (no -l -s)' \
|
||||
'GIT_DEBUG_SEND_PACK=3 git clone --reference B "file://$(pwd)/A" D 3>"$U.D"'
|
||||
test_expect_success 'cloning with reference (no -l -s)' '
|
||||
GIT_TRACE_PACKET=$U.D git clone --reference B "file://$(pwd)/A" D
|
||||
'
|
||||
|
||||
test_expect_success 'fetched no objects' \
|
||||
'! grep "^want" "$U.D"'
|
||||
test_expect_success 'fetched no objects' '
|
||||
test -s "$U.D" &&
|
||||
! grep " want" "$U.D"
|
||||
'
|
||||
|
||||
cd "$base_dir"
|
||||
|
||||
|
@ -173,12 +176,13 @@ test_expect_success 'fetch with incomplete alternates' '
|
|||
(
|
||||
cd K &&
|
||||
git remote add J "file://$base_dir/J" &&
|
||||
GIT_DEBUG_SEND_PACK=3 git fetch J 3>"$U.K"
|
||||
GIT_TRACE_PACKET=$U.K git fetch J
|
||||
) &&
|
||||
master_object=$(cd A && git for-each-ref --format="%(objectname)" refs/heads/master) &&
|
||||
! grep "^want $master_object" "$U.K" &&
|
||||
test -s "$U.K" &&
|
||||
! grep " want $master_object" "$U.K" &&
|
||||
tag_object=$(cd A && git for-each-ref --format="%(objectname)" refs/tags/HEAD) &&
|
||||
! grep "^want $tag_object" "$U.K"
|
||||
! grep " want $tag_object" "$U.K"
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -508,7 +508,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
|
|||
struct ref *refs;
|
||||
|
||||
connect_setup(transport, for_push, 0);
|
||||
get_remote_heads(data->fd[0], &refs,
|
||||
get_remote_heads(data->fd[0], NULL, 0, &refs,
|
||||
for_push ? REF_NORMAL : 0, &data->extra_have);
|
||||
data->got_remote_heads = 1;
|
||||
|
||||
|
@ -537,7 +537,7 @@ static int fetch_refs_via_pack(struct transport *transport,
|
|||
|
||||
if (!data->got_remote_heads) {
|
||||
connect_setup(transport, 0, 0);
|
||||
get_remote_heads(data->fd[0], &refs_tmp, 0, NULL);
|
||||
get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0, NULL);
|
||||
data->got_remote_heads = 1;
|
||||
}
|
||||
|
||||
|
@ -795,7 +795,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
|
|||
struct ref *tmp_refs;
|
||||
connect_setup(transport, 1, 0);
|
||||
|
||||
get_remote_heads(data->fd[0], &tmp_refs, REF_NORMAL, NULL);
|
||||
get_remote_heads(data->fd[0], NULL, 0, &tmp_refs, REF_NORMAL, NULL);
|
||||
data->got_remote_heads = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,6 @@ static unsigned int timeout;
|
|||
* otherwise maximum packet size (up to 65520 bytes).
|
||||
*/
|
||||
static int use_sideband;
|
||||
static int debug_fd;
|
||||
static int advertise_refs;
|
||||
static int stateless_rpc;
|
||||
|
||||
|
@ -53,13 +52,6 @@ static void reset_timeout(void)
|
|||
alarm(timeout);
|
||||
}
|
||||
|
||||
static int strip(char *line, int len)
|
||||
{
|
||||
if (len && line[len-1] == '\n')
|
||||
line[--len] = 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
|
||||
{
|
||||
if (use_sideband)
|
||||
|
@ -72,7 +64,8 @@ static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
|
|||
xwrite(fd, data, sz);
|
||||
return sz;
|
||||
}
|
||||
return safe_write(fd, data, sz);
|
||||
write_or_die(fd, data, sz);
|
||||
return sz;
|
||||
}
|
||||
|
||||
static FILE *pack_pipe = NULL;
|
||||
|
@ -415,7 +408,6 @@ static int ok_to_give_up(void)
|
|||
|
||||
static int get_common_commits(void)
|
||||
{
|
||||
static char line[1000];
|
||||
unsigned char sha1[20];
|
||||
char last_hex[41];
|
||||
int got_common = 0;
|
||||
|
@ -425,10 +417,10 @@ static int get_common_commits(void)
|
|||
save_commit_buffer = 0;
|
||||
|
||||
for (;;) {
|
||||
int len = packet_read_line(0, line, sizeof(line));
|
||||
char *line = packet_read_line(0, NULL);
|
||||
reset_timeout();
|
||||
|
||||
if (!len) {
|
||||
if (!line) {
|
||||
if (multi_ack == 2 && got_common
|
||||
&& !got_other && ok_to_give_up()) {
|
||||
sent_ready = 1;
|
||||
|
@ -447,7 +439,6 @@ static int get_common_commits(void)
|
|||
got_other = 0;
|
||||
continue;
|
||||
}
|
||||
strip(line, len);
|
||||
if (!prefixcmp(line, "have ")) {
|
||||
switch (got_sha1(line+5, sha1)) {
|
||||
case -1: /* they have what we do not */
|
||||
|
@ -581,36 +572,33 @@ error:
|
|||
static void receive_needs(void)
|
||||
{
|
||||
struct object_array shallows = OBJECT_ARRAY_INIT;
|
||||
static char line[1000];
|
||||
int len, depth = 0;
|
||||
int depth = 0;
|
||||
int has_non_tip = 0;
|
||||
|
||||
shallow_nr = 0;
|
||||
if (debug_fd)
|
||||
write_str_in_full(debug_fd, "#S\n");
|
||||
for (;;) {
|
||||
struct object *o;
|
||||
const char *features;
|
||||
unsigned char sha1_buf[20];
|
||||
len = packet_read_line(0, line, sizeof(line));
|
||||
char *line = packet_read_line(0, NULL);
|
||||
reset_timeout();
|
||||
if (!len)
|
||||
if (!line)
|
||||
break;
|
||||
if (debug_fd)
|
||||
write_in_full(debug_fd, line, len);
|
||||
|
||||
if (!prefixcmp(line, "shallow ")) {
|
||||
unsigned char sha1[20];
|
||||
struct object *object;
|
||||
if (get_sha1(line + 8, sha1))
|
||||
if (get_sha1_hex(line + 8, sha1))
|
||||
die("invalid shallow line: %s", line);
|
||||
object = parse_object(sha1);
|
||||
if (!object)
|
||||
die("did not find object for %s", line);
|
||||
if (object->type != OBJ_COMMIT)
|
||||
die("invalid shallow object %s", sha1_to_hex(sha1));
|
||||
object->flags |= CLIENT_SHALLOW;
|
||||
add_object_array(object, NULL, &shallows);
|
||||
if (!(object->flags & CLIENT_SHALLOW)) {
|
||||
object->flags |= CLIENT_SHALLOW;
|
||||
add_object_array(object, NULL, &shallows);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(line, "deepen ")) {
|
||||
|
@ -657,8 +645,6 @@ static void receive_needs(void)
|
|||
add_object_array(o, NULL, &want_obj);
|
||||
}
|
||||
}
|
||||
if (debug_fd)
|
||||
write_str_in_full(debug_fd, "#E\n");
|
||||
|
||||
/*
|
||||
* We have sent all our refs already, and the other end
|
||||
|
@ -854,8 +840,6 @@ int main(int argc, char **argv)
|
|||
if (is_repository_shallow())
|
||||
die("attempt to fetch/clone from a shallow repository");
|
||||
git_config(upload_pack_config, NULL);
|
||||
if (getenv("GIT_DEBUG_SEND_PACK"))
|
||||
debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK"));
|
||||
upload_pack();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
#include "cache.h"
|
||||
|
||||
static void check_pipe(int err)
|
||||
{
|
||||
if (err == EPIPE) {
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
raise(SIGPIPE);
|
||||
/* Should never happen, but just in case... */
|
||||
exit(141);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Some cases use stdio, but want to flush after the write
|
||||
* to get error handling (and to get better interactive
|
||||
|
@ -34,8 +44,7 @@ void maybe_flush_or_die(FILE *f, const char *desc)
|
|||
return;
|
||||
}
|
||||
if (fflush(f)) {
|
||||
if (errno == EPIPE)
|
||||
exit(0);
|
||||
check_pipe(errno);
|
||||
die_errno("write failure on '%s'", desc);
|
||||
}
|
||||
}
|
||||
|
@ -50,8 +59,7 @@ void fsync_or_die(int fd, const char *msg)
|
|||
void write_or_die(int fd, const void *buf, size_t count)
|
||||
{
|
||||
if (write_in_full(fd, buf, count) < 0) {
|
||||
if (errno == EPIPE)
|
||||
exit(0);
|
||||
check_pipe(errno);
|
||||
die_errno("write error");
|
||||
}
|
||||
}
|
||||
|
@ -59,8 +67,7 @@ void write_or_die(int fd, const void *buf, size_t count)
|
|||
int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg)
|
||||
{
|
||||
if (write_in_full(fd, buf, count) < 0) {
|
||||
if (errno == EPIPE)
|
||||
exit(0);
|
||||
check_pipe(errno);
|
||||
fprintf(stderr, "%s: write error (%s)\n",
|
||||
msg, strerror(errno));
|
||||
return 0;
|
||||
|
|
Загрузка…
Ссылка в новой задаче