{"thread":{"id":"6642","subject":"[PATCH] scan reflogs independently from refs","startedAt":"2007-02-03T18:13:39Z","lastAt":"2007-02-03T18:25:43Z","messageCount":2,"participants":["Nicolas Pitre"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"33463","messageId":"Pine.LNX.4.64.0702031252330.3021@xanadu.home","threadId":"6642","inReplyTo":null,"subject":"[PATCH] scan reflogs independently from refs","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2007-02-03T18:13:39Z","receivedAt":"2007-02-03T18:13:39Z","isPatch":true,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"Currently, the search for all reflogs depends on the existence of\ncorresponding refs under the .git/refs/ directory.  Let's scan the\n.git/logs/ directory directly instead.\n\nSigned-off-by: Nicolas Pitre <nico@cam.org>\n---\n\nThis applies on top of next.  Using separate reflog for HEAD should be \ncompletely safe now.\n\nThere is one occurrence of for_each_ref that I didn't move to \nfor_each_reflog which is in revision.c:handle_reflog() since it is \nunclear to me if we're really interested in all possible reflogs or only \nthose from real branches.  Actually I don't think having the information \nabout HEAD jumping around in its reflog is useful in this context.\n\n builtin-reflog.c |    7 ++-----\n fsck-objects.c   |    9 +++++++--\n reachable.c      |    4 ++--\n refs.c           |   50 ++++++++++++++++++++++++++++++++++++++++++++++++++\n refs.h           |    6 ++++++\n 5 files changed, 67 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin-reflog.c b/builtin-reflog.c\nindex ebe3070..6e29282 100644\n--- a/builtin-reflog.c\n+++ b/builtin-reflog.c\n@@ -245,14 +245,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,\n \tchar *newlog_path = NULL;\n \tint status = 0;\n \n-\tif (strncmp(ref, \"refs/\", 5))\n-\t\treturn error(\"not a ref '%s'\", ref);\n-\n \tmemset(&cb, 0, sizeof(cb));\n \t/* we take the lock for the ref itself to prevent it from\n \t * getting updated.\n \t */\n-\tlock = lock_ref_sha1(ref + 5, sha1);\n+\tlock = lock_any_ref_for_update(ref, sha1);\n \tif (!lock)\n \t\treturn error(\"cannot lock ref '%s'\", ref);\n \tif (!file_exists(lock->log_file))\n@@ -351,7 +348,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (do_all)\n-\t\tstatus |= for_each_ref(expire_reflog, &cb);\n+\t\tstatus |= for_each_reflog(expire_reflog, &cb);\n \twhile (i < argc) {\n \t\tconst char *ref = argv[i++];\n \t\tunsigned char sha1[20];\ndiff --git a/fsck-objects.c b/fsck-objects.c\nindex ecfb014..c9b4a39 100644\n--- a/fsck-objects.c\n+++ b/fsck-objects.c\n@@ -477,6 +477,12 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,\n \treturn 0;\n }\n \n+static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, int flag, void *cb_data)\n+{\n+\tfor_each_reflog_ent(logname, fsck_handle_reflog_ent, NULL);\n+\treturn 0;\n+}\n+\n static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct object *obj;\n@@ -495,14 +501,13 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f\n \tobj->used = 1;\n \tmark_reachable(obj, REACHABLE);\n \n-\tfor_each_reflog_ent(refname, fsck_handle_reflog_ent, NULL);\n-\n \treturn 0;\n }\n \n static void get_default_heads(void)\n {\n \tfor_each_ref(fsck_handle_ref, NULL);\n+\tfor_each_reflog(fsck_handle_reflog, NULL);\n \n \t/*\n \t * Not having any default heads isn't really fatal, but\ndiff --git a/reachable.c b/reachable.c\nindex a6a3348..01760d7 100644\n--- a/reachable.c\n+++ b/reachable.c\n@@ -188,9 +188,9 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog)\n \t/* Add all external refs */\n \tfor_each_ref(add_one_ref, revs);\n \n-\t/* Add all reflog info from refs */\n+\t/* Add all reflog info */\n \tif (mark_reflog)\n-\t\tfor_each_ref(add_one_reflog, revs);\n+\t\tfor_each_reflog(add_one_reflog, revs);\n \n \t/*\n \t * Set up the revision walk - this will move all commits\ndiff --git a/refs.c b/refs.c\nindex 8117328..dc3f2d8 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1173,3 +1173,53 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)\n \treturn ret;\n }\n \n+static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)\n+{\n+\tDIR *dir = opendir(git_path(\"logs/%s\", base));\n+\tint retval = errno;\n+\n+\tif (dir) {\n+\t\tstruct dirent *de;\n+\t\tint baselen = strlen(base);\n+\t\tchar *log = xmalloc(baselen + 257);\n+\n+\t\tmemcpy(log, base, baselen);\n+\t\tif (baselen && base[baselen-1] != '/')\n+\t\t\tlog[baselen++] = '/';\n+\n+\t\twhile ((de = readdir(dir)) != NULL) {\n+\t\t\tstruct stat st;\n+\t\t\tint namelen;\n+\n+\t\t\tif (de->d_name[0] == '.')\n+\t\t\t\tcontinue;\n+\t\t\tnamelen = strlen(de->d_name);\n+\t\t\tif (namelen > 255)\n+\t\t\t\tcontinue;\n+\t\t\tif (has_extension(de->d_name, \".lock\"))\n+\t\t\t\tcontinue;\n+\t\t\tmemcpy(log + baselen, de->d_name, namelen+1);\n+\t\t\tif (stat(git_path(\"logs/%s\", log), &st) < 0)\n+\t\t\t\tcontinue;\n+\t\t\tif (S_ISDIR(st.st_mode)) {\n+\t\t\t\tretval = do_for_each_reflog(log, fn, cb_data);\n+\t\t\t} else {\n+\t\t\t\tunsigned char sha1[20];\n+\t\t\t\tif (!resolve_ref(log, sha1, 0, NULL))\n+\t\t\t\t\tretval = error(\"bad ref for %s\", log);\n+\t\t\t\telse\n+\t\t\t\t\tretval = fn(log, sha1, 0, cb_data);\n+\t\t\t}\n+\t\t\tif (retval)\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tfree(log);\n+\t\tclosedir(dir);\n+\t}\n+\treturn retval;\n+}\n+\n+int for_each_reflog(each_ref_fn fn, void *cb_data)\n+{\n+\treturn do_for_each_reflog(\"\", fn, cb_data);\n+}\ndiff --git a/refs.h b/refs.h\nindex 33450f1..8dd6ed5 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -48,6 +48,12 @@ extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned\n typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);\n int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);\n \n+/*\n+ * Calls the specified function for each reflog file until it returns nonzero,\n+ * and returns the value\n+ */\n+extern int for_each_reflog(each_ref_fn, void *);\n+\n /** Returns 0 if target has the right format for a ref. **/\n extern int check_ref_format(const char *target);\n \n"},{"id":"33465","messageId":"Pine.LNX.4.64.0702031321510.3021@xanadu.home","threadId":"6642","inReplyTo":"Pine.LNX.4.64.0702031252330.3021@xanadu.home","subject":"[PATCH attempt #2] scan reflogs independently from refs","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2007-02-03T18:25:43Z","receivedAt":"2007-02-03T18:25:43Z","isPatch":true,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"Currently, the search for all reflogs depends on the existence of\ncorresponding refs under the .git/refs/ directory.  Let's scan the\n.git/logs/ directory directly instead.\n\nSigned-off-by: Nicolas Pitre <nico@cam.org>\n---\n\nOn Sat, 3 Feb 2007, Nicolas Pitre wrote:\n\n> This applies on top of next.\n\nWell, this was a lie.  My version of 'next' wasn't up to date.  Here's \nan updated patch.\n\ndiff --git a/builtin-fsck.c b/builtin-fsck.c\nindex fec1cbd..848901a 100644\n--- a/builtin-fsck.c\n+++ b/builtin-fsck.c\n@@ -477,6 +477,12 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,\n \treturn 0;\n }\n \n+static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, int flag, void *cb_data)\n+{\n+\tfor_each_reflog_ent(logname, fsck_handle_reflog_ent, NULL);\n+\treturn 0;\n+}\n+\n static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct object *obj;\n@@ -495,14 +501,13 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f\n \tobj->used = 1;\n \tmark_reachable(obj, REACHABLE);\n \n-\tfor_each_reflog_ent(refname, fsck_handle_reflog_ent, NULL);\n-\n \treturn 0;\n }\n \n static void get_default_heads(void)\n {\n \tfor_each_ref(fsck_handle_ref, NULL);\n+\tfor_each_reflog(fsck_handle_reflog, NULL);\n \n \t/*\n \t * Not having any default heads isn't really fatal, but\ndiff --git a/builtin-reflog.c b/builtin-reflog.c\nindex 7c2443a..7ee5d6f 100644\n--- a/builtin-reflog.c\n+++ b/builtin-reflog.c\n@@ -245,14 +245,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,\n \tchar *log_file, *newlog_path = NULL;\n \tint status = 0;\n \n-\tif (strncmp(ref, \"refs/\", 5))\n-\t\treturn error(\"not a ref '%s'\", ref);\n-\n \tmemset(&cb, 0, sizeof(cb));\n \t/* we take the lock for the ref itself to prevent it from\n \t * getting updated.\n \t */\n-\tlock = lock_ref_sha1(ref + 5, sha1);\n+\tlock = lock_any_ref_for_update(ref, sha1);\n \tif (!lock)\n \t\treturn error(\"cannot lock ref '%s'\", ref);\n \tlog_file = xstrdup(git_path(\"logs/%s\", ref));\n@@ -353,7 +350,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (do_all)\n-\t\tstatus |= for_each_ref(expire_reflog, &cb);\n+\t\tstatus |= for_each_reflog(expire_reflog, &cb);\n \twhile (i < argc) {\n \t\tconst char *ref = argv[i++];\n \t\tunsigned char sha1[20];\ndiff --git a/reachable.c b/reachable.c\nindex a6a3348..01760d7 100644\n--- a/reachable.c\n+++ b/reachable.c\n@@ -188,9 +188,9 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog)\n \t/* Add all external refs */\n \tfor_each_ref(add_one_ref, revs);\n \n-\t/* Add all reflog info from refs */\n+\t/* Add all reflog info */\n \tif (mark_reflog)\n-\t\tfor_each_ref(add_one_reflog, revs);\n+\t\tfor_each_reflog(add_one_reflog, revs);\n \n \t/*\n \t * Set up the revision walk - this will move all commits\ndiff --git a/refs.c b/refs.c\nindex d605da3..03e8dfe 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1203,3 +1203,53 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)\n \treturn ret;\n }\n \n+static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)\n+{\n+\tDIR *dir = opendir(git_path(\"logs/%s\", base));\n+\tint retval = errno;\n+\n+\tif (dir) {\n+\t\tstruct dirent *de;\n+\t\tint baselen = strlen(base);\n+\t\tchar *log = xmalloc(baselen + 257);\n+\n+\t\tmemcpy(log, base, baselen);\n+\t\tif (baselen && base[baselen-1] != '/')\n+\t\t\tlog[baselen++] = '/';\n+\n+\t\twhile ((de = readdir(dir)) != NULL) {\n+\t\t\tstruct stat st;\n+\t\t\tint namelen;\n+\n+\t\t\tif (de->d_name[0] == '.')\n+\t\t\t\tcontinue;\n+\t\t\tnamelen = strlen(de->d_name);\n+\t\t\tif (namelen > 255)\n+\t\t\t\tcontinue;\n+\t\t\tif (has_extension(de->d_name, \".lock\"))\n+\t\t\t\tcontinue;\n+\t\t\tmemcpy(log + baselen, de->d_name, namelen+1);\n+\t\t\tif (stat(git_path(\"logs/%s\", log), &st) < 0)\n+\t\t\t\tcontinue;\n+\t\t\tif (S_ISDIR(st.st_mode)) {\n+\t\t\t\tretval = do_for_each_reflog(log, fn, cb_data);\n+\t\t\t} else {\n+\t\t\t\tunsigned char sha1[20];\n+\t\t\t\tif (!resolve_ref(log, sha1, 0, NULL))\n+\t\t\t\t\tretval = error(\"bad ref for %s\", log);\n+\t\t\t\telse\n+\t\t\t\t\tretval = fn(log, sha1, 0, cb_data);\n+\t\t\t}\n+\t\t\tif (retval)\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tfree(log);\n+\t\tclosedir(dir);\n+\t}\n+\treturn retval;\n+}\n+\n+int for_each_reflog(each_ref_fn fn, void *cb_data)\n+{\n+\treturn do_for_each_reflog(\"\", fn, cb_data);\n+}\ndiff --git a/refs.h b/refs.h\nindex 94a58b4..acedffc 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -48,6 +48,12 @@ extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned\n typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);\n int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);\n \n+/*\n+ * Calls the specified function for each reflog file until it returns nonzero,\n+ * and returns the value\n+ */\n+extern int for_each_reflog(each_ref_fn, void *);\n+\n /** Returns 0 if target has the right format for a ref. **/\n extern int check_ref_format(const char *target);\n \n"}]}