{"thread":{"id":"4201","subject":"[PATCH 1/5] General ref log reading improvements.","startedAt":"2006-05-19T07:28:19Z","lastAt":"2006-05-19T07:28:19Z","messageCount":1,"participants":["Shawn Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"20272","messageId":"20060519072819.GB22257@spearce.org","threadId":"4201","inReplyTo":null,"subject":"[PATCH 1/5] General ref log reading improvements.","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2006-05-19T07:28:19Z","receivedAt":"2006-05-19T07:28:19Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Corrected the log starting time displayed in the error message\n(as it was always showing the epoch due to a bad input to strtoul).\n\nImproved the log parser so we only scan backwards towards the\n'\\n' from the end of the prior log; during this scan the last '>'\nis remembered to improve performance (rather than scanning forward\nto it).\n\nIf the log record matched is the last log record in the file only\nuse its new sha1 value if the date matches exactly; otherwise we\nleave the passed in sha1 alone as it already contains the current\nvalue of the ref.  This way lookups of dates later than the log\nend to stick with the current ref value in case the ref was updated\nwithout logging.\n\nIf it looks like someone changed the ref without logging it and we\nare going to return the sha1 which should have been valid during\nthe missing period then warn the user that there might be log data\nmissing and thus their query result may not be accurate.  The check\nisn't perfect as its just based on comparing the old and new sha1\nvalues between the two log records but its better than not checking\nat all.\n\nImplemented test cases based on git-rev-parse for most of the\nboundary conditions.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n\n---\n\n refs.c                |   54 +++++++++++++++++++++++++++++++----------\n t/t1400-update-ref.sh |   64 +++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 104 insertions(+), 14 deletions(-)\n mode change 100644 => 100755 t/t1400-update-ref.sh\n\n54717d099405378da50cd3d4715c1e5d6d793d68\ndiff --git a/refs.c b/refs.c\nindex ae9825d..9d37a02 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -432,11 +432,12 @@ int write_ref_sha1(struct ref_lock *lock\n \n int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1)\n {\n-\tconst char *logfile, *logdata, *logend, *rec, *c;\n+\tconst char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;\n \tchar *tz_c;\n \tint logfd, tz;\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@@ -448,32 +449,57 @@ int read_ref_at(const char *ref, unsigne\n \tlogdata = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0);\n \tclose(logfd);\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\twhile (logdata < rec && *(rec-1) != '\\n')\n+\t\tlastgt = NULL;\n+\t\twhile (logdata < rec && *(rec-1) != '\\n') {\n \t\t\trec--;\n-\t\tc = rec;\n-\t\twhile (c < logend && *c != '>' && *c != '\\n')\n-\t\t\tc++;\n-\t\tif (c == logend || *c == '\\n')\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(c + 1, NULL, 10);\n+\t\tdate = strtoul(lastgt + 1, &tz_c, 10);\n \t\tif (date <= at_time) {\n-\t\t\tif (get_sha1_hex(rec + 41, sha1))\n-\t\t\t\tdie(\"Log %s is corrupt.\", logfile);\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 (memcmp(logged_sha1, sha1, 20)) {\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} else 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} else {\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 (memcmp(logged_sha1, sha1, 20)) {\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 \t\t\treturn 0;\n \t\t}\n+\t\tlastrec = rec;\n \t}\n \n-\tc = logdata;\n-\twhile (c < logend && *c != '>' && *c != '\\n')\n-\t\tc++;\n-\tif (c == logend || *c == '\\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(c, &tz_c, 10);\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);\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nold mode 100644\nnew mode 100755\nindex f338c53..7858d86\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -9,6 +9,10 @@ test_description='Test git-update-ref an\n Z=0000000000000000000000000000000000000000\n A=1111111111111111111111111111111111111111\n B=2222222222222222222222222222222222222222\n+C=3333333333333333333333333333333333333333\n+D=4444444444444444444444444444444444444444\n+E=5555555555555555555555555555555555555555\n+F=6666666666666666666666666666666666666666\n m=refs/heads/master\n \n test_expect_success \\\n@@ -109,4 +113,64 @@ test_expect_success \\\n \t'diff expect .git/logs/$m'\n rm -f .git/$m .git/logs/$m expect\n \n+git-update-ref $m $D\n+cat >.git/logs/$m <<EOF\n+$C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500\n+$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500\n+$F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500\n+$Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500\n+EOF\n+\n+ed=\"Thu, 26 May 2005 18:32:00 -0500\"\n+gd=\"Thu, 26 May 2005 18:33:00 -0500\"\n+ld=\"Thu, 26 May 2005 18:43:00 -0500\"\n+test_expect_success \\\n+\t'Query \"master@May 25 2005\" (before history)' \\\n+\t'rm -f o e\n+\t git-rev-parse --verify \"master@May 25 2005\" >o 2>e &&\n+\t test $C = $(cat o) &&\n+\t test \"warning: Log .git/logs/$m only goes back to $ed.\" = \"$(cat e)\"'\n+test_expect_success \\\n+\t\"Query master@2005-05-25 (before history)\" \\\n+\t'rm -f o e\n+\t git-rev-parse --verify master@2005-05-25 >o 2>e &&\n+\t test $C = $(cat o) &&\n+\t echo test \"warning: Log .git/logs/$m only goes back to $ed.\" = \"$(cat e)\"'\n+test_expect_success \\\n+\t'Query \"master@May 26 2005 23:31:59\" (1 second before history)' \\\n+\t'rm -f o e\n+\t git-rev-parse --verify \"master@May 26 2005 23:31:59\" >o 2>e &&\n+\t test $C = $(cat o) &&\n+\t test \"warning: Log .git/logs/$m only goes back to $ed.\" = \"$(cat e)\"'\n+test_expect_success \\\n+\t'Query \"master@May 26 2005 23:32:00\" (exactly history start)' \\\n+\t'rm -f o e\n+\t git-rev-parse --verify \"master@May 26 2005 23:32:00\" >o 2>e &&\n+\t test $A = $(cat o) &&\n+\t test \"\" = \"$(cat e)\"'\n+test_expect_success \\\n+\t'Query \"master@2005-05-26 23:33:01\" (middle of history with gap)' \\\n+\t'rm -f o e\n+\t git-rev-parse --verify \"master@2005-05-26 23:33:01\" >o 2>e &&\n+\t test $B = $(cat o) &&\n+\t test \"warning: Log .git/logs/$m has gap after $gd.\" = \"$(cat e)\"'\n+test_expect_success \\\n+\t'Query \"master@2005-05-26 23:33:01\" (middle of history)' \\\n+\t'rm -f o e\n+\t git-rev-parse --verify \"master@2005-05-26 23:38:00\" >o 2>e &&\n+\t test $Z = $(cat o) &&\n+\t test \"\" = \"$(cat e)\"'\n+test_expect_success \\\n+\t'Query \"master@2005-05-26 23:43:00\" (exact end of history)' \\\n+\t'rm -f o e\n+\t git-rev-parse --verify \"master@2005-05-26 23:43:00\" >o 2>e &&\n+\t test $E = $(cat o) &&\n+\t test \"\" = \"$(cat e)\"'\n+test_expect_success \\\n+\t'Query \"master@2005-05-28\" (past end of history)' \\\n+\t'rm -f o e\n+\t git-rev-parse --verify \"master@2005-05-28\" >o 2>e &&\n+\t test $D = $(cat o) &&\n+\t test \"warning: Log .git/logs/$m unexpectedly ended on $ld.\" = \"$(cat e)\"'\n+\n test_done\n-- \n1.3.2.g7278\n"}]}