Commit Diff


commit - 2dd71cdd72c0c6c4ce607fdc73ae38fa898bfeb2
commit + 3fe5d0fee4a9e03753cf693bf839bf26cb872d54
blob - 8f5652bc11cd7acbd00b1be84dd5bb45e953f855
blob + dad0b7d03fbf6b57cc05881f0936f42db930e358
--- got/got.c
+++ got/got.c
@@ -8763,7 +8763,8 @@ struct got_send_progress_arg {
 static const struct got_error *
 send_progress(void *arg, int ncolored, int nfound, int ntrees,
     off_t packfile_size, int ncommits, int nobj_total, int nobj_deltify,
-    int nobj_written, off_t bytes_sent, const char *refname, int success)
+    int nobj_written, off_t bytes_sent, const char *refname,
+    const char *errmsg, int success)
 {
 	struct got_send_progress_arg *a = arg;
 	char scaled_packsize[FMT_SCALED_STRSIZE];
@@ -8795,6 +8796,8 @@ send_progress(void *arg, int ncolored, int nfound, int
 		if (a->printed_something)
 			putchar('\n');
 		printf("Server has %s %s", status, refname);
+		if (errmsg)
+			printf(": %s", errmsg);
 		a->printed_something = 1;
 		return NULL;
 	}
blob - 19e9e4f116e70c354ec4bbff8ce7e5a1615acd1f
blob + 038b51f30308b8ca6879fdbddcfd64c6c83b7594
--- include/got_send.h
+++ include/got_send.h
@@ -38,7 +38,7 @@ const struct got_error *got_send_connect(pid_t *, int 
 typedef const struct got_error *(*got_send_progress_cb)(void *,
     int ncolored, int nfound, int ntrees, off_t packfile_size, int ncommits,
     int nobj_total, int nobj_deltify, int nobj_written, off_t bytes_sent,
-    const char *refname, int success);
+    const char *refname, const char *, int success);
 
 /*
  * Attempt to generate a pack file and sent it to a server.
blob - c9234999b21b43c91b7595360c597b1d528f69c1
blob + 44f8d7667b1d387ae50d548b69f48ae1afba27e9
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -481,7 +481,9 @@ struct got_imsg_send_remote_ref {
 struct got_imsg_send_ref_status {
 	int success;
 	size_t name_len;
+	size_t errmsg_len;
 	/* Followed by name_len data bytes. */
+	/* Followed by errmsg_len data bytes. */
 } __attribute__((__packed__));
 
 /* Structure for GOT_IMSG_IDXPACK_REQUEST data. */
@@ -702,7 +704,7 @@ const struct got_error *got_privsep_recv_send_remote_r
     struct got_pathlist_head *, struct imsgbuf *);
 const struct got_error *got_privsep_send_packfd(struct imsgbuf *, int);
 const struct got_error *got_privsep_recv_send_progress(int *, off_t *,
-    int *, char **, struct imsgbuf *);
+    int *, char **, char **, struct imsgbuf *);
 const struct got_error *got_privsep_get_imsg_obj(struct got_object **,
     struct imsg *, struct imsgbuf *);
 const struct got_error *got_privsep_recv_obj(struct got_object **,
blob - 097787082caaa1182b3a16d565c2bdee8e33d6de
blob + 5462fb412f766dfe4181df6346d61dc9b5a584fb
--- lib/privsep.c
+++ lib/privsep.c
@@ -964,7 +964,7 @@ got_privsep_send_packfd(struct imsgbuf *ibuf, int fd)
 
 const struct got_error *
 got_privsep_recv_send_progress(int *done, off_t *bytes_sent,
-    int *success, char **refname, struct imsgbuf *ibuf)
+    int *success, char **refname, char **errmsg, struct imsgbuf *ibuf)
 {
 	const struct got_error *err = NULL;
 	struct imsg imsg;
@@ -975,6 +975,7 @@ got_privsep_recv_send_progress(int *done, off_t *bytes
 	*done = 0;
 	*success = 0;
 	*refname = NULL;
+	*errmsg = NULL;
 
 	err = got_privsep_recv_imsg(&imsg, ibuf, 0);
 	if (err)
@@ -995,13 +996,18 @@ got_privsep_recv_send_progress(int *done, off_t *bytes
 			break;
 		}
 		memcpy(&iref_status, imsg.data, sizeof(iref_status));
-		if (datalen != sizeof(iref_status) + iref_status.name_len) {
+		if (datalen != sizeof(iref_status) + iref_status.name_len +
+		    iref_status.errmsg_len) {
 			err = got_error(GOT_ERR_PRIVSEP_MSG);
 			break;
 		}
 		*success = iref_status.success;
 		*refname = strndup(imsg.data + sizeof(iref_status),
 		    iref_status.name_len);
+
+		if (iref_status.errmsg_len != 0)
+			*errmsg = strndup(imsg.data + sizeof(iref_status) +
+			    iref_status.name_len, iref_status.errmsg_len);
 		break;
 	case GOT_IMSG_SEND_DONE:
 		if (datalen != 0) {
blob - e2c9a83e2c62b08256e2396c00578d4f47c9aa35
blob + edb3e20183d33860e39b8ec07038fcae745ceb0f
--- lib/send.c
+++ lib/send.c
@@ -121,7 +121,7 @@ pack_progress(void *arg, int ncolored, int nfound, int
 
 	err = a->progress_cb(a->progress_arg, ncolored, nfound, ntrees,
 	    packfile_size, ncommits, nobj_total, nobj_deltify,
-	    nobj_written, 0, NULL, 0);
+	    nobj_written, 0, NULL, NULL, 0);
 	if (err)
 		return err;
 
@@ -665,13 +665,15 @@ got_send_pack(const char *remote_name, struct got_path
 	while (!done) {
 		int success = 0;
 		char *refname = NULL;
+		char *errmsg = NULL;
+
 		if (cancel_cb) {
 			err = (*cancel_cb)(cancel_arg);
 			if (err)
 				goto done;
 		}
 		err = got_privsep_recv_send_progress(&done, &bytes_sent,
-		    &success, &refname, &sendibuf);
+		    &success, &refname, &errmsg, &sendibuf);
 		if (err)
 			goto done;
 		if (refname && got_ref_name_is_valid(refname) && success &&
@@ -695,14 +697,16 @@ got_send_pack(const char *remote_name, struct got_path
 			    ppa.nfound, ppa.ntrees, ppa.packfile_size,
 			    ppa.ncommits, ppa.nobj_total, ppa.nobj_deltify,
 			    ppa.nobj_written, bytes_sent,
-			    refname, success);
+			    refname, errmsg, success);
 			if (err) {
 				free(refname);
+				free(errmsg);
 				goto done;
 			}
 			bytes_sent_cur = bytes_sent;
 		}
 		free(refname);
+		free(errmsg);
 	}
 done:
 	if (sendpid != -1) {
blob - 885867f78e6977f47b4133b83b2d41c0603514ef
blob + 40bd3e57d66c071b282b6f8d8bd9fec8fa887d49
--- libexec/got-send-pack/got-send-pack.c
+++ libexec/got-send-pack/got-send-pack.c
@@ -221,10 +221,11 @@ send_ref_status(struct imsgbuf *ibuf, const char *refn
     struct got_pathlist_head *refs, struct got_pathlist_head *delete_refs)
 {
 	struct ibuf *wbuf;
-	size_t len, reflen = strlen(refname);
+	size_t i, len, reflen, errmsglen = 0;
 	struct got_pathlist_entry *pe;
 	int ref_valid = 0;
-	char *eol;
+	char *eol, *sp;
+	const char *errmsg = "";
 
 	eol = strchr(refname, '\n');
 	if (eol == NULL) {
@@ -232,7 +233,28 @@ send_ref_status(struct imsgbuf *ibuf, const char *refn
 		    "unexpected message from server");
 	}
 	*eol = '\0';
+
+	sp = strchr(refname, ' ');
+	if (sp != NULL) {
+		*sp++ = '\0';
+		errmsg = sp;
+		errmsglen = strlen(errmsg);
+
+		for (i = 0; i < errmsglen; ++i) {
+			if (!isprint((unsigned char)errmsg[i])) {
+				return got_error_msg(GOT_ERR_BAD_PACKET,
+				    "non-printable error message received "
+				    "from the server");
+			}
+		}
+	}
 
+	reflen = strlen(refname);
+	if (!got_ref_name_is_valid(refname)) {
+		return got_error_msg(GOT_ERR_BAD_PACKET,
+		    "unexpected message from server");
+	}
+
 	TAILQ_FOREACH(pe, refs, entry) {
 		if (strcmp(refname, pe->path) == 0) {
 			ref_valid = 1;
@@ -252,7 +274,7 @@ send_ref_status(struct imsgbuf *ibuf, const char *refn
 		    "unexpected message from server");
 	}
 
-	len = sizeof(struct got_imsg_send_ref_status) + reflen;
+	len = sizeof(struct got_imsg_send_ref_status) + reflen + errmsglen;
 	if (len >= MAX_IMSGSIZE - IMSG_HEADER_SIZE)
 		return got_error(GOT_ERR_NO_SPACE);
 
@@ -266,8 +288,12 @@ send_ref_status(struct imsgbuf *ibuf, const char *refn
 		return got_error_from_errno("imsg_add SEND_REF_STATUS");
 	if (imsg_add(wbuf, &reflen, sizeof(reflen)) == -1)
 		return got_error_from_errno("imsg_add SEND_REF_STATUS");
+	if (imsg_add(wbuf, &errmsglen, sizeof(errmsglen)) == -1)
+		return got_error_from_errno("imsg_add SEND_REF_STATUS");
 	if (imsg_add(wbuf, refname, reflen) == -1)
 		return got_error_from_errno("imsg_add SEND_REF_STATUS");
+	if (imsg_add(wbuf, errmsg, errmsglen) == -1)
+		return got_error_from_errno("imsg_add SEND_REF_STATUS");
 
 	wbuf->fd = -1;
 	imsg_close(ibuf, wbuf);
blob - f989b0dbd6a3ca710184d881f574e55a5eb83e78
blob + 2bfbfe22cdf4a0255e78921e2da2bfcc76ffb03c
--- regress/cmdline/send.sh
+++ regress/cmdline/send.sh
@@ -1511,6 +1511,69 @@ EOF
 	git_fsck "$testroot" "$testroot/repo-clone"
 	ret=$?
 	test_done "$testroot" "$ret"
+}
+
+test_send_rejected() {
+	local testroot=`test_init send_rejected`
+	local testurl=ssh://127.0.0.1/$testroot
+	local commit_id=`git_show_head $testroot/repo`
+
+	if ! got clone -q "$testurl/repo" "$testroot/repo-clone"; then
+		echo "got clone command failed unexpectedly" >&2
+		test_done "$testroot" 1
+		return 1
+	fi
+
+	mkdir "$testroot/repo-clone/hooks"
+	cat <<'EOF' >$testroot/repo-clone/hooks/update
+case "$1" in
+*master*)
+	echo "rejecting push on master branch"
+	exit 1
+	;;
+esac
+exit 0
+EOF
+	chmod +x "$testroot/repo-clone/hooks/update"
+
+	cat > $testroot/repo/.git/got.conf <<EOF
+remote "origin" {
+	protocol ssh
+	server 127.0.0.1
+	repository "$testroot/repo-clone"
+}
+EOF
+
+	echo "modified alpha" >$testroot/repo/alpha
+	git_commit "$testroot/repo" -m "modified alpha"
+
+	got send -q -r "$testroot/repo" >$testroot/stdout 2>$testroot/stderr
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		echo "got send command failed unexpectedly" >&2
+		test_done "$testroot" $ret
+		return 1
+	fi
+
+	touch "$testroot/stdout.expected"
+	if ! cmp -s "$testroot/stdout.expected" "$testroot/stdout"; then
+		diff -u "$testroot/stdout.expected" "$testroot/stdout"
+		test_done "$testroot" 1
+		return 1
+	fi
+
+	cat <<EOF >$testroot/stderr.expected
+rejecting push on master branch
+error: hook declined to update refs/heads/master
+EOF
+
+	if ! cmp -s "$testroot/stderr.expected" "$testroot/stderr"; then
+		diff -u "$testroot/stderr.expected" "$testroot/stderr"
+		test_done "$testroot" 1
+		return 1
+	fi
+
+	test_done "$testroot" 0
 }
 
 test_parseargs "$@"
@@ -1526,3 +1589,4 @@ run_test test_send_all_branches
 run_test test_send_to_empty_repo
 run_test test_send_and_fetch_config
 run_test test_send_config
+run_test test_send_rejected