worktree add: introduce "try --orphan" hint

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>
This commit is contained in:
Jacob Abel 2023-05-17 21:48:52 +00:00 коммит произвёл Junio C Hamano
Родитель 7ab8918985
Коммит 35f0383ca6
5 изменённых файлов: 69 добавлений и 0 удалений

Просмотреть файл

@ -138,4 +138,8 @@ advice.*::
checkout. checkout.
diverging:: diverging::
Advice shown when a fast-forward is not possible. 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.
-- --

Просмотреть файл

@ -78,6 +78,7 @@ static struct {
[ADVICE_SUBMODULES_NOT_UPDATED] = { "submodulesNotUpdated", 1 }, [ADVICE_SUBMODULES_NOT_UPDATED] = { "submodulesNotUpdated", 1 },
[ADVICE_UPDATE_SPARSE_PATH] = { "updateSparsePath", 1 }, [ADVICE_UPDATE_SPARSE_PATH] = { "updateSparsePath", 1 },
[ADVICE_WAITING_FOR_EDITOR] = { "waitingForEditor", 1 }, [ADVICE_WAITING_FOR_EDITOR] = { "waitingForEditor", 1 },
[ADVICE_WORKTREE_ADD_ORPHAN] = { "worktreeAddOrphan", 1 },
}; };
static const char turn_off_instructions[] = static const char turn_off_instructions[] =

Просмотреть файл

@ -49,6 +49,7 @@ struct string_list;
ADVICE_UPDATE_SPARSE_PATH, ADVICE_UPDATE_SPARSE_PATH,
ADVICE_WAITING_FOR_EDITOR, ADVICE_WAITING_FOR_EDITOR,
ADVICE_SKIPPED_CHERRY_PICKS, ADVICE_SKIPPED_CHERRY_PICKS,
ADVICE_WORKTREE_ADD_ORPHAN,
}; };
int git_default_advice_config(const char *var, const char *value); int git_default_advice_config(const char *var, const char *value);

Просмотреть файл

@ -1,5 +1,6 @@
#include "cache.h" #include "cache.h"
#include "abspath.h" #include "abspath.h"
#include "advice.h"
#include "checkout.h" #include "checkout.h"
#include "config.h" #include "config.h"
#include "builtin.h" #include "builtin.h"
@ -39,6 +40,20 @@
#define BUILTIN_WORKTREE_UNLOCK_USAGE \ #define BUILTIN_WORKTREE_UNLOCK_USAGE \
N_("git worktree unlock <worktree>") 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[] = { static const char * const git_worktree_usage[] = {
BUILTIN_WORKTREE_ADD_USAGE, BUILTIN_WORKTREE_ADD_USAGE,
BUILTIN_WORKTREE_LIST_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 *opt_track = NULL;
const char *lock_reason = NULL; const char *lock_reason = NULL;
int keep_locked = 0; int keep_locked = 0;
int used_new_branch_options;
struct option options[] = { struct option options[] = {
OPT__FORCE(&opts.force, OPT__FORCE(&opts.force,
N_("checkout <branch> even if already checked out in other worktree"), 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]); path = prefix_filename(prefix, av[0]);
branch = ac < 2 ? "HEAD" : av[1]; branch = ac < 2 ? "HEAD" : av[1];
used_new_branch_options = new_branch || new_branch_force;
if (!strcmp(branch, "-")) if (!strcmp(branch, "-"))
branch = "@{-1}"; 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)) { 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); die(_("invalid reference: %s"), branch);
} }

Просмотреть файл

@ -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 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' ' test_expect_success 'local clone from linked checkout' '
git clone --local here here-clone && git clone --local here here-clone &&
( cd here-clone && git fsck ) ( cd here-clone && git fsck )