git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH/RFCv2 2/2] run post-receive and post-update hooks with empty stdin/no args for invalid ref deletion

From
Pang Yan Han <pangyanhan@gmail.com>
Date
Sep 25, 2011, 17:37 UTC
Message-ID
<20110925173734.GA29256@myhost>
In-Reply-To
<1316927182-14212-3-git-send-email-pangyanhan@gmail.com>

The post-receive and post-update hooks are triggered with invalid input on stdin and invalid args respectively during the deletion of corrupt or non-existent refs during a push.

Teach receive-pack to run post-receive hook with empty stdin and post-update hook with empty args in the event of an invalid ref deletion.

Signed-off-by: Pang Yan Han <pangyanhan@gmail.com>
---
This has been updated with the suggestions from Sitaram Chamarty, as stated in
http://thread.gmane.org/gmane.comp.version-control.git/182056/focus=182065
 builtin/receive-pack.c |   35 +++++++++++++-
 t/t5516-fetch-push.sh  |  118 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 150 insertions(+), 3 deletions(-)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index ae164da..28d0b09 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -153,6 +153,26 @@ struct command {
 	char ref_name[FLEX_ARRAY]; /* more */
 };
 
+/* For invalid refs */
+static struct command **invalid_delete;
+static size_t invalid_delete_nr;
+static size_t invalid_delete_alloc;
+
+static void invalid_delete_append(struct command *cmd)
+{
+	ALLOC_GROW(invalid_delete, invalid_delete_nr + 1, invalid_delete_alloc);
+	invalid_delete[invalid_delete_nr++] = cmd;
+}
+
+static int is_invalid_delete(struct command *cmd)
+{
+	size_t i;
+	for (i = 0; i < invalid_delete_nr; i++)
+		if (invalid_delete[i] == cmd)
+			return 1;
+	return 0;
+}
+
 static const char pre_receive_hook[] = "hooks/pre-receive";
 static const char post_receive_hook[] = "hooks/post-receive";
 
@@ -248,7 +268,8 @@ static int run_receive_hook(struct command *commands, const char *hook_name)
 	}
 
 	for (cmd = commands; cmd; cmd = cmd->next) {
-		if (!cmd->error_string) {
+		/* Run with empty stdin for invalid ref deletion */
+		if (!cmd->error_string && !is_invalid_delete(cmd)) {
 			size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
 				sha1_to_hex(cmd->old_sha1),
 				sha1_to_hex(cmd->new_sha1),
@@ -447,6 +468,7 @@ static const char *update(struct command *cmd)
 		if (!parse_object(old_sha1)) {
 			rp_warning("Allowing deletion of corrupt ref.");
 			old_sha1 = NULL;
+			invalid_delete_append(cmd);
 		}
 		if (delete_ref(namespaced_name, old_sha1, 0)) {
 			rp_error("failed to delete %s", name);
@@ -490,8 +512,14 @@ static void run_update_post_hook(struct command *commands)
 		char *p;
 		if (cmd->error_string)
 			continue;
-		p = xmalloc(strlen(cmd->ref_name) + 1);
-		strcpy(p, cmd->ref_name);
+		if (is_invalid_delete(cmd)) {
+			/* Run with empty args for invalid ref deletion */
+			p = xmalloc(1);
+			p[0] = '\0';
+		} else {
+			p = xmalloc(strlen(cmd->ref_name) + 1);
+			strcpy(p, cmd->ref_name);
+		}
 		argv[argc] = p;
 		argc++;
 	}
@@ -866,5 +894,6 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 	}
 	if (use_sideband)
 		packet_flush(1);
+	free(invalid_delete);
 	return 0;
 }
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 3abb290..038a3b3 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -40,6 +40,39 @@ mk_test () {
 	)
 }
 
+mk_test_with_hooks() {
+	mk_test "$@" &&
+	(
+	cd testrepo &&
+	mkdir .git/hooks &&
+	cd .git/hooks &&
+
+	cat >pre-receive <<'EOF' &&
+#!/bin/sh
+cat - >>pre-receive.actual
+EOF
+
+	cat >update <<'EOF' &&
+#!/bin/sh
+printf "%s %s %s\n" "$@" >>update.actual
+EOF
+	cat >post-receive <<'EOF' &&
+#!/bin/sh
+cat - >>post-receive.actual
+EOF
+
+	cat >post-update <<'EOF' &&
+#!/bin/sh
+for ref in "$@"
+do
+	printf "%s\n" "$ref" >>post-update.actual
+done
+EOF
+
+	chmod u+x pre-receive update post-receive post-update
+	)
+}
+
 mk_child() {
 	rm -rf "$1" &&
 	git clone testrepo "$1"
@@ -559,6 +592,91 @@ test_expect_success 'allow deleting an invalid remote ref' '
 
 '
 
+test_expect_success 'pushing valid refs triggers post-receive and post-update hooks' '
+	mk_test_with_hooks heads/master &&
+	orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) &&
+	newmaster=$(git show-ref -s --verify refs/heads/master) &&
+	git push testrepo refs/heads/master:refs/heads/master :refs/heads/nonexistent &&
+	(cd testrepo/.git &&
+	cat >pre-receive.expect <<'EOF' &&
+$orgmaster $newmaster refs/heads/master
+$_z40 $_z40 refs/heads/nonexistent
+EOF
+
+	cat >update.expect <<'EOF' &&
+refs/heads/master $orgmaster $newmaster
+refs/heads/nonexistent $_z40 $_z40
+EOF
+
+	cat >post-receive.expect <<'EOF' &&
+$orgmaster $newmaster refs/heads/master
+EOF
+
+	cat >post-update.expect <<'EOF' &&
+refs/heads/master
+
+EOF
+
+	test_cmp pre-receive.expect pre-receive.actual &&
+	test_cmp update.expect update.actual &&
+	test_cmp post-receive.expect post-receive.actual &&
+	test_cmp post-update.expect post-update.actual
+	)
+'
+
+test_expect_success 'deleting non-existent ref triggers post-receive and post-update hooks with empty args' '
+	mk_test_with_hooks heads/master &&
+	git push testrepo :refs/heads/nonexistent &&
+	(cd testrepo/.git &&
+	cat >pre-receive.expect <<'EOF' &&
+$_z40 $_z40 refs/heads/nonexistent
+EOF
+
+	cat >update.expect <<'EOF' &&
+refs/heads/nonexistent $_z40 $_z40
+EOF
+
+	cat >post-receive.expect <<'EOF' &&
+EOF
+
+	cat >post-update.expect <<'EOF' &&
+
+EOF
+
+	test_cmp pre-receive.expect pre-receive.actual &&
+	test_cmp update.expect update.actual &&
+	test_cmp post-receive.expect post-receive.actual &&
+	test_cmp post-update.expect post-update.actual
+	)
+'
+
+test_expect_success 'deleting invalid ref triggers post-receive and post-update hooks with empty args' '
+	mk_test_with_hooks heads/master &&
+	rm -f testrepo/.git/objects/??/* &&
+	git push testrepo :refs/heads/master &&
+	(cd testrepo/.git &&
+	cat >pre-receive.expect <<'EOF' &&
+$_z40 $_z40 refs/heads/master
+EOF
+
+	cat >update.expect <<'EOF' &&
+refs/heads/master $_z40 $_z40
+EOF
+
+	cat >post-receive.expect <<'EOF' &&
+EOF
+
+	cat >post-update.expect <<'EOF' &&
+
+EOF
+
+	test_cmp pre-receive.expect pre-receive.actual &&
+	test_cmp update.expect update.actual &&
+	test_cmp post-receive.expect post-receive.actual &&
+	test_cmp post-update.expect post-update.actual
+	)
+'
+
 test_expect_success 'allow deleting a ref using --delete' '
 	mk_test heads/master &&
 	(cd testrepo && git config receive.denyDeleteCurrent warn) &&
-- 
1.7.7.rc3.2.gd706f.dirty
Previous: Pang Yan HanNext: Sitaram Chamarty
Message 7 of 18 in “Teach receive-pack not to run update hook for corrupt/non existent ref”
  1. 0/2 Teach receive-pack not to run update hook for corrupt/non existent refPang Yan Han, Sep 25, 2011
  2. 1/2 is_url: Remove redundant assignmentPang Yan Han, Sep 25, 2011
  3. Tay Ray ChuanSep 25, 2011
  4. Junio C HamanoSep 26, 2011
  5. Jeff KingSep 26, 2011
  6. 2/2 receive-pack: Don't run update hook for corrupt or nonexistent refPang Yan Han, Sep 25, 2011
  7. 2/2 run post-receive and post-update hooks with empty stdin/no args for invalid ref deletionPang Yan Han, Sep 25, 2011
  8. Sitaram ChamartySep 25, 2011
  9. Pang Yan HanSep 25, 2011
  10. Sitaram ChamartySep 25, 2011
  11. Junio C HamanoSep 26, 2011
  12. Sitaram ChamartySep 26, 2011
  13. Junio C HamanoSep 26, 2011
  14. Junio C HamanoSep 27, 2011
  15. Pang Yan HanSep 27, 2011
  16. Junio C HamanoSep 27, 2011
  17. Pang Yan HanSep 27, 2011
  18. Sitaram ChamartySep 27, 2011

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.