Merge branch 'jk/for-each-reflog-ent-reverse' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 12 Jan 2015 20:19:17 +0000 (12:19 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 12 Jan 2015 20:19:17 +0000 (12:19 -0800)
* jk/for-each-reflog-ent-reverse:
for_each_reflog_ent_reverse: turn leftover check into assertion
for_each_reflog_ent_reverse: fix newlines on block boundaries

refs.c
t/t1410-reflog.sh
diff --git a/refs.c b/refs.c
index 5ff457ebfce7aba9cd470f3e2e1f3f5dbe9d9741..1f77fa6a2400a29819a65d189d0b25cf217af7df 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -3404,29 +3404,54 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void
 
                        bp = find_beginning_of_line(buf, scanp);
 
-                       if (*bp != '\n') {
-                               strbuf_splice(&sb, 0, 0, buf, endp - buf);
-                               if (pos)
-                                       break; /* need to fill another block */
-                               scanp = buf - 1; /* leave loop */
-                       } else {
+                       if (*bp == '\n') {
                                /*
-                                * (bp + 1) thru endp is the beginning of the
-                                * current line we have in sb
+                                * The newline is the end of the previous line,
+                                * so we know we have complete line starting
+                                * at (bp + 1). Prefix it onto any prior data
+                                * we collected for the line and process it.
                                 */
                                strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1));
                                scanp = bp;
                                endp = bp + 1;
+                               ret = show_one_reflog_ent(&sb, fn, cb_data);
+                               strbuf_reset(&sb);
+                               if (ret)
+                                       break;
+                       } else if (!pos) {
+                               /*
+                                * We are at the start of the buffer, and the
+                                * start of the file; there is no previous
+                                * line, and we have everything for this one.
+                                * Process it, and we can end the loop.
+                                */
+                               strbuf_splice(&sb, 0, 0, buf, endp - buf);
+                               ret = show_one_reflog_ent(&sb, fn, cb_data);
+                               strbuf_reset(&sb);
+                               break;
                        }
-                       ret = show_one_reflog_ent(&sb, fn, cb_data);
-                       strbuf_reset(&sb);
-                       if (ret)
+
+                       if (bp == buf) {
+                               /*
+                                * We are at the start of the buffer, and there
+                                * is more file to read backwards. Which means
+                                * we are in the middle of a line. Note that we
+                                * may get here even if *bp was a newline; that
+                                * just means we are at the exact end of the
+                                * previous line, rather than some spot in the
+                                * middle.
+                                *
+                                * Save away what we have to be combined with
+                                * the data from the next read.
+                                */
+                               strbuf_splice(&sb, 0, 0, buf, endp - buf);
                                break;
+                       }
                }
 
        }
        if (!ret && sb.len)
-               ret = show_one_reflog_ent(&sb, fn, cb_data);
+               die("BUG: reverse reflog parser had leftover data");
 
        fclose(logfp);
        strbuf_release(&sb);
index 8cf446165eff431106dd86d01754633fbd3ce246..779d4e3829b29d7092d54f6e342f923f0d239b96 100755 (executable)
@@ -287,4 +287,34 @@ test_expect_success 'stale dirs do not cause d/f conflicts (reflogs off)' '
        test_cmp expect actual
 '
 
+# Triggering the bug detected by this test requires a newline to fall
+# exactly BUFSIZ-1 bytes from the end of the file. We don't know
+# what that value is, since it's platform dependent. However, if
+# we choose some value N, we also catch any D which divides N evenly
+# (since we will read backwards in chunks of D). So we choose 8K,
+# which catches glibc (with an 8K BUFSIZ) and *BSD (1K).
+#
+# Each line is 114 characters, so we need 75 to still have a few before the
+# last 8K. The 89-character padding on the final entry lines up our
+# newline exactly.
+test_expect_success 'parsing reverse reflogs at BUFSIZ boundaries' '
+       git checkout -b reflogskip &&
+       z38=00000000000000000000000000000000000000 &&
+       ident="abc <xyz> 0000000001 +0000" &&
+       for i in $(test_seq 1 75); do
+               printf "$z38%02d $z38%02d %s\t" $i $(($i+1)) "$ident" &&
+               if test $i = 75; then
+                       for j in $(test_seq 1 89); do
+                               printf X
+                       done
+               else
+                       printf X
+               fi &&
+               printf "\n"
+       done >.git/logs/refs/heads/reflogskip &&
+       git rev-parse reflogskip@{73} >actual &&
+       echo ${z38}03 >expect &&
+       test_cmp expect actual
+'
+
 test_done