refs.c: make write_ref_sha1 static
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 311a6b52b1a9b65c997357e2d1d80e0e4743b2ca..6b4236a67f1eec5635ae89c70e81cd92d692f9db 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "refs.h"
 #include "object.h"
 #include "tag.h"
@@ -79,7 +80,8 @@ static int check_refname_component(const char *refname, int flags)
                if (refname[1] == '\0')
                        return -1; /* Component equals ".". */
        }
-       if (cp - refname >= 5 && !memcmp(cp - 5, ".lock", 5))
+       if (cp - refname >= LOCK_SUFFIX_LEN &&
+           !memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN))
                return -1; /* Refname ends with ".lock". */
        return cp - refname;
 }
@@ -785,13 +787,13 @@ static void prime_ref_dir(struct ref_dir *dir)
        }
 }
 
-static int entry_matches(struct ref_entry *entry, const char *refname)
+static int entry_matches(struct ref_entry *entry, const struct string_list *list)
 {
-       return refname && !strcmp(entry->name, refname);
+       return list && string_list_has_string(list, entry->name);
 }
 
 struct nonmatching_ref_data {
-       const char *skip;
+       const struct string_list *skip;
        struct ref_entry *found;
 };
 
@@ -815,16 +817,19 @@ static void report_refname_conflict(struct ref_entry *entry,
 /*
  * Return true iff a reference named refname could be created without
  * conflicting with the name of an existing reference in dir.  If
- * oldrefname is non-NULL, ignore potential conflicts with oldrefname
- * (e.g., because oldrefname is scheduled for deletion in the same
+ * skip is non-NULL, ignore potential conflicts with refs in skip
+ * (e.g., because they are scheduled for deletion in the same
  * operation).
  *
  * Two reference names conflict if one of them exactly matches the
  * leading components of the other; e.g., "foo/bar" conflicts with
  * both "foo" and with "foo/bar/baz" but not with "foo/bar" or
  * "foo/barbados".
+ *
+ * skip must be sorted.
  */
-static int is_refname_available(const char *refname, const char *oldrefname,
+static int is_refname_available(const char *refname,
+                               const struct string_list *skip,
                                struct ref_dir *dir)
 {
        const char *slash;
@@ -838,12 +843,12 @@ static int is_refname_available(const char *refname, const char *oldrefname,
                 * looking for a conflict with a leaf entry.
                 *
                 * If we find one, we still must make sure it is
-                * not "oldrefname".
+                * not in "skip".
                 */
                pos = search_ref_dir(dir, refname, slash - refname);
                if (pos >= 0) {
                        struct ref_entry *entry = dir->entries[pos];
-                       if (entry_matches(entry, oldrefname))
+                       if (entry_matches(entry, skip))
                                return 1;
                        report_refname_conflict(entry, refname);
                        return 0;
@@ -876,13 +881,13 @@ static int is_refname_available(const char *refname, const char *oldrefname,
                /*
                 * We found a directory named "refname". It is a
                 * problem iff it contains any ref that is not
-                * "oldrefname".
+                * in "skip".
                 */
                struct ref_entry *entry = dir->entries[pos];
                struct ref_dir *dir = get_ref_dir(entry);
                struct nonmatching_ref_data data;
 
-               data.skip = oldrefname;
+               data.skip = skip;
                sort_ref_dir(dir);
                if (!do_for_each_entry_in_dir(dir, 0, nonmatching_ref_fn, &data))
                        return 1;
@@ -2132,11 +2137,12 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
 }
 
 /*
- * Locks a "refs/" ref returning the lock on success and NULL on failure.
+ * Locks a ref returning the lock on success and NULL on failure.
  * On failure errno is set to something meaningful.
  */
 static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                                            const unsigned char *old_sha1,
+                                           const struct string_list *skip,
                                            int flags, int *type_p)
 {
        char *ref_file;
@@ -2148,6 +2154,11 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
        int missing = 0;
        int attempts_remaining = 3;
 
+       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+               errno = EINVAL;
+               return NULL;
+       }
+
        lock = xcalloc(1, sizeof(struct ref_lock));
        lock->lock_fd = -1;
 
@@ -2181,7 +2192,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
         * name is a proper prefix of our refname.
         */
        if (missing &&
-            !is_refname_available(refname, NULL, get_packed_refs(&ref_cache))) {
+            !is_refname_available(refname, skip, get_packed_refs(&ref_cache))) {
                last_errno = ENOTDIR;
                goto error_return;
        }
@@ -2191,7 +2202,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
        lflags = 0;
        if (flags & REF_NODEREF) {
                refname = orig_refname;
-               lflags |= LOCK_NODEREF;
+               lflags |= LOCK_NO_DEREF;
        }
        lock->ref_name = xstrdup(refname);
        lock->orig_ref_name = xstrdup(orig_refname);
@@ -2225,7 +2236,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                         */
                        goto retry;
                else
-                       unable_to_lock_index_die(ref_file, errno);
+                       unable_to_lock_die(ref_file, errno);
        }
        return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
 
@@ -2239,9 +2250,7 @@ struct ref_lock *lock_any_ref_for_update(const char *refname,
                                         const unsigned char *old_sha1,
                                         int flags, int *type_p)
 {
-       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
-               return NULL;
-       return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
+       return lock_ref_sha1_basic(refname, old_sha1, NULL, flags, type_p);
 }
 
 /*
@@ -2307,16 +2316,13 @@ int commit_packed_refs(void)
        if (!packed_ref_cache->lock)
                die("internal error: packed-refs not locked");
 
-       out = fdopen(packed_ref_cache->lock->fd, "w");
+       out = fdopen_lock_file(packed_ref_cache->lock, "w");
        if (!out)
                die_errno("unable to fdopen packed-refs descriptor");
 
        fprintf_or_die(out, "%s", PACKED_REFS_HEADER);
        do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
                                 0, write_packed_entry_fn, out);
-       if (fclose(out))
-               die_errno("write error");
-       packed_ref_cache->lock->fd = -1;
 
        if (commit_lock_file(packed_ref_cache->lock)) {
                save_errno = errno;
@@ -2446,8 +2452,8 @@ static void prune_ref(struct ref_to_prune *r)
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_delete(transaction, r->name, r->sha1,
-                                  REF_ISPRUNING, 1, &err) ||
-           ref_transaction_commit(transaction, NULL, &err)) {
+                                  REF_ISPRUNING, 1, NULL, &err) ||
+           ref_transaction_commit(transaction, &err)) {
                ref_transaction_free(transaction);
                error("%s", err.buf);
                strbuf_release(&err);
@@ -2598,16 +2604,17 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
        return ret;
 }
 
-static int delete_ref_loose(struct ref_lock *lock, int flag)
+static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
 {
        if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
-               /* loose */
-               int err, i = strlen(lock->lk->filename) - 5; /* .lock */
-
-               lock->lk->filename[i] = 0;
-               err = unlink_or_warn(lock->lk->filename);
-               lock->lk->filename[i] = '.';
-               if (err && errno != ENOENT)
+               /*
+                * loose.  The loose file name is the same as the
+                * lockfile name, minus ".lock":
+                */
+               char *loose_filename = get_locked_file_path(lock->lk);
+               int res = unlink_or_msg(loose_filename, err);
+               free(loose_filename);
+               if (res)
                        return 1;
        }
        return 0;
@@ -2621,8 +2628,8 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_delete(transaction, refname, sha1, delopt,
-                                  sha1 && !is_null_sha1(sha1), &err) ||
-           ref_transaction_commit(transaction, NULL, &err)) {
+                                  sha1 && !is_null_sha1(sha1), NULL, &err) ||
+           ref_transaction_commit(transaction, &err)) {
                error("%s", err.buf);
                ref_transaction_free(transaction);
                strbuf_release(&err);
@@ -2687,6 +2694,21 @@ static int rename_tmp_log(const char *newrefname)
        return 0;
 }
 
+static int rename_ref_available(const char *oldname, const char *newname)
+{
+       struct string_list skip = STRING_LIST_INIT_NODUP;
+       int ret;
+
+       string_list_insert(&skip, oldname);
+       ret = is_refname_available(newname, &skip, get_packed_refs(&ref_cache))
+           && is_refname_available(newname, &skip, get_loose_refs(&ref_cache));
+       string_list_clear(&skip, 0);
+       return ret;
+}
+
+static int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1,
+                         const char *logmsg);
+
 int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
 {
        unsigned char sha1[20], orig_sha1[20];
@@ -2706,10 +2728,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
        if (!symref)
                return error("refname %s not found", oldrefname);
 
-       if (!is_refname_available(newrefname, oldrefname, get_packed_refs(&ref_cache)))
-               return 1;
-
-       if (!is_refname_available(newrefname, oldrefname, get_loose_refs(&ref_cache)))
+       if (!rename_ref_available(oldrefname, newrefname))
                return 1;
 
        if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
@@ -2721,7 +2740,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
                goto rollback;
        }
 
-       if (!read_ref_full(newrefname, sha1, 1, &flag) &&
+       if (!read_ref_full(newrefname, sha1, 1, NULL) &&
            delete_ref(newrefname, sha1, REF_NODEREF)) {
                if (errno==EISDIR) {
                        if (remove_empty_directories(git_path("%s", newrefname))) {
@@ -2739,7 +2758,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
 
        logmoved = log;
 
-       lock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL);
+       lock = lock_ref_sha1_basic(newrefname, NULL, NULL, 0, NULL);
        if (!lock) {
                error("unable to lock %s for update", newrefname);
                goto rollback;
@@ -2754,7 +2773,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
        return 0;
 
  rollback:
-       lock = lock_ref_sha1_basic(oldrefname, NULL, 0, NULL);
+       lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, 0, NULL);
        if (!lock) {
                error("unable to lock %s for rollback", oldrefname);
                goto rollbacklog;
@@ -2934,8 +2953,11 @@ int is_branch(const char *refname)
        return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
 }
 
-/* This function must return a meaningful errno */
-int write_ref_sha1(struct ref_lock *lock,
+/*
+ * Write sha1 into the ref specified by the lock. Make sure that errno
+ * is sane on error.
+ */
+static int write_ref_sha1(struct ref_lock *lock,
        const unsigned char *sha1, const char *logmsg)
 {
        static char term = '\n';
@@ -2968,7 +2990,7 @@ int write_ref_sha1(struct ref_lock *lock,
            write_in_full(lock->lock_fd, &term, 1) != 1 ||
            close_ref(lock) < 0) {
                int save_errno = errno;
-               error("Couldn't write %s", lock->lk->filename);
+               error("Couldn't write %s", lock->lk->filename.buf);
                unlock_ref(lock);
                errno = save_errno;
                return -1;
@@ -3159,7 +3181,7 @@ static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
        return 1;
 }
 
-int read_ref_at(const char *refname, unsigned long at_time, int cnt,
+int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,
                unsigned char *sha1, char **msg,
                unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
 {
@@ -3177,8 +3199,12 @@ int read_ref_at(const char *refname, unsigned long at_time, int cnt,
 
        for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
 
-       if (!cb.reccnt)
-               die("Log for %s is empty.", refname);
+       if (!cb.reccnt) {
+               if (flags & GET_SHA1_QUIETLY)
+                       exit(128);
+               else
+                       die("Log for %s is empty.", refname);
+       }
        if (cb.found_it)
                return 0;
 
@@ -3400,6 +3426,7 @@ struct ref_update {
        int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
        struct ref_lock *lock;
        int type;
+       char *msg;
        const char refname[FLEX_ARRAY];
 };
 
@@ -3442,9 +3469,10 @@ void ref_transaction_free(struct ref_transaction *transaction)
        if (!transaction)
                return;
 
-       for (i = 0; i < transaction->nr; i++)
+       for (i = 0; i < transaction->nr; i++) {
+               free(transaction->updates[i]->msg);
                free(transaction->updates[i]);
-
+       }
        free(transaction->updates);
        free(transaction);
 }
@@ -3465,7 +3493,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
                           const char *refname,
                           const unsigned char *new_sha1,
                           const unsigned char *old_sha1,
-                          int flags, int have_old,
+                          int flags, int have_old, const char *msg,
                           struct strbuf *err)
 {
        struct ref_update *update;
@@ -3482,13 +3510,15 @@ int ref_transaction_update(struct ref_transaction *transaction,
        update->have_old = have_old;
        if (have_old)
                hashcpy(update->old_sha1, old_sha1);
+       if (msg)
+               update->msg = xstrdup(msg);
        return 0;
 }
 
 int ref_transaction_create(struct ref_transaction *transaction,
                           const char *refname,
                           const unsigned char *new_sha1,
-                          int flags,
+                          int flags, const char *msg,
                           struct strbuf *err)
 {
        struct ref_update *update;
@@ -3505,13 +3535,15 @@ int ref_transaction_create(struct ref_transaction *transaction,
        hashclr(update->old_sha1);
        update->flags = flags;
        update->have_old = 1;
+       if (msg)
+               update->msg = xstrdup(msg);
        return 0;
 }
 
 int ref_transaction_delete(struct ref_transaction *transaction,
                           const char *refname,
                           const unsigned char *old_sha1,
-                          int flags, int have_old,
+                          int flags, int have_old, const char *msg,
                           struct strbuf *err)
 {
        struct ref_update *update;
@@ -3529,6 +3561,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,
                assert(!is_null_sha1(old_sha1));
                hashcpy(update->old_sha1, old_sha1);
        }
+       if (msg)
+               update->msg = xstrdup(msg);
        return 0;
 }
 
@@ -3542,8 +3576,8 @@ int update_ref(const char *action, const char *refname,
        t = ref_transaction_begin(&err);
        if (!t ||
            ref_transaction_update(t, refname, sha1, oldval, flags,
-                                  !!oldval, &err) ||
-           ref_transaction_commit(t, action, &err)) {
+                                  !!oldval, action, &err) ||
+           ref_transaction_commit(t, &err)) {
                const char *str = "update_ref failed for ref '%s': %s";
 
                ref_transaction_free(t);
@@ -3589,7 +3623,7 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n,
 }
 
 int ref_transaction_commit(struct ref_transaction *transaction,
-                          const char *msg, struct strbuf *err)
+                          struct strbuf *err)
 {
        int ret = 0, delnum = 0, i;
        const char **delnames;
@@ -3609,25 +3643,29 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 
        /* Copy, sort, and reject duplicate refs */
        qsort(updates, n, sizeof(*updates), ref_update_compare);
-       ret = ref_update_reject_duplicates(updates, n, err);
-       if (ret)
+       if (ref_update_reject_duplicates(updates, n, err)) {
+               ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
+       }
 
        /* Acquire all locks while verifying old values */
        for (i = 0; i < n; i++) {
                struct ref_update *update = updates[i];
 
-               update->lock = lock_any_ref_for_update(update->refname,
-                                                      (update->have_old ?
-                                                       update->old_sha1 :
-                                                       NULL),
-                                                      update->flags,
-                                                      &update->type);
+               update->lock = lock_ref_sha1_basic(update->refname,
+                                                  (update->have_old ?
+                                                   update->old_sha1 :
+                                                   NULL),
+                                                  NULL,
+                                                  update->flags,
+                                                  &update->type);
                if (!update->lock) {
+                       ret = (errno == ENOTDIR)
+                               ? TRANSACTION_NAME_CONFLICT
+                               : TRANSACTION_GENERIC_ERROR;
                        if (err)
                                strbuf_addf(err, "Cannot lock the ref '%s'.",
                                            update->refname);
-                       ret = 1;
                        goto cleanup;
                }
        }
@@ -3637,15 +3675,16 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                struct ref_update *update = updates[i];
 
                if (!is_null_sha1(update->new_sha1)) {
-                       ret = write_ref_sha1(update->lock, update->new_sha1,
-                                            msg);
-                       update->lock = NULL; /* freed by write_ref_sha1 */
-                       if (ret) {
+                       if (write_ref_sha1(update->lock, update->new_sha1,
+                                          update->msg)) {
+                               update->lock = NULL; /* freed by write_ref_sha1 */
                                if (err)
                                        strbuf_addf(err, "Cannot update the ref '%s'.",
                                                    update->refname);
+                               ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        }
+                       update->lock = NULL; /* freed by write_ref_sha1 */
                }
        }
 
@@ -3654,13 +3693,16 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                struct ref_update *update = updates[i];
 
                if (update->lock) {
-                       ret |= delete_ref_loose(update->lock, update->type);
+                       if (delete_ref_loose(update->lock, update->type, err))
+                               ret = TRANSACTION_GENERIC_ERROR;
+
                        if (!(update->flags & REF_ISPRUNING))
                                delnames[delnum++] = update->lock->ref_name;
                }
        }
 
-       ret |= repack_without_refs(delnames, delnum, err);
+       if (repack_without_refs(delnames, delnum, err))
+               ret = TRANSACTION_GENERIC_ERROR;
        for (i = 0; i < delnum; i++)
                unlink_or_warn(git_path("logs/%s", delnames[i]));
        clear_loose_ref_cache(&ref_cache);