Commit 27b75514d9b1c8e9f188ef9c483760647a8c1b72

Stefan Sperling 2021-08-28T10:46:51

do not send a pack file when 'got send' is only deleting branches The git protocol spec says the client MUST NOT send a pack file if the only command used is 'delete'. Fixes 'got send -d' against Github's server which closed the session upon receiving the empty pack file we sent. This problem wasn't caught by regression tests since git-daemon does accept an empty pack file. Problem reported by jrick.

diff --git a/got/got.c b/got/got.c
index eaabe5b..f48598b 100644
--- a/got/got.c
+++ b/got/got.c
@@ -7432,12 +7432,15 @@ send_progress(void *arg, off_t packfile_size, int ncommits, int nobj_total,
 				if (got_path_cmp(branchname, refname,
 				    strlen(branchname), strlen(refname)) == 0) {
 					status = "deleted";
+					a->sent_something = 1;
 					break;
 				}
 			}
 		}
 
-		printf("\nServer has %s %s", status, refname);
+		if (a->printed_something)
+			putchar('\n');
+		printf("Server has %s %s", status, refname);
 		a->printed_something = 1;
 		return NULL;
 	}
diff --git a/lib/send.c b/lib/send.c
index 52892c3..110f62e 100644
--- a/lib/send.c
+++ b/lib/send.c
@@ -453,7 +453,7 @@ got_send_pack(const char *remote_name, struct got_pathlist_head *branch_names,
 	struct got_object_id *my_id = NULL;
 	int i, nours = 0, ntheirs = 0;
 	size_t nalloc_ours = 0, nalloc_theirs = 0;
-	int refs_to_send = 0;
+	int refs_to_send = 0, refs_to_delete = 0;
 	off_t bytes_sent = 0;
 	struct pack_progress_arg ppa;
 	uint8_t packsha1[SHA1_DIGEST_LENGTH];
@@ -746,37 +746,43 @@ got_send_pack(const char *remote_name, struct got_pathlist_head *branch_names,
 	TAILQ_FOREACH(pe, delete_branches, entry) {
 		const char *branchname = pe->path;
 		if (find_their_ref(&their_refs, branchname))
-			refs_to_send++;
+			refs_to_delete++;
 	}
 
-	if (refs_to_send == 0) {
+	if (refs_to_send == 0 && refs_to_delete == 0) {
 		got_privsep_send_stop(imsg_sendfds[0]);
 		goto done;
 	}
 
-	memset(&ppa, 0, sizeof(ppa));
-	ppa.progress_cb = progress_cb;
-	ppa.progress_arg = progress_arg;
-	err = got_pack_create(packsha1, packfile, their_ids, ntheirs,
-	    our_ids, nours, repo, 0, 1, pack_progress, &ppa,
-	    cancel_cb, cancel_arg);
-	if (err)
-		goto done;
+	if (refs_to_send > 0) {
+		memset(&ppa, 0, sizeof(ppa));
+		ppa.progress_cb = progress_cb;
+		ppa.progress_arg = progress_arg;
+		err = got_pack_create(packsha1, packfile, their_ids, ntheirs,
+		    our_ids, nours, repo, 0, 1, pack_progress, &ppa,
+		    cancel_cb, cancel_arg);
+		if (err)
+			goto done;
 
-	if (fflush(packfile) == -1) {
-		err = got_error_from_errno("fflush");
-		goto done;
-	}
+		if (fflush(packfile) == -1) {
+			err = got_error_from_errno("fflush");
+			goto done;
+		}
 
-	npackfd = dup(fileno(packfile));
-	if (npackfd == -1) {
-		err = got_error_from_errno("dup");
-		goto done;
+		npackfd = dup(fileno(packfile));
+		if (npackfd == -1) {
+			err = got_error_from_errno("dup");
+			goto done;
+		}
+		err = got_privsep_send_packfd(&sendibuf, npackfd);
+		if (err != NULL)
+			goto done;
+		npackfd = -1;
+	} else {
+		err = got_privsep_send_packfd(&sendibuf, -1);
+		if (err != NULL)
+			goto done;
 	}
-	err = got_privsep_send_packfd(&sendibuf, npackfd);
-	if (err != NULL)
-		goto done;
-	npackfd = -1;
 
 	while (!done) {
 		int success = 0;
diff --git a/libexec/got-send-pack/got-send-pack.c b/libexec/got-send-pack/got-send-pack.c
index 70cdbda..2a886f2 100644
--- a/libexec/got-send-pack/got-send-pack.c
+++ b/libexec/got-send-pack/got-send-pack.c
@@ -820,9 +820,11 @@ send_pack(int fd, struct got_pathlist_head *refs,
 	if (err)
 		goto done;
 
-	err = send_pack_file(fd, packfd, ibuf);
-	if (err)
-		goto done;
+	if (packfd != -1) {
+		err = send_pack_file(fd, packfd, ibuf);
+		if (err)
+			goto done;
+	}
 
 	err = readpkt(&n, fd, buf, sizeof(buf));
 	if (err)
diff --git a/regress/cmdline/send.sh b/regress/cmdline/send.sh
index d8ab1ac..b7603c9 100755
--- a/regress/cmdline/send.sh
+++ b/regress/cmdline/send.sh
@@ -354,19 +354,15 @@ EOF
 	fi
 
 	got send -r $testroot/repo -d refs/heads/branch2 origin \
-		> $testroot/stdout.raw 2>$testroot/stderr
+		> $testroot/stdout 2>$testroot/stderr
 	ret="$?"
 	if [ "$ret" != "0" ]; then
 		echo "got send command failed unexpectedly" >&2
 		test_done "$testroot" "$ret"
 		return 1
 	fi
-	tr -d '\r' < $testroot/stdout.raw > $testroot/stdout
 
 	echo 'Connecting to "origin" 127.0.0.1' > $testroot/stdout.expected
-	echo -n "packing 0 references; 0 objects; deltify: 0%; " \
-		>> $testroot/stdout.expected
-	echo "uploading pack:     32B 100%" >> $testroot/stdout.expected
 	echo "Server has deleted refs/heads/branch2" \
 		>> $testroot/stdout.expected