]> git.ipfire.org Git - thirdparty/git.git/commitdiff
worktree add: emit warn when there is a bad HEAD
authorJacob Abel <jacobabel@nullpo.dev>
Wed, 17 May 2023 21:49:06 +0000 (21:49 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 17 May 2023 22:55:25 +0000 (15:55 -0700)
Add a warning to `worktree add` when the command tries to reference
HEAD, there exist valid local branches, and the HEAD points to a
non-existent reference.

Current Behavior:
% git -C foo worktree list
/path/to/repo/foo     dadc8e6dac [main]
/path/to/repo/foo_wt  0000000000 [badref]
% git -C foo worktree add ../wt1
Preparing worktree (new branch 'wt1')
HEAD is now at dadc8e6dac dummy commit
% git -C foo_wt worktree add ../wt2
hint: If you meant to create a worktree containing a new orphan branch
[...]
hint: Disable this message with "git config advice.worktreeAddOrphan false"
fatal: invalid reference: HEAD
%

New Behavior:
% git -C foo worktree list
/path/to/repo/foo     dadc8e6dac [main]
/path/to/repo/foo_wt  0000000000 [badref]
% git -C foo worktree add ../wt1
Preparing worktree (new branch 'wt1')
HEAD is now at dadc8e6dac dummy commit
% git -C foo_wt worktree add ../wt2
warning: HEAD points to an invalid (or orphaned) reference.
HEAD path: '/path/to/repo/foo/.git/worktrees/foo_wt/HEAD'
HEAD contents: 'ref: refs/heads/badref'
hint: If you meant to create a worktree containing a new orphan branch
[...]
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>
builtin/worktree.c
t/t2400-worktree-add.sh

index 093b2cb032f27632627c13be30f36b147e470f60..5f620843341a9cc26d0c99f35080ad2bd2b68e41 100644 (file)
@@ -638,6 +638,9 @@ static int first_valid_ref(const char *refname,
  *
  * - Checks whether any valid local branches exist.
  *
+ * - Emits a warning if there exist any valid branches but HEAD does not point
+ *   to a valid reference.
+ *
  * Returns 1 if any of the previous checks are true, otherwise returns 0.
  */
 static int can_use_local_refs(const struct add_opts *opts)
@@ -645,6 +648,23 @@ static int can_use_local_refs(const struct add_opts *opts)
        if (head_ref(first_valid_ref, NULL)) {
                return 1;
        } else if (for_each_branch_ref(first_valid_ref, NULL)) {
+               if (!opts->quiet) {
+                       struct strbuf path = STRBUF_INIT;
+                       struct strbuf contents = STRBUF_INIT;
+
+                       strbuf_add_real_path(&path, get_worktree_git_dir(NULL));
+                       strbuf_addstr(&path, "/HEAD");
+                       strbuf_read_file(&contents, path.buf, 64);
+                       strbuf_stripspace(&contents, 0);
+                       strbuf_strip_suffix(&contents, "\n");
+
+                       warning(_("HEAD points to an invalid (or orphaned) reference.\n"
+                                 "HEAD path: '%s'\n"
+                                 "HEAD contents: '%s'"),
+                                 path.buf, contents.buf);
+                       strbuf_release(&path);
+                       strbuf_release(&contents);
+               }
                return 1;
        }
        return 0;
@@ -666,16 +686,12 @@ static int can_use_local_refs(const struct add_opts *opts)
 static int can_use_remote_refs(const struct add_opts *opts)
 {
        if (!guess_remote) {
-               if (!opts->quiet)
-                       fprintf_ln(stderr, WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT);
                return 0;
        } else if (for_each_remote_ref(first_valid_ref, NULL)) {
                return 1;
        } else if (!opts->force && remote_get(NULL)) {
                die(_("No local or remote refs exist despite at least one remote\n"
                      "present, stopping; use 'add -f' to overide or fetch a remote first"));
-       } else if (!opts->quiet) {
-               fprintf_ln(stderr, WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT);
        }
        return 0;
 }
@@ -828,8 +844,12 @@ static int add(int ac, const char **av, const char *prefix)
                int n;
                const char *s = worktree_basename(path, &n);
                new_branch = xstrndup(s, n);
-       } else if (opts.orphan || opts.detach) {
+       } else if (opts.orphan) {
                // No-op
+       } else if (opts.detach) {
+               // Check HEAD
+               if (!strcmp(branch, "HEAD"))
+                       can_use_local_refs(&opts);
        } else if (ac < 2 && new_branch) {
                // DWIM: Infer --orphan when repo has no refs.
                opts.orphan = dwim_orphan(&opts, !!opt_track, 0);
@@ -854,6 +874,10 @@ static int add(int ac, const char **av, const char *prefix)
                                branch = remote;
                        }
                }
+
+               if (!strcmp(branch, "HEAD"))
+                       can_use_local_refs(&opts);
+
        }
 
        if (!opts.orphan && !lookup_commit_reference_by_name(branch)) {
index c7ca8df586967cb24396f620256e89b1459ad5a6..0ac468e69e904f161abe3c96148a23607315cfac 100755 (executable)
@@ -722,6 +722,7 @@ test_dwim_orphan () {
        local use_quiet=0 &&
        local remote=0 &&
        local remote_ref=0 &&
+       local use_detach=0 &&
        local use_new_branch=0 &&
 
        local outcome="$1" &&
@@ -747,6 +748,10 @@ test_dwim_orphan () {
                success=0 &&
                outcome_text='"add" error inferred "--orphan" gives illegal opts combo'
                ;;
+       "warn_bad_head")
+               success=0 &&
+               outcome_text='"add" error, warn on bad HEAD, hint use orphan'
+               ;;
        *)
                echo "test_dwim_orphan(): invalid outcome: '$outcome'" >&2 &&
                return 1
@@ -818,7 +823,7 @@ test_dwim_orphan () {
                        context="$context, invalid (or orphan) HEAD"
                        ;;
 
-               # Whether the code path is tested with the base add command or -b
+               # Whether the code path is tested with the base add command, -b, or --detach
                "no_-b")
                        use_new_branch=0 &&
                        context="$context, no --branch"
@@ -827,6 +832,10 @@ test_dwim_orphan () {
                        use_new_branch=1 &&
                        context="$context, --branch"
                        ;;
+               "detach")
+                       use_detach=1 &&
+                       context="$context, --detach"
+                       ;;
 
                # Whether to check that all output is suppressed (except errors)
                # or that the output is as expected
@@ -887,6 +896,9 @@ test_dwim_orphan () {
        if [ $use_new_branch -eq 1 ]
        then
                args="$args -b foo"
+       elif [ $use_detach -eq 1 ]
+       then
+               args="$args --detach"
        else
                context="DWIM (no --branch), $context"
        fi &&
@@ -1023,6 +1035,10 @@ do
                test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote no_remote_ref no_guess_remote
                test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote no_remote_ref guess_remote
                test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote remote_ref guess_remote
+
+               test_dwim_orphan 'warn_bad_head' $dwim_test_args no_-b local_ref bad_head
+               test_dwim_orphan 'warn_bad_head' $dwim_test_args -b local_ref bad_head
+               test_dwim_orphan 'warn_bad_head' $dwim_test_args detach local_ref bad_head
        done
 
        test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode no_-b no_checkout