]> git.ipfire.org Git - thirdparty/git.git/commitdiff
worktree add: introduce "try --orphan" hint
authorJacob Abel <jacobabel@nullpo.dev>
Wed, 17 May 2023 21:48:52 +0000 (21:48 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 17 May 2023 22:55:24 +0000 (15:55 -0700)
Add a new advice/hint in `git worktree add` for when the user
tries to create a new worktree from a reference that doesn't exist.

Current Behavior:

% git init foo
Initialized empty Git repository in /path/to/foo/
% touch file
% git -C foo commit -q -a -m "test commit"
% git -C foo switch --orphan norefbranch
% git -C foo worktree add newbranch/
Preparing worktree (new branch 'newbranch')
fatal: invalid reference: HEAD
%

New Behavior:

% git init --bare foo
Initialized empty Git repository in /path/to/foo/
% touch file
% git -C foo commit -q -a -m "test commit"
% git -C foo switch --orphan norefbranch
% git -C foo worktree add newbranch/
Preparing worktree (new branch 'newbranch')
hint: If you meant to create a worktree containing a new orphan branch
hint: (branch with no commits) for this repository, you can do so
hint: using the --orphan option:
hint:
hint:   git worktree add --orphan newbranch/
hint:
hint: Disable this message with "git config advice.worktreeAddOrphan false"
fatal: invalid reference: HEAD
% git -C foo worktree add -b newbranch2 new_wt/
Preparing worktree (new branch 'newbranch')
hint: If you meant to create a worktree containing a new orphan branch
hint: (branch with no commits) for this repository, you can do so
hint: using the --orphan option:
hint:
hint:   git worktree add --orphan -b newbranch2 new_wt/
hint:
hint: Disable this message with "git config advice.worktreeAddOrphan false"
fatal: invalid reference: HEAD
%

Signed-off-by: Jacob Abel <jacobabel@nullpo.dev>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config/advice.txt
advice.c
advice.h
builtin/worktree.c
t/t2400-worktree-add.sh

index c96b5b2e5d2160c3f6b1147fc6500f22acf06a55..c548a91e6761c762b19f26e2bb1f5a36f8192ba9 100644 (file)
@@ -138,4 +138,8 @@ advice.*::
                checkout.
        diverging::
                Advice shown when a fast-forward is not possible.
+       worktreeAddOrphan::
+               Advice shown when a user tries to create a worktree from an
+               invalid reference, to instruct how to create a new orphan
+               branch instead.
 --
index d6232439c3863b4c6136adfd12465b0aa714e81f..e5a9bb9b446e95bf6e8e63e3cfed7803fb72104a 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -78,6 +78,7 @@ static struct {
        [ADVICE_SUBMODULES_NOT_UPDATED]                 = { "submodulesNotUpdated", 1 },
        [ADVICE_UPDATE_SPARSE_PATH]                     = { "updateSparsePath", 1 },
        [ADVICE_WAITING_FOR_EDITOR]                     = { "waitingForEditor", 1 },
+       [ADVICE_WORKTREE_ADD_ORPHAN]                    = { "worktreeAddOrphan", 1 },
 };
 
 static const char turn_off_instructions[] =
index 0f584163f5808ce536ff5d1a5294eed2437ba23b..2affbe142616de0d329c9aaaaee9ca355fb73adf 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -49,6 +49,7 @@ struct string_list;
        ADVICE_UPDATE_SPARSE_PATH,
        ADVICE_WAITING_FOR_EDITOR,
        ADVICE_SKIPPED_CHERRY_PICKS,
+       ADVICE_WORKTREE_ADD_ORPHAN,
 };
 
 int git_default_advice_config(const char *var, const char *value);
index 48de7fc3b023c69e5635d90b1e428b79e6dd7af1..15bdb380c7a58d5e7c71156a3db699f2535aff00 100644 (file)
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "abspath.h"
+#include "advice.h"
 #include "checkout.h"
 #include "config.h"
 #include "builtin.h"
 #define BUILTIN_WORKTREE_UNLOCK_USAGE \
        N_("git worktree unlock <worktree>")
 
+#define WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT \
+       _("If you meant to create a worktree containing a new orphan branch\n" \
+       "(branch with no commits) for this repository, you can do so\n" \
+       "using the --orphan flag:\n" \
+       "\n" \
+       "       git worktree add --orphan -b %s %s\n")
+
+#define WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT \
+       _("If you meant to create a worktree containing a new orphan branch\n" \
+       "(branch with no commits) for this repository, you can do so\n" \
+       "using the --orphan flag:\n" \
+       "\n" \
+       "       git worktree add --orphan %s\n")
+
 static const char * const git_worktree_usage[] = {
        BUILTIN_WORKTREE_ADD_USAGE,
        BUILTIN_WORKTREE_LIST_USAGE,
@@ -634,6 +649,7 @@ static int add(int ac, const char **av, const char *prefix)
        const char *opt_track = NULL;
        const char *lock_reason = NULL;
        int keep_locked = 0;
+       int used_new_branch_options;
        struct option options[] = {
                OPT__FORCE(&opts.force,
                           N_("checkout <branch> even if already checked out in other worktree"),
@@ -686,6 +702,7 @@ static int add(int ac, const char **av, const char *prefix)
 
        path = prefix_filename(prefix, av[0]);
        branch = ac < 2 ? "HEAD" : av[1];
+       used_new_branch_options = new_branch || new_branch_force;
 
        if (!strcmp(branch, "-"))
                branch = "@{-1}";
@@ -728,6 +745,15 @@ static int add(int ac, const char **av, const char *prefix)
        }
 
        if (!opts.orphan && !lookup_commit_reference_by_name(branch)) {
+               int attempt_hint = !opts.quiet && (ac < 2);
+               if (attempt_hint && used_new_branch_options) {
+                       advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,
+                               WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT,
+                               new_branch, path);
+               } else if (attempt_hint) {
+                       advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,
+                               WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT, path);
+               }
                die(_("invalid reference: %s"), branch);
        }
 
index fba90582b67d526144d2d2edea27ff99026612ca..46eef261799c44f2863aeb98dbd8fc47f9b5979a 100755 (executable)
@@ -401,6 +401,43 @@ test_expect_success '"add" worktree with orphan branch, lock, and reason' '
        test_cmp expect .git/worktrees/orphan-with-lock-reason/locked
 '
 
+# Note: Quoted arguments containing spaces are not supported.
+test_wt_add_orphan_hint () {
+       local context="$1" &&
+       local use_branch=$2 &&
+       shift 2 &&
+       local opts="$*" &&
+       test_expect_success "'worktree add' show orphan hint in bad/orphan HEAD w/ $context" '
+               test_when_finished "rm -rf repo" &&
+               git init repo &&
+               (cd repo && test_commit commit) &&
+               git -C repo switch --orphan noref &&
+               test_must_fail git -C repo worktree add $opts foobar/ 2>actual &&
+               ! grep "error: unknown switch" actual &&
+               grep "hint: If you meant to create a worktree containing a new orphan branch" actual &&
+               if [ $use_branch -eq 1 ]
+               then
+                       grep -E "^hint:\s+git worktree add --orphan -b \S+ \S+\s*$" actual
+               else
+                       grep -E "^hint:\s+git worktree add --orphan \S+\s*$" actual
+               fi
+
+       '
+}
+
+test_wt_add_orphan_hint 'no opts' 0
+test_wt_add_orphan_hint '-b' 1 -b foobar_branch
+test_wt_add_orphan_hint '-B' 1 -B foobar_branch
+
+test_expect_success "'worktree add' doesn't show orphan hint in bad/orphan HEAD w/ --quiet" '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (cd repo && test_commit commit) &&
+       test_must_fail git -C repo worktree add --quiet foobar_branch foobar/ 2>actual &&
+       ! grep "error: unknown switch" actual &&
+       ! grep "hint: If you meant to create a worktree containing a new orphan branch" actual
+'
+
 test_expect_success 'local clone from linked checkout' '
        git clone --local here here-clone &&
        ( cd here-clone && git fsck )