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

[PATCH attempt #2] scan reflogs independently from refs

From
Nicolas Pitre <nico@cam.org>
Date
Feb 3, 2007, 18:25 UTC
Message-ID
<Pine.LNX.4.64.0702031321510.3021@xanadu.home>
In-Reply-To
<Pine.LNX.4.64.0702031252330.3021@xanadu.home>

Currently, the search for all reflogs depends on the existence of corresponding refs under the .git/refs/ directory. Let's scan the .git/logs/ directory directly instead.

Signed-off-by: Nicolas Pitre <nico@cam.org>
---
On Sat, 3 Feb 2007, Nicolas Pitre wrote:
> This applies on top of next.

Well, this was a lie. My version of 'next' wasn't up to date. Here's an updated patch.

diff --git a/builtin-fsck.c b/builtin-fsck.c
index fec1cbd..848901a 100644
--- a/builtin-fsck.c
+++ b/builtin-fsck.c
@@ -477,6 +477,12 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
 	return 0;
 }
 
+static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	for_each_reflog_ent(logname, fsck_handle_reflog_ent, NULL);
+	return 0;
+}
+
 static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct object *obj;
@@ -495,14 +501,13 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f
 	obj->used = 1;
 	mark_reachable(obj, REACHABLE);
 
-	for_each_reflog_ent(refname, fsck_handle_reflog_ent, NULL);
-
 	return 0;
 }
 
 static void get_default_heads(void)
 {
 	for_each_ref(fsck_handle_ref, NULL);
+	for_each_reflog(fsck_handle_reflog, NULL);
 
 	/*
 	 * Not having any default heads isn't really fatal, but
diff --git a/builtin-reflog.c b/builtin-reflog.c
index 7c2443a..7ee5d6f 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -245,14 +245,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
 	char *log_file, *newlog_path = NULL;
 	int status = 0;
 
-	if (strncmp(ref, "refs/", 5))
-		return error("not a ref '%s'", ref);
-
 	memset(&cb, 0, sizeof(cb));
 	/* we take the lock for the ref itself to prevent it from
 	 * getting updated.
 	 */
-	lock = lock_ref_sha1(ref + 5, sha1);
+	lock = lock_any_ref_for_update(ref, sha1);
 	if (!lock)
 		return error("cannot lock ref '%s'", ref);
 	log_file = xstrdup(git_path("logs/%s", ref));
@@ -353,7 +350,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 	}
 
 	if (do_all)
-		status |= for_each_ref(expire_reflog, &cb);
+		status |= for_each_reflog(expire_reflog, &cb);
 	while (i < argc) {
 		const char *ref = argv[i++];
 		unsigned char sha1[20];
diff --git a/reachable.c b/reachable.c
index a6a3348..01760d7 100644
--- a/reachable.c
+++ b/reachable.c
@@ -188,9 +188,9 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog)
 	/* Add all external refs */
 	for_each_ref(add_one_ref, revs);
 
-	/* Add all reflog info from refs */
+	/* Add all reflog info */
 	if (mark_reflog)
-		for_each_ref(add_one_reflog, revs);
+		for_each_reflog(add_one_reflog, revs);
 
 	/*
 	 * Set up the revision walk - this will move all commits
diff --git a/refs.c b/refs.c
index d605da3..03e8dfe 100644
--- a/refs.c
+++ b/refs.c
@@ -1203,3 +1203,53 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
 	return ret;
 }
 
+static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)
+{
+	DIR *dir = opendir(git_path("logs/%s", base));
+	int retval = errno;
+
+	if (dir) {
+		struct dirent *de;
+		int baselen = strlen(base);
+		char *log = xmalloc(baselen + 257);
+
+		memcpy(log, base, baselen);
+		if (baselen && base[baselen-1] != '/')
+			log[baselen++] = '/';
+
+		while ((de = readdir(dir)) != NULL) {
+			struct stat st;
+			int namelen;
+
+			if (de->d_name[0] == '.')
+				continue;
+			namelen = strlen(de->d_name);
+			if (namelen > 255)
+				continue;
+			if (has_extension(de->d_name, ".lock"))
+				continue;
+			memcpy(log + baselen, de->d_name, namelen+1);
+			if (stat(git_path("logs/%s", log), &st) < 0)
+				continue;
+			if (S_ISDIR(st.st_mode)) {
+				retval = do_for_each_reflog(log, fn, cb_data);
+			} else {
+				unsigned char sha1[20];
+				if (!resolve_ref(log, sha1, 0, NULL))
+					retval = error("bad ref for %s", log);
+				else
+					retval = fn(log, sha1, 0, cb_data);
+			}
+			if (retval)
+				break;
+		}
+		free(log);
+		closedir(dir);
+	}
+	return retval;
+}
+
+int for_each_reflog(each_ref_fn fn, void *cb_data)
+{
+	return do_for_each_reflog("", fn, cb_data);
+}
diff --git a/refs.h b/refs.h
index 94a58b4..acedffc 100644
--- a/refs.h
+++ b/refs.h
@@ -48,6 +48,12 @@ extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned
 typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);
 int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);
 
+/*
+ * Calls the specified function for each reflog file until it returns nonzero,
+ * and returns the value
+ */
+extern int for_each_reflog(each_ref_fn, void *);
+
 /** Returns 0 if target has the right format for a ref. **/
 extern int check_ref_format(const char *target);
 
Previous: Nicolas Pitre
Message 2 of 2 in “scan reflogs independently from refs”
  1. scan reflogs independently from refsNicolas Pitre, Feb 3, 2007
  2. scan reflogs independently from refsNicolas Pitre, Feb 3, 2007

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.