From: David Turner <[email protected]>

When renaming refs, don't dereference either the origin or the destination
before renaming.

The origin does not need to be dereferenced because it is presently
forbidden to rename symbolic refs.

Not dereferencing the destination fixes a bug where renaming on top of
a broken symref would use the pointed-to ref name for the moved
reflog.

Add a test for the reflog bug.

Signed-off-by: David Turner <[email protected]>
Signed-off-by: Junio C Hamano <[email protected]>
Signed-off-by: Michael Haggerty <[email protected]>
---
 refs/files-backend.c | 13 ++++++++-----
 t/t3200-branch.sh    |  9 +++++++++
 2 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/refs/files-backend.c b/refs/files-backend.c
index 91416db..e4cdd5a 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2358,11 +2358,12 @@ int rename_ref(const char *oldrefname, const char 
*newrefname, const char *logms
        struct stat loginfo;
        int log = !lstat(git_path("logs/%s", oldrefname), &loginfo);
        struct strbuf err = STRBUF_INIT;
+       const int resolve_flags = RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE;
 
        if (log && S_ISLNK(loginfo.st_mode))
                return error("reflog for %s is a symlink", oldrefname);
 
-       if (!resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING, orig_sha1, 
&flag))
+       if (!resolve_ref_unsafe(oldrefname, resolve_flags, orig_sha1, &flag))
                return error("refname %s not found", oldrefname);
 
        if (flag & REF_ISSYMREF)
@@ -2380,8 +2381,8 @@ int rename_ref(const char *oldrefname, const char 
*newrefname, const char *logms
                goto rollback;
        }
 
-       if (!read_ref_full(newrefname, RESOLVE_REF_READING, sha1, NULL) &&
-           delete_ref(newrefname, sha1, REF_NODEREF)) {
+       if (!read_ref_full(newrefname, resolve_flags, sha1, NULL) &&
+           delete_ref(newrefname, NULL, REF_NODEREF)) {
                if (errno==EISDIR) {
                        struct strbuf path = STRBUF_INIT;
                        int result;
@@ -2405,7 +2406,8 @@ int rename_ref(const char *oldrefname, const char 
*newrefname, const char *logms
 
        logmoved = log;
 
-       lock = lock_ref_sha1_basic(newrefname, NULL, NULL, NULL, 0, NULL, &err);
+       lock = lock_ref_sha1_basic(newrefname, NULL, NULL, NULL, REF_NODEREF,
+                                  NULL, &err);
        if (!lock) {
                error("unable to rename '%s' to '%s': %s", oldrefname, 
newrefname, err.buf);
                strbuf_release(&err);
@@ -2423,7 +2425,8 @@ int rename_ref(const char *oldrefname, const char 
*newrefname, const char *logms
        return 0;
 
  rollback:
-       lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, NULL, 0, NULL, &err);
+       lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, NULL, REF_NODEREF,
+                                  NULL, &err);
        if (!lock) {
                error("unable to lock %s for rollback: %s", oldrefname, 
err.buf);
                strbuf_release(&err);
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index f3e3b6c..4281160 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -79,6 +79,15 @@ test_expect_success 'git branch -m dumps usage' '
        test_i18ngrep "branch name required" err
 '
 
+test_expect_success 'git branch -m m broken_symref should work' '
+       test_when_finished "git branch -D broken_symref" &&
+       git branch -l m &&
+       git symbolic-ref refs/heads/broken_symref refs/heads/i_am_broken &&
+       git branch -m m broken_symref &&
+       git reflog exists refs/heads/broken_symref &&
+       test_must_fail git reflog exists refs/heads/i_am_broken
+'
+
 test_expect_success 'git branch -m m m/m should work' '
        git branch -l m &&
        git branch -m m m/m &&
-- 
2.8.1

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to