Git Mailing List Archive on lore.kernel.org
 help / color / Atom feed
From: Jiang Xin <worldhello.net@gmail.com>
To: Junio C Hamano <gitster@pobox.com>, Git List <git@vger.kernel.org>
Cc: Jiang Xin <zhiyou.jx@alibaba-inc.com>
Subject: [PATCH v2 1/5] receive-pack: add new proc-receive hook
Date: Sun,  8 Mar 2020 22:56:27 +0800
Message-ID: <20200308145631.64040-2-zhiyou.jx@alibaba-inc.com> (raw)
In-Reply-To: <CANYiYbE2V3bLfEgH-aRDv4Y5V+_BTZn-oUN7fOVpARm+_14BdQ@mail.gmail.com>

Git calls an internal `execute_commands` function to handle commands
sent from client to `git-receive-pack`.  Regardless of what references
the user pushes, git creates or updates the corresponding references if
the user has write-permission.  A contributor who has no
write-permission, cannot push to the repository directly.  So, the
contributor has to write commits to an alternate location, and sends
pull request by emails or by other ways.  We call this workflow as a
distributed workflow.

It would be more convenient to work in a centralized workflow like what
Gerrit provided for some cases.  For example, a read-only user who
cannot push to a branch directly can run the following `git push`
command to push commits to a pseudo reference (has a prefix "refs/for/",
not "refs/heads/") to create a code review.

    git push origin \
        HEAD:refs/for/<branch-name>/<session>

The `<branch-name>` in the above example can be as simple as "master",
or a more complicated branch name like "foo/bar".  The `<session>` in
the above example command can be the local branch name of the client
side, such as "my/topic".

We cannot implement a centralized workflow elegantly by using
"pre-receive" + "post-receive", because Git will call the internal
function "execute_commands" to create references (even the special
pseudo reference) between these two hooks.  Even though we can delete
the temporarily created pseudo reference via the "post-receive" hook,
having a temporary reference is not safe for concurrent pushes.

So, add a filter and a new handler to support this kind of workflow.
The filter will check the prefix of the reference name, and if the
command has a special reference name, the filter will turn a specific
field (`have_special_ref`) on for the command.  Commands with this filed
turned on will be executed by a new handler (an hook named
"proc-receive") instead of the internal `execute_commands` function.
We can use this "proc-receive" command to create pull requests or send
emails for code review.

This "proc-receive" hook executes once for the receive operation. It
takes no arguments, but gets the same information as the pre-receive
hook does on its standard input.

Signed-off-by: Jiang Xin <zhiyou.jx@alibaba-inc.com>
---
 builtin/receive-pack.c       |  93 +++++-
 t/t5411-proc-receive-hook.sh | 547 +++++++++++++++++++++++++++++++++++
 2 files changed, 628 insertions(+), 12 deletions(-)
 create mode 100755 t/t5411-proc-receive-hook.sh

diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 2cc18bbffd..23d0c224d2 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -312,7 +312,8 @@ struct command {
 	struct command *next;
 	const char *error_string;
 	unsigned int skip_update:1,
-		     did_not_exist:1;
+		     did_not_exist:1,
+		     have_special_ref:1;
 	int index;
 	struct object_id old_oid;
 	struct object_id new_oid;
@@ -669,6 +670,9 @@ static void prepare_push_cert_sha1(struct child_process *proc)
 
 struct receive_hook_feed_state {
 	struct command *cmd;
+	int feed_normal_ref;
+	int feed_special_ref;
+	int hook_must_exist;
 	int skip_broken;
 	struct strbuf buf;
 	const struct string_list *push_options;
@@ -684,8 +688,14 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed,
 	int code;
 
 	argv[0] = find_hook(hook_name);
-	if (!argv[0])
-		return 0;
+	if (!argv[0]) {
+		if (feed_state->hook_must_exist) {
+			rp_error("cannot to find hook '%s'", hook_name);
+			return 1;
+		} else {
+			return 0;
+		}
+	}
 
 	argv[1] = NULL;
 
@@ -751,9 +761,17 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
 	struct receive_hook_feed_state *state = state_;
 	struct command *cmd = state->cmd;
 
-	while (cmd &&
-	       state->skip_broken && (cmd->error_string || cmd->did_not_exist))
-		cmd = cmd->next;
+	while (cmd) {
+		if (state->skip_broken && (cmd->error_string || cmd->did_not_exist))
+			cmd = cmd->next;
+		else if (state->feed_special_ref && cmd->have_special_ref)
+			break;
+		else if (state->feed_normal_ref && !cmd->have_special_ref)
+			break;
+		else
+			cmd = cmd->next;
+	}
+
 	if (!cmd)
 		return -1; /* EOF */
 	strbuf_reset(&state->buf);
@@ -778,6 +796,9 @@ static int run_receive_hook(struct command *commands,
 
 	strbuf_init(&state.buf, 0);
 	state.cmd = commands;
+	state.feed_normal_ref = 1;
+	state.feed_special_ref = 1;
+	state.hook_must_exist = 0;
 	state.skip_broken = skip_broken;
 	if (feed_receive_hook(&state, NULL, NULL))
 		return 0;
@@ -817,6 +838,27 @@ static int run_update_hook(struct command *cmd)
 	return finish_command(&proc);
 }
 
+static int run_proc_receive_hook(struct command *commands,
+				 const struct string_list *push_options)
+{
+	struct receive_hook_feed_state state;
+	int status;
+
+	strbuf_init(&state.buf, 0);
+	state.cmd = commands;
+	state.feed_normal_ref = 0;
+	state.feed_special_ref = 1;
+	state.hook_must_exist = 1;
+	state.skip_broken = 1;
+	if (feed_receive_hook(&state, NULL, NULL))
+		return 0;
+	state.cmd = commands;
+	state.push_options = push_options;
+	status = run_and_feed_hook("proc-receive", feed_receive_hook, &state);
+	strbuf_release(&state.buf);
+	return status;
+}
+
 static char *refuse_unconfigured_deny_msg =
 	N_("By default, updating the current branch in a non-bare repository\n"
 	   "is denied, because it will make the index and work tree inconsistent\n"
@@ -1392,7 +1434,7 @@ static void execute_commands_non_atomic(struct command *commands,
 	struct strbuf err = STRBUF_INIT;
 
 	for (cmd = commands; cmd; cmd = cmd->next) {
-		if (!should_process_cmd(cmd))
+		if (!should_process_cmd(cmd) || cmd->have_special_ref)
 			continue;
 
 		transaction = ref_transaction_begin(&err);
@@ -1432,7 +1474,7 @@ static void execute_commands_atomic(struct command *commands,
 	}
 
 	for (cmd = commands; cmd; cmd = cmd->next) {
-		if (!should_process_cmd(cmd))
+		if (!should_process_cmd(cmd) || cmd->have_special_ref)
 			continue;
 
 		cmd->error_string = update(cmd, si);
@@ -1468,6 +1510,8 @@ static void execute_commands(struct command *commands,
 	struct iterate_data data;
 	struct async muxer;
 	int err_fd = 0;
+	int have_special_ref = 0;
+	int have_normal_ref = 0;
 
 	if (unpacker_error) {
 		for (cmd = commands; cmd; cmd = cmd->next)
@@ -1497,6 +1541,22 @@ static void execute_commands(struct command *commands,
 
 	reject_updates_to_hidden(commands);
 
+	/* Try to find commands that have special prefix in their reference names,
+	 * and mark them to run an external "proc-receive" hook later.
+	 */
+	for (cmd = commands; cmd; cmd = cmd->next) {
+		if (!should_process_cmd(cmd))
+			continue;
+
+		/* TODO: replace the fixed prefix by looking up git config variables. */
+		if (!strncmp(cmd->ref_name, "refs/for/", 9)) {
+			cmd->have_special_ref = 1;
+			have_special_ref = 1;
+		} else {
+			have_normal_ref = 1;
+		}
+	}
+
 	if (run_receive_hook(commands, "pre-receive", 0, push_options)) {
 		for (cmd = commands; cmd; cmd = cmd->next) {
 			if (!cmd->error_string)
@@ -1523,10 +1583,19 @@ static void execute_commands(struct command *commands,
 	free(head_name_to_free);
 	head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL);
 
-	if (use_atomic)
-		execute_commands_atomic(commands, si);
-	else
-		execute_commands_non_atomic(commands, si);
+	if (have_special_ref && run_proc_receive_hook(commands, push_options)) {
+		for (cmd = commands; cmd; cmd = cmd->next) {
+			if (!cmd->error_string  && (cmd->have_special_ref || use_atomic))
+				cmd->error_string = "fail to run proc-receive hook";
+		}
+	}
+
+	if (have_normal_ref) {
+		if (use_atomic)
+			execute_commands_atomic(commands, si);
+		else
+			execute_commands_non_atomic(commands, si);
+	}
 
 	if (shallow_update)
 		warn_if_skipped_connectivity_check(commands, si);
diff --git a/t/t5411-proc-receive-hook.sh b/t/t5411-proc-receive-hook.sh
new file mode 100755
index 0000000000..01f67965bf
--- /dev/null
+++ b/t/t5411-proc-receive-hook.sh
@@ -0,0 +1,547 @@
+#!/bin/sh
+#
+# Copyright (c) 2018-2020 Jiang Xin
+#
+
+test_description='Test proc-receive hook on special git-push refspec'
+
+. ./test-lib.sh
+
+bare=bare.git
+
+create_commits_in () {
+	repo="$1" &&
+	if ! parent=$(git -C "$repo" rev-parse HEAD^{} 2>/dev/null)
+	then
+		parent=
+	fi &&
+	T=$(git -C "$repo" write-tree) &&
+	shift &&
+	while test $# -gt 0
+	do
+		name=$1 &&
+		test_tick &&
+		if test -z "$parent"
+		then
+			oid=$(echo $name | git -C "$repo" commit-tree $T)
+		else
+			oid=$(echo $name | git -C "$repo" commit-tree -p $parent $T)
+		fi &&
+		eval $name=$oid &&
+		parent=$oid &&
+		shift ||
+		return 1
+	done &&
+	git -C "$repo" update-ref refs/heads/master $oid
+}
+
+test_expect_success "setup repository" '
+	git init --bare $bare &&
+
+	# Enable push options for bare.git.
+	git -C $bare config receive.advertisePushOptions true &&
+
+	git clone --no-local $bare work &&
+	create_commits_in work A B
+'
+
+test_expect_success "setup hooks" '
+	## pre-receive hook
+	cat >$bare/hooks/pre-receive <<-EOF &&
+	#!/bin/sh
+
+	printf >&2 "execute: pre-receive hook\n"
+
+	while read old new ref
+	do
+		printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n"
+	done
+	EOF
+
+	## proc-receive hook
+	cat >$bare/hooks/proc-receive <<-EOF &&
+	#!/bin/sh
+
+	printf >&2 "execute: proc-receive hook\n"
+
+	while read old new ref
+	do
+		printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n"
+	done
+	EOF
+
+	## post-receive hook
+	cat >$bare/hooks/post-receive <<-EOF &&
+	#!/bin/sh
+
+	printf >&2 "execute: post-receive hook\n"
+
+	while read old new ref
+	do
+		printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n"
+	done
+	EOF
+	chmod a+x \
+		$bare/hooks/pre-receive \
+		$bare/hooks/post-receive \
+		$bare/hooks/proc-receive \
+'
+
+test_expect_success "(1) standard git-push command" '
+	(
+		cd work &&
+		git update-ref HEAD $A &&
+		git push origin HEAD HEAD:maint HEAD:a/b/c 2>&1
+	) >out &&
+	grep "^remote:" out | sed -e "s/  *\$//g" >actual &&
+	cat >expect <<-EOF &&
+	remote: execute: pre-receive hook
+	remote: >> old: $ZERO_OID, new: $A, ref: refs/heads/master.
+	remote: >> old: $ZERO_OID, new: $A, ref: refs/heads/maint.
+	remote: >> old: $ZERO_OID, new: $A, ref: refs/heads/a/b/c.
+	remote: execute: post-receive hook
+	remote: >> old: $ZERO_OID, new: $A, ref: refs/heads/master.
+	remote: >> old: $ZERO_OID, new: $A, ref: refs/heads/maint.
+	remote: >> old: $ZERO_OID, new: $A, ref: refs/heads/a/b/c.
+	EOF
+	test_cmp expect actual &&
+	(
+		cd $bare &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$A refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(1) push one special ref" '
+	(
+		cd work &&
+		git update-ref HEAD $B &&
+		git push origin HEAD:refs/for/master/my/topic
+	) >out 2>&1 &&
+	grep "^remote:" out | sed -e "s/  *\$//g" >actual &&
+	cat >expect <<-EOF &&
+	remote: execute: pre-receive hook
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/master/my/topic.
+	remote: execute: proc-receive hook
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/master/my/topic.
+	remote: execute: post-receive hook
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/master/my/topic.
+	EOF
+	test_cmp expect actual &&
+	(
+		cd $bare &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$A refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(1) push both a normal and a special refs" '
+	(
+		cd work &&
+		git push origin \
+			HEAD:refs/for/maint/my/topic \
+			HEAD:refs/heads/master
+	) >out 2>&1 &&
+	grep "^remote:" out | sed -e "s/  *\$//g" >actual &&
+	cat >expect <<-EOF &&
+	remote: execute: pre-receive hook
+	remote: >> old: $A, new: $B, ref: refs/heads/master.
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/maint/my/topic.
+	remote: execute: proc-receive hook
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/maint/my/topic.
+	remote: execute: post-receive hook
+	remote: >> old: $A, new: $B, ref: refs/heads/master.
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/maint/my/topic.
+	EOF
+	test_cmp expect actual &&
+	(
+		cd $bare &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$B refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(1) cleanup" '
+	(
+		cd $bare &&
+		git update-ref refs/heads/master $A $B &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$A refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(2) remove proc-receive hook" '
+	mv $bare/hooks/proc-receive $bare/hooks/proc-receive.ok
+'
+
+test_expect_success "(2) standard git-push command" '
+	(
+		cd work &&
+		git update-ref HEAD $B &&
+		git push origin HEAD:master 2>&1
+	) >out &&
+	grep "^remote:" out | sed -e "s/  *\$//g" >actual &&
+	cat >expect <<-EOF &&
+	remote: execute: pre-receive hook
+	remote: >> old: $A, new: $B, ref: refs/heads/master.
+	remote: execute: post-receive hook
+	remote: >> old: $A, new: $B, ref: refs/heads/master.
+	EOF
+	test_cmp expect actual &&
+	(
+		cd $bare &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$B refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(2) cleanup" '
+	(
+		cd $bare &&
+		git update-ref refs/heads/master $A $B &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$A refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(2) push one special ref (failed)" '
+	(
+		cd work &&
+		git update-ref HEAD $B &&
+		test_must_fail git push origin HEAD:refs/for/a/b/c/my/topic
+	) >out 2>&1 &&
+	grep "^remote:" out | sed -e "s/  *\$//g" >actual &&
+	cat >expect <<-EOF &&
+	remote: execute: pre-receive hook
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/a/b/c/my/topic.
+	remote: error: cannot to find hook '"'"'proc-receive'"'"'
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(2) push both a normal and a special refs (one failed)" '
+	(
+		cd work &&
+		git update-ref HEAD $B &&
+		test_must_fail git push origin \
+			HEAD:refs/for/maint/my/topic \
+			HEAD:refs/heads/master
+	) >out 2>&1 &&
+	grep "^remote:" out | sed -e "s/  *\$//g" >actual &&
+	cat >expect <<-EOF &&
+	remote: execute: pre-receive hook
+	remote: >> old: $A, new: $B, ref: refs/heads/master.
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/maint/my/topic.
+	remote: error: cannot to find hook '"'"'proc-receive'"'"'
+	remote: execute: post-receive hook
+	remote: >> old: $A, new: $B, ref: refs/heads/master.
+	EOF
+	test_cmp expect actual &&
+	(
+		cd $bare &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$B refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(2) cleanup" '
+	(
+		cd $bare &&
+		git update-ref refs/heads/master $A $B &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$A refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(2) atomic push both a normal and a special refs (failed)" '
+	(
+		cd work &&
+		git update-ref HEAD $B &&
+		test_must_fail git push --atomic origin \
+			HEAD:refs/for/maint/my/topic \
+			HEAD:refs/heads/master
+	) >out 2>&1 &&
+	grep "^remote:" out | sed -e "s/  *\$//g" >actual &&
+	cat >expect <<-EOF &&
+	remote: execute: pre-receive hook
+	remote: >> old: $A, new: $B, ref: refs/heads/master.
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/maint/my/topic.
+	remote: error: cannot to find hook '"'"'proc-receive'"'"'
+	EOF
+	test_cmp expect actual &&
+	(
+		cd $bare &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$A refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(3) new proc-receive hook (return error)" '
+	cat >$bare/hooks/proc-receive <<-EOF &&
+	#!/bin/sh
+
+	printf >&2 "execute: proc-receive hook\n"
+
+	while read old new ref
+	do
+		printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n"
+	done
+
+	printf >&2 "fail to run proc-receive\n"
+	exit 1
+	EOF
+	chmod a+x $bare/hooks/proc-receive
+'
+
+test_expect_success "(3) standard git-push command" '
+	(
+		cd work &&
+		git update-ref HEAD $B &&
+		git push origin HEAD:master 2>&1
+	) >out &&
+	grep "^remote:" out | sed -e "s/  *\$//g" >actual &&
+	cat >expect <<-EOF &&
+	remote: execute: pre-receive hook
+	remote: >> old: $A, new: $B, ref: refs/heads/master.
+	remote: execute: post-receive hook
+	remote: >> old: $A, new: $B, ref: refs/heads/master.
+	EOF
+	test_cmp expect actual &&
+	(
+		cd $bare &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$B refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(3) cleanup" '
+	(
+		cd $bare &&
+		git update-ref refs/heads/master $A $B &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$A refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(3) push both a normal and a special refs (one failed)" '
+	(
+		cd work &&
+		git update-ref HEAD $B &&
+		test_must_fail git push origin \
+			HEAD:refs/for/maint/my/topic \
+			HEAD:refs/heads/master
+	) >out 2>&1 &&
+	grep "^remote:" out | sed -e "s/  *\$//g" >actual &&
+	cat >expect <<-EOF &&
+	remote: execute: pre-receive hook
+	remote: >> old: $A, new: $B, ref: refs/heads/master.
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/maint/my/topic.
+	remote: execute: proc-receive hook
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/maint/my/topic.
+	remote: fail to run proc-receive
+	remote: execute: post-receive hook
+	remote: >> old: $A, new: $B, ref: refs/heads/master.
+	EOF
+	test_cmp expect actual &&
+	(
+		cd $bare &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$B refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(3) cleanup" '
+	(
+		cd $bare &&
+		git update-ref refs/heads/master $A $B &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$A refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(3) atomic push a normal and a special refs (failed)" '
+	(
+		cd work &&
+		git update-ref HEAD $B &&
+		test_must_fail git push --atomic origin \
+			HEAD:refs/for/maint/my/topic \
+			HEAD:refs/heads/master
+	) >out 2>&1 &&
+	grep "^remote:" out | sed -e "s/  *\$//g" >actual &&
+	cat >expect <<-EOF &&
+	remote: execute: pre-receive hook
+	remote: >> old: $A, new: $B, ref: refs/heads/master.
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/maint/my/topic.
+	remote: execute: proc-receive hook
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/maint/my/topic.
+	remote: fail to run proc-receive
+	EOF
+	test_cmp expect actual &&
+	(
+		cd $bare &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$A refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(4) restore proc-receive hook" '
+	mv $bare/hooks/proc-receive $bare/hooks/proc-receive.fail &&
+	mv $bare/hooks/proc-receive.ok $bare/hooks/proc-receive
+'
+
+test_expect_success "(4) push two special references" '
+	(
+		cd work &&
+		git update-ref HEAD $B &&
+		git push origin \
+			HEAD:refs/for/maint/my/topic \
+			HEAD:refs/for/a/b/c/my/topic
+	) >out 2>&1 &&
+	grep "^remote:" out | sed -e "s/  *\$//g" >actual &&
+	cat >expect <<-EOF &&
+	remote: execute: pre-receive hook
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/maint/my/topic.
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/a/b/c/my/topic.
+	remote: execute: proc-receive hook
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/maint/my/topic.
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/a/b/c/my/topic.
+	remote: execute: post-receive hook
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/maint/my/topic.
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/a/b/c/my/topic.
+	EOF
+	test_cmp expect actual &&
+	(
+		cd $bare &&
+		git show-ref
+	) >actual &&
+	cat >expect <<-EOF &&
+	$A refs/heads/a/b/c
+	$A refs/heads/maint
+	$A refs/heads/master
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(5) new pre-receive hook hook (return error)" '
+	mv $bare/hooks/pre-receive $bare/hooks/pre-receive.ok &&
+	cat >$bare/hooks/pre-receive <<-EOF &&
+	#!/bin/sh
+
+	printf >&2 "execute: pre-receive hook\n"
+
+	while read old new ref
+	do
+		printf >&2 ">> old: \$old, new: \$new, ref: \$ref.\n"
+	done
+
+	printf >&2 ">> ERROR: declined in pre-receive hook\n"
+	exit 1
+	EOF
+	chmod a+x $bare/hooks/pre-receive
+'
+
+test_expect_success "(5) push two special references (declined)" '
+	(
+		cd work &&
+		test_must_fail git push origin \
+			HEAD:refs/for/master/my/topic \
+			HEAD:refs/for/maint/my/topic
+	) >out 2>&1 &&
+	grep "^remote:" out | sed -e "s/  *\$//g" >actual &&
+	cat >expect <<-EOF &&
+	remote: execute: pre-receive hook
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/master/my/topic.
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/maint/my/topic.
+	remote: >> ERROR: declined in pre-receive hook
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success "(5) push both a normal and a special refs (declined)" '
+	(
+		cd work &&
+		test_must_fail git push origin \
+			HEAD:refs/for/master/my/topic \
+			HEAD:refs/heads/master
+	) >out 2>&1 &&
+	grep "^remote:" out | sed -e "s/  *\$//g" >actual &&
+	cat >expect <<-EOF &&
+	remote: execute: pre-receive hook
+	remote: >> old: $A, new: $B, ref: refs/heads/master.
+	remote: >> old: $ZERO_OID, new: $B, ref: refs/for/master/my/topic.
+	remote: >> ERROR: declined in pre-receive hook
+	EOF
+	test_cmp expect actual
+'
+
+test_done
-- 
2.26.0.rc0.5.gb02b988a14.dirty


  parent reply index

Thread overview: 182+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-04 11:33 [PATCH 0/7] New execute-commands hook for centralized workflow Jiang Xin
2020-03-04 11:33 ` [PATCH 1/7] receive-pack: new external execute-commands hook Jiang Xin
2020-03-04 11:33 ` [PATCH 2/7] receive-pack: feed all commands to post-receive Jiang Xin
2020-03-04 11:33 ` [PATCH 3/7] receive-pack: try `execute-commands --pre-receive` Jiang Xin
2020-03-04 11:33 ` [PATCH 4/7] receive-pack: read env from execute-commands output Jiang Xin
2020-03-04 11:33 ` [PATCH 5/7] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-03-04 11:33 ` [PATCH 6/7] receive-pack: new config receive.executeCommandsHookRefs Jiang Xin
2020-03-04 11:33 ` [PATCH 7/7] hook: add document and example for "execute-commands" hook Jiang Xin
2020-03-04 20:39 ` [PATCH 0/7] New execute-commands hook for centralized workflow Junio C Hamano
2020-03-05 16:51   ` Jiang Xin
2020-03-08 14:56     ` [PATCH v2 0/5] New proc-receive " Jiang Xin
2020-03-08 14:56     ` Jiang Xin [this message]
2020-03-09 17:12       ` [PATCH v2 1/5] receive-pack: add new proc-receive hook Junio C Hamano
2020-03-10  6:03         ` Jiang Xin
2020-03-13 12:23           ` [PATCH v3 0/4] New proc-receive hook for centralized workflow Jiang Xin
2020-03-22 13:18             ` [PATCH v4 0/5] " Jiang Xin
2020-03-25  5:19               ` Junio C Hamano
2020-03-22 13:18             ` [PATCH v4 1/5] transport: not report a non-head push as a branch Jiang Xin
2020-03-25  6:04               ` Junio C Hamano
2020-03-22 13:18             ` [PATCH v4 2/5] receive-pack: add new proc-receive hook Jiang Xin
2020-03-25 14:36               ` [PATCH 0/3] Never report references we not push Jiang Xin
2020-03-29 14:33                 ` [PATCH v2 0/4] " Jiang Xin
2020-03-29 14:35                   ` Jiang Xin
2020-04-16 16:24                   ` [PATCH v3 0/5] fix git-push porcelain output and atomic report issue Jiang Xin
2020-04-17  9:45                     ` [PATCH v4 " Jiang Xin
2020-04-17  9:45                     ` [PATCH v4 1/5] send-pack: fix inconsistent porcelain output Jiang Xin
2020-04-17 19:51                       ` Junio C Hamano
2020-04-17  9:45                     ` [PATCH v4 2/5] t5543: never report what we do not push Jiang Xin
2020-04-17  9:45                     ` [PATCH v4 3/5] send-pack: mark failure of atomic push properly Jiang Xin
2020-04-17  9:45                     ` [PATCH v4 4/5] transport-helper: mark failure for atomic push Jiang Xin
2020-04-17  9:45                     ` [PATCH v4 5/5] transport-helper: new method reject_atomic_push() Jiang Xin
2020-04-16 16:24                   ` [PATCH v3 1/5] send-pack: fix inconsistent porcelain output Jiang Xin
2020-04-16 16:24                   ` [PATCH v3 2/5] t5543: never report what we do not push Jiang Xin
2020-04-16 16:24                   ` [PATCH v3 3/5] send-pack: mark failure of atomic push properly Jiang Xin
2020-04-16 16:24                   ` [PATCH v3 4/5] transport-helper: mark failure for atomic push Jiang Xin
2020-04-16 16:24                   ` [PATCH v3 5/5] transport-helper: new method reject_atomic_push() Jiang Xin
2020-03-29 14:33                 ` [PATCH v2 1/4] t5543: never report what we do not push Jiang Xin
2020-03-29 14:33                 ` [PATCH v2 2/4] send-pack: mark failure of atomic push properly Jiang Xin
2020-03-29 14:33                 ` [PATCH v2 3/4] transport-helper: mark failure for atomic push Jiang Xin
2020-03-29 14:33                 ` [PATCH v2 4/4] transport-helper: new method reject_atomic_push() Jiang Xin
2020-03-25 14:36               ` [PATCH 1/3] t5543: never report what we do not push Jiang Xin
2020-03-25 15:05                 ` Junio C Hamano
2020-03-26  2:25                   ` Jiang Xin
2020-03-25 14:36               ` [PATCH 2/3] send-pack: mark failure of atomic push properly Jiang Xin
2020-03-25 15:15                 ` Junio C Hamano
2020-03-25 14:36               ` [PATCH 3/3] transport-helper: enforce atomic in push_refs_with_push Jiang Xin
2020-03-25 15:32                 ` Junio C Hamano
2020-03-22 13:18             ` [PATCH v4 3/5] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-03-22 13:18             ` [PATCH v4 4/5] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-03-22 13:18             ` [PATCH v4 5/5] receive-pack: refactor report for proc-receive Jiang Xin
2020-03-13 12:23           ` [PATCH v3 1/4] receive-pack: add new proc-receive hook Jiang Xin
2020-03-13 12:23           ` [PATCH v3 2/4] receive-pack: refactor report for proc-receive Jiang Xin
2020-03-13 12:23           ` [PATCH v3 3/4] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-03-13 12:23           ` [PATCH v3 4/4] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-03-08 14:56     ` [PATCH v2 2/5] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-03-08 15:38     ` [PATCH v2 3/5] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-03-08 15:38     ` [PATCH v2 4/5] receive-pack: read env from proc-receive output Jiang Xin
2020-03-08 15:38     ` [PATCH v2 5/5] hook: add document and example for "proc-receive" hook Jiang Xin
2020-03-30 16:57 ` [PATCH v5 0/6] New proc-receive hook for centralized workflow Jiang Xin
2020-03-30 16:57 ` [PATCH v5 1/6] transport: not report a non-head push as a branch Jiang Xin
2020-03-30 16:57 ` [PATCH v5 2/6] receive-pack: add new proc-receive hook Jiang Xin
2020-03-31  0:19   ` Junio C Hamano
2020-03-31  0:21   ` Junio C Hamano
2020-03-30 16:57 ` [PATCH v5 3/6] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-03-30 16:57 ` [PATCH v5 4/6] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-03-30 16:57 ` [PATCH v5 5/6] receive-pack: refactor report for proc-receive Jiang Xin
2020-03-30 16:57 ` [PATCH v5 6/6] doc: add documentation for the proc-receive hook Jiang Xin
2020-04-02 16:35 ` [PATCH v6 0/7] New proc-receive hook for centralized workflow Jiang Xin
2020-04-02 18:26   ` Junio C Hamano
2020-04-03 16:08     ` [PATCH v7 " Jiang Xin
2020-04-04 13:43       ` [PATCH v8 " Jiang Xin
2020-04-07 12:08         ` [PATCH v9 0/6] " Jiang Xin
2020-04-12 13:30           ` [PATCH v10 0/8] " Jiang Xin
2020-04-13 16:48             ` [PATCH v11 0/7] " Jiang Xin
2020-04-13 16:48             ` [PATCH v11 1/7] transport: not report a non-head push as a branch Jiang Xin
2020-04-13 16:48             ` [PATCH v11 2/7] connect: export parse_feature_value() Jiang Xin
2020-04-13 16:48             ` [PATCH v11 3/7] receive-pack: add new proc-receive hook Jiang Xin
2020-04-13 16:48             ` [PATCH v11 4/7] send-pack: extension for client-side status report Jiang Xin
2020-04-13 16:48             ` [PATCH v11 5/7] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-04-13 16:48             ` [PATCH v11 6/7] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-04-13 16:48             ` [PATCH v11 7/7] doc: add documentation for the proc-receive hook Jiang Xin
2020-04-12 13:30           ` [PATCH v10 1/8] transport: not report a non-head push as a branch Jiang Xin
2020-04-12 20:26             ` Junio C Hamano
2020-04-13 11:15               ` Jiang Xin
2020-04-12 13:30           ` [PATCH v10 2/8] receive-pack: add new proc-receive hook Jiang Xin
2020-04-12 21:30             ` Junio C Hamano
2020-04-13 10:58               ` Jiang Xin
2020-04-13 21:50                 ` Junio C Hamano
2020-04-14 12:32                   ` [PATCH v12 0/7] New proc-receive hook for centralized workflow Jiang Xin
2020-04-18 16:03                     ` [PATCH v13 0/8] " Jiang Xin
2020-04-18 16:03                     ` [PATCH v13 1/8] transport: not report a non-head push as a branch Jiang Xin
2020-04-18 16:03                     ` [PATCH v13 2/8] connect: export parse_feature_value() Jiang Xin
2020-04-18 16:03                     ` [PATCH v13 3/8] receive-pack: add new proc-receive hook Jiang Xin
2020-04-18 16:03                     ` [PATCH v13 4/8] send-pack: extension for client-side status report Jiang Xin
2020-04-18 16:03                     ` [PATCH v13 5/8] receive-pack: feed extended_status to post-receive Jiang Xin
2020-04-18 16:03                     ` [PATCH v13 6/8] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-04-18 16:03                     ` [PATCH v13 7/8] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-04-18 16:03                     ` [PATCH v13 8/8] doc: add documentation for the proc-receive hook Jiang Xin
2020-04-14 12:32                   ` [PATCH v12 1/7] transport: not report a non-head push as a branch Jiang Xin
2020-04-14 12:32                   ` [PATCH v12 2/7] connect: export parse_feature_value() Jiang Xin
2020-04-14 12:32                   ` [PATCH v12 3/7] receive-pack: add new proc-receive hook Jiang Xin
2020-04-15 15:48                     ` Junio C Hamano
2020-04-15 15:55                       ` Jiang Xin
2020-04-15 18:34                         ` Junio C Hamano
2020-04-27 17:00                           ` Jiang Xin
2020-04-29  7:56                             ` Jeff King
2020-04-30 15:33                               ` Jiang Xin
2020-05-05 14:41                                 ` [PATCH v14 0/7] New proc-receive hook for centralized workflow Jiang Xin
2020-05-06 23:14                                   ` Junio C Hamano
2020-05-07  1:37                                     ` Jiang Xin
2020-05-07 11:18                                       ` Jiang Xin
2020-05-07 16:10                                     ` [PATCH v15 " Jiang Xin
2020-05-18  9:40                                       ` [PATCH v16 00/11] " Jiang Xin
2020-05-18  9:40                                       ` [PATCH v16 01/11] transport: not report a non-head push as a branch Jiang Xin
2020-05-18  9:40                                       ` [PATCH v16 02/11] t5411: add basic test cases for proc-receive hook Jiang Xin
2020-05-18  9:40                                       ` [PATCH v16 03/11] receive-pack: add new " Jiang Xin
2020-05-18  9:40                                       ` [PATCH v16 04/11] New capability "report-status-v2" for git-push Jiang Xin
2020-05-18  9:40                                       ` [PATCH v16 05/11] doc: add document for capability report-status-v2 Jiang Xin
2020-05-18  9:40                                       ` [PATCH v16 06/11] receive-pack: feed report options to post-receive Jiang Xin
2020-05-18  9:40                                       ` [PATCH v16 07/11] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-05-18  9:40                                       ` [PATCH v16 08/11] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-05-18  9:40                                       ` [PATCH v16 09/11] t5411: test updates of remote-tracking branches Jiang Xin
2020-05-18  9:40                                       ` [PATCH v16 10/11] transport: parse report options for tracking refs Jiang Xin
2020-05-18  9:40                                       ` [PATCH v16 11/11] doc: add documentation for the proc-receive hook Jiang Xin
2020-05-07 16:10                                     ` [PATCH v15 1/7] transport: not report a non-head push as a branch Jiang Xin
2020-05-07 16:10                                     ` [PATCH v15 2/7] receive-pack: add new proc-receive hook Jiang Xin
2020-05-07 16:10                                     ` [PATCH v15 3/7] New capability "report-status-v2" for git-push Jiang Xin
2020-05-07 16:10                                     ` [PATCH v15 4/7] receive-pack: feed report options to post-receive Jiang Xin
2020-05-07 16:10                                     ` [PATCH v15 5/7] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-05-07 16:10                                     ` [PATCH v15 6/7] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-05-07 16:10                                     ` [PATCH v15 7/7] doc: add documentation for the proc-receive hook Jiang Xin
2020-05-05 14:41                                 ` [PATCH v14 1/7] transport: not report a non-head push as a branch Jiang Xin
2020-05-05 14:41                                 ` [PATCH v14 2/7] receive-pack: add new proc-receive hook Jiang Xin
2020-05-05 14:41                                 ` [PATCH v14 3/7] New capability "report-status-v2" for git-push Jiang Xin
2020-05-05 15:25                                   ` [PATCH v14 8/7] fixup! " Jiang Xin
2020-05-05 14:41                                 ` [PATCH v14 4/7] receive-pack: feed report options to post-receive Jiang Xin
2020-05-05 14:41                                 ` [PATCH v14 5/7] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-05-05 14:41                                 ` [PATCH v14 6/7] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-05-05 14:41                                 ` [PATCH v14 7/7] doc: add documentation for the proc-receive hook Jiang Xin
2020-05-07 17:27                                 ` [PATCH v12 3/7] receive-pack: add new " Jeff King
2020-04-14 12:32                   ` [PATCH v12 4/7] send-pack: extension for client-side status report Jiang Xin
2020-04-15 20:36                     ` Junio C Hamano
2020-04-14 12:32                   ` [PATCH v12 5/7] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-04-14 12:32                   ` [PATCH v12 6/7] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-04-14 12:32                   ` [PATCH v12 7/7] doc: add documentation for the proc-receive hook Jiang Xin
2020-04-12 13:30           ` [PATCH v10 3/8] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-04-12 21:38             ` Junio C Hamano
2020-04-13 11:16               ` Jiang Xin
2020-04-12 13:30           ` [PATCH v10 4/8] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-04-12 21:46             ` Junio C Hamano
2020-04-13 11:16               ` Jiang Xin
2020-04-12 13:30           ` [PATCH v10 5/8] connect: export parse_feature_value() Jiang Xin
2020-04-12 13:30           ` [PATCH v10 6/8] receive-pack: extension for server-side report Jiang Xin
2020-04-12 13:30           ` [PATCH v10 7/8] send-pack: extension for client-side status report Jiang Xin
2020-04-12 13:30           ` [PATCH v10 8/8] doc: add documentation for the proc-receive hook Jiang Xin
2020-04-07 12:08         ` [PATCH v9 1/6] transport: not report a non-head push as a branch Jiang Xin
2020-04-07 12:08         ` [PATCH v9 2/6] receive-pack: add new proc-receive hook Jiang Xin
2020-04-07 12:08         ` [PATCH v9 3/6] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-04-07 12:08         ` [PATCH v9 4/6] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-04-07 12:08         ` [PATCH v9 5/6] receive-pack: refactor report for proc-receive Jiang Xin
2020-04-07 12:08         ` [PATCH v9 6/6] doc: add documentation for the proc-receive hook Jiang Xin
2020-04-04 13:43       ` [PATCH v8 1/7] transport: not report a non-head push as a branch Jiang Xin
2020-04-04 13:43       ` [PATCH v8 2/7] receive-pack: add new proc-receive hook Jiang Xin
2020-04-04 13:43       ` [PATCH v8 3/7] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-04-04 13:43       ` [PATCH v8 4/7] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-04-04 13:43       ` [PATCH v8 5/7] receive-pack: refactor report for proc-receive Jiang Xin
2020-04-04 13:43       ` [PATCH v8 6/7] t5412: test the proc-receive hook on HTTP protocol Jiang Xin
2020-04-04 13:43       ` [PATCH v8 7/7] doc: add documentation for the proc-receive hook Jiang Xin
2020-04-03 16:08     ` [PATCH v7 1/7] transport: not report a non-head push as a branch Jiang Xin
2020-04-03 16:08     ` [PATCH v7 2/7] receive-pack: add new proc-receive hook Jiang Xin
2020-04-03 16:08     ` [PATCH v7 3/7] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-04-03 16:08     ` [PATCH v7 4/7] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-04-03 16:08     ` [PATCH v7 5/7] receive-pack: refactor report for proc-receive Jiang Xin
2020-04-03 16:08     ` [PATCH v7 6/7] t5412: test proc-receive hook on HTTP protocol Jiang Xin
2020-04-03 16:08     ` [PATCH v7 7/7] doc: add documentation for the proc-receive hook Jiang Xin
2020-04-02 16:35 ` [PATCH v6 1/7] transport: not report a non-head push as a branch Jiang Xin
2020-04-02 16:35 ` [PATCH v6 2/7] receive-pack: add new proc-receive hook Jiang Xin
2020-04-02 16:35 ` [PATCH v6 3/7] refs.c: refactor to reuse ref_is_hidden() Jiang Xin
2020-04-02 16:35 ` [PATCH v6 4/7] receive-pack: new config receive.procReceiveRefs Jiang Xin
2020-04-02 16:35 ` [PATCH v6 5/7] receive-pack: refactor report for proc-receive Jiang Xin
2020-04-02 16:35 ` [PATCH v6 6/7] t5412: test proc-receive hook on HTTP protocol Jiang Xin
2020-04-02 16:35 ` [PATCH v6 7/7] doc: add documentation for the proc-receive hook Jiang Xin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200308145631.64040-2-zhiyou.jx@alibaba-inc.com \
    --to=worldhello.net@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=zhiyou.jx@alibaba-inc.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Git Mailing List Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/git/0 git/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 git git/ https://lore.kernel.org/git \
		git@vger.kernel.org
	public-inbox-index git

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.git


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git