Merge branch 'es/worktree-checkout-hook'
authorJunio C Hamano <gitster@pobox.com>
Wed, 27 Dec 2017 19:16:21 +0000 (11:16 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 27 Dec 2017 19:16:21 +0000 (11:16 -0800)
"git worktree add" learned to run the post-checkout hook, just like
"git checkout" does, after the initial checkout.

* es/worktree-checkout-hook:
  worktree: invoke post-checkout hook (unless --no-checkout)

Documentation/githooks.txt
builtin/worktree.c
t/t2025-worktree-add.sh

index b63f2ea..f877f7b 100644 (file)
@@ -170,7 +170,8 @@ This hook cannot affect the outcome of 'git checkout'.
 
 It is also run after 'git clone', unless the --no-checkout (-n) option is
 used. The first parameter given to the hook is the null-ref, the second the
-ref of the new HEAD and the flag is always 1.
+ref of the new HEAD and the flag is always 1. Likewise for 'git worktree add'
+unless --no-checkout is used.
 
 This hook can be used to perform repository validity checks, auto-display
 differences from the previous HEAD if different, or set working dir metadata
index 002a569..7cef5b1 100644 (file)
@@ -230,20 +230,21 @@ static int add_worktree(const char *path, const char *refname,
        int counter = 0, len, ret;
        struct strbuf symref = STRBUF_INIT;
        struct commit *commit = NULL;
+       int is_branch = 0;
 
        if (file_exists(path) && !is_empty_dir(path))
                die(_("'%s' already exists"), path);
 
        /* is 'refname' a branch or commit? */
        if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
-                ref_exists(symref.buf)) { /* it's a branch */
+           ref_exists(symref.buf)) {
+               is_branch = 1;
                if (!opts->force)
                        die_if_checked_out(symref.buf, 0);
-       } else { /* must be a commit */
-               commit = lookup_commit_reference_by_name(refname);
-               if (!commit)
-                       die(_("invalid reference: %s"), refname);
        }
+       commit = lookup_commit_reference_by_name(refname);
+       if (!commit)
+               die(_("invalid reference: %s"), refname);
 
        name = worktree_basename(path, &len);
        git_path_buf(&sb_repo, "worktrees/%.*s", (int)(path + len - name), name);
@@ -308,7 +309,7 @@ static int add_worktree(const char *path, const char *refname,
        argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
        cp.git_cmd = 1;
 
-       if (commit)
+       if (!is_branch)
                argv_array_pushl(&cp.args, "update-ref", "HEAD",
                                 oid_to_hex(&commit->object.oid), NULL);
        else
@@ -339,6 +340,15 @@ done:
                strbuf_addf(&sb, "%s/locked", sb_repo.buf);
                unlink_or_warn(sb.buf);
        }
+
+       /*
+        * 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);
+
        argv_array_clear(&child_env);
        strbuf_release(&sb);
        strbuf_release(&symref);
index 6ce9b9c..1285668 100755 (executable)
@@ -444,4 +444,33 @@ test_expect_success 'git worktree --no-guess-remote option overrides config' '
        )
 '
 
+post_checkout_hook () {
+       test_when_finished "rm -f .git/hooks/post-checkout" &&
+       mkdir -p .git/hooks &&
+       write_script .git/hooks/post-checkout <<-\EOF
+       echo $* >hook.actual
+       EOF
+}
+
+test_expect_success '"add" invokes post-checkout hook (branch)' '
+       post_checkout_hook &&
+       printf "%s %s 1\n" $_z40 $(git rev-parse HEAD) >hook.expect &&
+       git worktree add gumby &&
+       test_cmp hook.expect hook.actual
+'
+
+test_expect_success '"add" invokes post-checkout hook (detached)' '
+       post_checkout_hook &&
+       printf "%s %s 1\n" $_z40 $(git rev-parse HEAD) >hook.expect &&
+       git worktree add --detach grumpy &&
+       test_cmp hook.expect hook.actual
+'
+
+test_expect_success '"add --no-checkout" suppresses post-checkout hook' '
+       post_checkout_hook &&
+       rm -f hook.actual &&
+       git worktree add --no-checkout gloopy &&
+       test_path_is_missing hook.actual
+'
+
 test_done