git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 13/13] for_each_reflog(): reimplement using iterators

From
Michael Haggerty <mhagger@alum.mit.edu>
Date
May 30, 2016, 07:55 UTC
Message-ID
<0e0bc1c4c503ebd0ba6978e63bbb6ac54efa8fdf.1464537050.git.mhagger@alum.mit.edu>
In-Reply-To
<cover.1464537050.git.mhagger@alum.mit.edu>

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 <mhagger@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
Previous: Michael Haggerty
Message 27 of 27 in “Reference iterators”
  1. 00/13 Reference iteratorsMichael Haggerty, May 30, 2016
  2. 01/13 refs: remove unnecessary "extern" keywordsMichael Haggerty, May 30, 2016
  3. 02/13 do_for_each_ref(): move docstring to the header fileMichael Haggerty, May 30, 2016
  4. 03/13 refs: use name "prefix" consistentlyMichael Haggerty, May 30, 2016
  5. 04/13 delete_refs(): add a flags argumentMichael Haggerty, May 30, 2016
  6. 05/13 remote rm: handle symbolic refs correctlyMichael Haggerty, May 30, 2016
  7. 06/13 get_ref_cache(): only create an instance if there is a submoduleMichael Haggerty, May 30, 2016
  8. 07/13 entry_resolves_to_object(): rename function from ref_resolves_to_object()Michael Haggerty, May 30, 2016
  9. 08/13 ref_resolves_to_object(): new functionMichael Haggerty, May 30, 2016
  10. 09/13 refs: introduce an iterator interfaceMichael Haggerty, May 30, 2016
  11. Ramsay JonesMay 30, 2016
  12. Ramsay JonesMay 30, 2016
  13. Michael HaggertyMay 31, 2016
  14. Eric SunshineMay 31, 2016
  15. Michael HaggertyMay 31, 2016
  16. Eric SunshineMay 31, 2016
  17. Michael HaggertyJun 3, 2016
  18. Junio C HamanoMay 31, 2016
  19. Michael HaggertyMay 31, 2016
  20. Duy NguyenJun 2, 2016
  21. Michael HaggertyJun 2, 2016
  22. 10/13 do_for_each_ref(): reimplement using reference iterationMichael Haggerty, May 30, 2016
  23. 11/13 for_each_reflog(): don't abort for bad referencesMichael Haggerty, May 30, 2016
  24. 12/13 dir_iterator: new API for iterating over a directory treeMichael Haggerty, May 30, 2016
  25. David TurnerJun 1, 2016
  26. Michael HaggertyJun 3, 2016
  27. 13/13 for_each_reflog(): reimplement using iteratorsMichael Haggerty, May 30, 2016

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.