]> git.ipfire.org Git - thirdparty/git.git/commitdiff
switch: mention the --detach option when dying due to lack of a branch
authorAlex Henrie <alexhenrie24@gmail.com>
Sat, 26 Feb 2022 06:12:13 +0000 (23:12 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sat, 26 Feb 2022 06:21:48 +0000 (22:21 -0800)
Users who are accustomed to doing `git checkout <tag>` assume that
`git switch <tag>` will do the same thing. Inform them of the --detach
option so they aren't left wondering why `git switch` doesn't work but
`git checkout` does.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config/advice.txt
advice.c
advice.h
builtin/checkout.c
t/t2060-switch.sh

index adee26fbbbc1c2c55e03d940a09d02ac55fa66bb..c40eb09cb7e97104d2b5b44b89c4fd1d087f5d33 100644 (file)
@@ -85,6 +85,9 @@ advice.*::
                linkgit:git-switch[1] or linkgit:git-checkout[1]
                to move to the detach HEAD state, to instruct how to
                create a local branch after the fact.
+       suggestDetachingHead::
+               Advice shown when linkgit:git-switch[1] refuses to detach HEAD
+               without the explicit `--detach` option.
        checkoutAmbiguousRemoteBranchName::
                Advice shown when the argument to
                linkgit:git-checkout[1] and linkgit:git-switch[1]
index e00d30254c399eb5eeb231d7d23600b12ad16016..2e1fd483040bf399e3a689145d6c8dd3dfa277f5 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -42,6 +42,7 @@ static struct {
        [ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME]  = { "checkoutAmbiguousRemoteBranchName", 1 },
        [ADVICE_COMMIT_BEFORE_MERGE]                    = { "commitBeforeMerge", 1 },
        [ADVICE_DETACHED_HEAD]                          = { "detachedHead", 1 },
+       [ADVICE_SUGGEST_DETACHING_HEAD]                 = { "suggestDetachingHead", 1 },
        [ADVICE_FETCH_SHOW_FORCED_UPDATES]              = { "fetchShowForcedUpdates", 1 },
        [ADVICE_GRAFT_FILE_DEPRECATED]                  = { "graftFileDeprecated", 1 },
        [ADVICE_IGNORED_HOOK]                           = { "ignoredHook", 1 },
index a7521d6087e0242ac8060d6bf1ff409d4ec942d8..a3957123a1618d02b1d8b8a3438dbd92a577523a 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -20,6 +20,7 @@ struct string_list;
        ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,
        ADVICE_COMMIT_BEFORE_MERGE,
        ADVICE_DETACHED_HEAD,
+       ADVICE_SUGGEST_DETACHING_HEAD,
        ADVICE_FETCH_SHOW_FORCED_UPDATES,
        ADVICE_GRAFT_FILE_DEPRECATED,
        ADVICE_IGNORED_HOOK,
index d9b31bbb6d62a9c1b8807fa314d310608a352b26..9244827ca0281fade95f35b3d21c10a8c5df3a57 100644 (file)
@@ -1397,23 +1397,31 @@ static void die_expecting_a_branch(const struct branch_info *branch_info)
 {
        struct object_id oid;
        char *to_free;
+       int code;
 
        if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free, 0) == 1) {
                const char *ref = to_free;
 
                if (skip_prefix(ref, "refs/tags/", &ref))
-                       die(_("a branch is expected, got tag '%s'"), ref);
-               if (skip_prefix(ref, "refs/remotes/", &ref))
-                       die(_("a branch is expected, got remote branch '%s'"), ref);
-               die(_("a branch is expected, got '%s'"), ref);
+                       code = die_message(_("a branch is expected, got tag '%s'"), ref);
+               else if (skip_prefix(ref, "refs/remotes/", &ref))
+                       code = die_message(_("a branch is expected, got remote branch '%s'"), ref);
+               else
+                       code = die_message(_("a branch is expected, got '%s'"), ref);
        }
-       if (branch_info->commit)
-               die(_("a branch is expected, got commit '%s'"), branch_info->name);
-       /*
-        * This case should never happen because we already die() on
-        * non-commit, but just in case.
-        */
-       die(_("a branch is expected, got '%s'"), branch_info->name);
+       else if (branch_info->commit)
+               code = die_message(_("a branch is expected, got commit '%s'"), branch_info->name);
+       else
+               /*
+                * This case should never happen because we already die() on
+                * non-commit, but just in case.
+                */
+               code = die_message(_("a branch is expected, got '%s'"), branch_info->name);
+
+       if (advice_enabled(ADVICE_SUGGEST_DETACHING_HEAD))
+               advise(_("If you want to detach HEAD at the commit, try again with the --detach option."));
+
+       exit(code);
 }
 
 static void die_if_some_operation_in_progress(void)
index ebb961be293ef935d88c3ad3b8056725d248684b..5a7caf958c346620f6daffcc78ceaa75e03afe8f 100755 (executable)
@@ -32,6 +32,17 @@ test_expect_success 'switch and detach' '
        test_must_fail git symbolic-ref HEAD
 '
 
+test_expect_success 'suggestion to detach' '
+       test_must_fail git switch main^{commit} 2>stderr &&
+       grep "try again with the --detach option" stderr
+'
+
+test_expect_success 'suggestion to detach is suppressed with advice.suggestDetachingHead=false' '
+       test_config advice.suggestDetachingHead false &&
+       test_must_fail git switch main^{commit} 2>stderr &&
+       ! grep "try again with the --detach option" stderr
+'
+
 test_expect_success 'switch and detach current branch' '
        test_when_finished git switch main &&
        git switch main &&