Fix git branch -m for symrefs.
authorMiklos Vajna <vmiklos@frugalware.org>
Sun, 26 Oct 2008 02:33:56 +0000 (03:33 +0100)
committerJunio C Hamano <gitster@pobox.com>
Sun, 26 Oct 2008 21:42:57 +0000 (14:42 -0700)
This had two problems with symrefs. First, it copied the actual sha1
instead of the "pointer", second it failed to remove the old ref after a
successful rename.

Given that till now delete_ref() always dereferenced symrefs, a new
parameters has been introduced to delete_ref() to allow deleting refs
without a dereference.

Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin-branch.c
builtin-remote.c
builtin-reset.c
builtin-send-pack.c
builtin-tag.c
builtin-update-ref.c
cache.h
receive-pack.c
refs.c
t/t3200-branch.sh

index b1a2ad7a6b3b150cda8d031a87352a4daedc40ea..4b4abfd3639d2c6a1405e2e056ed5362b4b73abb 100644 (file)
@@ -160,7 +160,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
                        continue;
                }
 
-               if (delete_ref(name, sha1)) {
+               if (delete_ref(name, sha1, 0)) {
                        error("Error deleting %sbranch '%s'", remote,
                               argv[i]);
                        ret = 1;
index 90a4e35828697f349a38ba3486c40813db32cee0..584280fbf5bbbd485a1388adb15104a1ad1cf19c 100644 (file)
@@ -340,7 +340,7 @@ static int remove_branches(struct string_list *branches)
                const char *refname = item->string;
                unsigned char *sha1 = item->util;
 
-               if (delete_ref(refname, sha1))
+               if (delete_ref(refname, sha1, 0))
                        result |= error("Could not remove branch %s", refname);
        }
        return result;
@@ -570,7 +570,7 @@ static int prune(int argc, const char **argv)
                        const char *refname = states.stale.items[i].util;
 
                        if (!dry_run)
-                               result |= delete_ref(refname, NULL);
+                               result |= delete_ref(refname, NULL, 0);
 
                        printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
                               abbrev_ref(refname, "refs/remotes/"));
index 16e6bb20f1af6a7a684a747b3c0cc26ceabe8ce7..9514b77f8c0b4e8a576e888df905b501e198df24 100644 (file)
@@ -279,7 +279,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
        }
        else if (old_orig)
-               delete_ref("ORIG_HEAD", old_orig);
+               delete_ref("ORIG_HEAD", old_orig, 0);
        prepend_reflog_action("updating HEAD", msg, sizeof(msg));
        update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);
 
index 2af9f2934142f55858f4b5c76deb6397ac74b9f8..ea1ad6e35f59ef361b40e8f5a54bc9fc9940e59a 100644 (file)
@@ -226,7 +226,7 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
                if (args.verbose)
                        fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
                if (ref->deletion) {
-                       delete_ref(rs.dst, NULL);
+                       delete_ref(rs.dst, NULL, 0);
                } else
                        update_ref("update by push", rs.dst,
                                        ref->new_sha1, NULL, 0, 0);
index f2853d08c77368b37b40c7ea51f5a124208d385f..5b141c584680ecc2362fc0e036e01dbeefa4ee90 100644 (file)
@@ -125,7 +125,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
 static int delete_tag(const char *name, const char *ref,
                                const unsigned char *sha1)
 {
-       if (delete_ref(ref, sha1))
+       if (delete_ref(ref, sha1, 0))
                return 1;
        printf("Deleted tag '%s'\n", name);
        return 0;
index 56a0b1b39cf4c4fc51dbbff256240655bc36a038..d8f3142c06c2d7c5202c9c132a12457d6e8b43a6 100644 (file)
@@ -48,7 +48,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
                die("%s: not a valid old SHA1", oldval);
 
        if (delete)
-               return delete_ref(refname, oldval ? oldsha1 : NULL);
+               return delete_ref(refname, oldval ? oldsha1 : NULL, 0);
        else
                return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
                                  no_deref ? REF_NODEREF : 0, DIE_ON_ERR);
diff --git a/cache.h b/cache.h
index 884fae826cb8c296520aecbc8c23d9848dacb606..715348a0663e278d4eecc0c04c0ad9b8148493b2 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -420,7 +420,7 @@ extern int commit_locked_index(struct lock_file *);
 extern void set_alternate_index_output(const char *);
 extern int close_lock_file(struct lock_file *);
 extern void rollback_lock_file(struct lock_file *);
-extern int delete_ref(const char *, const unsigned char *sha1);
+extern int delete_ref(const char *, const unsigned char *sha1, int delopt);
 
 /* Environment bits from configuration mechanism */
 extern int trust_executable_bit;
index d44c19e6b577023dcbaa188a0e67130ff4e5bd9a..f0145bd9011689b29e3aad3db5fb55cd94aacb2a 100644 (file)
@@ -222,7 +222,7 @@ static const char *update(struct command *cmd)
                        warning ("Allowing deletion of corrupt ref.");
                        old_sha1 = NULL;
                }
-               if (delete_ref(name, old_sha1)) {
+               if (delete_ref(name, old_sha1, 0)) {
                        error("failed to delete %s", name);
                        return "failed to delete";
                }
diff --git a/refs.c b/refs.c
index 39a3b23804d2da715c564459bf320be23d41c1bf..b929301752c3081c7d42cea8e0297e663fe500cb 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -912,25 +912,33 @@ static int repack_without_ref(const char *refname)
        return commit_lock_file(&packlock);
 }
 
-int delete_ref(const char *refname, const unsigned char *sha1)
+int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
 {
        struct ref_lock *lock;
-       int err, i, ret = 0, flag = 0;
+       int err, i = 0, ret = 0, flag = 0;
 
        lock = lock_ref_sha1_basic(refname, sha1, 0, &flag);
        if (!lock)
                return 1;
        if (!(flag & REF_ISPACKED)) {
                /* loose */
-               i = strlen(lock->lk->filename) - 5; /* .lock */
-               lock->lk->filename[i] = 0;
-               err = unlink(lock->lk->filename);
+               const char *path;
+
+               if (!(delopt & REF_NODEREF)) {
+                       i = strlen(lock->lk->filename) - 5; /* .lock */
+                       lock->lk->filename[i] = 0;
+                       path = lock->lk->filename;
+               } else {
+                       path = git_path(refname);
+               }
+               err = unlink(path);
                if (err && errno != ENOENT) {
                        ret = 1;
                        error("unlink(%s) failed: %s",
-                             lock->lk->filename, strerror(errno));
+                             path, strerror(errno));
                }
-               lock->lk->filename[i] = '.';
+               if (!(delopt & REF_NODEREF))
+                       lock->lk->filename[i] = '.';
        }
        /* removing the loose one could have resurrected an earlier
         * packed one.  Also, if it was not loose we need to repack
@@ -955,11 +963,16 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        struct ref_lock *lock;
        struct stat loginfo;
        int log = !lstat(git_path("logs/%s", oldref), &loginfo);
+       const char *symref = NULL;
+       int is_symref = 0;
 
        if (S_ISLNK(loginfo.st_mode))
                return error("reflog for %s is a symlink", oldref);
 
-       if (!resolve_ref(oldref, orig_sha1, 1, &flag))
+       symref = resolve_ref(oldref, orig_sha1, 1, &flag);
+       if (flag & REF_ISSYMREF)
+               is_symref = 1;
+       if (!symref)
                return error("refname %s not found", oldref);
 
        if (!is_refname_available(newref, oldref, get_packed_refs(), 0))
@@ -979,12 +992,12 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
                return error("unable to move logfile logs/%s to tmp-renamed-log: %s",
                        oldref, strerror(errno));
 
-       if (delete_ref(oldref, orig_sha1)) {
+       if (delete_ref(oldref, orig_sha1, REF_NODEREF)) {
                error("unable to delete old %s", oldref);
                goto rollback;
        }
 
-       if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1)) {
+       if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1, REF_NODEREF)) {
                if (errno==EISDIR) {
                        if (remove_empty_directories(git_path("%s", newref))) {
                                error("Directory not empty: %s", newref);
@@ -1022,18 +1035,20 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        }
        logmoved = log;
 
-       lock = lock_ref_sha1_basic(newref, NULL, 0, NULL);
-       if (!lock) {
-               error("unable to lock %s for update", newref);
-               goto rollback;
-       }
-
-       lock->force_write = 1;
-       hashcpy(lock->old_sha1, orig_sha1);
-       if (write_ref_sha1(lock, orig_sha1, logmsg)) {
-               error("unable to write current sha1 into %s", newref);
-               goto rollback;
-       }
+       if (!is_symref) {
+               lock = lock_ref_sha1_basic(newref, NULL, 0, NULL);
+               if (!lock) {
+                       error("unable to lock %s for update", newref);
+                       goto rollback;
+               }
+               lock->force_write = 1;
+               hashcpy(lock->old_sha1, orig_sha1);
+               if (write_ref_sha1(lock, orig_sha1, logmsg)) {
+                       error("unable to write current sha1 into %s", newref);
+                       goto rollback;
+               }
+       } else
+               create_symref(newref, symref, logmsg);
 
        return 0;
 
index 2147eacc5057128facc08816a2980646bed28ec5..fdeb1f529ce14d5500976c0e5683c3cbc2fa3501 100755 (executable)
@@ -112,6 +112,15 @@ test_expect_success 'config information was renamed, too' \
        "test $(git config branch.s.dummy) = Hello &&
         test_must_fail git config branch.s/s/dummy"
 
+test_expect_success 'renaming a symref' \
+'
+       git symbolic-ref refs/heads/master2 refs/heads/master &&
+       git branch -m master2 master3 &&
+       git symbolic-ref refs/heads/master3 &&
+       test -f .git/refs/heads/master &&
+       ! test -f .git/refs/heads/master2
+'
+
 test_expect_success \
     'git branch -m u v should fail when the reflog for u is a symlink' '
      git branch -l u &&