{"thread":{"id":"5770","subject":"[PATCH] refs: refactor reflog reading code to be reusable","startedAt":"2006-09-30T21:29:18Z","lastAt":"2006-09-30T21:29:18Z","messageCount":1,"participants":["Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"28037","messageId":"20060930212918.GA7100@coredump.intra.peff.net","threadId":"5770","inReplyTo":null,"subject":"[PATCH] refs: refactor reflog reading code to be reusable","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2006-09-30T21:29:18Z","receivedAt":"2006-09-30T21:29:18Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This just moves code around; the behavior of read_ref_at (currently the\nonly user) should be identical.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nThis patch is in preparation for doing more interesting things with\nreflogs, like git-describe.\n\n refs.c |  176 +++++++++++++++++++++++++++++++++++++++------------------------\n refs.h |   21 ++++++++\n 2 files changed, 130 insertions(+), 67 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 3d4cdd1..f5dfe4d 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -615,83 +615,125 @@ int write_ref_sha1(struct ref_lock *lock\n \treturn 0;\n }\n \n-int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1)\n+int reflog_open(struct reflog *log, const char *ref)\n {\n-\tconst char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;\n-\tchar *tz_c;\n-\tint logfd, tz;\n+\tint logfd;\n \tstruct stat st;\n-\tunsigned long date;\n-\tunsigned char logged_sha1[20];\n \n-\tlogfile = git_path(\"logs/%s\", ref);\n-\tlogfd = open(logfile, O_RDONLY, 0);\n+\tlog->file = xstrdup(git_path(\"logs/%s\", ref));\n+\tlogfd = open(log->file, O_RDONLY, 0);\n \tif (logfd < 0)\n-\t\tdie(\"Unable to read log %s: %s\", logfile, strerror(errno));\n+\t\treturn -1;\n \tfstat(logfd, &st);\n-\tif (!st.st_size)\n-\t\tdie(\"Log %s is empty.\", logfile);\n-\tlogdata = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0);\n+\tlog->size = st.st_size;\n+\tlog->data = mmap(NULL, log->size, PROT_READ, MAP_PRIVATE, logfd, 0);\n \tclose(logfd);\n+\treturn 0;\n+}\n \n-\tlastrec = NULL;\n-\trec = logend = logdata + st.st_size;\n-\twhile (logdata < rec) {\n-\t\tif (logdata < rec && *(rec-1) == '\\n')\n-\t\t\trec--;\n-\t\tlastgt = NULL;\n-\t\twhile (logdata < rec && *(rec-1) != '\\n') {\n-\t\t\trec--;\n-\t\t\tif (*rec == '>')\n-\t\t\t\tlastgt = rec;\n-\t\t}\n-\t\tif (!lastgt)\n-\t\t\tdie(\"Log %s is corrupt.\", logfile);\n-\t\tdate = strtoul(lastgt + 1, &tz_c, 10);\n-\t\tif (date <= at_time) {\n-\t\t\tif (lastrec) {\n-\t\t\t\tif (get_sha1_hex(lastrec, logged_sha1))\n-\t\t\t\t\tdie(\"Log %s is corrupt.\", logfile);\n-\t\t\t\tif (get_sha1_hex(rec + 41, sha1))\n-\t\t\t\t\tdie(\"Log %s is corrupt.\", logfile);\n-\t\t\t\tif (hashcmp(logged_sha1, sha1)) {\n-\t\t\t\t\ttz = strtoul(tz_c, NULL, 10);\n-\t\t\t\t\tfprintf(stderr,\n-\t\t\t\t\t\t\"warning: Log %s has gap after %s.\\n\",\n-\t\t\t\t\t\tlogfile, show_rfc2822_date(date, tz));\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\telse if (date == at_time) {\n-\t\t\t\tif (get_sha1_hex(rec + 41, sha1))\n-\t\t\t\t\tdie(\"Log %s is corrupt.\", logfile);\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\tif (get_sha1_hex(rec + 41, logged_sha1))\n-\t\t\t\t\tdie(\"Log %s is corrupt.\", logfile);\n-\t\t\t\tif (hashcmp(logged_sha1, sha1)) {\n-\t\t\t\t\ttz = strtoul(tz_c, NULL, 10);\n-\t\t\t\t\tfprintf(stderr,\n-\t\t\t\t\t\t\"warning: Log %s unexpectedly ended on %s.\\n\",\n-\t\t\t\t\t\tlogfile, show_rfc2822_date(date, tz));\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\tmunmap((void*)logdata, st.st_size);\n+void reflog_close(struct reflog *log)\n+{\n+\tfree(log->file);\n+\tmunmap((void *)log->data, log->size);\n+}\n+\n+int reflog_next(struct reflog *log, struct reflog_entry *out)\n+{\n+\tconst char *rec, *lastgt;\n+\tchar *tz_c;\n+\n+\trec = log->rec;\n+\tif (rec == log->data)\n+\t\treturn 0;\n+\n+\tlastgt = NULL;\n+\tif (log->data < rec && *(rec-1) == '\\n')\n+\t\trec--;\n+\twhile (log->data < rec && *(rec-1) != '\\n') {\n+\t\trec--;\n+\t\tif (*rec == '>')\n+\t\t\tlastgt = rec;\n+\t}\n+\tif (log->data == rec - 1)\n+\t\trec--;\n+\tif (!lastgt)\n+\t\tdie(\"Log %s is corrupt (nolastgt).\", log->file);\n+\n+\tout->date = strtoul(lastgt + 1, &tz_c, 10);\n+\tout->tz = strtoul(tz_c, NULL, 10);\n+\tout->rec = rec;\n+\tout->log = log;\n+\tlog->rec = rec;\n+\n+\treturn 1;\n+}\n+\n+void reflog_start(struct reflog *log)\n+{\n+\tlog->rec = log->data + log->size;\n+}\n+\n+void reflog_entry_from(const struct reflog_entry *e, unsigned char *sha1)\n+{\n+\tif (get_sha1_hex(e->rec, sha1))\n+\t\tdie(\"Log %s is corrupt.\", e->log->file);\n+}\n+\n+void reflog_entry_to(const struct reflog_entry *e, unsigned char *sha1)\n+{\n+\tif (get_sha1_hex(e->rec+41, sha1))\n+\t\tdie(\"Log %s is corrupt.\", e->log->file);\n+}\n+\n+int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1)\n+{\n+\tstruct reflog log;\n+\tstruct reflog_entry cur, last;\n+\tint last_is_valid;\n+\tunsigned char sha1_from[20];\n+\tunsigned char sha1_to[20];\n+\n+\tif (reflog_open(&log, ref) < 0)\n+\t\tdie(\"Unable to read log %s: %s\", log.file, strerror(errno));\n+\n+\tlast_is_valid = 0;\n+\treflog_start(&log);\n+\tdo {\n+\t\tif (!reflog_next(&log, &cur)) {\n+\t\t\tif (!last_is_valid)\n+\t\t\t\tdie(\"Log %s is empty.\", log.file);\n+\t\t\treflog_entry_to(&last, sha1);\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"warning: Log %s only goes back to %s.\\n\",\n+\t\t\t\tlog.file,\n+\t\t\t\tshow_rfc2822_date(last.date, last.tz));\n+\t\t\treflog_close(&log);\n \t\t\treturn 0;\n \t\t}\n-\t\tlastrec = rec;\n+\t\tlast = cur;\n+\t\tlast_is_valid = 1;\n+\t} while (at_time < cur.date);\n+\n+\treflog_entry_to(&cur, sha1_to);\n+\tif (last_is_valid) {\n+\t\treflog_entry_from(&last, sha1_from);\n+\t\tif (hashcmp(sha1_to, sha1_from)) {\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"warning: Log %s has gap after %s.\\n\",\n+\t\t\t\tlog.file,\n+\t\t\t\tshow_rfc2822_date(cur.date, cur.tz));\n+\t\t}\n+\t}\n+\telse if(cur.date != at_time) {\n+\t\tif (hashcmp(sha1_to, sha1)) {\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"warning: Log %s unexpectedly ended on %s.\\n\",\n+\t\t\t\tlog.file,\n+\t\t\t\tshow_rfc2822_date(cur.date, cur.tz));\n+\t\t}\n \t}\n \n-\trec = logdata;\n-\twhile (rec < logend && *rec != '>' && *rec != '\\n')\n-\t\trec++;\n-\tif (rec == logend || *rec == '\\n')\n-\t\tdie(\"Log %s is corrupt.\", logfile);\n-\tdate = strtoul(rec + 1, &tz_c, 10);\n-\ttz = strtoul(tz_c, NULL, 10);\n-\tif (get_sha1_hex(logdata, sha1))\n-\t\tdie(\"Log %s is corrupt.\", logfile);\n-\tmunmap((void*)logdata, st.st_size);\n-\tfprintf(stderr, \"warning: Log %s only goes back to %s.\\n\",\n-\t\tlogfile, show_rfc2822_date(date, tz));\n+\treflog_close(&log);\n+\thashcpy(sha1, sha1_to);\n \treturn 0;\n }\ndiff --git a/refs.h b/refs.h\nindex 0d4d79e..9f2667b 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -10,6 +10,20 @@ struct ref_lock {\n \tint force_write;\n };\n \n+struct reflog {\n+\tchar *file;\n+\tconst char *data;\n+\tunsigned long size;\n+\tconst char *rec;\n+};\n+\n+struct reflog_entry {\n+\tunsigned long date;\n+\tunsigned tz;\n+\tconst char *rec;\n+\tconst struct reflog *log;\n+};\n+\n /*\n  * Calls the specified function for each ref file until it returns nonzero,\n  * and returns the value\n@@ -44,4 +58,11 @@ extern int read_ref_at(const char *ref, \n /** Returns 0 if target has the right format for a ref. **/\n extern int check_ref_format(const char *target);\n \n+int reflog_open(struct reflog *log, const char *ref);\n+void reflog_close(struct reflog *log);\n+void reflog_start(struct reflog *log);\n+int reflog_next(struct reflog *log, struct reflog_entry *out);\n+void reflog_entry_from(const struct reflog_entry *e, unsigned char *sha1);\n+void reflog_entry_to(const struct reflog_entry *e, unsigned char *sha1);\n+\n #endif /* REFS_H */\n-- \n1.4.2.1.g67e7-dirty\n"}]}