Code

git branch -m: forbid renaming of a symref
[git.git] / refs.c
diff --git a/refs.c b/refs.c
index 9b495eb16ef03b14791aa44e1098cd918859f0cc..8a38e0822fab4fd2a06e59265ac713a90700f80d 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -159,6 +159,8 @@ static struct cached_refs {
 } cached_refs;
 static struct ref_list *current_ref;
 
+static struct ref_list *extra_refs;
+
 static void free_ref_list(struct ref_list *list)
 {
        struct ref_list *next;
@@ -215,6 +217,17 @@ static void read_packed_refs(FILE *f, struct cached_refs *cached_refs)
        cached_refs->packed = sort_ref_list(list);
 }
 
+void add_extra_ref(const char *name, const unsigned char *sha1, int flag)
+{
+       extra_refs = add_ref(name, sha1, flag, extra_refs, NULL);
+}
+
+void clear_extra_refs(void)
+{
+       free_ref_list(extra_refs);
+       extra_refs = NULL;
+}
+
 static struct ref_list *get_packed_refs(void)
 {
        if (!cached_refs.did_packed) {
@@ -547,6 +560,11 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
        struct ref_list *packed = get_packed_refs();
        struct ref_list *loose = get_loose_refs();
 
+       struct ref_list *extra;
+
+       for (extra = extra_refs; extra; extra = extra->next)
+               retval = do_one_ref(base, fn, trim, cb_data, extra);
+
        while (packed && loose) {
                struct ref_list *entry;
                int cmp = strcmp(packed->name, loose->name);
@@ -894,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);
-               if (err) {
+               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
@@ -937,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;
 
-       if (S_ISLNK(loginfo.st_mode))
+       if (log && 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)
+               return error("refname %s is a symbolic ref, renaming it is not supported",
+                       oldref);
+       if (!symref)
                return error("refname %s not found", oldref);
 
        if (!is_refname_available(newref, oldref, get_packed_refs(), 0))
@@ -961,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);
@@ -1009,7 +1040,6 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
                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)) {
@@ -1394,6 +1424,10 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
        tz = strtoul(tz_c, NULL, 10);
        if (get_sha1_hex(logdata, sha1))
                die("Log %s is corrupt.", logfile);
+       if (is_null_sha1(sha1)) {
+               if (get_sha1_hex(logdata + 41, sha1))
+                       die("Log %s is corrupt.", logfile);
+       }
        if (msg)
                *msg = ref_msg(logdata, logend);
        munmap(log_mapped, mapsz);