From 9f9b9010cb0b8c2874428d2f4bd21f06f747bfee Mon Sep 17 00:00:00 2001
From: Kenneth Cochran <kenneth.cochran...@gmail.com>
Date: Sun, 3 Mar 2019 15:34:20 -0600
Subject: [RFC PATCH 3/4] worktree: symref should be found anywhere in chain
Cc: Sahil Dua <sahildua2...@gmail.com>,
    Duy Nguyen <pclo...@gmail.com>,
    Jeff King <p...@peff.net>

Currently, when searching for a shared symref, a symref chain is
fully dereferenced before checking the name. This poses problems for
`git branch -d` which will happily delete a checked out symref.

The existing behaviour (recognizing a non-symbolic ref by the same name)
still exists, but it will now also find any symref that is in between the
starting symref and the first non-symbolic ref.

Concretely, for the following chain
HEAD -> symref1 -> symref2 -> master
previously the function was only able to find master.
With these changes applied, it will be able to find any of the above references.

Signed-off-by: Kenneth Cochran <kenneth.cochran...@gmail.com>
---
 t/t3207-branch-alias.sh | 13 +++++++++++++
 worktree.c              | 18 ++++++++++++------
 2 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/t/t3207-branch-alias.sh b/t/t3207-branch-alias.sh
index 9d4c8c2914..c1edeed4eb 100755
--- a/t/t3207-branch-alias.sh
+++ b/t/t3207-branch-alias.sh
@@ -55,4 +55,17 @@ test_expect_success 'git branch --alias refuses to overwrite 
existing symref' '
        test_must_fail git branch --alias syme
 '
 
+test_expect_success 'git branch -d refuses to delete a checked out symref' '
+       git branch --alias symd &&
+       git checkout symd &&
+       test_must_fail git branch -d symd
+'
+
+test_expect_success 'git branch -d refuses to delete an indirectly checked out 
symref' '
+       git symbolic-ref refs/heads/symd2 refs/heads/symd &&
+       git checkout symd2 &&
+       test_must_fail git branch -d symd2 &&
+       test_must_fail git branch -d symd
+'
+
 test_done
diff --git a/worktree.c b/worktree.c
index d6a0ee7f73..b58325c6c1 100644
--- a/worktree.c
+++ b/worktree.c
@@ -387,6 +387,17 @@ int is_worktree_being_bisected(const struct worktree *wt,
        return found_rebase;
 }
 
+static int find_symref_by_name(const char *ref_name, const struct object_id 
*oid,
+                              int flags, void *cb_data)
+{
+       const char *target = (const char *)cb_data;
+
+       if ((flags & REF_ISSYMREF) && !strcmp(target, ref_name))
+               return 1;
+       else
+               return 0;
+}
+
 /*
  * note: this function should be able to detect shared symref even if
  * HEAD is temporarily detached (e.g. in the middle of rebase or
@@ -406,9 +417,7 @@ const struct worktree *find_shared_symref(const char 
*symref,
 
        for (i = 0; worktrees[i]; i++) {
                struct worktree *wt = worktrees[i];
-               const char *symref_target;
                struct ref_store *refs;
-               int flags;
 
                if (wt->is_bare)
                        continue;
@@ -425,10 +434,7 @@ const struct worktree *find_shared_symref(const char 
*symref,
                }
 
                refs = get_worktree_ref_store(wt);
-               symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
-                                                       NULL, &flags);
-               if ((flags & REF_ISSYMREF) &&
-                   symref_target && !strcmp(symref_target, target)) {
+               if(refs_for_each_ref_in_chain(refs, find_symref_by_name, (void 
*)target, symref)) {
                        existing = wt;
                        break;
                }
-- 
2.17.1


Reply via email to