From 90cff968b3757061914f83b85a8d58cb9f29e72a Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sat, 26 Mar 2011 19:46:34 +0100 Subject: [PATCH 1/5] HOME must be set before calling git-init when creating test repositories Otherwise the created test repositories will be affected by users ~/.gitconfig. For example, setting core.logAllrefupdates in users config will make all calls to "git config --unset core.logAllrefupdates" fail which will break the first test which uses the statement and expects it to succeed. Signed-off-by: Alex Riesen Acked-by: Jeff King Signed-off-by: Junio C Hamano --- t/test-lib.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index 0fdc541a7c..c91e232437 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -1004,14 +1004,14 @@ rm -fr "$test" || { exit 1 } +HOME="$TRASH_DIRECTORY" +export HOME + test_create_repo "$test" # Use -P to resolve symlinks in our working directory so that the cwd # in subprocesses like git equals our $PWD (for pathname comparisons). cd -P "$test" || exit 1 -HOME=$(pwd) -export HOME - this_test=${0##*/} this_test=${this_test%%-*} for skp in $GIT_SKIP_TESTS From 8e9182e09127b20b8d8ce19655037991feac798d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Mar 2011 10:16:29 -0700 Subject: [PATCH 2/5] enable "no-done" extension only when fetching over smart-http When 'no-done' protocol extension is used, the upload-pack (i.e. the server side) process stops listening to the fetch-pack after issuing the final NAK, and starts sending the generated pack data back, but there may be more "have" send by the latter in flight that the fetch-pack is expecting to be responded with ACK/NAK. This will typically result in a deadlock (both will block on write that the other end never reads) or SIGPIPE on the fetch-pack end (upload-pack will finish writing a small pack and goes away). Disable it unless fetch-pack is running under smart-http, where there is no such streaming issue. Signed-off-by: Junio C Hamano Acked-by: Shawn O. Pearce --- builtin/fetch-pack.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 59fbda522b..52707a80ad 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -708,7 +708,8 @@ static struct ref *do_fetch_pack(int fd[2], if (server_supports("no-done")) { if (args.verbose) fprintf(stderr, "Server supports no-done\n"); - no_done = 1; + if (args.stateless_rpc) + no_done = 1; } } else if (server_supports("multi_ack")) { From 44d8dc54e73e8010c4bdf57a422fc8d5ce709029 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Mar 2011 10:06:19 -0700 Subject: [PATCH 3/5] Fix potential local deadlock during fetch-pack The fetch-pack/upload-pack protocol relies on the underlying transport (local pipe or TCP socket) to have enough slack to allow one window worth of data in flight without blocking the writer. Traditionally we always relied on being able to have two windows of 32 "have"s in flight (roughly 3k bytes) to stream. The recent "progressive-stride" change allows "fetch-pack" to send up to 1024 "have"s without reading any response from "upload-pack". The outgoing pipe of "upload-pack" can be clogged with many ACK and NAK that are unread, while "fetch-pack" is still stuffing its outgoing pipe with more "have"s, leading to a deadlock. Revert the change unless we are in stateless rpc (aka smart-http) mode, as using a large window full of "have"s is still a good way to help reduce the number of back-and-forth, and there is no buffering issue there (it is strictly "ping-pong" without an overlap). Signed-off-by: Junio C Hamano --- builtin/fetch-pack.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 3c2c9406c4..147d67dca4 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -219,16 +219,17 @@ static void send_request(int fd, struct strbuf *buf) } #define INITIAL_FLUSH 16 +#define PIPESAFE_FLUSH 32 #define LARGE_FLUSH 1024 static int next_flush(int count) { - if (count < INITIAL_FLUSH * 2) - count += INITIAL_FLUSH; - else if (count < LARGE_FLUSH) + int flush_limit = args.stateless_rpc ? LARGE_FLUSH : PIPESAFE_FLUSH; + + if (count < flush_limit) count <<= 1; else - count += LARGE_FLUSH; + count += flush_limit; return count; } From cf2ad8e64175bcf4b2bb693a9e4c0a89076111dd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Mar 2011 10:24:59 -0700 Subject: [PATCH 4/5] enable "no-done" extension only when serving over smart-http Do not advertise no-done capability when upload-pack is not serving over smart-http, as there is no way for this server to know when it should stop reading in-flight data from the client, even though it is necessary to drain all the in-flight data in order to unblock the client. Signed-off-by: Junio C Hamano Acked-by: Shawn O. Pearce --- upload-pack.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/upload-pack.c b/upload-pack.c index 5924f6f988..a247fb9e27 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -640,15 +640,16 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo { static const char *capabilities = "multi_ack thin-pack side-band" " side-band-64k ofs-delta shallow no-progress" - " include-tag multi_ack_detailed no-done"; + " include-tag multi_ack_detailed"; struct object *o = parse_object(sha1); if (!o) die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1)); if (capabilities) - packet_write(1, "%s %s%c%s\n", sha1_to_hex(sha1), refname, - 0, capabilities); + packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname, + 0, capabilities, + stateless_rpc ? " no-done" : ""); else packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname); capabilities = NULL; From 4e10cf9a17467c08754b36683c240fbab69156de Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Mar 2011 12:29:10 -0700 Subject: [PATCH 5/5] Revert two "no-done" reverts Last night I had to make these two emergency reverts, but now we have a better understanding of which part of the topic was broken, let's get rid of the revert to fix it correctly. Signed-off-by: Junio C Hamano --- builtin/fetch-pack.c | 18 +++++++++++++++--- upload-pack.c | 20 ++++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 1724b76052..bf9990ce15 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -15,6 +15,7 @@ static int transfer_unpack_limit = -1; static int fetch_unpack_limit = -1; static int unpack_limit = 100; static int prefer_ofs_delta = 1; +static int no_done = 0; static struct fetch_pack_args args = { /* .uploadpack = */ "git-upload-pack", }; @@ -250,6 +251,7 @@ static int find_common(int fd[2], unsigned char *result_sha1, const unsigned char *sha1; unsigned in_vain = 0; int got_continue = 0; + int got_ready = 0; struct strbuf req_buf = STRBUF_INIT; size_t state_len = 0; @@ -288,6 +290,7 @@ static int find_common(int fd[2], unsigned char *result_sha1, struct strbuf c = STRBUF_INIT; if (multi_ack == 2) strbuf_addstr(&c, " multi_ack_detailed"); if (multi_ack == 1) strbuf_addstr(&c, " multi_ack"); + if (no_done) strbuf_addstr(&c, " no-done"); if (use_sideband == 2) strbuf_addstr(&c, " side-band-64k"); if (use_sideband == 1) strbuf_addstr(&c, " side-band"); if (args.use_thin_pack) strbuf_addstr(&c, " thin-pack"); @@ -406,8 +409,10 @@ static int find_common(int fd[2], unsigned char *result_sha1, retval = 0; in_vain = 0; got_continue = 1; - if (ack == ACK_ready) + if (ack == ACK_ready) { rev_list = NULL; + got_ready = 1; + } break; } } @@ -421,8 +426,10 @@ static int find_common(int fd[2], unsigned char *result_sha1, } } done: - packet_buf_write(&req_buf, "done\n"); - send_request(fd[1], &req_buf); + if (!got_ready || !no_done) { + packet_buf_write(&req_buf, "done\n"); + send_request(fd[1], &req_buf); + } if (args.verbose) fprintf(stderr, "done\n"); if (retval != 0) { @@ -725,6 +732,11 @@ static struct ref *do_fetch_pack(int fd[2], if (args.verbose) fprintf(stderr, "Server supports multi_ack_detailed\n"); multi_ack = 2; + if (server_supports("no-done")) { + if (args.verbose) + fprintf(stderr, "Server supports no-done\n"); + no_done = 1; + } } else if (server_supports("multi_ack")) { if (args.verbose) diff --git a/upload-pack.c b/upload-pack.c index eb80dd9aad..72aa661f8d 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -27,6 +27,7 @@ static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=< static unsigned long oldest_have; static int multi_ack, nr_our_refs; +static int no_done; static int use_thin_pack, use_ofs_delta, use_include_tag; static int no_progress, daemon_mode; static int shallow_nr; @@ -431,6 +432,7 @@ static int get_common_commits(void) char last_hex[41]; int got_common = 0; int got_other = 0; + int sent_ready = 0; save_commit_buffer = 0; @@ -440,10 +442,17 @@ static int get_common_commits(void) if (!len) { if (multi_ack == 2 && got_common - && !got_other && ok_to_give_up()) + && !got_other && ok_to_give_up()) { + sent_ready = 1; packet_write(1, "ACK %s ready\n", last_hex); + } if (have_obj.nr == 0 || multi_ack) packet_write(1, "NAK\n"); + + if (no_done && sent_ready) { + packet_write(1, "ACK %s\n", last_hex); + return 0; + } if (stateless_rpc) exit(0); got_common = 0; @@ -457,9 +466,10 @@ static int get_common_commits(void) got_other = 1; if (multi_ack && ok_to_give_up()) { const char *hex = sha1_to_hex(sha1); - if (multi_ack == 2) + if (multi_ack == 2) { + sent_ready = 1; packet_write(1, "ACK %s ready\n", hex); - else + } else packet_write(1, "ACK %s continue\n", hex); } break; @@ -535,6 +545,8 @@ static void receive_needs(void) multi_ack = 2; else if (strstr(line+45, "multi_ack")) multi_ack = 1; + if (strstr(line+45, "no-done")) + no_done = 1; if (strstr(line+45, "thin-pack")) use_thin_pack = 1; if (strstr(line+45, "ofs-delta")) @@ -628,7 +640,7 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo { static const char *capabilities = "multi_ack thin-pack side-band" " side-band-64k ofs-delta shallow no-progress" - " include-tag multi_ack_detailed"; + " include-tag multi_ack_detailed no-done"; struct object *o = parse_object(sha1); if (!o)