Allow references with reflogs to be iterated over using a ref_iterator.
The latter is implemented as a files_reflog_iterator, which in turn uses
dir_iterator to read the "logs" directory.

Note that reflog iteration doesn't correctly handle per-worktree
reflogs (either before or after this patch).

Signed-off-by: Michael Haggerty <mhag...@alum.mit.edu>
---
 refs/files-backend.c | 113 ++++++++++++++++++++++++++++++++-------------------
 refs/refs-internal.h |   7 ++++
 2 files changed, 78 insertions(+), 42 deletions(-)

diff --git a/refs/files-backend.c b/refs/files-backend.c
index a7cc0e2..2e0d006 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -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"
@@ -3292,60 +3293,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)
+
+struct files_reflog_iterator {
+       struct ref_iterator base;
+
+       struct dir_iterator *dir_iterator;
+       struct object_id oid;
+};
+
+static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
 {
-       DIR *d = opendir(git_path("logs/%s", name->buf));
-       int retval = 0;
-       struct dirent *de;
-       int oldlen = name->len;
+       struct files_reflog_iterator *iter =
+               (struct files_reflog_iterator *)ref_iterator;
+       struct dir_iterator *diter = iter->dir_iterator;
+       int ok;
 
-       if (!d)
-               return name->len ? errno : 0;
+       while ((ok = dir_iterator_advance(diter)) == ITER_OK) {
+               int flags;
 
-       while ((de = readdir(d)) != NULL) {
-               struct stat st;
-
-               if (de->d_name[0] == '.')
+               if (!S_ISREG(diter->st.st_mode))
+                       continue;
+               if (diter->basename[0] == '.')
                        continue;
-               if (ends_with(de->d_name, ".lock"))
+               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))
-                                       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;
+}
+
+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,
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index f16a38e..87ad8b1 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -403,6 +403,13 @@ struct ref_iterator *files_ref_iterator_begin(const char 
*submodule,
                                              const char *prefix,
                                              unsigned int flags);
 
+/*
+ * Iterate over the references in the main ref_store that have a
+ * reflog. The paths within a directory are iterated over in arbitrary
+ * order.
+ */
+struct ref_iterator *files_reflog_iterator_begin(void);
+
 /* Internal implementation of reference iteration: */
 
 /*
-- 
2.8.1

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to