Skip to content

Commit ade546b

Browse files
sunshinecogitster
authored andcommitted
worktree: invoke post-checkout hook (unless --no-checkout)
git-clone and git-checkout both invoke the post-checkout hook following a successful checkout, yet git-worktree neglects to do so even though it too "checks out" the worktree. Fix this oversight. Implementation note: The newly-created worktree may reference a branch or be detached. In the latter case, a commit lookup is performed, though the result is used only in a boolean sense to (a) determine if the commit actually exists, and (b) assign either the branch name or commit ID to HEAD. Since the post-commit hook needs to know the ID of the checked-out commit, the lookup now needs to be done in all cases, rather than only when detached. Consequently, a new boolean is needed to handle (b) since the lookup result itself can no longer perform that role. Reported-by: Matthew K Gumbel <[email protected]> Signed-off-by: Eric Sunshine <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 95ec6b1 commit ade546b

File tree

3 files changed

+47
-7
lines changed

3 files changed

+47
-7
lines changed

Documentation/githooks.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ This hook cannot affect the outcome of 'git checkout'.
170170

171171
It is also run after 'git clone', unless the --no-checkout (-n) option is
172172
used. The first parameter given to the hook is the null-ref, the second the
173-
ref of the new HEAD and the flag is always 1.
173+
ref of the new HEAD and the flag is always 1. Likewise for 'git worktree add'
174+
unless --no-checkout is used.
174175

175176
This hook can be used to perform repository validity checks, auto-display
176177
differences from the previous HEAD if different, or set working dir metadata

builtin/worktree.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,20 +218,21 @@ static int add_worktree(const char *path, const char *refname,
218218
int counter = 0, len, ret;
219219
struct strbuf symref = STRBUF_INIT;
220220
struct commit *commit = NULL;
221+
int is_branch = 0;
221222

222223
if (file_exists(path) && !is_empty_dir(path))
223224
die(_("'%s' already exists"), path);
224225

225226
/* is 'refname' a branch or commit? */
226227
if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
227-
ref_exists(symref.buf)) { /* it's a branch */
228+
ref_exists(symref.buf)) {
229+
is_branch = 1;
228230
if (!opts->force)
229231
die_if_checked_out(symref.buf, 0);
230-
} else { /* must be a commit */
231-
commit = lookup_commit_reference_by_name(refname);
232-
if (!commit)
233-
die(_("invalid reference: %s"), refname);
234232
}
233+
commit = lookup_commit_reference_by_name(refname);
234+
if (!commit)
235+
die(_("invalid reference: %s"), refname);
235236

236237
name = worktree_basename(path, &len);
237238
git_path_buf(&sb_repo, "worktrees/%.*s", (int)(path + len - name), name);
@@ -296,7 +297,7 @@ static int add_worktree(const char *path, const char *refname,
296297
argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
297298
cp.git_cmd = 1;
298299

299-
if (commit)
300+
if (!is_branch)
300301
argv_array_pushl(&cp.args, "update-ref", "HEAD",
301302
oid_to_hex(&commit->object.oid), NULL);
302303
else
@@ -327,6 +328,15 @@ static int add_worktree(const char *path, const char *refname,
327328
strbuf_addf(&sb, "%s/locked", sb_repo.buf);
328329
unlink_or_warn(sb.buf);
329330
}
331+
332+
/*
333+
* Hook failure does not warrant worktree deletion, so run hook after
334+
* is_junk is cleared, but do return appropriate code when hook fails.
335+
*/
336+
if (!ret && opts->checkout)
337+
ret = run_hook_le(NULL, "post-checkout", oid_to_hex(&null_oid),
338+
oid_to_hex(&commit->object.oid), "1", NULL);
339+
330340
argv_array_clear(&child_env);
331341
strbuf_release(&sb);
332342
strbuf_release(&symref);

t/t2025-worktree-add.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,4 +314,33 @@ test_expect_success 'rename a branch under bisect not allowed' '
314314
test_must_fail git branch -M under-bisect bisect-with-new-name
315315
'
316316

317+
post_checkout_hook () {
318+
test_when_finished "rm -f .git/hooks/post-checkout" &&
319+
mkdir -p .git/hooks &&
320+
write_script .git/hooks/post-checkout <<-\EOF
321+
echo $* >hook.actual
322+
EOF
323+
}
324+
325+
test_expect_success '"add" invokes post-checkout hook (branch)' '
326+
post_checkout_hook &&
327+
printf "%s %s 1\n" $_z40 $(git rev-parse HEAD) >hook.expect &&
328+
git worktree add gumby &&
329+
test_cmp hook.expect hook.actual
330+
'
331+
332+
test_expect_success '"add" invokes post-checkout hook (detached)' '
333+
post_checkout_hook &&
334+
printf "%s %s 1\n" $_z40 $(git rev-parse HEAD) >hook.expect &&
335+
git worktree add --detach grumpy &&
336+
test_cmp hook.expect hook.actual
337+
'
338+
339+
test_expect_success '"add --no-checkout" suppresses post-checkout hook' '
340+
post_checkout_hook &&
341+
rm -f hook.actual &&
342+
git worktree add --no-checkout gloopy &&
343+
test_path_is_missing hook.actual
344+
'
345+
317346
test_done

0 commit comments

Comments
 (0)