Merge branch 'jn/unpack-trees-checkout-m-carry-deletion' into maint
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 211429df066dd5f79c29dbce54b92cd3546d95d0..27927f2319130cc0575817542dfd47c37cc5149b 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1162,7 +1162,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
 
                if (de->d_name[0] == '.')
                        continue;
-               if (has_extension(de->d_name, ".lock"))
+               if (ends_with(de->d_name, ".lock"))
                        continue;
                strbuf_addstr(&refname, de->d_name);
                refdir = *refs->name
@@ -1542,9 +1542,8 @@ static enum peel_status peel_object(const unsigned char *name, unsigned char *sh
 
        if (o->type == OBJ_NONE) {
                int type = sha1_object_info(name, NULL);
-               if (type < 0)
+               if (type < 0 || !object_as_type(o, type, 0))
                        return PEEL_INVALID;
-               o->type = type;
        }
 
        if (o->type != OBJ_TAG)
@@ -2859,24 +2858,38 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
                len += copy_msg(logrec + len - 1, msg) - 1;
        written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
        free(logrec);
-       if (close(logfd) != 0 || written != len)
-               return error("Unable to append to %s", log_file);
+       if (written != len) {
+               int save_errno = errno;
+               close(logfd);
+               error("Unable to append to %s", log_file);
+               errno = save_errno;
+               return -1;
+       }
+       if (close(logfd)) {
+               int save_errno = errno;
+               error("Unable to append to %s", log_file);
+               errno = save_errno;
+               return -1;
+       }
        return 0;
 }
 
-static int is_branch(const char *refname)
+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,
        const unsigned char *sha1, const char *logmsg)
 {
        static char term = '\n';
        struct object *o;
 
-       if (!lock)
+       if (!lock) {
+               errno = EINVAL;
                return -1;
+       }
        if (!lock->force_write && !hashcmp(lock->old_sha1, sha1)) {
                unlock_ref(lock);
                return 0;
@@ -2886,19 +2899,23 @@ int write_ref_sha1(struct ref_lock *lock,
                error("Trying to write ref %s with nonexistent object %s",
                        lock->ref_name, sha1_to_hex(sha1));
                unlock_ref(lock);
+               errno = EINVAL;
                return -1;
        }
        if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
                error("Trying to write non-commit object %s to branch %s",
                        sha1_to_hex(sha1), lock->ref_name);
                unlock_ref(lock);
+               errno = EINVAL;
                return -1;
        }
        if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
-           write_in_full(lock->lock_fd, &term, 1) != 1
-               || close_ref(lock) < 0) {
+           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);
                unlock_ref(lock);
+               errno = save_errno;
                return -1;
        }
        clear_loose_ref_cache(&ref_cache);
@@ -3280,7 +3297,7 @@ static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data
 
                if (de->d_name[0] == '.')
                        continue;
-               if (has_extension(de->d_name, ".lock"))
+               if (ends_with(de->d_name, ".lock"))
                        continue;
                strbuf_addstr(name, de->d_name);
                if (stat(git_path("logs/%s", name->buf), &st) < 0) {
@@ -3335,10 +3352,13 @@ static struct ref_lock *update_ref_lock(const char *refname,
 
 static int update_ref_write(const char *action, const char *refname,
                            const unsigned char *sha1, struct ref_lock *lock,
-                           enum action_on_err onerr)
+                           struct strbuf *err, enum action_on_err onerr)
 {
        if (write_ref_sha1(lock, sha1, action) < 0) {
                const char *str = "Cannot update the ref '%s'.";
+               if (err)
+                       strbuf_addf(err, str, refname);
+
                switch (onerr) {
                case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
                case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
@@ -3407,19 +3427,25 @@ static struct ref_update *add_update(struct ref_transaction *transaction,
        return update;
 }
 
-void 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 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,
+                          struct strbuf *err)
 {
-       struct ref_update *update = add_update(transaction, refname);
+       struct ref_update *update;
 
+       if (have_old && !old_sha1)
+               die("BUG: have_old is true but old_sha1 is NULL");
+
+       update = add_update(transaction, refname);
        hashcpy(update->new_sha1, new_sha1);
        update->flags = flags;
        update->have_old = have_old;
        if (have_old)
                hashcpy(update->old_sha1, old_sha1);
+       return 0;
 }
 
 void ref_transaction_create(struct ref_transaction *transaction,
@@ -3459,7 +3485,7 @@ int update_ref(const char *action, const char *refname,
        lock = update_ref_lock(refname, oldval, flags, NULL, onerr);
        if (!lock)
                return 1;
-       return update_ref_write(action, refname, sha1, lock, onerr);
+       return update_ref_write(action, refname, sha1, lock, NULL, onerr);
 }
 
 static int ref_update_compare(const void *r1, const void *r2)
@@ -3470,29 +3496,23 @@ static int ref_update_compare(const void *r1, const void *r2)
 }
 
 static int ref_update_reject_duplicates(struct ref_update **updates, int n,
-                                       enum action_on_err onerr)
+                                       struct strbuf *err)
 {
        int i;
        for (i = 1; i < n; i++)
                if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) {
                        const char *str =
                                "Multiple updates for ref '%s' not allowed.";
-                       switch (onerr) {
-                       case UPDATE_REFS_MSG_ON_ERR:
-                               error(str, updates[i]->refname); break;
-                       case UPDATE_REFS_DIE_ON_ERR:
-                               die(str, updates[i]->refname); break;
-                       case UPDATE_REFS_QUIET_ON_ERR:
-                               break;
-                       }
+                       if (err)
+                               strbuf_addf(err, str, updates[i]->refname);
+
                        return 1;
                }
        return 0;
 }
 
 int ref_transaction_commit(struct ref_transaction *transaction,
-                          const char *msg, struct strbuf *err,
-                          enum action_on_err onerr)
+                          const char *msg, struct strbuf *err)
 {
        int ret = 0, delnum = 0, i;
        const char **delnames;
@@ -3507,7 +3527,7 @@ 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, onerr);
+       ret = ref_update_reject_duplicates(updates, n, err);
        if (ret)
                goto cleanup;
 
@@ -3519,7 +3539,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                                               (update->have_old ?
                                                update->old_sha1 : NULL),
                                               update->flags,
-                                              &update->type, onerr);
+                                              &update->type,
+                                              UPDATE_REFS_QUIET_ON_ERR);
                if (!update->lock) {
                        if (err)
                                strbuf_addf(err, "Cannot lock the ref '%s'.",
@@ -3537,7 +3558,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                        ret = update_ref_write(msg,
                                               update->refname,
                                               update->new_sha1,
-                                              update->lock, onerr);
+                                              update->lock, err,
+                                              UPDATE_REFS_QUIET_ON_ERR);
                        update->lock = NULL; /* freed by update_ref_write */
                        if (ret)
                                goto cleanup;