{"thread":{"id":"59709","subject":"[PATCH v10 6/8] worktree add: introduce \"try --orphan\" hint","startedAt":"2023-05-07T12:07:21Z","lastAt":"2023-05-07T12:07:21Z","messageCount":1,"participants":["Jacob Abel"],"isPatch":true,"patchVersion":10,"patchTotal":8},"messages":[{"id":"476723","messageId":"20230507120530.14669-7-jacobabel@nullpo.dev","threadId":"59709","inReplyTo":null,"subject":"[PATCH v10 6/8] worktree add: introduce \"try --orphan\" hint","fromName":"Jacob Abel","fromEmail":"jacobabel@nullpo.dev","sentAt":"2023-05-07T12:06:53Z","receivedAt":"2023-05-07T12:07:21Z","isPatch":true,"sender":{"key":"jacobabel@nullpo.dev","avatar":"https://avatars.githubusercontent.com/u/9424043?v=4"},"body":"Add a new advice/hint in `git worktree add` for when the user\ntries to create a new worktree from a reference that doesn't exist.\n\nCurrent Behavior:\n\n% git init foo\nInitialized empty Git repository in /path/to/foo/\n% touch file\n% git -C foo commit -q -a -m \"test commit\"\n% git -C foo switch --orphan norefbranch\n% git -C foo worktree add newbranch/\nPreparing worktree (new branch 'newbranch')\nfatal: invalid reference: HEAD\n%\n\nNew Behavior:\n\n% git init --bare foo\nInitialized empty Git repository in /path/to/foo/\n% touch file\n% git -C foo commit -q -a -m \"test commit\"\n% git -C foo switch --orphan norefbranch\n% git -C foo worktree add newbranch/\nPreparing worktree (new branch 'newbranch')\nhint: If you meant to create a worktree containing a new orphan branch\nhint: (branch with no commits) for this repository, you can do so\nhint: using the --orphan option:\nhint:\nhint:   git worktree add --orphan newbranch/\nhint:\nhint: Disable this message with \"git config advice.worktreeAddOrphan false\"\nfatal: invalid reference: HEAD\n% git -C foo worktree add -b newbranch2 new_wt/\nPreparing worktree (new branch 'newbranch')\nhint: If you meant to create a worktree containing a new orphan branch\nhint: (branch with no commits) for this repository, you can do so\nhint: using the --orphan option:\nhint:\nhint:   git worktree add --orphan -b newbranch2 new_wt/\nhint:\nhint: Disable this message with \"git config advice.worktreeAddOrphan false\"\nfatal: invalid reference: HEAD\n%\n\nSigned-off-by: Jacob Abel <jacobabel@nullpo.dev>\n---\n Documentation/config/advice.txt |  4 ++++\n advice.c                        |  1 +\n advice.h                        |  1 +\n builtin/worktree.c              | 26 +++++++++++++++++++++++\n t/t2400-worktree-add.sh         | 37 +++++++++++++++++++++++++++++++++\n 5 files changed, 69 insertions(+)\n\ndiff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt\nindex c96b5b2e5d..c548a91e67 100644\n--- a/Documentation/config/advice.txt\n+++ b/Documentation/config/advice.txt\n@@ -138,4 +138,8 @@ advice.*::\n \t\tcheckout.\n \tdiverging::\n \t\tAdvice shown when a fast-forward is not possible.\n+\tworktreeAddOrphan::\n+\t\tAdvice shown when a user tries to create a worktree from an\n+\t\tinvalid reference, to instruct how to create a new orphan\n+\t\tbranch instead.\n --\ndiff --git a/advice.c b/advice.c\nindex d6232439c3..e5a9bb9b44 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -78,6 +78,7 @@ static struct {\n \t[ADVICE_SUBMODULES_NOT_UPDATED] \t\t= { \"submodulesNotUpdated\", 1 },\n \t[ADVICE_UPDATE_SPARSE_PATH]\t\t\t= { \"updateSparsePath\", 1 },\n \t[ADVICE_WAITING_FOR_EDITOR]\t\t\t= { \"waitingForEditor\", 1 },\n+\t[ADVICE_WORKTREE_ADD_ORPHAN]\t\t\t= { \"worktreeAddOrphan\", 1 },\n };\n\n static const char turn_off_instructions[] =\ndiff --git a/advice.h b/advice.h\nindex 0f584163f5..2affbe1426 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -49,6 +49,7 @@ struct string_list;\n \tADVICE_UPDATE_SPARSE_PATH,\n \tADVICE_WAITING_FOR_EDITOR,\n \tADVICE_SKIPPED_CHERRY_PICKS,\n+\tADVICE_WORKTREE_ADD_ORPHAN,\n };\n\n int git_default_advice_config(const char *var, const char *value);\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 48de7fc3b0..15bdb380c7 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -1,5 +1,6 @@\n #include \"cache.h\"\n #include \"abspath.h\"\n+#include \"advice.h\"\n #include \"checkout.h\"\n #include \"config.h\"\n #include \"builtin.h\"\n@@ -39,6 +40,20 @@\n #define BUILTIN_WORKTREE_UNLOCK_USAGE \\\n \tN_(\"git worktree unlock <worktree>\")\n\n+#define WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT \\\n+\t_(\"If you meant to create a worktree containing a new orphan branch\\n\" \\\n+\t\"(branch with no commits) for this repository, you can do so\\n\" \\\n+\t\"using the --orphan flag:\\n\" \\\n+\t\"\\n\" \\\n+\t\"\tgit worktree add --orphan -b %s %s\\n\")\n+\n+#define WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT \\\n+\t_(\"If you meant to create a worktree containing a new orphan branch\\n\" \\\n+\t\"(branch with no commits) for this repository, you can do so\\n\" \\\n+\t\"using the --orphan flag:\\n\" \\\n+\t\"\\n\" \\\n+\t\"\tgit worktree add --orphan %s\\n\")\n+\n static const char * const git_worktree_usage[] = {\n \tBUILTIN_WORKTREE_ADD_USAGE,\n \tBUILTIN_WORKTREE_LIST_USAGE,\n@@ -634,6 +649,7 @@ static int add(int ac, const char **av, const char *prefix)\n \tconst char *opt_track = NULL;\n \tconst char *lock_reason = NULL;\n \tint keep_locked = 0;\n+\tint used_new_branch_options;\n \tstruct option options[] = {\n \t\tOPT__FORCE(&opts.force,\n \t\t\t   N_(\"checkout <branch> even if already checked out in other worktree\"),\n@@ -686,6 +702,7 @@ static int add(int ac, const char **av, const char *prefix)\n\n \tpath = prefix_filename(prefix, av[0]);\n \tbranch = ac < 2 ? \"HEAD\" : av[1];\n+\tused_new_branch_options = new_branch || new_branch_force;\n\n \tif (!strcmp(branch, \"-\"))\n \t\tbranch = \"@{-1}\";\n@@ -728,6 +745,15 @@ static int add(int ac, const char **av, const char *prefix)\n \t}\n\n \tif (!opts.orphan && !lookup_commit_reference_by_name(branch)) {\n+\t\tint attempt_hint = !opts.quiet && (ac < 2);\n+\t\tif (attempt_hint && used_new_branch_options) {\n+\t\t\tadvise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,\n+\t\t\t\tWORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT,\n+\t\t\t\tnew_branch, path);\n+\t\t} else if (attempt_hint) {\n+\t\t\tadvise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,\n+\t\t\t\tWORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT, path);\n+\t\t}\n \t\tdie(_(\"invalid reference: %s\"), branch);\n \t}\n\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex fba90582b6..46eef26179 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -401,6 +401,43 @@ test_expect_success '\"add\" worktree with orphan branch, lock, and reason' '\n \ttest_cmp expect .git/worktrees/orphan-with-lock-reason/locked\n '\n\n+# Note: Quoted arguments containing spaces are not supported.\n+test_wt_add_orphan_hint () {\n+\tlocal context=\"$1\" &&\n+\tlocal use_branch=$2 &&\n+\tshift 2 &&\n+\tlocal opts=\"$*\" &&\n+\ttest_expect_success \"'worktree add' show orphan hint in bad/orphan HEAD w/ $context\" '\n+\t\ttest_when_finished \"rm -rf repo\" &&\n+\t\tgit init repo &&\n+\t\t(cd repo && test_commit commit) &&\n+\t\tgit -C repo switch --orphan noref &&\n+\t\ttest_must_fail git -C repo worktree add $opts foobar/ 2>actual &&\n+\t\t! grep \"error: unknown switch\" actual &&\n+\t\tgrep \"hint: If you meant to create a worktree containing a new orphan branch\" actual &&\n+\t\tif [ $use_branch -eq 1 ]\n+\t\tthen\n+\t\t\tgrep -E \"^hint:\\s+git worktree add --orphan -b \\S+ \\S+\\s*$\" actual\n+\t\telse\n+\t\t\tgrep -E \"^hint:\\s+git worktree add --orphan \\S+\\s*$\" actual\n+\t\tfi\n+\n+\t'\n+}\n+\n+test_wt_add_orphan_hint 'no opts' 0\n+test_wt_add_orphan_hint '-b' 1 -b foobar_branch\n+test_wt_add_orphan_hint '-B' 1 -B foobar_branch\n+\n+test_expect_success \"'worktree add' doesn't show orphan hint in bad/orphan HEAD w/ --quiet\" '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(cd repo && test_commit commit) &&\n+\ttest_must_fail git -C repo worktree add --quiet foobar_branch foobar/ 2>actual &&\n+\t! grep \"error: unknown switch\" actual &&\n+\t! grep \"hint: If you meant to create a worktree containing a new orphan branch\" actual\n+'\n+\n test_expect_success 'local clone from linked checkout' '\n \tgit clone --local here here-clone &&\n \t( cd here-clone && git fsck )\n--\n2.39.3\n\n\n"}]}