summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Turner <dturner@twopensource.com>2016-02-24 17:58:51 -0500
committerJunio C Hamano <gitster@pobox.com>2016-02-25 16:01:03 -0800
commit77512d62225b69f91d8e28da8dbca2b6ac943e90 (patch)
tree41aa7c30bb8b676e6272b0b6852741fdbe825eb9
parentc5f09f025ab930a08014a23dc921d02990eb44c1 (diff)
downloadgit-77512d62225b69f91d8e28da8dbca2b6ac943e90.tar.gz
refs: don't dereference on rename
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 <dturner@twopensource.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r--refs/files-backend.c15
-rwxr-xr-xt/t3200-branch.sh9
2 files changed, 18 insertions, 6 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 46ce6e15ee..2e3d5be98f 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2372,7 +2372,7 @@ static int files_rename_ref(const char *oldrefname, const char *newrefname,
const char *logmsg)
{
unsigned char sha1[20], orig_sha1[20];
- int flag = 0, logmoved = 0;
+ int flag = 0, logmoved = 0, resolve_flags;
struct ref_lock *lock;
struct stat loginfo;
int log = !lstat(git_path("logs/%s", oldrefname), &loginfo);
@@ -2382,7 +2382,8 @@ static int files_rename_ref(const char *oldrefname, const char *newrefname,
if (log && S_ISLNK(loginfo.st_mode))
return error("reflog for %s is a symlink", oldrefname);
- symref = resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING,
+ resolve_flags = RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE;
+ symref = resolve_ref_unsafe(oldrefname, resolve_flags,
orig_sha1, &flag);
if (flag & REF_ISSYMREF)
return error("refname %s is a symbolic ref, renaming it is not supported",
@@ -2402,8 +2403,8 @@ static int files_rename_ref(const char *oldrefname, const char *newrefname,
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;
@@ -2427,7 +2428,8 @@ static int files_rename_ref(const char *oldrefname, const char *newrefname,
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);
@@ -2445,7 +2447,8 @@ static int files_rename_ref(const char *oldrefname, const char *newrefname,
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 cdaf6f64ec..07e9749c65 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 &&