Merge branch 'bw/pathspec-remove-unused-extern-decl' into maint
[gitweb.git] / refs / files-backend.c
index 4232da8cb7184ff0c501efdce3c64e9e091c2ad8..12290d249643b5aaab18961ca91637910d0d7261 100644 (file)
@@ -2,6 +2,7 @@
 #include "../refs.h"
 #include "refs-internal.h"
 #include "../iterator.h"
+#include "../dir-iterator.h"
 #include "../lockfile.h"
 #include "../object.h"
 #include "../dir.h"
@@ -2029,14 +2030,14 @@ static int verify_lock(struct ref_lock *lock,
                        errno = save_errno;
                        return -1;
                } else {
-                       hashclr(lock->old_oid.hash);
+                       oidclr(&lock->old_oid);
                        return 0;
                }
        }
        if (old_sha1 && hashcmp(lock->old_oid.hash, old_sha1)) {
                strbuf_addf(err, "ref '%s' is at %s but expected %s",
                            lock->ref_name,
-                           sha1_to_hex(lock->old_oid.hash),
+                           oid_to_hex(&lock->old_oid),
                            sha1_to_hex(old_sha1));
                errno = EBUSY;
                return -1;
@@ -3291,60 +3292,88 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat
        strbuf_release(&sb);
        return ret;
 }
-/*
- * Call fn for each reflog in the namespace indicated by name.  name
- * must be empty or end with '/'.  Name will be used as a scratch
- * space, but its contents will be restored before return.
- */
-static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data)
-{
-       DIR *d = opendir(git_path("logs/%s", name->buf));
-       int retval = 0;
-       struct dirent *de;
-       int oldlen = name->len;
 
-       if (!d)
-               return name->len ? errno : 0;
+struct files_reflog_iterator {
+       struct ref_iterator base;
 
-       while ((de = readdir(d)) != NULL) {
-               struct stat st;
+       struct dir_iterator *dir_iterator;
+       struct object_id oid;
+};
 
-               if (de->d_name[0] == '.')
+static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
+{
+       struct files_reflog_iterator *iter =
+               (struct files_reflog_iterator *)ref_iterator;
+       struct dir_iterator *diter = iter->dir_iterator;
+       int ok;
+
+       while ((ok = dir_iterator_advance(diter)) == ITER_OK) {
+               int flags;
+
+               if (!S_ISREG(diter->st.st_mode))
                        continue;
-               if (ends_with(de->d_name, ".lock"))
+               if (diter->basename[0] == '.')
+                       continue;
+               if (ends_with(diter->basename, ".lock"))
                        continue;
-               strbuf_addstr(name, de->d_name);
-               if (stat(git_path("logs/%s", name->buf), &st) < 0) {
-                       ; /* silently ignore */
-               } else {
-                       if (S_ISDIR(st.st_mode)) {
-                               strbuf_addch(name, '/');
-                               retval = do_for_each_reflog(name, fn, cb_data);
-                       } else {
-                               struct object_id oid;
 
-                               if (read_ref_full(name->buf, 0, oid.hash, NULL))
-                                       retval = error("bad ref for %s", name->buf);
-                               else
-                                       retval = fn(name->buf, &oid, 0, cb_data);
-                       }
-                       if (retval)
-                               break;
+               if (read_ref_full(diter->relative_path, 0,
+                                 iter->oid.hash, &flags)) {
+                       error("bad ref for %s", diter->path.buf);
+                       continue;
                }
-               strbuf_setlen(name, oldlen);
+
+               iter->base.refname = diter->relative_path;
+               iter->base.oid = &iter->oid;
+               iter->base.flags = flags;
+               return ITER_OK;
        }
-       closedir(d);
-       return retval;
+
+       iter->dir_iterator = NULL;
+       if (ref_iterator_abort(ref_iterator) == ITER_ERROR)
+               ok = ITER_ERROR;
+       return ok;
+}
+
+static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator,
+                                  struct object_id *peeled)
+{
+       die("BUG: ref_iterator_peel() called for reflog_iterator");
+}
+
+static int files_reflog_iterator_abort(struct ref_iterator *ref_iterator)
+{
+       struct files_reflog_iterator *iter =
+               (struct files_reflog_iterator *)ref_iterator;
+       int ok = ITER_DONE;
+
+       if (iter->dir_iterator)
+               ok = dir_iterator_abort(iter->dir_iterator);
+
+       base_ref_iterator_free(ref_iterator);
+       return ok;
+}
+
+static struct ref_iterator_vtable files_reflog_iterator_vtable = {
+       files_reflog_iterator_advance,
+       files_reflog_iterator_peel,
+       files_reflog_iterator_abort
+};
+
+struct ref_iterator *files_reflog_iterator_begin(void)
+{
+       struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
+       struct ref_iterator *ref_iterator = &iter->base;
+
+       base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
+       iter->dir_iterator = dir_iterator_begin(git_path("logs"));
+       return ref_iterator;
 }
 
 int for_each_reflog(each_ref_fn fn, void *cb_data)
 {
-       int retval;
-       struct strbuf name;
-       strbuf_init(&name, PATH_MAX);
-       retval = do_for_each_reflog(&name, fn, cb_data);
-       strbuf_release(&name);
-       return retval;
+       return do_for_each_ref_iterator(files_reflog_iterator_begin(),
+                                       fn, cb_data);
 }
 
 static int ref_update_reject_duplicates(struct string_list *refnames,
@@ -3488,6 +3517,38 @@ static const char *original_update_refname(struct ref_update *update)
        return update->refname;
 }
 
+/*
+ * Check whether the REF_HAVE_OLD and old_oid values stored in update
+ * are consistent with oid, which is the reference's current value. If
+ * everything is OK, return 0; otherwise, write an error message to
+ * err and return -1.
+ */
+static int check_old_oid(struct ref_update *update, struct object_id *oid,
+                        struct strbuf *err)
+{
+       if (!(update->flags & REF_HAVE_OLD) ||
+                  !hashcmp(oid->hash, update->old_sha1))
+               return 0;
+
+       if (is_null_sha1(update->old_sha1))
+               strbuf_addf(err, "cannot lock ref '%s': "
+                           "reference already exists",
+                           original_update_refname(update));
+       else if (is_null_oid(oid))
+               strbuf_addf(err, "cannot lock ref '%s': "
+                           "reference is missing but expected %s",
+                           original_update_refname(update),
+                           sha1_to_hex(update->old_sha1));
+       else
+               strbuf_addf(err, "cannot lock ref '%s': "
+                           "is at %s but expected %s",
+                           original_update_refname(update),
+                           oid_to_hex(oid),
+                           sha1_to_hex(update->old_sha1));
+
+       return -1;
+}
+
 /*
  * Prepare for carrying out update:
  * - Lock the reference referred to by update.
@@ -3533,7 +3594,7 @@ static int lock_ref_for_update(struct ref_update *update,
 
                reason = strbuf_detach(err, NULL);
                strbuf_addf(err, "cannot lock ref '%s': %s",
-                           update->refname, reason);
+                           original_update_refname(update), reason);
                free(reason);
                return ret;
        }
@@ -3547,28 +3608,17 @@ static int lock_ref_for_update(struct ref_update *update,
                         * the transaction, so we have to read it here
                         * to record and possibly check old_sha1:
                         */
-                       if (read_ref_full(update->refname,
-                                         mustexist ? RESOLVE_REF_READING : 0,
+                       if (read_ref_full(referent.buf, 0,
                                          lock->old_oid.hash, NULL)) {
                                if (update->flags & REF_HAVE_OLD) {
                                        strbuf_addf(err, "cannot lock ref '%s': "
-                                                   "can't resolve old value",
-                                                   update->refname);
-                                       return TRANSACTION_GENERIC_ERROR;
-                               } else {
-                                       hashclr(lock->old_oid.hash);
+                                                   "error reading reference",
+                                                   original_update_refname(update));
+                                       return -1;
                                }
-                       }
-                       if ((update->flags & REF_HAVE_OLD) &&
-                           hashcmp(lock->old_oid.hash, update->old_sha1)) {
-                               strbuf_addf(err, "cannot lock ref '%s': "
-                                           "is at %s but expected %s",
-                                           update->refname,
-                                           sha1_to_hex(lock->old_oid.hash),
-                                           sha1_to_hex(update->old_sha1));
+                       } else if (check_old_oid(update, &lock->old_oid, err)) {
                                return TRANSACTION_GENERIC_ERROR;
                        }
-
                } else {
                        /*
                         * Create a new update for the reference this
@@ -3585,6 +3635,9 @@ static int lock_ref_for_update(struct ref_update *update,
        } else {
                struct ref_update *parent_update;
 
+               if (check_old_oid(update, &lock->old_oid, err))
+                       return TRANSACTION_GENERIC_ERROR;
+
                /*
                 * If this update is happening indirectly because of a
                 * symref update, record the old SHA-1 in the parent
@@ -3595,20 +3648,6 @@ static int lock_ref_for_update(struct ref_update *update,
                     parent_update = parent_update->parent_update) {
                        oidcpy(&parent_update->lock->old_oid, &lock->old_oid);
                }
-
-               if ((update->flags & REF_HAVE_OLD) &&
-                   hashcmp(lock->old_oid.hash, update->old_sha1)) {
-                       if (is_null_sha1(update->old_sha1))
-                               strbuf_addf(err, "cannot lock ref '%s': reference already exists",
-                                           original_update_refname(update));
-                       else
-                               strbuf_addf(err, "cannot lock ref '%s': is at %s but expected %s",
-                                           original_update_refname(update),
-                                           sha1_to_hex(lock->old_oid.hash),
-                                           sha1_to_hex(update->old_sha1));
-
-                       return TRANSACTION_GENERIC_ERROR;
-               }
        }
 
        if ((update->flags & REF_HAVE_NEW) &&
@@ -3630,7 +3669,7 @@ static int lock_ref_for_update(struct ref_update *update,
                         */
                        update->lock = NULL;
                        strbuf_addf(err,
-                                   "cannot update the ref '%s': %s",
+                                   "cannot update ref '%s': %s",
                                    update->refname, write_err);
                        free(write_err);
                        return TRANSACTION_GENERIC_ERROR;