path: root/builtin/worktree.c
diff options
authorEric Sunshine <>2018-02-15 19:18:41 (GMT)
committerJunio C Hamano <>2018-02-15 21:44:00 (GMT)
commita4bf1e3c2e4d0583a1822419fb54a566e6e46162 (patch)
treeda78628ba23ed96da1de2eed6e5b6592e2bdd99b /builtin/worktree.c
parentade546be4786637ba039478d6f027becb1160803 (diff)
worktree: add: fix 'post-checkout' not knowing new worktree location
Although "git worktree add" learned to run the 'post-checkout' hook in ade546be47 (worktree: invoke post-checkout hook, 2017-12-07), it neglected to change to the directory of the newly-created worktree before running the hook. Instead, the hook runs within the directory from which the "git worktree add" command itself was invoked, which effectively neuters the hook since it knows nothing about the new worktree directory. Further, ade546be47 failed to sanitize the environment before running the hook, which means that user-assigned values of GIT_DIR and GIT_WORK_TREE could mislead the hook about the location of the new worktree. In the case of "git worktree add" being run from a bare repository, the GIT_DIR="." assigned by Git itself leaks into the hook's environment and breaks Git commands; this is so even when the working directory is correctly changed to the new worktree before the hook runs since ".", relative to the new worktree directory, does not point at the bare repository. Fix these problems by (1) changing to the new worktree's directory before running the hook, and (2) sanitizing the environment of GIT_DIR and GIT_WORK_TREE so hooks can't be confused by misleading values. Enhance the t2025 'post-checkout' tests to verify that the hook is indeed run within the correct directory and that Git commands invoked by the hook compute Git-dir and top-level worktree locations correctly. While at it, also add two new tests: (1) verify that the hook is run within the correct directory even when the new worktree is created from a sibling worktree (as opposed to the main worktree); (2) verify that the hook is provided with correct context when the new worktree is created from a bare repository (test provided by Lars Schneider). Implementation Notes: Rather than sanitizing the environment of GIT_DIR and GIT_WORK_TREE, an alternative would be to set them explicitly, as is already done for other Git commands run internally by "git worktree add". This patch opts instead to sanitize the environment in order to clearly document that the worktree is fully functional by the time the hook is run, thus does not require special environmental overrides. The hook is run manually, rather than via run_hook_le(), since it needs to change the working directory to that of the worktree, and run_hook_le() does not provide such functionality. As this is a one-off case, adding 'run_hook' overloads which allow the directory to be set does not seem warranted at this time. Reported-by: Lars Schneider <> Signed-off-by: Eric Sunshine <> Signed-off-by: Junio C Hamano <>
Diffstat (limited to 'builtin/worktree.c')
1 files changed, 17 insertions, 3 deletions
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 9591f10..81e4041 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -333,9 +333,23 @@ done:
* Hook failure does not warrant worktree deletion, so run hook after
* is_junk is cleared, but do return appropriate code when hook fails.
- if (!ret && opts->checkout)
- ret = run_hook_le(NULL, "post-checkout", oid_to_hex(&null_oid),
- oid_to_hex(&commit->object.oid), "1", NULL);
+ if (!ret && opts->checkout) {
+ const char *hook = find_hook("post-checkout");
+ if (hook) {
+ const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
+ cp.git_cmd = 0;
+ cp.no_stdin = 1;
+ cp.stdout_to_stderr = 1;
+ cp.dir = path;
+ cp.env = env;
+ cp.argv = NULL;
+ argv_array_pushl(&cp.args, absolute_path(hook),
+ oid_to_hex(&null_oid),
+ oid_to_hex(&commit->object.oid),
+ "1", NULL);
+ ret = run_command(&cp);
+ }
+ }