Skip to contentSkip to conversation

Rebase: Run post-checkout hook on checkout

PATCH New [email protected] 3 participants 8 messages Dec 24, 2018 – Dec 29, 2018

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Rebase: Run post-checkout hook on checkout

From: Orgad Shaneh <[email protected]>

---
 builtin/rebase.c              | 11 +++++++++--
 t/t5403-post-checkout-hook.sh | 20 ++++++++++++++++++++
 2 files changed, 29 insertions(+), 2 deletions(-)
builtin/rebase.c +9 -2
index b5c99ec10c..7f7a2c738e 100644
@@ -530,6 +530,7 @@ static int run_specific_rebase(struct rebase_options *opts)
 
 #define RESET_HEAD_DETACH (1<<0)
 #define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_HOOK (1<<2)
 
 static int reset_head(struct object_id *oid, const char *action,
 		      const char *switch_to_branch, unsigned flags,
@@ -537,6 +538,7 @@ static int reset_head(struct object_id *oid, const char *action,
 {
 	unsigned detach_head = flags & RESET_HEAD_DETACH;
 	unsigned reset_hard = flags & RESET_HEAD_HARD;
+	unsigned run_hook = flags & RESET_HEAD_RUN_HOOK;
 	struct object_id head_oid;
 	struct tree_desc desc[2] = { { NULL }, { NULL } };
 	struct lock_file lock = LOCK_INIT;
@@ -636,6 +638,10 @@ static int reset_head(struct object_id *oid, const char *action,
 			ret = update_ref(reflog_head, "HEAD", oid, NULL, 0,
 					 UPDATE_REFS_MSG_ON_ERR);
 	}
+	if (run_hook)
+		run_hook_le(NULL, "post-checkout",
+			    oid_to_hex(orig ? orig : &null_oid),
+			    oid_to_hex(oid), "1", NULL);
 
 leave_reset_head:
 	strbuf_release(&msg);
@@ -1465,7 +1471,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 					    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
 					    options.switch_to);
 				if (reset_head(&oid, "checkout",
-					       options.head_name, 0,
+					       options.head_name,
+					       RESET_HEAD_RUN_HOOK,
 					       NULL, buf.buf) < 0) {
 					ret = !!error(_("could not switch to "
 							"%s"),
@@ -1539,7 +1546,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	strbuf_addf(&msg, "%s: checkout %s",
 		    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
 	if (reset_head(&options.onto->object.oid, "checkout", NULL,
-		       RESET_HEAD_DETACH, NULL, msg.buf))
+		       RESET_HEAD_DETACH | RESET_HEAD_RUN_HOOK, NULL, msg.buf))
 		die(_("Could not detach HEAD"));
 	strbuf_release(&msg);
 
t/t5403-post-checkout-hook.sh +20 -0
index 9f9a5163c5..5b4e582caa 100755
@@ -13,6 +13,8 @@ test_expect_success setup '
 	EOF
 	test_commit one &&
 	test_commit two &&
+	test_commit rebase-on-me &&
+	git reset --hard HEAD^ &&
 	test_commit three three
 '
 
@@ -51,6 +53,24 @@ test_expect_success 'post-checkout receives the right args when not switching br
 	rm -f .git/post-checkout.args
 '
 
+test_expect_success 'post-checkout is triggered on rebase' '
+	git checkout -b rebase-test master &&
+	rm -f .git/post-checkout.args &&
+	git rebase rebase-on-me &&
+	read old new flag < .git/post-checkout.args &&
+	test $old != $new && test $flag = 1 &&
+	rm -f .git/post-checkout.args
+'
+
+test_expect_success 'post-checkout is triggered on rebase with fast-forward' '
+	git checkout -b ff-rebase-test rebase-on-me^ &&
+	rm -f .git/post-checkout.args &&
+	git rebase rebase-on-me &&
+	read old new flag < .git/post-checkout.args &&
+	test $old != $new && test $flag = 1 &&
+	rm -f .git/post-checkout.args
+'
+
 if test "$(git config --bool core.filemode)" = true; then
 mkdir -p templates/hooks
 write_script templates/hooks/post-checkout <<-\EOF
Signature · 1 line
2.20.1
#1 lore raw

PATCH 1/2 t5403: Refactor

From: Orgad Shaneh <[email protected]>

* Replace multiple clones and commits by test_commits.
* Replace 3 invocations of awk by read.

---
 t/t5403-post-checkout-hook.sh | 80 +++++++++++++----------------------
 1 file changed, 29 insertions(+), 51 deletions(-)
t/t5403-post-checkout-hook.sh +29 -51
index fc898c9eac..9f9a5163c5 100755
@@ -7,77 +7,55 @@ test_description='Test the post-checkout hook.'
 . ./test-lib.sh
 
 test_expect_success setup '
-	echo Data for commit0. >a &&
-	echo Data for commit0. >b &&
-	git update-index --add a &&
-	git update-index --add b &&
-	tree0=$(git write-tree) &&
-	commit0=$(echo setup | git commit-tree $tree0) &&
-	git update-ref refs/heads/master $commit0 &&
-	git clone ./. clone1 &&
-	git clone ./. clone2 &&
-	GIT_DIR=clone2/.git git branch new2 &&
-	echo Data for commit1. >clone2/b &&
-	GIT_DIR=clone2/.git git add clone2/b &&
-	GIT_DIR=clone2/.git git commit -m new2
-'
-
-for clone in 1 2; do
-    cat >clone${clone}/.git/hooks/post-checkout <<'EOF'
-#!/bin/sh
-echo $@ > $GIT_DIR/post-checkout.args
-EOF
-    chmod u+x clone${clone}/.git/hooks/post-checkout
-done
-
-test_expect_success 'post-checkout runs as expected ' '
-	GIT_DIR=clone1/.git git checkout master &&
-	test -e clone1/.git/post-checkout.args
+	mv .git/hooks-disabled .git/hooks &&
+	write_script .git/hooks/post-checkout <<-\EOF &&
+	echo $@ >.git/post-checkout.args
+	EOF
+	test_commit one &&
+	test_commit two &&
+	test_commit three three
 '
 
 test_expect_success 'post-checkout receives the right arguments with HEAD unchanged ' '
-	old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
-	new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
-	flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
-	test $old = $new && test $flag = 1
+	git checkout master &&
+	test -e .git/post-checkout.args &&
+	read old new flag <.git/post-checkout.args &&
+	test $old = $new && test $flag = 1 &&
+	rm -f .git/post-checkout.args
 '
 
 test_expect_success 'post-checkout runs as expected ' '
-	GIT_DIR=clone1/.git git checkout master &&
-	test -e clone1/.git/post-checkout.args
+	git checkout master &&
+	test -e .git/post-checkout.args &&
+	rm -f .git/post-checkout.args
 '
 
 test_expect_success 'post-checkout args are correct with git checkout -b ' '
-	GIT_DIR=clone1/.git git checkout -b new1 &&
-	old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
-	new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
-	flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
-	test $old = $new && test $flag = 1
+	git checkout -b new1 &&
+	read old new flag <.git/post-checkout.args &&
+	test $old = $new && test $flag = 1 &&
+	rm -f .git/post-checkout.args
 '
 
 test_expect_success 'post-checkout receives the right args with HEAD changed ' '
-	GIT_DIR=clone2/.git git checkout new2 &&
-	old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
-	new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
-	flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
-	test $old != $new && test $flag = 1
+	git checkout two &&
+	read old new flag <.git/post-checkout.args &&
+	test $old != $new && test $flag = 1 &&
+	rm -f .git/post-checkout.args
 '
 
 test_expect_success 'post-checkout receives the right args when not switching branches ' '
-	GIT_DIR=clone2/.git git checkout master b &&
-	old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
-	new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
-	flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
-	test $old = $new && test $flag = 0
+	git checkout master -- three &&
+	read old new flag <.git/post-checkout.args &&
+	test $old = $new && test $flag = 0 &&
+	rm -f .git/post-checkout.args
 '
 
 if test "$(git config --bool core.filemode)" = true; then
 mkdir -p templates/hooks
-cat >templates/hooks/post-checkout <<'EOF'
-#!/bin/sh
-echo $@ > $GIT_DIR/post-checkout.args
+write_script templates/hooks/post-checkout <<-\EOF
+echo $@ >$GIT_DIR/post-checkout.args
 EOF
-chmod +x templates/hooks/post-checkout
 
 test_expect_success 'post-checkout hook is triggered by clone' '
 	git clone --template=templates . clone3 &&
Signature · 1 line
2.20.1
#5 lore raw

PATCH 2/2 Rebase: Run post-checkout hook on checkout

In reply to [email protected]

From: Orgad Shaneh <[email protected]>

---
 builtin/rebase.c              | 11 +++++++++--
 t/t5403-post-checkout-hook.sh | 20 ++++++++++++++++++++
 2 files changed, 29 insertions(+), 2 deletions(-)
builtin/rebase.c +9 -2
index b5c99ec10c..7f7a2c738e 100644
@@ -530,6 +530,7 @@ static int run_specific_rebase(struct rebase_options *opts)
 
 #define RESET_HEAD_DETACH (1<<0)
 #define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_HOOK (1<<2)
 
 static int reset_head(struct object_id *oid, const char *action,
 		      const char *switch_to_branch, unsigned flags,
@@ -537,6 +538,7 @@ static int reset_head(struct object_id *oid, const char *action,
 {
 	unsigned detach_head = flags & RESET_HEAD_DETACH;
 	unsigned reset_hard = flags & RESET_HEAD_HARD;
+	unsigned run_hook = flags & RESET_HEAD_RUN_HOOK;
 	struct object_id head_oid;
 	struct tree_desc desc[2] = { { NULL }, { NULL } };
 	struct lock_file lock = LOCK_INIT;
@@ -636,6 +638,10 @@ static int reset_head(struct object_id *oid, const char *action,
 			ret = update_ref(reflog_head, "HEAD", oid, NULL, 0,
 					 UPDATE_REFS_MSG_ON_ERR);
 	}
+	if (run_hook)
+		run_hook_le(NULL, "post-checkout",
+			    oid_to_hex(orig ? orig : &null_oid),
+			    oid_to_hex(oid), "1", NULL);
 
 leave_reset_head:
 	strbuf_release(&msg);
@@ -1465,7 +1471,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 					    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
 					    options.switch_to);
 				if (reset_head(&oid, "checkout",
-					       options.head_name, 0,
+					       options.head_name,
+					       RESET_HEAD_RUN_HOOK,
 					       NULL, buf.buf) < 0) {
 					ret = !!error(_("could not switch to "
 							"%s"),
@@ -1539,7 +1546,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	strbuf_addf(&msg, "%s: checkout %s",
 		    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
 	if (reset_head(&options.onto->object.oid, "checkout", NULL,
-		       RESET_HEAD_DETACH, NULL, msg.buf))
+		       RESET_HEAD_DETACH | RESET_HEAD_RUN_HOOK, NULL, msg.buf))
 		die(_("Could not detach HEAD"));
 	strbuf_release(&msg);
 
t/t5403-post-checkout-hook.sh +20 -0
index 9f9a5163c5..5b4e582caa 100755
@@ -13,6 +13,8 @@ test_expect_success setup '
 	EOF
 	test_commit one &&
 	test_commit two &&
+	test_commit rebase-on-me &&
+	git reset --hard HEAD^ &&
 	test_commit three three
 '
 
@@ -51,6 +53,24 @@ test_expect_success 'post-checkout receives the right args when not switching br
 	rm -f .git/post-checkout.args
 '
 
+test_expect_success 'post-checkout is triggered on rebase' '
+	git checkout -b rebase-test master &&
+	rm -f .git/post-checkout.args &&
+	git rebase rebase-on-me &&
+	read old new flag < .git/post-checkout.args &&
+	test $old != $new && test $flag = 1 &&
+	rm -f .git/post-checkout.args
+'
+
+test_expect_success 'post-checkout is triggered on rebase with fast-forward' '
+	git checkout -b ff-rebase-test rebase-on-me^ &&
+	rm -f .git/post-checkout.args &&
+	git rebase rebase-on-me &&
+	read old new flag < .git/post-checkout.args &&
+	test $old != $new && test $flag = 1 &&
+	rm -f .git/post-checkout.args
+'
+
 if test "$(git config --bool core.filemode)" = true; then
 mkdir -p templates/hooks
 write_script templates/hooks/post-checkout <<-\EOF
Signature · 1 line
2.20.1
#2 lore raw

Re: [PATCH 1/2] t5403: Refactor

In reply to [email protected]
Quoted 1 line · [email protected] writes:

Subject: Re: [PATCH 1/2] t5403: Refactor

Hmph. "Refactor" alone leaves readers wondering "refactor what?", "refactor for what?" and "refactor how?". Given that the overfall goal this change seeks seems to be to simplify it by losing about 20 lines, how about justifying it like so?

Subject: t5403: simplify by using a single repository

There is no strong reason to use separate clones to run these tests; just use a single test repository prepared with more modern test_commit shell helper function.

While at it, replace three "awk '{print $N}'" on the same file with shell built-in "read" into three variables.

Quoted 35 lines

From: Orgad Shaneh <[email protected]>

* Replace multiple clones and commits by test_commits.
* Replace 3 invocations of awk by read.

---
 t/t5403-post-checkout-hook.sh | 80 +++++++++++++----------------------
 1 file changed, 29 insertions(+), 51 deletions(-)
t/t5403-post-checkout-hook.sh +0 -17
index fc898c9eac..9f9a5163c5 100755
@@ -7,77 +7,55 @@ test_description='Test the post-checkout hook.'
 . ./test-lib.sh
 
 test_expect_success setup '
-	echo Data for commit0. >a &&
-	echo Data for commit0. >b &&
-	git update-index --add a &&
-	git update-index --add b &&
-	tree0=$(git write-tree) &&
-	commit0=$(echo setup | git commit-tree $tree0) &&
-	git update-ref refs/heads/master $commit0 &&
-	git clone ./. clone1 &&
-	git clone ./. clone2 &&
-	GIT_DIR=clone2/.git git branch new2 &&
-	echo Data for commit1. >clone2/b &&
-	GIT_DIR=clone2/.git git add clone2/b &&
-	GIT_DIR=clone2/.git git commit -m new2
-'
-
-for clone in 1 2; do
-    cat >clone${clone}/.git/hooks/post-checkout <<'EOF'

t/t5403-post-checkout-hook.sh test_description='Test the post-checkout hook.' · line 10

Quoted 10 lines
t/t5403-post-checkout-hook.sh +1 -9
-#!/bin/sh
-echo $@ > $GIT_DIR/post-checkout.args
-EOF
-    chmod u+x clone${clone}/.git/hooks/post-checkout
-done
-
-test_expect_success 'post-checkout runs as expected ' '
-	GIT_DIR=clone1/.git git checkout master &&
-	test -e clone1/.git/post-checkout.args
+	mv .git/hooks-disabled .git/hooks &&

I am not sure why you want to do this---it sends a wrong signal to readers saying that you want to use whatever hook that are in the moved-away .git/hooks-disabled/ directory. I am guessing that the only reason why you do this is because there must be .git/hooks directory in order for write_script below to work, so a more readable approach would be to "mkdir .git/hooks" instead, no?

t/t5403-post-checkout-hook.sh test_description='Test the post-checkout hook.' · line 12

Quoted 2 lines
t/t5403-post-checkout-hook.sh +2 -0
+	write_script .git/hooks/post-checkout <<-\EOF &&
+	echo $@ >.git/post-checkout.args

A dollar-at inside a shell script that is not in a pair of dq always makes readers wonder if the author forgot dq around it or omitted eq around it deliberately; avoid it.

In this case, use "$@" (i.e. within dq) would be more friendly to readers. The second best is to write it $*, as in this context we know that $1, $2 and $3 will not contain any $IFS, that echo will take these three separate arguments and write them on one line separated by a space in between, which will allow "read A B C" read them. Or "$*" to feed such a single line with three arguments separated by a space in between as a single string to echo for the same effect.

t/t5403-post-checkout-hook.sh test_description='Test the post-checkout hook.' · line 16

Quoted 4 lines
t/t5403-post-checkout-hook.sh +4 -0
+	EOF
+	test_commit one &&
+	test_commit two &&
+	test_commit three three

Makes readers wonder why the last one duplicates. Is this because you somehow do not want to use three.t as the pathname in a later test? "test_commit X" that creates test file X.t is a quite well established convention (see "git grep '\.t\>' t/"), by the way.

t/t5403-post-checkout-hook.sh test_description='Test the post-checkout hook.' · line 29

Quoted 10 lines
t/t5403-post-checkout-hook.sh +2 -4
 '
 
 test_expect_success 'post-checkout receives the right arguments with HEAD unchanged ' '
-	old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
-	new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
-	flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
-	test $old = $new && test $flag = 1
 
+	git checkout master &&
+	test -e .git/post-checkout.args &&

Use "test -f", as you do know you'd be creating a file ("-e" succeeds as long as it _exists_, and does not care if it is a file or directory or whatever).

t/t5403-post-checkout-hook.sh test_description='Test the post-checkout hook.' · line 23

Quoted 1 line
t/t5403-post-checkout-hook.sh +1 -0
+	read old new flag <.git/post-checkout.args &&

This indeed is much nicer.

t/t5403-post-checkout-hook.sh test_description='Test the post-checkout hook.' · line 25

Quoted 2 lines
t/t5403-post-checkout-hook.sh +2 -0
+	test $old = $new && test $flag = 1 &&
+	rm -f .git/post-checkout.args

The last one is not a test but a clean-up. If any of the earlier step failed (e.g. $old and $new were different), the output file would be left behind, resulting in confusing the next test.

Instead, do it like so:

	test_expect_success 'title of the test' '
        	test_when_finished "rm -f .git/post-checkout.args" &&
		git checkout master &&
		test -f .git/post-checkout.args &&
		read old new flag <.git/post-checkout.args &&
		test $old = $new &&
		test $flag = 1
	'

That is, use test_when_finished() before the step that creates the file that may be left behind to arrange that it will be cleaned at the end.

This comment on clean-up applies to _all_ tests in this patch that has "rm -f .git/post-checkout.args" at the end.

t/t5403-post-checkout-hook.sh test_description='Test the post-checkout hook.' · line 31

Quoted 9 lines
t/t5403-post-checkout-hook.sh +3 -2
 '
 
 test_expect_success 'post-checkout runs as expected ' '
-	GIT_DIR=clone1/.git git checkout master &&
-	test -e clone1/.git/post-checkout.args
+	git checkout master &&
+	test -e .git/post-checkout.args &&
+	rm -f .git/post-checkout.args
 '

Now that the script got so simplified, this one looks even more redundant, given that the previous one already checked the same "checkout 'master' when already at the tip of 'master'" situation.

Do we still need this one, in other words?

t/t5403-post-checkout-hook.sh test_description='Test the post-checkout hook.' · line 38

Quoted 11 lines
t/t5403-post-checkout-hook.sh +4 -5
 test_expect_success 'post-checkout args are correct with git checkout -b ' '
-	GIT_DIR=clone1/.git git checkout -b new1 &&
-	old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
-	new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
-	flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
-	test $old = $new && test $flag = 1
+	git checkout -b new1 &&
+	read old new flag <.git/post-checkout.args &&
+	test $old = $new && test $flag = 1 &&
+	rm -f .git/post-checkout.args
 '

This one forgets "did the hook run and create the file" before "read", unlike the previous tests. It is not strictly necessary as "read" will fail if the file is not there, but it'd be better to be consistent.

t/t5403-post-checkout-hook.sh test_description='Test the post-checkout hook.' · line 54

Quoted 1 line
t/t5403-post-checkout-hook.sh +0 -0
 if test "$(git config --bool core.filemode)" = true; then

This is a tangent but this conditional came from an ancient d42ec126 ("disable post-checkout test on Cygwin", 2009-03-17) that says

disable post-checkout test on Cygwin

It is broken because of the tricks we have to play with lstat to get the bearable perfomance out of the call. Sadly, it disables access to Cygwin's executable attribute, which Windows filesystems do not have at all.

I wonder if this is still relevant these days (Cc'ed Ramsay for input). Windows port should be running enabled hooks (and X_OK is made pretty much no-op in compat/mingw.c IIUC), so the above conditional is overly broad anyway, even if Cygwin still has issues with the executable bit.

Quoted 11 lines
t/t5403-post-checkout-hook.sh +2 -4
 mkdir -p templates/hooks
-cat >templates/hooks/post-checkout <<'EOF'
-#!/bin/sh
-echo $@ > $GIT_DIR/post-checkout.args
+write_script templates/hooks/post-checkout <<-\EOF
+echo $@ >$GIT_DIR/post-checkout.args
 EOF
-chmod +x templates/hooks/post-checkout
 
 test_expect_success 'post-checkout hook is triggered by clone' '
 	git clone --template=templates . clone3 &&
#6 lore raw
In reply to [email protected]
Quoted 3 lines · [email protected] writes:

From: Orgad Shaneh <[email protected]>

Re: [PATCH 2/2] Rebase: Run post-checkout hook on checkout

There is no explanation here?

Is this a regression fix (i.e. scripted version of "rebase" used to run the hook)? Or a new feature (i.e. no earlier version of "rebase" run the hook but you think it ought to run it)?

Quoted 11 lines
---
 builtin/rebase.c              | 11 +++++++++--
 t/t5403-post-checkout-hook.sh | 20 ++++++++++++++++++++
 2 files changed, 29 insertions(+), 2 deletions(-)
builtin/rebase.c +0 -0
index b5c99ec10c..7f7a2c738e 100644

builtin/rebase.c static int run_specific_rebase(struct rebase_options *opts) · line 533

Quoted 4 lines
builtin/rebase.c +1 -0
@@ -530,6 +530,7 @@ static int run_specific_rebase(struct rebase_options *opts)
 
 #define RESET_HEAD_DETACH (1<<0)
 #define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_HOOK (1<<2)

Would it be plausible that the only possible hook that can be run by reset_head() function will always be post-checkout and nothing else, I wonder? Shouldn't this bit be called *_RUN_POST_CHECKOUT to make sure it is specific enough?

Quoted 9 lines
builtin/rebase.c +1 -0
@@ -537,6 +538,7 @@ static int reset_head(struct object_id *oid, const char *action,
 {
 	unsigned detach_head = flags & RESET_HEAD_DETACH;
 	unsigned reset_hard = flags & RESET_HEAD_HARD;
+	unsigned run_hook = flags & RESET_HEAD_RUN_HOOK;
 	struct object_id head_oid;
 	struct tree_desc desc[2] = { { NULL }, { NULL } };
 	struct lock_file lock = LOCK_INIT;

builtin/rebase.c static int reset_head(struct object_id *oid, const char *action, · line 645

Quoted 8 lines
builtin/rebase.c +4 -0
@@ -636,6 +638,10 @@ static int reset_head(struct object_id *oid, const char *action,
 			ret = update_ref(reflog_head, "HEAD", oid, NULL, 0,
 					 UPDATE_REFS_MSG_ON_ERR);
 	}
+	if (run_hook)
+		run_hook_le(NULL, "post-checkout",
+			    oid_to_hex(orig ? orig : &null_oid),
+			    oid_to_hex(oid), "1", NULL);
 

This function is never about checking out paths from tree-ish/index and is always about checking out a branch, so hardcoded "1" is justified here. Good.

Quoted 11 lines
builtin/rebase.c +2 -1
@@ -1465,7 +1471,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 					    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
 					    options.switch_to);
 				if (reset_head(&oid, "checkout",
-					       options.head_name, 0,
+					       options.head_name,
+					       RESET_HEAD_RUN_HOOK,
 					       NULL, buf.buf) < 0) {
 					ret = !!error(_("could not switch to "
 							"%s"),

builtin/rebase.c int cmd_rebase(int argc, const char **argv, const char *prefix) · line 1551

Quoted 7 lines
builtin/rebase.c +1 -1
@@ -1539,7 +1546,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	strbuf_addf(&msg, "%s: checkout %s",
 		    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
 	if (reset_head(&options.onto->object.oid, "checkout", NULL,
-		       RESET_HEAD_DETACH, NULL, msg.buf))
+		       RESET_HEAD_DETACH | RESET_HEAD_RUN_HOOK, NULL, msg.buf))
 		die(_("Could not detach HEAD"));
 	strbuf_release(&msg);

So... among 6 callers of reset_head(), these two whose *action is "checkout" gets the RUN_HOOK bit. Makes sense.

Quoted 14 lines
t/t5403-post-checkout-hook.sh +2 -0
index 9f9a5163c5..5b4e582caa 100755
@@ -13,6 +13,8 @@ test_expect_success setup '
 	EOF
 	test_commit one &&
 	test_commit two &&
+	test_commit rebase-on-me &&
+	git reset --hard HEAD^ &&
 	test_commit three three
 '
 

t/t5403-post-checkout-hook.sh test_expect_success 'post-checkout receives the right args when not switching br · line 58

Quoted 6 lines
t/t5403-post-checkout-hook.sh +3 -0
@@ -51,6 +53,24 @@ test_expect_success 'post-checkout receives the right args when not switching br
 	rm -f .git/post-checkout.args
 '
 
+test_expect_success 'post-checkout is triggered on rebase' '
+	git checkout -b rebase-test master &&
+	rm -f .git/post-checkout.args &&

Read the title of this whole test script file; it should verify what is in the file before removing it.

t/t5403-post-checkout-hook.sh test_expect_success 'post-checkout receives the right args when not switching br · line 60

Quoted 2 lines
t/t5403-post-checkout-hook.sh +2 -0
+	git rebase rebase-on-me &&
+	read old new flag < .git/post-checkout.args &&

No SP between "<" and ".git/post-checkout.args".

t/t5403-post-checkout-hook.sh test_expect_success 'post-checkout receives the right args when not switching br · line 63

Quoted 3 lines
t/t5403-post-checkout-hook.sh +3 -0
+	test $old != $new && test $flag = 1 &&
+	rm -f .git/post-checkout.args
+'

Regarding the clean-up of this file, see my review on the previous one.

t/t5403-post-checkout-hook.sh test_expect_success 'post-checkout receives the right args when not switching br · line 65

Quoted 1 line
t/t5403-post-checkout-hook.sh +1 -0
+test_expect_success 'post-checkout is triggered on rebase with fast-forward' '

The same comment as above applies to this.

Thanks.

#3 lore raw

Re: [PATCH 1/2] t5403: Refactor

In reply to Junio C Hamano
Quoted 4 lines · Junio C Hamano
Quoted 1 line · [email protected] writes:

Subject: Re: [PATCH 1/2] t5403: Refactor

[snip]

Quoted 7 lines
Quoted 1 line

if test "$(git config --bool core.filemode)" = true; then

This is a tangent but this conditional came from an ancient d42ec126 ("disable post-checkout test on Cygwin", 2009-03-17) that says

disable post-checkout test on Cygwin

Quoted 7 lines

It is broken because of the tricks we have to play with lstat to get the bearable perfomance out of the call. Sadly, it disables access to Cygwin's executable attribute, which Windows filesystems do not have at all.

I wonder if this is still relevant these days (Cc'ed Ramsay for input).

Ah, no, the 'tricks we have to play with lstat' mentioned in that commit message are long gone! ;-) If you remove that conditional, then the test passes just fine.

ATB,
Ramsay Jones

#7 lore raw
In reply to Junio C Hamano
Quoted 5 lines · Junio C Hamano
Quoted 1 line · [email protected] writes:

From: Orgad Shaneh <[email protected]>

Quoted 7 lines
Quoted 1 line

Re: [PATCH 2/2] Rebase: Run post-checkout hook on checkout

There is no explanation here?

Is this a regression fix (i.e. scripted version of "rebase" used to run the hook)? Or a new feature (i.e. no earlier version of "rebase" run the hook but you think it ought to run it)?

Added an explanation.

[snip]

Quoted 7 lines
Quoted 2 lines

+#define RESET_HEAD_RUN_HOOK (1<<2)

Would it be plausible that the only possible hook that can be run by reset_head() function will always be post-checkout and nothing else, I wonder? Shouldn't this bit be called *_RUN_POST_CHECKOUT to make sure it is specific enough?

Done

[snip]

Quoted 7 lines

t/t5403-post-checkout-hook.sh test_expect_success 'post-checkout receives the right args when not switching br · line 58

Quoted 4 lines
t/t5403-post-checkout-hook.sh +3 -0
+test_expect_success 'post-checkout is triggered on rebase' '
+     git checkout -b rebase-test master &&
+     rm -f .git/post-checkout.args &&

Read the title of this whole test script file; it should verify what is in the file before removing it.

Why? Other tests already do it. This test is meant for rebase, not plain checkout.

Quoted 5 lines

Review

Quoted 3 lines
(quoted diff) +2 -0
+     git rebase rebase-on-me &&
+     read old new flag < .git/post-checkout.args &&

No SP between "<" and ".git/post-checkout.args".

Done

Quoted 6 lines

t/t5403-post-checkout-hook.sh test_expect_success 'post-checkout receives the right args when not switching br · line 63

Quoted 3 lines
t/t5403-post-checkout-hook.sh +3 -0
+     test $old != $new && test $flag = 1 &&
+     rm -f .git/post-checkout.args
+'

Regarding the clean-up of this file, see my review on the previous one.

Done

Quoted 3 lines
Quoted 1 line

+test_expect_success 'post-checkout is triggered on rebase with fast-forward' '

The same comment as above applies to this.

Done

Thanks,
- Orgad

#4 lore raw

Re: [PATCH 1/2] t5403: Refactor

In reply to Junio C Hamano
Quoted 10 lines · Junio C Hamano
Quoted 1 line · [email protected] writes:

Subject: Re: [PATCH 1/2] t5403: Refactor

Hmph. "Refactor" alone leaves readers wondering "refactor what?", "refactor for what?" and "refactor how?". Given that the overfall goal this change seeks seems to be to simplify it by losing about 20 lines, how about justifying it like so?

Quoted 8 lines

Subject: t5403: simplify by using a single repository

There is no strong reason to use separate clones to run these tests; just use a single test repository prepared with more modern test_commit shell helper function.

While at it, replace three "awk '{print $N}'" on the same file with shell built-in "read" into three variables.

Done

[snip]

Quoted 8 lines
Quoted 1 line

+ mv .git/hooks-disabled .git/hooks &&

I am not sure why you want to do this---it sends a wrong signal to readers saying that you want to use whatever hook that are in the moved-away .git/hooks-disabled/ directory. I am guessing that the only reason why you do this is because there must be .git/hooks directory in order for write_script below to work, so a more readable approach would be to "mkdir .git/hooks" instead, no?

Agreed. Done.

Quoted 9 lines

t/t5403-post-checkout-hook.sh test_description='Test the post-checkout hook.' · line 12

Quoted 2 lines
t/t5403-post-checkout-hook.sh +2 -0
+     write_script .git/hooks/post-checkout <<-\EOF &&
+     echo $@ >.git/post-checkout.args

A dollar-at inside a shell script that is not in a pair of dq always makes readers wonder if the author forgot dq around it or omitted eq around it deliberately; avoid it.

In this case, use "$@" (i.e. within dq) would be more friendly to readers.

Done.

[snip]

Quoted 9 lines

t/t5403-post-checkout-hook.sh test_description='Test the post-checkout hook.' · line 16

Quoted 4 lines
t/t5403-post-checkout-hook.sh +4 -0
+     EOF
+     test_commit one &&
+     test_commit two &&
+     test_commit three three

Makes readers wonder why the last one duplicates. Is this because you somehow do not want to use three.t as the pathname in a later test? "test_commit X" that creates test file X.t is a quite well established convention (see "git grep '\.t\>' t/"), by the way.

Done. I wasn't aware of that.

[snip]

Quoted 5 lines
Quoted 1 line

+ test -e .git/post-checkout.args &&

Use "test -f", as you do know you'd be creating a file ("-e" succeeds as long as it _exists_, and does not care if it is a file or directory or whatever).

Just removed these test -e lines. read fails anyway if the file doesn't exist.

Quoted 3 lines
Quoted 1 line

+ read old new flag <.git/post-checkout.args &&

This indeed is much nicer.

Credit goes to Johannes :)

Quoted 18 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+     test $old = $new && test $flag = 1 &&
+     rm -f .git/post-checkout.args

The last one is not a test but a clean-up. If any of the earlier step failed (e.g. $old and $new were different), the output file would be left behind, resulting in confusing the next test.

Instead, do it like so:

        test_expect_success 'title of the test' '
                test_when_finished "rm -f .git/post-checkout.args" &&
                git checkout master &&
                test -f .git/post-checkout.args &&
                read old new flag <.git/post-checkout.args &&
                test $old = $new &&
                test $flag = 1
        '
Quoted 6 lines

That is, use test_when_finished() before the step that creates the file that may be left behind to arrange that it will be cleaned at the end.

This comment on clean-up applies to _all_ tests in this patch that has "rm -f .git/post-checkout.args" at the end.

Done

Quoted 8 lines

t/t5403-post-checkout-hook.sh test_description='Test the post-checkout hook.' · line 25

Quoted 7 lines

test_expect_success 'post-checkout runs as expected ' '

t/t5403-post-checkout-hook.sh +3 -2
-     GIT_DIR=clone1/.git git checkout master &&
-     test -e clone1/.git/post-checkout.args
+     git checkout master &&
+     test -e .git/post-checkout.args &&
+     rm -f .git/post-checkout.args
 '
Quoted 5 lines

Now that the script got so simplified, this one looks even more redundant, given that the previous one already checked the same "checkout 'master' when already at the tip of 'master'" situation.

Do we still need this one, in other words?

I agree. Removed.

Quoted 12 lines

t/t5403-post-checkout-hook.sh test_description='Test the post-checkout hook.' · line 38

Quoted 11 lines

test_expect_success 'post-checkout args are correct with git checkout -b ' '

t/t5403-post-checkout-hook.sh +4 -5
-     GIT_DIR=clone1/.git git checkout -b new1 &&
-     old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
-     new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
-     flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
-     test $old = $new && test $flag = 1
+     git checkout -b new1 &&
+     read old new flag <.git/post-checkout.args &&
+     test $old = $new && test $flag = 1 &&
+     rm -f .git/post-checkout.args
 '
Quoted 4 lines

This one forgets "did the hook run and create the file" before "read", unlike the previous tests. It is not strictly necessary as "read" will fail if the file is not there, but it'd be better to be consistent.

Made consistent by removing file existence test, and left only read.

Quoted 12 lines
Quoted 1 line

if test "$(git config --bool core.filemode)" = true; then

This is a tangent but this conditional came from an ancient d42ec126 ("disable post-checkout test on Cygwin", 2009-03-17) that says

disable post-checkout test on Cygwin

It is broken because of the tricks we have to play with lstat to get the bearable perfomance out of the call. Sadly, it disables access to Cygwin's executable attribute, which Windows filesystems do not have at all.

Quoted 5 lines

I wonder if this is still relevant these days (Cc'ed Ramsay for input). Windows port should be running enabled hooks (and X_OK is made pretty much no-op in compat/mingw.c IIUC), so the above conditional is overly broad anyway, even if Cygwin still has issues with the executable bit.

Reverted.

Thanks,
- Orgad