Merge branch 'ar/mksnpath'
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index b929301752c3081c7d42cea8e0297e663fe500cb..9ae447b10b79a4ba3c834dd74977db5bd321f055 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -390,6 +390,18 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re
        return retval;
 }
 
+/*
+ * If the "reading" argument is set, this function finds out what _object_
+ * the ref points at by "reading" the ref.  The ref, if it is not symbolic,
+ * has to exist, and if it is symbolic, it has to point at an existing ref,
+ * because the "read" goes through the symref to the ref it points at.
+ *
+ * The access that is not "reading" may often be "writing", but does not
+ * have to; it can be merely checking _where it leads to_. If it is a
+ * prelude to "writing" to the ref, a write to a symref that points at
+ * yet-to-be-born ref will create the real ref pointed by the symref.
+ * reading=0 allows the caller to check where such a symref leads to.
+ */
 const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
 {
        int depth = MAXDEPTH;
@@ -401,7 +413,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
                *flag = 0;
 
        for (;;) {
-               const char *path = git_path("%s", ref);
+               char path[PATH_MAX];
                struct stat st;
                char *buf;
                int fd;
@@ -409,13 +421,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
                if (--depth < 0)
                        return NULL;
 
-               /* Special case: non-existing file.
-                * Not having the refs/heads/new-branch is OK
-                * if we are writing into it, so is .git/HEAD
-                * that points at refs/heads/master still to be
-                * born.  It is NOT OK if we are resolving for
-                * reading.
-                */
+               git_snpath(path, sizeof(path), "%s", ref);
+               /* Special case: non-existing file. */
                if (lstat(path, &st) < 0) {
                        struct ref_list *list = get_packed_refs();
                        while (list) {
@@ -790,7 +797,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
        struct ref_lock *lock;
        struct stat st;
        int last_errno = 0;
-       int type;
+       int type, lflags;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
 
        lock = xcalloc(1, sizeof(struct ref_lock));
@@ -830,8 +837,11 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
 
        lock->lk = xcalloc(1, sizeof(struct lock_file));
 
-       if (flags & REF_NODEREF)
+       lflags = LOCK_DIE_ON_ERROR;
+       if (flags & REF_NODEREF) {
                ref = orig_ref;
+               lflags |= LOCK_NODEREF;
+       }
        lock->ref_name = xstrdup(ref);
        lock->orig_ref_name = xstrdup(orig_ref);
        ref_file = git_path("%s", ref);
@@ -845,8 +855,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
                error("unable to create directory for %s", ref_file);
                goto error_return;
        }
-       lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1);
 
+       lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags);
        return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
 
  error_return:
@@ -920,7 +930,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
        lock = lock_ref_sha1_basic(refname, sha1, 0, &flag);
        if (!lock)
                return 1;
-       if (!(flag & REF_ISPACKED)) {
+       if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
                /* loose */
                const char *path;
 
@@ -964,14 +974,14 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        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))
+       if (log && S_ISLNK(loginfo.st_mode))
                return error("reflog for %s is a symlink", oldref);
 
        symref = resolve_ref(oldref, orig_sha1, 1, &flag);
        if (flag & REF_ISSYMREF)
-               is_symref = 1;
+               return error("refname %s is a symbolic ref, renaming it is not supported",
+                       oldref);
        if (!symref)
                return error("refname %s not found", oldref);
 
@@ -1035,20 +1045,17 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        }
        logmoved = log;
 
-       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);
+       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;
+       }
 
        return 0;
 
@@ -1136,13 +1143,14 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
        int logfd, written, oflags = O_APPEND | O_WRONLY;
        unsigned maxlen, len;
        int msglen;
-       char *log_file, *logrec;
+       char log_file[PATH_MAX];
+       char *logrec;
        const char *committer;
 
        if (log_all_ref_updates < 0)
                log_all_ref_updates = !is_bare_repository();
 
-       log_file = git_path("logs/%s", ref_name);
+       git_snpath(log_file, sizeof(log_file), "logs/%s", ref_name);
 
        if (log_all_ref_updates &&
            (!prefixcmp(ref_name, "refs/heads/") ||
@@ -1271,7 +1279,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
        const char *lockpath;
        char ref[1000];
        int fd, len, written;
-       char *git_HEAD = xstrdup(git_path("%s", ref_target));
+       char *git_HEAD = git_pathdup("%s", ref_target);
        unsigned char old_sha1[20], new_sha1[20];
 
        if (logmsg && read_ref(ref_target, old_sha1))