{"thread":{"id":"51436","subject":"[PATCH v2 01/14] apply: replace marc.info link with public-inbox","startedAt":"2019-07-05T17:06:57Z","lastAt":"2019-07-12T10:45:07Z","messageCount":61,"participants":["Thomas Gummerer","Johannes Schindelin","Junio C Hamano","Ramsay Jones"],"isPatch":true,"patchVersion":2,"patchTotal":14},"messages":[{"id":"378631","messageId":"20190705170630.27500-2-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 01/14] apply: replace marc.info link with public-inbox","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:17Z","receivedAt":"2019-07-05T17:06:57Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"public-inbox.org links include the whole message ID by default.  This\nmeans the message can still be found even if the site goes away, which\nis not the case with the marc.info link.  Replace the marc.info link\nwith a more future proof one.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/apply.c b/apply.c\nindex f15afa9f6a..599cf8956f 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -478,7 +478,7 @@ static char *find_name_gnu(struct apply_state *state,\n \n \t/*\n \t * Proposed \"new-style\" GNU patch/diff format; see\n-\t * http://marc.info/?l=git&m=112927316408690&w=2\n+\t * https://public-inbox.org/git/7vll0wvb2a.fsf@assigned-by-dhcp.cox.net/\n \t */\n \tif (unquote_c_style(&name, line, NULL)) {\n \t\tstrbuf_release(&name);\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378632","messageId":"20190705170630.27500-1-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190414210933.20875-1-t.gummerer@gmail.com/","subject":"[PATCH v2 00/14] output improvements for git range-diff","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:16Z","receivedAt":"2019-07-05T17:06:59Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"It's been quite a while since I sent the RFC [1] (thanks all for the\ncomments on that), and the series changed shapes quite a bit since the\nlast round.\n\nSince it's been such a long time, just to remind everyone, the goal of\nthis series is to make the range-diff output clearer, by showing\ninformation about the filenames to which the current diff belongs.\n\nIn the previous round, we did this using \"section headers\" that\ninclude information about the current file and adding that to the\nouter diff's hunk headers.\n\nIn this round we still keep the section headers (with slightly more\ninformation), but in addition we also add the filename to the inner\ndiff hunk headers.  In the outer diff hunk headers we then display\neither the section header or the inner diff hunk header using a\nuserdiff pattern.\n\nIn terms of code changes the biggest change is that we're now re-using\nthe 'parse_git_header' function from the apply code to parse the diff\nheaders, instead of trying to parse them with some hacky parsing code\nin range-diff.c.  This way we are sure that the diff headers are\nproperly parsed.\n\nI had also considered just outputting the section headers directly\nfrom 'git log', but then decided against that.  Parsing the headers\nallows a future enhancement of range-diff, where we would allow\nparsing an mbox file [2].\n\nI split the \"only pass required data\" commits up, in the hopes of\nmaking them easier to review, but I'm also happy to squash them if\npeople feel like that makes it easier to review them.\n\nAn added advantage of this is that we're also getting rid of things\nlike the similarity index, which are not important in the range-diff,\nand are thus not represented in the \"section header\".\n\nOne thing that did not change is that the new/deleted strings are not\ntranslated in this version either.  This is similar to the regular\ndiff output, where we also don't translate these.  We can still\nconsider translating them in the future though.\n\n[1]: https://public-inbox.org/git/20190414210933.20875-1-t.gummerer@gmail.com/\n[2]: https://github.com/gitgitgadget/git/issues/207\n\nI'm including the range-diff between this version of the series and\nthe RFC, and a diff between the range diff and the range-diff without\nthese changes below.  Probably not useful in reviewing the code, but\ngood to show off the changes made in this series.\n\nrange-diff:\n\n -:  ---------- >  1:  ef2245edda apply: replace marc.info link with public-inbox\n -:  ---------- >  2:  94578fa45c apply: only pass required data to skip_tree_prefix\n -:  ---------- >  3:  988269a68e apply: only pass required data to git_header_name\n -:  ---------- >  4:  a2c1ef3f5f apply: only pass required data to check_header_line\n -:  ---------- >  5:  0f4cfe21cb apply: only pass required data to find_name_*\n -:  ---------- >  6:  7f1d7a4569 apply: only pass required data to gitdiff_* functions\n -:  ---------- >  7:  a5af8b0845 apply: make parse_git_header public\n 1:  0e678d222c =  8:  1f25bb1002 range-diff: fix function parameter indentation\n -:  ---------- >  9:  01ed0f2a6a range-diff: split lines manually\n 2:  48716230fc ! 10:  044a79868b range-diff: don't remove funcname from inner diff\n    @@ range-diff.c: static int read_patches(const char *range, struct string_list *lis\n      \t\t\t\tstrbuf_addch(&buf, '\\n');\n      \t\t\t}\n      \t\t\tcontinue;\n    --\t\t} else if (starts_with(line.buf, \"@@ \"))\n    +-\t\t} else if (starts_with(line, \"@@ \"))\n     -\t\t\tstrbuf_addstr(&buf, \"@@\");\n    --\t\telse if (!line.buf[0] || starts_with(line.buf, \"index \"))\n    -+\t\t} else if (starts_with(line.buf, \"@@ \")) {\n    -+\t\t\tchar *skip_lineno = strstr(line.buf + 3, \"@@\");\n    -+\t\t\tstrbuf_remove(&line, 0, skip_lineno - line.buf);\n    -+\t\t\tstrbuf_addch(&buf, ' ');\n    -+\t\t\tstrbuf_addbuf(&buf, &line);\n    -+\t\t} else if (!line.buf[0] || starts_with(line.buf, \"index \"))\n    +-\t\telse if (!line[0] || starts_with(line, \"index \"))\n    ++\t\t} else if (skip_prefix(line, \"@@ \", &p)) {\n    ++\t\t\tif (!(p = strstr(p, \"@@\")))\n    ++\t\t\t\tdie(_(\"invalid hunk header in inner diff\"));\n    ++\t\t\tstrbuf_addstr(&buf, p);\n    ++\t\t} else if (!line[0] || starts_with(line, \"index \"))\n      \t\t\t/*\n      \t\t\t * A completely blank (not ' \\n', which is context)\n      \t\t\t * line is not valid in a diff.  We skip it\n    +\n    + ## t/t3206-range-diff.sh ##\n    +@@ t/t3206-range-diff.sh: test_expect_success 'changed commit' '\n    + \t      14\n    + \t4:  a63e992 ! 4:  d966c5c s/12/B/\n    + \t    @@ -8,7 +8,7 @@\n    +-\t     @@\n    ++\t     @@ A\n    + \t      9\n    + \t      10\n    + \t    - B\n    +@@ t/t3206-range-diff.sh: test_expect_success 'changed commit with sm config' '\n    + \t      14\n    + \t4:  a63e992 ! 4:  d966c5c s/12/B/\n    + \t    @@ -8,7 +8,7 @@\n    +-\t     @@\n    ++\t     @@ A\n    + \t      9\n    + \t      10\n    + \t    - B\n    +@@ t/t3206-range-diff.sh: test_expect_success 'dual-coloring' '\n    + \t:      14<RESET>\n    + \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n    + \t:    <REVERSE><CYAN>@@ -8,7 +8,7 @@<RESET>\n    +-\t:    <CYAN> @@<RESET>\n    ++\t:    <CYAN> @@ A<RESET>\n    + \t:      9<RESET>\n    + \t:      10<RESET>\n    + \t:    <REVERSE><RED>-<RESET><FAINT> BB<RESET>\n 3:  71679cb747 <  -:  ---------- range-diff: add section header instead of diff header\n -:  ---------- > 11:  69654fe76d range-diff: suppress line count in outer diff\n -:  ---------- > 12:  c38f929b9a range-diff: add section header instead of diff header\n -:  ---------- > 13:  6df03ecdcf range-diff: add filename to inner diff\n 4:  8f45b6a995 ! 14:  5ceef49035 range-diff: add section headers to the outer hunk header\n    @@ Metadata\n     Author: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      ## Commit message ##\n    -    range-diff: add section headers to the outer hunk header\n    +    range-diff: add headers to the outer hunk header\n     \n    -    Add the section headers we introduced in the previous commit to the\n    -    outer diff's hunk headers.  This makes it easier to understand which\n    -    change we are actually looking at.  For example an outer hunk header\n    -    might now look like:\n    +    Add the section headers/hunk headers we introduced in the previous\n    +    commits to the outer diff's hunk headers.  This makes it easier to\n    +    understand which change we are actually looking at.  For example an\n    +    outer hunk header might now look like:\n     \n    -        @@ -77,15 +78,43 @@ modified file Documentation/config/interactive.txt\n    +        @@  Documentation/config/interactive.txt\n     \n         while previously it would have only been\n     \n    -        @@ -77,15 +78,43 @@\n    +        @@\n     \n         which doesn't give a lot of context for the change that follows.\n     \n    @@ Commit message\n     \n      ## range-diff.c ##\n     @@ range-diff.c: static int read_patches(const char *range, struct string_list *list)\n    - \t\t\tstrbuf_addstr(&buf, \" ##\\n\");\n    + \t\t\tstrbuf_addstr(&buf, \" ##\");\n      \t\t} else if (in_header) {\n    - \t\t\tif (starts_with(line.buf, \"Author: \")) {\n    + \t\t\tif (starts_with(line, \"Author: \")) {\n     +\t\t\t\tstrbuf_addstr(&buf, \" ## Metadata ##\\n\");\n    - \t\t\t\tstrbuf_addbuf(&buf, &line);\n    + \t\t\t\tstrbuf_addstr(&buf, line);\n      \t\t\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n     +\t\t\t\tstrbuf_addstr(&buf, \" ## Commit message ##\\n\");\n    - \t\t\t} else if (starts_with(line.buf, \"    \")) {\n    - \t\t\t\tstrbuf_rtrim(&line);\n    - \t\t\t\tstrbuf_addbuf(&buf, &line);\n    + \t\t\t} else if (starts_with(line, \"    \")) {\n    + \t\t\t\tp = line + len - 2;\n    + \t\t\t\twhile (isspace(*p) && p >= line)\n     @@ range-diff.c: static void output_pair_header(struct diff_options *diffopt,\n      \tfwrite(buf->buf, buf->len, 1, diffopt->file);\n      }\n    @@ range-diff.c: static void output_pair_header(struct diff_options *diffopt,\n     -static struct userdiff_driver no_func_name = {\n     -\t.funcname = { \"$^\", 0 }\n     +static struct userdiff_driver section_headers = {\n    -+\t.funcname = { \"^ ## (.*) ##$\", REG_EXTENDED }\n    ++\t.funcname = { \"^ ## (.*) ##$\\n\"\n    ++\t\t      \"^.?@@ (.*)$\", REG_EXTENDED }\n      };\n      \n      static struct diff_filespec *get_filespec(const char *name, const char *p)\n    @@ range-diff.c: static struct diff_filespec *get_filespec(const char *name, const\n      \n      \treturn spec;\n      }\n    +\n    + ## t/t3206-range-diff.sh ##\n    +@@ t/t3206-range-diff.sh: test_expect_success 'changed commit' '\n    + \t1:  4de457d = 1:  a4b3333 s/5/A/\n    + \t2:  fccce22 = 2:  f51d370 s/4/A/\n    + \t3:  147e64e ! 3:  0559556 s/11/B/\n    +-\t    @@\n    ++\t    @@ file: A\n    + \t      9\n    + \t      10\n    + \t     -11\n    +@@ t/t3206-range-diff.sh: test_expect_success 'changed commit' '\n    + \t      13\n    + \t      14\n    + \t4:  a63e992 ! 4:  d966c5c s/12/B/\n    +-\t    @@\n    ++\t    @@ file\n    + \t     @@ file: A\n    + \t      9\n    + \t      10\n    +@@ t/t3206-range-diff.sh: test_expect_success 'changed commit with sm config' '\n    + \t1:  4de457d = 1:  a4b3333 s/5/A/\n    + \t2:  fccce22 = 2:  f51d370 s/4/A/\n    + \t3:  147e64e ! 3:  0559556 s/11/B/\n    +-\t    @@\n    ++\t    @@ file: A\n    + \t      9\n    + \t      10\n    + \t     -11\n    +@@ t/t3206-range-diff.sh: test_expect_success 'changed commit with sm config' '\n    + \t      13\n    + \t      14\n    + \t4:  a63e992 ! 4:  d966c5c s/12/B/\n    +-\t    @@\n    ++\t    @@ file\n    + \t     @@ file: A\n    + \t      9\n    + \t      10\n    +@@ t/t3206-range-diff.sh: test_expect_success 'renamed file' '\n    + \tsed s/Z/\\ /g >expected <<-EOF &&\n    + \t1:  4de457d = 1:  f258d75 s/5/A/\n    + \t2:  fccce22 ! 2:  017b62d s/4/A/\n    +-\t    @@\n    ++\t    @@ Metadata\n    + \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n    + \t    Z\n    ++\t    Z ## Commit message ##\n    + \t    -    s/4/A/\n    + \t    +    s/4/A/ + rename file\n    + \t    Z\n    +@@ t/t3206-range-diff.sh: test_expect_success 'renamed file' '\n    + \t    Z 1\n    + \t    Z 2\n    + \t3:  147e64e ! 3:  3ce7af6 s/11/B/\n    +-\t    @@\n    +-\t    Z\n    ++\t    @@ Metadata\n    ++\t    Z ## Commit message ##\n    + \t    Z    s/11/B/\n    + \t    Z\n    + \t    - ## file ##\n    +@@ t/t3206-range-diff.sh: test_expect_success 'renamed file' '\n    + \t    Z 9\n    + \t    Z 10\n    + \t4:  a63e992 ! 4:  1e6226b s/12/B/\n    +-\t    @@\n    +-\t    Z\n    ++\t    @@ Metadata\n    ++\t    Z ## Commit message ##\n    + \t    Z    s/12/B/\n    + \t    Z\n    + \t    - ## file ##\n    +@@ t/t3206-range-diff.sh: test_expect_success 'file added and later removed' '\n    + \tsed s/Z/\\ /g >expected <<-EOF &&\n    + \t1:  4de457d = 1:  096b1ba s/5/A/\n    + \t2:  fccce22 ! 2:  d92e698 s/4/A/\n    +-\t    @@\n    ++\t    @@ Metadata\n    + \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n    + \t    Z\n    ++\t    Z ## Commit message ##\n    + \t    -    s/4/A/\n    + \t    +    s/4/A/ + new-file\n    + \t    Z\n    + \t    Z ## file ##\n    + \t    Z@@\n    +-\t    @@\n    ++\t    @@ file\n    + \t    Z A\n    + \t    Z 6\n    + \t    Z 7\n    + \t    +\n    + \t    + ## new-file (new) ##\n    + \t3:  147e64e ! 3:  9a1db4d s/11/B/\n    +-\t    @@\n    ++\t    @@ Metadata\n    + \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n    + \t    Z\n    ++\t    Z ## Commit message ##\n    + \t    -    s/11/B/\n    + \t    +    s/11/B/ + remove file\n    + \t    Z\n    + \t    Z ## file ##\n    + \t    Z@@ file: A\n    +-\t    @@\n    ++\t    @@ file: A\n    + \t    Z 12\n    + \t    Z 13\n    + \t    Z 14\n    +@@ t/t3206-range-diff.sh: test_expect_success 'changed message' '\n    + \tsed s/Z/\\ /g >expected <<-EOF &&\n    + \t1:  4de457d = 1:  f686024 s/5/A/\n    + \t2:  fccce22 ! 2:  4ab067d s/4/A/\n    +-\t    @@\n    +-\t    Z\n    ++\t    @@ Metadata\n    ++\t    Z ## Commit message ##\n    + \t    Z    s/4/A/\n    + \t    Z\n    + \t    +    Also a silly comment here!\n    +@@ t/t3206-range-diff.sh: test_expect_success 'dual-coloring' '\n    + \tsed -e \"s|^:||\" >expect <<-\\EOF &&\n    + \t:<YELLOW>1:  a4b3333 = 1:  f686024 s/5/A/<RESET>\n    + \t:<RED>2:  f51d370 <RESET><YELLOW>!<RESET><GREEN> 2:  4ab067d<RESET><YELLOW> s/4/A/<RESET>\n    +-\t:    <REVERSE><CYAN>@@<RESET>\n    +-\t:     <RESET>\n    ++\t:    <REVERSE><CYAN>@@<RESET> <RESET>Metadata<RESET>\n    ++\t:      ## Commit message ##<RESET>\n    + \t:         s/4/A/<RESET>\n    + \t:     <RESET>\n    + \t:    <REVERSE><GREEN>+<RESET><BOLD>    Also a silly comment here!<RESET>\n    +@@ t/t3206-range-diff.sh: test_expect_success 'dual-coloring' '\n    + \t:    <CYAN> @@<RESET>\n    + \t:      1<RESET>\n    + \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n    +-\t:    <REVERSE><CYAN>@@<RESET>\n    ++\t:    <REVERSE><CYAN>@@<RESET> <RESET>file: A<RESET>\n    + \t:      9<RESET>\n    + \t:      10<RESET>\n    + \t:    <RED> -11<RESET>\n    +@@ t/t3206-range-diff.sh: test_expect_success 'dual-coloring' '\n    + \t:      13<RESET>\n    + \t:      14<RESET>\n    + \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n    +-\t:    <REVERSE><CYAN>@@<RESET>\n    ++\t:    <REVERSE><CYAN>@@<RESET> <RESET>file<RESET>\n    + \t:    <CYAN> @@ file: A<RESET>\n    + \t:      9<RESET>\n    + \t:      10<RESET>\n\ndiff between range-diffs (a diff between range-diffs seems a bit\ninsane, though with some coloring I find it actually somewhat\nreadable):\n\ndiff --git a/rd1 b/rd2\nindex 5fa847fbab..d6f99d1548 100644\n--- a/rd1\n+++ b/rd2\n@@ -8,7 +8,7 @@\n  1:  0e678d222c =  8:  1f25bb1002 range-diff: fix function parameter indentation\n  -:  ---------- >  9:  01ed0f2a6a range-diff: split lines manually\n  2:  48716230fc ! 10:  044a79868b range-diff: don't remove funcname from inner diff\n-    @@ -27,15 +27,45 @@\n+    @@ range-diff.c: static int read_patches(const char *range, struct string_list *lis\n       \t\t\t\tstrbuf_addch(&buf, '\\n');\n       \t\t\t}\n       \t\t\tcontinue;\n@@ -32,10 +32,8 @@\n       \t\t\t * A completely blank (not ' \\n', which is context)\n       \t\t\t * line is not valid in a diff.  We skip it\n     +\n-    + diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\n-    + --- a/t/t3206-range-diff.sh\n-    + +++ b/t/t3206-range-diff.sh\n-    +@@\n+    + ## t/t3206-range-diff.sh ##\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'changed commit' '\n     + \t      14\n     + \t4:  a63e992 ! 4:  d966c5c s/12/B/\n     + \t    @@ -8,7 +8,7 @@\n@@ -44,7 +42,7 @@\n     + \t      9\n     + \t      10\n     + \t    - B\n-    +@@\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'changed commit with sm config' '\n     + \t      14\n     + \t4:  a63e992 ! 4:  d966c5c s/12/B/\n     + \t    @@ -8,7 +8,7 @@\n@@ -53,7 +51,7 @@\n     + \t      9\n     + \t      10\n     + \t    - B\n-    +@@\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'dual-coloring' '\n     + \t:      14<RESET>\n     + \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n     + \t:    <REVERSE><CYAN>@@ -8,7 +8,7 @@<RESET>\n@@ -67,9 +65,10 @@\n  -:  ---------- > 12:  c38f929b9a range-diff: add section header instead of diff header\n  -:  ---------- > 13:  6df03ecdcf range-diff: add filename to inner diff\n  4:  8f45b6a995 ! 14:  5ceef49035 range-diff: add section headers to the outer hunk header\n-    @@ -1,17 +1,17 @@\n+    @@ Metadata\n      Author: Thomas Gummerer <t.gummerer@gmail.com>\n      \n+      ## Commit message ##\n     -    range-diff: add section headers to the outer hunk header\n     +    range-diff: add headers to the outer hunk header\n      \n@@ -92,10 +91,10 @@\n      \n          which doesn't give a lot of context for the change that follows.\n      \n-    @@ -24,16 +24,16 @@\n-      --- a/range-diff.c\n-      +++ b/range-diff.c\n-     @@\n+    @@ Commit message\n+     \n+      ## range-diff.c ##\n+     @@ range-diff.c: static int read_patches(const char *range, struct string_list *list)\n     - \t\t\tstrbuf_addstr(&buf, \" ##\\n\");\n     + \t\t\tstrbuf_addstr(&buf, \" ##\");\n       \t\t} else if (in_header) {\n@@ -112,10 +111,10 @@\n     + \t\t\t} else if (starts_with(line, \"    \")) {\n     + \t\t\t\tp = line + len - 2;\n     + \t\t\t\twhile (isspace(*p) && p >= line)\n-     @@\n+     @@ range-diff.c: static void output_pair_header(struct diff_options *diffopt,\n       \tfwrite(buf->buf, buf->len, 1, diffopt->file);\n       }\n-    @@ -41,7 +41,8 @@\n+    @@ range-diff.c: static void output_pair_header(struct diff_options *diffopt,\n      -static struct userdiff_driver no_func_name = {\n      -\t.funcname = { \"$^\", 0 }\n      +static struct userdiff_driver section_headers = {\n@@ -125,15 +124,13 @@\n       };\n       \n       static struct diff_filespec *get_filespec(const char *name, const char *p)\n-    @@ -54,3 +55,154 @@ $^\n+    @@ range-diff.c: static struct diff_filespec *get_filespec(const char *name, const\n       \n       \treturn spec;\n       }\n     +\n-    + diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\n-    + --- a/t/t3206-range-diff.sh\n-    + +++ b/t/t3206-range-diff.sh\n-    +@@\n+    + ## t/t3206-range-diff.sh ##\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'changed commit' '\n     + \t1:  4de457d = 1:  a4b3333 s/5/A/\n     + \t2:  fccce22 = 2:  f51d370 s/4/A/\n     + \t3:  147e64e ! 3:  0559556 s/11/B/\n@@ -142,7 +139,7 @@\n     + \t      9\n     + \t      10\n     + \t     -11\n-    +@@\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'changed commit' '\n     + \t      13\n     + \t      14\n     + \t4:  a63e992 ! 4:  d966c5c s/12/B/\n@@ -151,7 +148,7 @@\n     + \t     @@ file: A\n     + \t      9\n     + \t      10\n-    +@@\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'changed commit with sm config' '\n     + \t1:  4de457d = 1:  a4b3333 s/5/A/\n     + \t2:  fccce22 = 2:  f51d370 s/4/A/\n     + \t3:  147e64e ! 3:  0559556 s/11/B/\n@@ -160,7 +157,7 @@\n     + \t      9\n     + \t      10\n     + \t     -11\n-    +@@\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'changed commit with sm config' '\n     + \t      13\n     + \t      14\n     + \t4:  a63e992 ! 4:  d966c5c s/12/B/\n@@ -169,7 +166,7 @@\n     + \t     @@ file: A\n     + \t      9\n     + \t      10\n-    +@@\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'renamed file' '\n     + \tsed s/Z/\\ /g >expected <<-EOF &&\n     + \t1:  4de457d = 1:  f258d75 s/5/A/\n     + \t2:  fccce22 ! 2:  017b62d s/4/A/\n@@ -181,7 +178,7 @@\n     + \t    -    s/4/A/\n     + \t    +    s/4/A/ + rename file\n     + \t    Z\n-    +@@\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'renamed file' '\n     + \t    Z 1\n     + \t    Z 2\n     + \t3:  147e64e ! 3:  3ce7af6 s/11/B/\n@@ -192,7 +189,7 @@\n     + \t    Z    s/11/B/\n     + \t    Z\n     + \t    - ## file ##\n-    +@@\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'renamed file' '\n     + \t    Z 9\n     + \t    Z 10\n     + \t4:  a63e992 ! 4:  1e6226b s/12/B/\n@@ -203,7 +200,7 @@\n     + \t    Z    s/12/B/\n     + \t    Z\n     + \t    - ## file ##\n-    +@@\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'file added and later removed' '\n     + \tsed s/Z/\\ /g >expected <<-EOF &&\n     + \t1:  4de457d = 1:  096b1ba s/5/A/\n     + \t2:  fccce22 ! 2:  d92e698 s/4/A/\n@@ -240,7 +237,7 @@\n     + \t    Z 12\n     + \t    Z 13\n     + \t    Z 14\n-    +@@\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'changed message' '\n     + \tsed s/Z/\\ /g >expected <<-EOF &&\n     + \t1:  4de457d = 1:  f686024 s/5/A/\n     + \t2:  fccce22 ! 2:  4ab067d s/4/A/\n@@ -251,7 +248,7 @@\n     + \t    Z    s/4/A/\n     + \t    Z\n     + \t    +    Also a silly comment here!\n-    +@@\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'dual-coloring' '\n     + \tsed -e \"s|^:||\" >expect <<-\\EOF &&\n     + \t:<YELLOW>1:  a4b3333 = 1:  f686024 s/5/A/<RESET>\n     + \t:<RED>2:  f51d370 <RESET><YELLOW>!<RESET><GREEN> 2:  4ab067d<RESET><YELLOW> s/4/A/<RESET>\n@@ -262,7 +259,7 @@\n     + \t:         s/4/A/<RESET>\n     + \t:     <RESET>\n     + \t:    <REVERSE><GREEN>+<RESET><BOLD>    Also a silly comment here!<RESET>\n-    +@@\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'dual-coloring' '\n     + \t:    <CYAN> @@<RESET>\n     + \t:      1<RESET>\n     + \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n@@ -271,7 +268,7 @@\n     + \t:      9<RESET>\n     + \t:      10<RESET>\n     + \t:    <RED> -11<RESET>\n-    +@@\n+    +@@ t/t3206-range-diff.sh: test_expect_success 'dual-coloring' '\n     + \t:      13<RESET>\n     + \t:      14<RESET>\n     + \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n\nThomas Gummerer (14):\n  apply: replace marc.info link with public-inbox\n  apply: only pass required data to skip_tree_prefix\n  apply: only pass required data to git_header_name\n  apply: only pass required data to check_header_line\n  apply: only pass required data to find_name_*\n  apply: only pass required data to gitdiff_* functions\n  apply: make parse_git_header public\n  range-diff: fix function parameter indentation\n  range-diff: split lines manually\n  range-diff: don't remove funcname from inner diff\n  range-diff: suppress line count in outer diff\n  range-diff: add section header instead of diff header\n  range-diff: add filename to inner diff\n  range-diff: add headers to the outer hunk header\n\n apply.c                | 185 ++++++++++++++++++-----------------------\n apply.h                |  48 +++++++++++\n diff.c                 |   5 +-\n diff.h                 |   1 +\n range-diff.c           | 126 +++++++++++++++++++---------\n t/t3206-range-diff.sh  | 124 ++++++++++++++++++++++-----\n t/t3206/history.export |  84 ++++++++++++++++++-\n 7 files changed, 407 insertions(+), 166 deletions(-)\n\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378633","messageId":"20190705170630.27500-4-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 03/14] apply: only pass required data to git_header_name","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:19Z","receivedAt":"2019-07-05T17:07:00Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'git_header_name()' function takes 'struct apply_state'\nas parameter, even though it only needs the p_value from that struct.\n\nThis function is in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex fc7083fcbc..ac668e754d 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -1164,7 +1164,7 @@ static const char *skip_tree_prefix(int p_value,\n  * creation or deletion of an empty file.  In any of these cases,\n  * both sides are the same name under a/ and b/ respectively.\n  */\n-static char *git_header_name(struct apply_state *state,\n+static char *git_header_name(int p_value,\n \t\t\t     const char *line,\n \t\t\t     int llen)\n {\n@@ -1184,7 +1184,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\tgoto free_and_fail1;\n \n \t\t/* strip the a/b prefix including trailing slash */\n-\t\tcp = skip_tree_prefix(state->p_value, first.buf, first.len);\n+\t\tcp = skip_tree_prefix(p_value, first.buf, first.len);\n \t\tif (!cp)\n \t\t\tgoto free_and_fail1;\n \t\tstrbuf_remove(&first, 0, cp - first.buf);\n@@ -1201,7 +1201,7 @@ static char *git_header_name(struct apply_state *state,\n \t\tif (*second == '\"') {\n \t\t\tif (unquote_c_style(&sp, second, NULL))\n \t\t\t\tgoto free_and_fail1;\n-\t\t\tcp = skip_tree_prefix(state->p_value, sp.buf, sp.len);\n+\t\t\tcp = skip_tree_prefix(p_value, sp.buf, sp.len);\n \t\t\tif (!cp)\n \t\t\t\tgoto free_and_fail1;\n \t\t\t/* They must match, otherwise ignore */\n@@ -1212,7 +1212,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t}\n \n \t\t/* unquoted second */\n-\t\tcp = skip_tree_prefix(state->p_value, second, line + llen - second);\n+\t\tcp = skip_tree_prefix(p_value, second, line + llen - second);\n \t\tif (!cp)\n \t\t\tgoto free_and_fail1;\n \t\tif (line + llen - cp != first.len ||\n@@ -1227,7 +1227,7 @@ static char *git_header_name(struct apply_state *state,\n \t}\n \n \t/* unquoted first name */\n-\tname = skip_tree_prefix(state->p_value, line, llen);\n+\tname = skip_tree_prefix(p_value, line, llen);\n \tif (!name)\n \t\treturn NULL;\n \n@@ -1243,7 +1243,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\tif (unquote_c_style(&sp, second, NULL))\n \t\t\t\tgoto free_and_fail2;\n \n-\t\t\tnp = skip_tree_prefix(state->p_value, sp.buf, sp.len);\n+\t\t\tnp = skip_tree_prefix(p_value, sp.buf, sp.len);\n \t\t\tif (!np)\n \t\t\t\tgoto free_and_fail2;\n \n@@ -1287,7 +1287,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\t */\n \t\t\tif (!name[len + 1])\n \t\t\t\treturn NULL; /* no postimage name */\n-\t\t\tsecond = skip_tree_prefix(state->p_value, name + len + 1,\n+\t\t\tsecond = skip_tree_prefix(p_value, name + len + 1,\n \t\t\t\t\t\t  line_len - (len + 1));\n \t\t\tif (!second)\n \t\t\t\treturn NULL;\n@@ -1333,7 +1333,7 @@ static int parse_git_header(struct apply_state *state,\n \t * or removing or adding empty files), so we get\n \t * the default name from the header.\n \t */\n-\tpatch->def_name = git_header_name(state, line, len);\n+\tpatch->def_name = git_header_name(state->p_value, line, len);\n \tif (patch->def_name && state->root.len) {\n \t\tchar *s = xstrfmt(\"%s%s\", state->root.buf, patch->def_name);\n \t\tfree(patch->def_name);\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378634","messageId":"20190705170630.27500-3-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 02/14] apply: only pass required data to skip_tree_prefix","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:18Z","receivedAt":"2019-07-05T17:07:00Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'skip_tree_prefix()' function takes 'struct apply_state'\nas parameter, even though it only needs the p_value from that struct.\n\nThis function is in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 18 +++++++++---------\n 1 file changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 599cf8956f..fc7083fcbc 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -1137,17 +1137,17 @@ static int gitdiff_unrecognized(struct apply_state *state,\n  * Skip p_value leading components from \"line\"; as we do not accept\n  * absolute paths, return NULL in that case.\n  */\n-static const char *skip_tree_prefix(struct apply_state *state,\n+static const char *skip_tree_prefix(int p_value,\n \t\t\t\t    const char *line,\n \t\t\t\t    int llen)\n {\n \tint nslash;\n \tint i;\n \n-\tif (!state->p_value)\n+\tif (!p_value)\n \t\treturn (llen && line[0] == '/') ? NULL : line;\n \n-\tnslash = state->p_value;\n+\tnslash = p_value;\n \tfor (i = 0; i < llen; i++) {\n \t\tint ch = line[i];\n \t\tif (ch == '/' && --nslash <= 0)\n@@ -1184,7 +1184,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\tgoto free_and_fail1;\n \n \t\t/* strip the a/b prefix including trailing slash */\n-\t\tcp = skip_tree_prefix(state, first.buf, first.len);\n+\t\tcp = skip_tree_prefix(state->p_value, first.buf, first.len);\n \t\tif (!cp)\n \t\t\tgoto free_and_fail1;\n \t\tstrbuf_remove(&first, 0, cp - first.buf);\n@@ -1201,7 +1201,7 @@ static char *git_header_name(struct apply_state *state,\n \t\tif (*second == '\"') {\n \t\t\tif (unquote_c_style(&sp, second, NULL))\n \t\t\t\tgoto free_and_fail1;\n-\t\t\tcp = skip_tree_prefix(state, sp.buf, sp.len);\n+\t\t\tcp = skip_tree_prefix(state->p_value, sp.buf, sp.len);\n \t\t\tif (!cp)\n \t\t\t\tgoto free_and_fail1;\n \t\t\t/* They must match, otherwise ignore */\n@@ -1212,7 +1212,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t}\n \n \t\t/* unquoted second */\n-\t\tcp = skip_tree_prefix(state, second, line + llen - second);\n+\t\tcp = skip_tree_prefix(state->p_value, second, line + llen - second);\n \t\tif (!cp)\n \t\t\tgoto free_and_fail1;\n \t\tif (line + llen - cp != first.len ||\n@@ -1227,7 +1227,7 @@ static char *git_header_name(struct apply_state *state,\n \t}\n \n \t/* unquoted first name */\n-\tname = skip_tree_prefix(state, line, llen);\n+\tname = skip_tree_prefix(state->p_value, line, llen);\n \tif (!name)\n \t\treturn NULL;\n \n@@ -1243,7 +1243,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\tif (unquote_c_style(&sp, second, NULL))\n \t\t\t\tgoto free_and_fail2;\n \n-\t\t\tnp = skip_tree_prefix(state, sp.buf, sp.len);\n+\t\t\tnp = skip_tree_prefix(state->p_value, sp.buf, sp.len);\n \t\t\tif (!np)\n \t\t\t\tgoto free_and_fail2;\n \n@@ -1287,7 +1287,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\t */\n \t\t\tif (!name[len + 1])\n \t\t\t\treturn NULL; /* no postimage name */\n-\t\t\tsecond = skip_tree_prefix(state, name + len + 1,\n+\t\t\tsecond = skip_tree_prefix(state->p_value, name + len + 1,\n \t\t\t\t\t\t  line_len - (len + 1));\n \t\t\tif (!second)\n \t\t\t\treturn NULL;\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378635","messageId":"20190705170630.27500-5-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 04/14] apply: only pass required data to check_header_line","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:20Z","receivedAt":"2019-07-05T17:07:02Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'check_header_line()' function takes 'struct\napply_state' as parameter, even though it only needs the linenr from\nthat struct.\n\nThis function is in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex ac668e754d..1602fd5db0 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -1302,15 +1302,15 @@ static char *git_header_name(int p_value,\n \t}\n }\n \n-static int check_header_line(struct apply_state *state, struct patch *patch)\n+static int check_header_line(int linenr, struct patch *patch)\n {\n \tint extensions = (patch->is_delete == 1) + (patch->is_new == 1) +\n \t\t\t (patch->is_rename == 1) + (patch->is_copy == 1);\n \tif (extensions > 1)\n \t\treturn error(_(\"inconsistent header lines %d and %d\"),\n-\t\t\t     patch->extension_linenr, state->linenr);\n+\t\t\t     patch->extension_linenr, linenr);\n \tif (extensions && !patch->extension_linenr)\n-\t\tpatch->extension_linenr = state->linenr;\n+\t\tpatch->extension_linenr = linenr;\n \treturn 0;\n }\n \n@@ -1380,7 +1380,7 @@ static int parse_git_header(struct apply_state *state,\n \t\t\tres = p->fn(state, line + oplen, patch);\n \t\t\tif (res < 0)\n \t\t\t\treturn -1;\n-\t\t\tif (check_header_line(state, patch))\n+\t\t\tif (check_header_line(state->linenr, patch))\n \t\t\t\treturn -1;\n \t\t\tif (res > 0)\n \t\t\t\treturn offset;\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378636","messageId":"20190705170630.27500-6-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 05/14] apply: only pass required data to find_name_*","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:21Z","receivedAt":"2019-07-05T17:07:04Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'find_name_*()' functions take 'struct apply_state' as\nparameter, even though they only need the 'root' member from that\nstruct.\n\nThese functions are in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 48 ++++++++++++++++++++++++------------------------\n 1 file changed, 24 insertions(+), 24 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 1602fd5db0..3cd4e3d3b3 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -469,7 +469,7 @@ static char *squash_slash(char *name)\n \treturn name;\n }\n \n-static char *find_name_gnu(struct apply_state *state,\n+static char *find_name_gnu(struct strbuf *root,\n \t\t\t   const char *line,\n \t\t\t   int p_value)\n {\n@@ -495,8 +495,8 @@ static char *find_name_gnu(struct apply_state *state,\n \t}\n \n \tstrbuf_remove(&name, 0, cp - name.buf);\n-\tif (state->root.len)\n-\t\tstrbuf_insert(&name, 0, state->root.buf, state->root.len);\n+\tif (root->len)\n+\t\tstrbuf_insert(&name, 0, root->buf, root->len);\n \treturn squash_slash(strbuf_detach(&name, NULL));\n }\n \n@@ -659,7 +659,7 @@ static size_t diff_timestamp_len(const char *line, size_t len)\n \treturn line + len - end;\n }\n \n-static char *find_name_common(struct apply_state *state,\n+static char *find_name_common(struct strbuf *root,\n \t\t\t      const char *line,\n \t\t\t      const char *def,\n \t\t\t      int p_value,\n@@ -702,30 +702,30 @@ static char *find_name_common(struct apply_state *state,\n \t\t\treturn squash_slash(xstrdup(def));\n \t}\n \n-\tif (state->root.len) {\n-\t\tchar *ret = xstrfmt(\"%s%.*s\", state->root.buf, len, start);\n+\tif (root->len) {\n+\t\tchar *ret = xstrfmt(\"%s%.*s\", root->buf, len, start);\n \t\treturn squash_slash(ret);\n \t}\n \n \treturn squash_slash(xmemdupz(start, len));\n }\n \n-static char *find_name(struct apply_state *state,\n+static char *find_name(struct strbuf *root,\n \t\t       const char *line,\n \t\t       char *def,\n \t\t       int p_value,\n \t\t       int terminate)\n {\n \tif (*line == '\"') {\n-\t\tchar *name = find_name_gnu(state, line, p_value);\n+\t\tchar *name = find_name_gnu(root, line, p_value);\n \t\tif (name)\n \t\t\treturn name;\n \t}\n \n-\treturn find_name_common(state, line, def, p_value, NULL, terminate);\n+\treturn find_name_common(root, line, def, p_value, NULL, terminate);\n }\n \n-static char *find_name_traditional(struct apply_state *state,\n+static char *find_name_traditional(struct strbuf *root,\n \t\t\t\t   const char *line,\n \t\t\t\t   char *def,\n \t\t\t\t   int p_value)\n@@ -734,7 +734,7 @@ static char *find_name_traditional(struct apply_state *state,\n \tsize_t date_len;\n \n \tif (*line == '\"') {\n-\t\tchar *name = find_name_gnu(state, line, p_value);\n+\t\tchar *name = find_name_gnu(root, line, p_value);\n \t\tif (name)\n \t\t\treturn name;\n \t}\n@@ -742,10 +742,10 @@ static char *find_name_traditional(struct apply_state *state,\n \tlen = strchrnul(line, '\\n') - line;\n \tdate_len = diff_timestamp_len(line, len);\n \tif (!date_len)\n-\t\treturn find_name_common(state, line, def, p_value, NULL, TERM_TAB);\n+\t\treturn find_name_common(root, line, def, p_value, NULL, TERM_TAB);\n \tlen -= date_len;\n \n-\treturn find_name_common(state, line, def, p_value, line + len, 0);\n+\treturn find_name_common(root, line, def, p_value, line + len, 0);\n }\n \n /*\n@@ -759,7 +759,7 @@ static int guess_p_value(struct apply_state *state, const char *nameline)\n \n \tif (is_dev_null(nameline))\n \t\treturn -1;\n-\tname = find_name_traditional(state, nameline, NULL, 0);\n+\tname = find_name_traditional(&state->root, nameline, NULL, 0);\n \tif (!name)\n \t\treturn -1;\n \tcp = strchr(name, '/');\n@@ -883,17 +883,17 @@ static int parse_traditional_patch(struct apply_state *state,\n \tif (is_dev_null(first)) {\n \t\tpatch->is_new = 1;\n \t\tpatch->is_delete = 0;\n-\t\tname = find_name_traditional(state, second, NULL, state->p_value);\n+\t\tname = find_name_traditional(&state->root, second, NULL, state->p_value);\n \t\tpatch->new_name = name;\n \t} else if (is_dev_null(second)) {\n \t\tpatch->is_new = 0;\n \t\tpatch->is_delete = 1;\n-\t\tname = find_name_traditional(state, first, NULL, state->p_value);\n+\t\tname = find_name_traditional(&state->root, first, NULL, state->p_value);\n \t\tpatch->old_name = name;\n \t} else {\n \t\tchar *first_name;\n-\t\tfirst_name = find_name_traditional(state, first, NULL, state->p_value);\n-\t\tname = find_name_traditional(state, second, first_name, state->p_value);\n+\t\tfirst_name = find_name_traditional(&state->root, first, NULL, state->p_value);\n+\t\tname = find_name_traditional(&state->root, second, first_name, state->p_value);\n \t\tfree(first_name);\n \t\tif (has_epoch_timestamp(first)) {\n \t\t\tpatch->is_new = 1;\n@@ -940,7 +940,7 @@ static int gitdiff_verify_name(struct apply_state *state,\n \t\t\t       int side)\n {\n \tif (!*name && !isnull) {\n-\t\t*name = find_name(state, line, NULL, state->p_value, TERM_TAB);\n+\t\t*name = find_name(&state->root, line, NULL, state->p_value, TERM_TAB);\n \t\treturn 0;\n \t}\n \n@@ -949,7 +949,7 @@ static int gitdiff_verify_name(struct apply_state *state,\n \t\tif (isnull)\n \t\t\treturn error(_(\"git apply: bad git-diff - expected /dev/null, got %s on line %d\"),\n \t\t\t\t     *name, state->linenr);\n-\t\tanother = find_name(state, line, NULL, state->p_value, TERM_TAB);\n+\t\tanother = find_name(&state->root, line, NULL, state->p_value, TERM_TAB);\n \t\tif (!another || strcmp(another, *name)) {\n \t\t\tfree(another);\n \t\t\treturn error((side == DIFF_NEW_NAME) ?\n@@ -1032,7 +1032,7 @@ static int gitdiff_copysrc(struct apply_state *state,\n {\n \tpatch->is_copy = 1;\n \tfree(patch->old_name);\n-\tpatch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->old_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n@@ -1042,7 +1042,7 @@ static int gitdiff_copydst(struct apply_state *state,\n {\n \tpatch->is_copy = 1;\n \tfree(patch->new_name);\n-\tpatch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->new_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n@@ -1052,7 +1052,7 @@ static int gitdiff_renamesrc(struct apply_state *state,\n {\n \tpatch->is_rename = 1;\n \tfree(patch->old_name);\n-\tpatch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->old_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n@@ -1062,7 +1062,7 @@ static int gitdiff_renamedst(struct apply_state *state,\n {\n \tpatch->is_rename = 1;\n \tfree(patch->new_name);\n-\tpatch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->new_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378638","messageId":"20190705170630.27500-8-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 07/14] apply: make parse_git_header public","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:23Z","receivedAt":"2019-07-05T17:07:06Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Make parse_git_header a \"public\" function in apply.h, so we can re-use\nit in range-diff in a subsequent commit.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n\nI considered creating a separate struct for only the metadata here,\nand embedding that in 'struct patch'.  As struct patch is mostly\nmetadata fields though, I decided against that to avoid more code\nchurn here.\n\n apply.c | 68 ++++++++++++++++-----------------------------------------\n apply.h | 48 ++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 67 insertions(+), 49 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 468f1d3fee..04319c233f 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -207,40 +207,6 @@ struct fragment {\n #define BINARY_DELTA_DEFLATED\t1\n #define BINARY_LITERAL_DEFLATED 2\n \n-/*\n- * This represents a \"patch\" to a file, both metainfo changes\n- * such as creation/deletion, filemode and content changes represented\n- * as a series of fragments.\n- */\n-struct patch {\n-\tchar *new_name, *old_name, *def_name;\n-\tunsigned int old_mode, new_mode;\n-\tint is_new, is_delete;\t/* -1 = unknown, 0 = false, 1 = true */\n-\tint rejected;\n-\tunsigned ws_rule;\n-\tint lines_added, lines_deleted;\n-\tint score;\n-\tint extension_linenr; /* first line specifying delete/new/rename/copy */\n-\tunsigned int is_toplevel_relative:1;\n-\tunsigned int inaccurate_eof:1;\n-\tunsigned int is_binary:1;\n-\tunsigned int is_copy:1;\n-\tunsigned int is_rename:1;\n-\tunsigned int recount:1;\n-\tunsigned int conflicted_threeway:1;\n-\tunsigned int direct_to_threeway:1;\n-\tunsigned int crlf_in_old:1;\n-\tstruct fragment *fragments;\n-\tchar *result;\n-\tsize_t resultsize;\n-\tchar old_oid_prefix[GIT_MAX_HEXSZ + 1];\n-\tchar new_oid_prefix[GIT_MAX_HEXSZ + 1];\n-\tstruct patch *next;\n-\n-\t/* three-way fallback result */\n-\tstruct object_id threeway_stage[3];\n-};\n-\n static void free_fragment_list(struct fragment *list)\n {\n \twhile (list) {\n@@ -1321,11 +1287,13 @@ static int check_header_line(int linenr, struct patch *patch)\n }\n \n /* Verify that we recognize the lines following a git header */\n-static int parse_git_header(struct apply_state *state,\n-\t\t\t    const char *line,\n-\t\t\t    int len,\n-\t\t\t    unsigned int size,\n-\t\t\t    struct patch *patch)\n+int parse_git_header(struct strbuf *root,\n+\t\t     int *linenr,\n+\t\t     int p_value,\n+\t\t     const char *line,\n+\t\t     int len,\n+\t\t     unsigned int size,\n+\t\t     struct patch *patch)\n {\n \tunsigned long offset;\n \tstruct parse_git_header_state parse_hdr_state;\n@@ -1340,21 +1308,21 @@ static int parse_git_header(struct apply_state *state,\n \t * or removing or adding empty files), so we get\n \t * the default name from the header.\n \t */\n-\tpatch->def_name = git_header_name(state->p_value, line, len);\n-\tif (patch->def_name && state->root.len) {\n-\t\tchar *s = xstrfmt(\"%s%s\", state->root.buf, patch->def_name);\n+\tpatch->def_name = git_header_name(p_value, line, len);\n+\tif (patch->def_name && root->len) {\n+\t\tchar *s = xstrfmt(\"%s%s\", root->buf, patch->def_name);\n \t\tfree(patch->def_name);\n \t\tpatch->def_name = s;\n \t}\n \n \tline += len;\n \tsize -= len;\n-\tstate->linenr++;\n-\tparse_hdr_state.root = &state->root;\n-\tparse_hdr_state.linenr = state->linenr;\n-\tparse_hdr_state.p_value = state->p_value;\n+\t(*linenr)++;\n+\tparse_hdr_state.root = root;\n+\tparse_hdr_state.linenr = *linenr;\n+\tparse_hdr_state.p_value = p_value;\n \n-\tfor (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) {\n+\tfor (offset = len ; size > 0 ; offset += len, size -= len, line += len, (*linenr)++) {\n \t\tstatic const struct opentry {\n \t\t\tconst char *str;\n \t\t\tint (*fn)(struct parse_git_header_state *, const char *, struct patch *);\n@@ -1391,7 +1359,7 @@ static int parse_git_header(struct apply_state *state,\n \t\t\tres = p->fn(&parse_hdr_state, line + oplen, patch);\n \t\t\tif (res < 0)\n \t\t\t\treturn -1;\n-\t\t\tif (check_header_line(state->linenr, patch))\n+\t\t\tif (check_header_line(*linenr, patch))\n \t\t\t\treturn -1;\n \t\t\tif (res > 0)\n \t\t\t\treturn offset;\n@@ -1572,7 +1540,9 @@ static int find_header(struct apply_state *state,\n \t\t * or mode change, so we handle that specially\n \t\t */\n \t\tif (!memcmp(\"diff --git \", line, 11)) {\n-\t\t\tint git_hdr_len = parse_git_header(state, line, len, size, patch);\n+\t\t\tint git_hdr_len = parse_git_header(&state->root, &state->linenr,\n+\t\t\t\t\t\t\t   state->p_value, line, len,\n+\t\t\t\t\t\t\t   size, patch);\n \t\t\tif (git_hdr_len < 0)\n \t\t\t\treturn -128;\n \t\t\tif (git_hdr_len <= len)\ndiff --git a/apply.h b/apply.h\nindex 5948348133..e7a1586b17 100644\n--- a/apply.h\n+++ b/apply.h\n@@ -117,6 +117,40 @@ struct apply_state {\n \tint applied_after_fixing_ws;\n };\n \n+/*\n+ * This represents a \"patch\" to a file, both metainfo changes\n+ * such as creation/deletion, filemode and content changes represented\n+ * as a series of fragments.\n+ */\n+struct patch {\n+\tchar *new_name, *old_name, *def_name;\n+\tunsigned int old_mode, new_mode;\n+\tint is_new, is_delete;\t/* -1 = unknown, 0 = false, 1 = true */\n+\tint rejected;\n+\tunsigned ws_rule;\n+\tint lines_added, lines_deleted;\n+\tint score;\n+\tint extension_linenr; /* first line specifying delete/new/rename/copy */\n+\tunsigned int is_toplevel_relative:1;\n+\tunsigned int inaccurate_eof:1;\n+\tunsigned int is_binary:1;\n+\tunsigned int is_copy:1;\n+\tunsigned int is_rename:1;\n+\tunsigned int recount:1;\n+\tunsigned int conflicted_threeway:1;\n+\tunsigned int direct_to_threeway:1;\n+\tunsigned int crlf_in_old:1;\n+\tstruct fragment *fragments;\n+\tchar *result;\n+\tsize_t resultsize;\n+\tchar old_oid_prefix[GIT_MAX_HEXSZ + 1];\n+\tchar new_oid_prefix[GIT_MAX_HEXSZ + 1];\n+\tstruct patch *next;\n+\n+\t/* three-way fallback result */\n+\tstruct object_id threeway_stage[3];\n+};\n+\n int apply_parse_options(int argc, const char **argv,\n \t\t\tstruct apply_state *state,\n \t\t\tint *force_apply, int *options,\n@@ -127,6 +161,20 @@ int init_apply_state(struct apply_state *state,\n void clear_apply_state(struct apply_state *state);\n int check_apply_state(struct apply_state *state, int force_apply);\n \n+/*\n+ * Parse a get header, starting at line.  Fills the relevant metadata\n+ * information in 'struct patch'.\n+ *\n+ * Returns -1 on failure, the length of the parsed header otherwise.\n+ */\n+int parse_git_header(struct strbuf *root,\n+\t\t     int *linenr,\n+\t\t     int p_value,\n+\t\t     const char *line,\n+\t\t     int len,\n+\t\t     unsigned int size,\n+\t\t     struct patch *patch);\n+\n /*\n  * Some aspects of the apply behavior are controlled by the following\n  * bits in the \"options\" parameter passed to apply_all_patches().\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378637","messageId":"20190705170630.27500-7-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 06/14] apply: only pass required data to gitdiff_* functions","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:22Z","receivedAt":"2019-07-05T17:07:07Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'gitdiff_*()' functions take 'struct apply_state' as\nparameter, even though they only needs the root, linenr and p_value\nfrom that struct.\n\nThese functions are in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 59 ++++++++++++++++++++++++++++++++++-----------------------\n 1 file changed, 35 insertions(+), 24 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 3cd4e3d3b3..468f1d3fee 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -22,6 +22,12 @@\n #include \"rerere.h\"\n #include \"apply.h\"\n \n+struct parse_git_header_state {\n+\tstruct strbuf *root;\n+\tint linenr;\n+\tint p_value;\n+};\n+\n static void git_apply_config(void)\n {\n \tgit_config_get_string_const(\"apply.whitespace\", &apply_default_whitespace);\n@@ -914,7 +920,7 @@ static int parse_traditional_patch(struct apply_state *state,\n \treturn 0;\n }\n \n-static int gitdiff_hdrend(struct apply_state *state,\n+static int gitdiff_hdrend(struct parse_git_header_state *state,\n \t\t\t  const char *line,\n \t\t\t  struct patch *patch)\n {\n@@ -933,14 +939,14 @@ static int gitdiff_hdrend(struct apply_state *state,\n #define DIFF_OLD_NAME 0\n #define DIFF_NEW_NAME 1\n \n-static int gitdiff_verify_name(struct apply_state *state,\n+static int gitdiff_verify_name(struct parse_git_header_state *state,\n \t\t\t       const char *line,\n \t\t\t       int isnull,\n \t\t\t       char **name,\n \t\t\t       int side)\n {\n \tif (!*name && !isnull) {\n-\t\t*name = find_name(&state->root, line, NULL, state->p_value, TERM_TAB);\n+\t\t*name = find_name(state->root, line, NULL, state->p_value, TERM_TAB);\n \t\treturn 0;\n \t}\n \n@@ -949,7 +955,7 @@ static int gitdiff_verify_name(struct apply_state *state,\n \t\tif (isnull)\n \t\t\treturn error(_(\"git apply: bad git-diff - expected /dev/null, got %s on line %d\"),\n \t\t\t\t     *name, state->linenr);\n-\t\tanother = find_name(&state->root, line, NULL, state->p_value, TERM_TAB);\n+\t\tanother = find_name(state->root, line, NULL, state->p_value, TERM_TAB);\n \t\tif (!another || strcmp(another, *name)) {\n \t\t\tfree(another);\n \t\t\treturn error((side == DIFF_NEW_NAME) ?\n@@ -965,7 +971,7 @@ static int gitdiff_verify_name(struct apply_state *state,\n \treturn 0;\n }\n \n-static int gitdiff_oldname(struct apply_state *state,\n+static int gitdiff_oldname(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n@@ -974,7 +980,7 @@ static int gitdiff_oldname(struct apply_state *state,\n \t\t\t\t   DIFF_OLD_NAME);\n }\n \n-static int gitdiff_newname(struct apply_state *state,\n+static int gitdiff_newname(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n@@ -992,21 +998,21 @@ static int parse_mode_line(const char *line, int linenr, unsigned int *mode)\n \treturn 0;\n }\n \n-static int gitdiff_oldmode(struct apply_state *state,\n+static int gitdiff_oldmode(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n \treturn parse_mode_line(line, state->linenr, &patch->old_mode);\n }\n \n-static int gitdiff_newmode(struct apply_state *state,\n+static int gitdiff_newmode(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n \treturn parse_mode_line(line, state->linenr, &patch->new_mode);\n }\n \n-static int gitdiff_delete(struct apply_state *state,\n+static int gitdiff_delete(struct parse_git_header_state *state,\n \t\t\t  const char *line,\n \t\t\t  struct patch *patch)\n {\n@@ -1016,7 +1022,7 @@ static int gitdiff_delete(struct apply_state *state,\n \treturn gitdiff_oldmode(state, line, patch);\n }\n \n-static int gitdiff_newfile(struct apply_state *state,\n+static int gitdiff_newfile(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n@@ -1026,47 +1032,47 @@ static int gitdiff_newfile(struct apply_state *state,\n \treturn gitdiff_newmode(state, line, patch);\n }\n \n-static int gitdiff_copysrc(struct apply_state *state,\n+static int gitdiff_copysrc(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n \tpatch->is_copy = 1;\n \tfree(patch->old_name);\n-\tpatch->old_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->old_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-static int gitdiff_copydst(struct apply_state *state,\n+static int gitdiff_copydst(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n \tpatch->is_copy = 1;\n \tfree(patch->new_name);\n-\tpatch->new_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->new_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-static int gitdiff_renamesrc(struct apply_state *state,\n+static int gitdiff_renamesrc(struct parse_git_header_state *state,\n \t\t\t     const char *line,\n \t\t\t     struct patch *patch)\n {\n \tpatch->is_rename = 1;\n \tfree(patch->old_name);\n-\tpatch->old_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->old_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-static int gitdiff_renamedst(struct apply_state *state,\n+static int gitdiff_renamedst(struct parse_git_header_state *state,\n \t\t\t     const char *line,\n \t\t\t     struct patch *patch)\n {\n \tpatch->is_rename = 1;\n \tfree(patch->new_name);\n-\tpatch->new_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->new_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-static int gitdiff_similarity(struct apply_state *state,\n+static int gitdiff_similarity(struct parse_git_header_state *state,\n \t\t\t      const char *line,\n \t\t\t      struct patch *patch)\n {\n@@ -1076,7 +1082,7 @@ static int gitdiff_similarity(struct apply_state *state,\n \treturn 0;\n }\n \n-static int gitdiff_dissimilarity(struct apply_state *state,\n+static int gitdiff_dissimilarity(struct parse_git_header_state *state,\n \t\t\t\t const char *line,\n \t\t\t\t struct patch *patch)\n {\n@@ -1086,7 +1092,7 @@ static int gitdiff_dissimilarity(struct apply_state *state,\n \treturn 0;\n }\n \n-static int gitdiff_index(struct apply_state *state,\n+static int gitdiff_index(struct parse_git_header_state *state,\n \t\t\t const char *line,\n \t\t\t struct patch *patch)\n {\n@@ -1126,7 +1132,7 @@ static int gitdiff_index(struct apply_state *state,\n  * This is normal for a diff that doesn't change anything: we'll fall through\n  * into the next diff. Tell the parser to break out.\n  */\n-static int gitdiff_unrecognized(struct apply_state *state,\n+static int gitdiff_unrecognized(struct parse_git_header_state *state,\n \t\t\t\tconst char *line,\n \t\t\t\tstruct patch *patch)\n {\n@@ -1322,6 +1328,7 @@ static int parse_git_header(struct apply_state *state,\n \t\t\t    struct patch *patch)\n {\n \tunsigned long offset;\n+\tstruct parse_git_header_state parse_hdr_state;\n \n \t/* A git diff has explicit new/delete information, so we don't guess */\n \tpatch->is_new = 0;\n@@ -1343,10 +1350,14 @@ static int parse_git_header(struct apply_state *state,\n \tline += len;\n \tsize -= len;\n \tstate->linenr++;\n+\tparse_hdr_state.root = &state->root;\n+\tparse_hdr_state.linenr = state->linenr;\n+\tparse_hdr_state.p_value = state->p_value;\n+\n \tfor (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) {\n \t\tstatic const struct opentry {\n \t\t\tconst char *str;\n-\t\t\tint (*fn)(struct apply_state *, const char *, struct patch *);\n+\t\t\tint (*fn)(struct parse_git_header_state *, const char *, struct patch *);\n \t\t} optable[] = {\n \t\t\t{ \"@@ -\", gitdiff_hdrend },\n \t\t\t{ \"--- \", gitdiff_oldname },\n@@ -1377,7 +1388,7 @@ static int parse_git_header(struct apply_state *state,\n \t\t\tint res;\n \t\t\tif (len < oplen || memcmp(p->str, line, oplen))\n \t\t\t\tcontinue;\n-\t\t\tres = p->fn(state, line + oplen, patch);\n+\t\t\tres = p->fn(&parse_hdr_state, line + oplen, patch);\n \t\t\tif (res < 0)\n \t\t\t\treturn -1;\n \t\t\tif (check_header_line(state->linenr, patch))\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378639","messageId":"20190705170630.27500-9-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 08/14] range-diff: fix function parameter indentation","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:24Z","receivedAt":"2019-07-05T17:07:08Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Fix the indentation of the function parameters for a couple of\nfunctions, to match the style in the rest of the file.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 48b0e1b4ce..9242b8975f 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -148,7 +148,7 @@ static int read_patches(const char *range, struct string_list *list)\n }\n \n static int patch_util_cmp(const void *dummy, const struct patch_util *a,\n-\t\t     const struct patch_util *b, const char *keydata)\n+\t\t\t  const struct patch_util *b, const char *keydata)\n {\n \treturn strcmp(a->diff, keydata ? keydata : b->diff);\n }\n@@ -373,7 +373,7 @@ static struct diff_filespec *get_filespec(const char *name, const char *p)\n }\n \n static void patch_diff(const char *a, const char *b,\n-\t\t\t      struct diff_options *diffopt)\n+\t\t       struct diff_options *diffopt)\n {\n \tdiff_queue(&diff_queued_diff,\n \t\t   get_filespec(\"a\", a), get_filespec(\"b\", b));\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378640","messageId":"20190705170630.27500-10-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 09/14] range-diff: split lines manually","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:25Z","receivedAt":"2019-07-05T17:07:11Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently range-diff uses the 'strbuf_getline()' function for doing\nits line by line processing.  In a future patch we want to do parts of\nthat parsing using the 'parse_git_header()' function, which does\nrequires reading parts of the input from that function, which doesn't\nuse strbufs.\n\nSwitch range-diff to do our own line by line parsing, so we can re-use\nthe parse_git_header function later.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n\nLonger term it might be better to have both range-diff and apply code\nuse strbufs.  However I didn't feel it's worth making that change for\nthis patch series.\n\n range-diff.c | 69 +++++++++++++++++++++++++++++-----------------------\n 1 file changed, 39 insertions(+), 30 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 9242b8975f..916afa44c0 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -24,6 +24,17 @@ struct patch_util {\n \tstruct object_id oid;\n };\n \n+static unsigned long linelen(const char *buffer, unsigned long size)\n+{\n+\tunsigned long len = 0;\n+\twhile (size--) {\n+\t\tlen++;\n+\t\tif (*buffer++ == '\\n')\n+\t\t\tbreak;\n+\t}\n+\treturn len;\n+}\n+\n /*\n  * Reads the patches into a string list, with the `util` field being populated\n  * as struct object_id (will need to be free()d).\n@@ -31,10 +42,12 @@ struct patch_util {\n static int read_patches(const char *range, struct string_list *list)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tFILE *in;\n-\tstruct strbuf buf = STRBUF_INIT, line = STRBUF_INIT;\n+\tstruct strbuf buf = STRBUF_INIT, file = STRBUF_INIT;\n \tstruct patch_util *util = NULL;\n \tint in_header = 1;\n+\tchar *line;\n+\tint offset, len;\n+\tsize_t size;\n \n \targv_array_pushl(&cp.args, \"log\", \"--no-color\", \"-p\", \"--no-merges\",\n \t\t\t\"--reverse\", \"--date-order\", \"--decorate=no\",\n@@ -54,17 +67,15 @@ static int read_patches(const char *range, struct string_list *list)\n \n \tif (start_command(&cp))\n \t\treturn error_errno(_(\"could not start `log`\"));\n-\tin = fdopen(cp.out, \"r\");\n-\tif (!in) {\n-\t\terror_errno(_(\"could not read `log` output\"));\n-\t\tfinish_command(&cp);\n-\t\treturn -1;\n-\t}\n+\tstrbuf_read(&file, cp.out, 0);\n \n-\twhile (strbuf_getline(&line, in) != EOF) {\n+\tline = strbuf_detach(&file, &size);\n+\tfor (offset = 0; size > 0; offset += len, size -= len, line += len) {\n \t\tconst char *p;\n \n-\t\tif (skip_prefix(line.buf, \"commit \", &p)) {\n+\t\tlen = linelen(line, size);\n+\t\tline[len - 1] = '\\0';\n+\t\tif (skip_prefix(line, \"commit \", &p)) {\n \t\t\tif (util) {\n \t\t\t\tstring_list_append(list, buf.buf)->util = util;\n \t\t\t\tstrbuf_reset(&buf);\n@@ -75,8 +86,6 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\t\tfree(util);\n \t\t\t\tstring_list_clear(list, 1);\n \t\t\t\tstrbuf_release(&buf);\n-\t\t\t\tstrbuf_release(&line);\n-\t\t\t\tfclose(in);\n \t\t\t\tfinish_command(&cp);\n \t\t\t\treturn -1;\n \t\t\t}\n@@ -85,26 +94,28 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\tcontinue;\n \t\t}\n \n-\t\tif (starts_with(line.buf, \"diff --git\")) {\n+\t\tif (starts_with(line, \"diff --git\")) {\n \t\t\tin_header = 0;\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\t\tif (!util->diff_offset)\n \t\t\t\tutil->diff_offset = buf.len;\n \t\t\tstrbuf_addch(&buf, ' ');\n-\t\t\tstrbuf_addbuf(&buf, &line);\n+\t\t\tstrbuf_addstr(&buf, line);\n \t\t} else if (in_header) {\n-\t\t\tif (starts_with(line.buf, \"Author: \")) {\n-\t\t\t\tstrbuf_addbuf(&buf, &line);\n+\t\t\tif (starts_with(line, \"Author: \")) {\n+\t\t\t\tstrbuf_addstr(&buf, line);\n \t\t\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n-\t\t\t} else if (starts_with(line.buf, \"    \")) {\n-\t\t\t\tstrbuf_rtrim(&line);\n-\t\t\t\tstrbuf_addbuf(&buf, &line);\n+\t\t\t} else if (starts_with(line, \"    \")) {\n+\t\t\t\tp = line + len - 2;\n+\t\t\t\twhile (isspace(*p) && p >= line)\n+\t\t\t\t\tp--;\n+\t\t\t\tstrbuf_add(&buf, line, p - line + 1);\n \t\t\t\tstrbuf_addch(&buf, '\\n');\n \t\t\t}\n \t\t\tcontinue;\n-\t\t} else if (starts_with(line.buf, \"@@ \"))\n+\t\t} else if (starts_with(line, \"@@ \"))\n \t\t\tstrbuf_addstr(&buf, \"@@\");\n-\t\telse if (!line.buf[0] || starts_with(line.buf, \"index \"))\n+\t\telse if (!line[0] || starts_with(line, \"index \"))\n \t\t\t/*\n \t\t\t * A completely blank (not ' \\n', which is context)\n \t\t\t * line is not valid in a diff.  We skip it\n@@ -117,25 +128,23 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\t * we are not interested.\n \t\t\t */\n \t\t\tcontinue;\n-\t\telse if (line.buf[0] == '>') {\n+\t\telse if (line[0] == '>') {\n \t\t\tstrbuf_addch(&buf, '+');\n-\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n-\t\t} else if (line.buf[0] == '<') {\n+\t\t\tstrbuf_addstr(&buf, line + 1);\n+\t\t} else if (line[0] == '<') {\n \t\t\tstrbuf_addch(&buf, '-');\n-\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n-\t\t} else if (line.buf[0] == '#') {\n+\t\t\tstrbuf_addstr(&buf, line + 1);\n+\t\t} else if (line[0] == '#') {\n \t\t\tstrbuf_addch(&buf, ' ');\n-\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n+\t\t\tstrbuf_addstr(&buf, line + 1);\n \t\t} else {\n \t\t\tstrbuf_addch(&buf, ' ');\n-\t\t\tstrbuf_addbuf(&buf, &line);\n+\t\t\tstrbuf_addstr(&buf, line);\n \t\t}\n \n \t\tstrbuf_addch(&buf, '\\n');\n \t\tutil->diffsize++;\n \t}\n-\tfclose(in);\n-\tstrbuf_release(&line);\n \n \tif (util)\n \t\tstring_list_append(list, buf.buf)->util = util;\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378641","messageId":"20190705170630.27500-11-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 10/14] range-diff: don't remove funcname from inner diff","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:26Z","receivedAt":"2019-07-05T17:07:12Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"When postprocessing the inner diff in range-diff, we currently replace\nthe whole hunk header line with just \"@@\".  This matches how 'git\ntbdiff' used to handle hunk headers as well.\n\nMost likely this is being done because line numbers in the hunk header\nare not relevant without other changes.  They can for example easily\nchange if a range is rebased, and lines are added/removed before a\nchange that we actually care about in our ranges.\n\nHowever it can still be useful to have the function name that 'git\ndiff' extracts as additional context for the change.\n\nNote that it is not guaranteed that the hunk header actually shows up\nin the range-diff, and this change only aims to improve the case where\na hunk header would already be included in the final output.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c          | 8 +++++---\n t/t3206-range-diff.sh | 6 +++---\n 2 files changed, 8 insertions(+), 6 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 916afa44c0..484b1ec5a9 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -113,9 +113,11 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\t\tstrbuf_addch(&buf, '\\n');\n \t\t\t}\n \t\t\tcontinue;\n-\t\t} else if (starts_with(line, \"@@ \"))\n-\t\t\tstrbuf_addstr(&buf, \"@@\");\n-\t\telse if (!line[0] || starts_with(line, \"index \"))\n+\t\t} else if (skip_prefix(line, \"@@ \", &p)) {\n+\t\t\tif (!(p = strstr(p, \"@@\")))\n+\t\t\t\tdie(_(\"invalid hunk header in inner diff\"));\n+\t\t\tstrbuf_addstr(&buf, p);\n+\t\t} else if (!line[0] || starts_with(line, \"index \"))\n \t\t\t/*\n \t\t\t * A completely blank (not ' \\n', which is context)\n \t\t\t * line is not valid in a diff.  We skip it\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex 048feaf6dd..aebd4e3693 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -110,7 +110,7 @@ test_expect_success 'changed commit' '\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n \t    @@ -8,7 +8,7 @@\n-\t     @@\n+\t     @@ A\n \t      9\n \t      10\n \t    - B\n@@ -169,7 +169,7 @@ test_expect_success 'changed commit with sm config' '\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n \t    @@ -8,7 +8,7 @@\n-\t     @@\n+\t     @@ A\n \t      9\n \t      10\n \t    - B\n@@ -231,7 +231,7 @@ test_expect_success 'dual-coloring' '\n \t:      14<RESET>\n \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n \t:    <REVERSE><CYAN>@@ -8,7 +8,7 @@<RESET>\n-\t:    <CYAN> @@<RESET>\n+\t:    <CYAN> @@ A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n \t:    <REVERSE><RED>-<RESET><FAINT> BB<RESET>\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378642","messageId":"20190705170630.27500-12-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 11/14] range-diff: suppress line count in outer diff","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:27Z","receivedAt":"2019-07-05T17:07:14Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"The line count in the outer diff's hunk headers of a range diff is not\nall that interesting.  It merely shows how far along the inner diff\nare on both sides.  That number is of no use for human readers, and\nrange-diffs are not meant to be machine readable.\n\nIn a subsequent commit we're going to add some more contextual\ninformation such as the filename corresponding to the diff to the hunk\nheaders.  Remove the unnecessary information, and just keep the \"@@\"\nto indicate that a new hunk of the outer diff is starting.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n diff.c                |  5 ++++-\n diff.h                |  1 +\n range-diff.c          |  1 +\n t/t3206-range-diff.sh | 16 ++++++++--------\n 4 files changed, 14 insertions(+), 9 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex ec5c095199..9c28ff0a92 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1672,7 +1672,10 @@ static void emit_hunk_header(struct emit_callback *ecbdata,\n \tif (ecbdata->opt->flags.dual_color_diffed_diffs)\n \t\tstrbuf_addstr(&msgbuf, reverse);\n \tstrbuf_addstr(&msgbuf, frag);\n-\tstrbuf_add(&msgbuf, line, ep - line);\n+\tif (ecbdata->opt->flags.suppress_hunk_header_line_count)\n+\t\tstrbuf_add(&msgbuf, atat, sizeof(atat));\n+\telse\n+\t\tstrbuf_add(&msgbuf, line, ep - line);\n \tstrbuf_addstr(&msgbuf, reset);\n \n \t/*\ndiff --git a/diff.h b/diff.h\nindex c9db9825bb..49913049f9 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -98,6 +98,7 @@ struct diff_flags {\n \tunsigned stat_with_summary;\n \tunsigned suppress_diff_headers;\n \tunsigned dual_color_diffed_diffs;\n+\tunsigned suppress_hunk_header_line_count;\n };\n \n static inline void diff_flags_or(struct diff_flags *a,\ndiff --git a/range-diff.c b/range-diff.c\nindex 484b1ec5a9..b31fbab026 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -480,6 +480,7 @@ int show_range_diff(const char *range1, const char *range2,\n \t\t\topts.output_format = DIFF_FORMAT_PATCH;\n \t\topts.flags.suppress_diff_headers = 1;\n \t\topts.flags.dual_color_diffed_diffs = dual_color;\n+\t\topts.flags.suppress_hunk_header_line_count = 1;\n \t\topts.output_prefix = output_prefix_cb;\n \t\tstrbuf_addstr(&indent, \"    \");\n \t\topts.output_prefix_data = &indent;\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex aebd4e3693..9f89af7178 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -99,7 +99,7 @@ test_expect_success 'changed commit' '\n \t1:  4de457d = 1:  a4b3333 s/5/A/\n \t2:  fccce22 = 2:  f51d370 s/4/A/\n \t3:  147e64e ! 3:  0559556 s/11/B/\n-\t    @@ -10,7 +10,7 @@\n+\t    @@\n \t      9\n \t      10\n \t     -11\n@@ -109,7 +109,7 @@ test_expect_success 'changed commit' '\n \t      13\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n-\t    @@ -8,7 +8,7 @@\n+\t    @@\n \t     @@ A\n \t      9\n \t      10\n@@ -158,7 +158,7 @@ test_expect_success 'changed commit with sm config' '\n \t1:  4de457d = 1:  a4b3333 s/5/A/\n \t2:  fccce22 = 2:  f51d370 s/4/A/\n \t3:  147e64e ! 3:  0559556 s/11/B/\n-\t    @@ -10,7 +10,7 @@\n+\t    @@\n \t      9\n \t      10\n \t     -11\n@@ -168,7 +168,7 @@ test_expect_success 'changed commit with sm config' '\n \t      13\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n-\t    @@ -8,7 +8,7 @@\n+\t    @@\n \t     @@ A\n \t      9\n \t      10\n@@ -191,7 +191,7 @@ test_expect_success 'changed message' '\n \tsed s/Z/\\ /g >expected <<-EOF &&\n \t1:  4de457d = 1:  f686024 s/5/A/\n \t2:  fccce22 ! 2:  4ab067d s/4/A/\n-\t    @@ -2,6 +2,8 @@\n+\t    @@\n \t    Z\n \t    Z    s/4/A/\n \t    Z\n@@ -210,7 +210,7 @@ test_expect_success 'dual-coloring' '\n \tsed -e \"s|^:||\" >expect <<-\\EOF &&\n \t:<YELLOW>1:  a4b3333 = 1:  f686024 s/5/A/<RESET>\n \t:<RED>2:  f51d370 <RESET><YELLOW>!<RESET><GREEN> 2:  4ab067d<RESET><YELLOW> s/4/A/<RESET>\n-\t:    <REVERSE><CYAN>@@ -2,6 +2,8 @@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET>\n \t:     <RESET>\n \t:         s/4/A/<RESET>\n \t:     <RESET>\n@@ -220,7 +220,7 @@ test_expect_success 'dual-coloring' '\n \t:      --- a/file<RESET>\n \t:      +++ b/file<RESET>\n \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n-\t:    <REVERSE><CYAN>@@ -10,7 +10,7 @@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n \t:    <RED> -11<RESET>\n@@ -230,7 +230,7 @@ test_expect_success 'dual-coloring' '\n \t:      13<RESET>\n \t:      14<RESET>\n \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n-\t:    <REVERSE><CYAN>@@ -8,7 +8,7 @@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET>\n \t:    <CYAN> @@ A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378643","messageId":"20190705170630.27500-13-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 12/14] range-diff: add section header instead of diff header","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:28Z","receivedAt":"2019-07-05T17:07:15Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently range-diff keeps the diff header of the inner diff\nintact (apart from stripping lines starting with index).  This diff\nheader is somewhat useful, especially when files get different\nnames in different ranges.\n\nHowever there is no real need to keep the whole diff header for that.\nThe main reason we currently do that is probably because it is easy to\ndo.\n\nIntroduce a new range diff hunk header, that's enclosed by \"##\",\nsimilar to how line numbers in diff hunks are enclosed by \"@@\", and\ngive human readable information of what exactly happened to the file,\nincluding the file name.\n\nThis improves the readability of the range-diff by giving more concise\ninformation to the users.  For example if a file was renamed in one\niteration, but not in another, the diff of the headers would be quite\nnoisy.  However the diff of a single line is concise and should be\neasier to understand.\n\nAdditionaly, this allows us to add these range diff section headers to\nthe outer diffs hunk headers using a custom userdiff pattern, which\nshould help making the range-diff more readable.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c           | 35 ++++++++++++----\n t/t3206-range-diff.sh  | 91 +++++++++++++++++++++++++++++++++++++++---\n t/t3206/history.export | 84 ++++++++++++++++++++++++++++++++++++--\n 3 files changed, 193 insertions(+), 17 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex b31fbab026..cc01f7f573 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -10,6 +10,7 @@\n #include \"commit.h\"\n #include \"pretty.h\"\n #include \"userdiff.h\"\n+#include \"apply.h\"\n \n struct patch_util {\n \t/* For the search for an exact match */\n@@ -95,12 +96,36 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t}\n \n \t\tif (starts_with(line, \"diff --git\")) {\n+\t\t\tstruct patch patch;\n+\t\t\tstruct strbuf root = STRBUF_INIT;\n+\t\t\tint linenr = 0;\n+\n \t\t\tin_header = 0;\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\t\tif (!util->diff_offset)\n \t\t\t\tutil->diff_offset = buf.len;\n-\t\t\tstrbuf_addch(&buf, ' ');\n-\t\t\tstrbuf_addstr(&buf, line);\n+\t\t\tmemset(&patch, 0, sizeof(patch));\n+\t\t\tline[len - 1] = '\\n';\n+\t\t\tlen = parse_git_header(&root, &linenr, 1, line,\n+\t\t\t\t\t       len, size, &patch);\n+\t\t\tif (len < 0)\n+\t\t\t\tdie(_(\"could not parse git header\"));\n+\t\t\tstrbuf_addstr(&buf, \" ## \");\n+\t\t\tif (patch.is_new > 0)\n+\t\t\t\tstrbuf_addf(&buf, \"%s (new)\", patch.new_name);\n+\t\t\telse if (patch.is_delete > 0)\n+\t\t\t\tstrbuf_addf(&buf, \"%s (deleted)\", patch.old_name);\n+\t\t\telse if (patch.is_rename)\n+\t\t\t\tstrbuf_addf(&buf, \"%s => %s\", patch.old_name, patch.new_name);\n+\t\t\telse\n+\t\t\t\tstrbuf_addstr(&buf, patch.new_name);\n+\n+\t\t\tif (patch.new_mode && patch.old_mode &&\n+\t\t\t    patch.old_mode != patch.new_mode)\n+\t\t\t\tstrbuf_addf(&buf, \" (mode change %06o => %06o)\",\n+\t\t\t\t\t    patch.old_mode, patch.new_mode);\n+\n+\t\t\tstrbuf_addstr(&buf, \" ##\");\n \t\t} else if (in_header) {\n \t\t\tif (starts_with(line, \"Author: \")) {\n \t\t\t\tstrbuf_addstr(&buf, line);\n@@ -117,17 +142,13 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\tif (!(p = strstr(p, \"@@\")))\n \t\t\t\tdie(_(\"invalid hunk header in inner diff\"));\n \t\t\tstrbuf_addstr(&buf, p);\n-\t\t} else if (!line[0] || starts_with(line, \"index \"))\n+\t\t} else if (!line[0])\n \t\t\t/*\n \t\t\t * A completely blank (not ' \\n', which is context)\n \t\t\t * line is not valid in a diff.  We skip it\n \t\t\t * silently, because this neatly handles the blank\n \t\t\t * separator line between commits in git-log\n \t\t\t * output.\n-\t\t\t *\n-\t\t\t * We also want to ignore the diff's `index` lines\n-\t\t\t * because they contain exact blob hashes in which\n-\t\t\t * we are not interested.\n \t\t\t */\n \t\t\tcontinue;\n \t\telse if (line[0] == '>') {\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex 9f89af7178..c277756057 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -181,6 +181,85 @@ test_expect_success 'changed commit with sm config' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'renamed file' '\n+\tgit range-diff --no-color --submodule=log topic...renamed-file >actual &&\n+\tsed s/Z/\\ /g >expected <<-EOF &&\n+\t1:  4de457d = 1:  f258d75 s/5/A/\n+\t2:  fccce22 ! 2:  017b62d s/4/A/\n+\t    @@\n+\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n+\t    Z\n+\t    -    s/4/A/\n+\t    +    s/4/A/ + rename file\n+\t    Z\n+\t    - ## file ##\n+\t    + ## file => renamed-file ##\n+\t    Z@@\n+\t    Z 1\n+\t    Z 2\n+\t3:  147e64e ! 3:  3ce7af6 s/11/B/\n+\t    @@\n+\t    Z\n+\t    Z    s/11/B/\n+\t    Z\n+\t    - ## file ##\n+\t    + ## renamed-file ##\n+\t    Z@@ A\n+\t    Z 8\n+\t    Z 9\n+\t4:  a63e992 ! 4:  1e6226b s/12/B/\n+\t    @@\n+\t    Z\n+\t    Z    s/12/B/\n+\t    Z\n+\t    - ## file ##\n+\t    + ## renamed-file ##\n+\t    Z@@ A\n+\t    Z 9\n+\t    Z 10\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'file added and later removed' '\n+\tgit range-diff --no-color --submodule=log topic...added-removed >actual &&\n+\tsed s/Z/\\ /g >expected <<-EOF &&\n+\t1:  4de457d = 1:  096b1ba s/5/A/\n+\t2:  fccce22 ! 2:  d92e698 s/4/A/\n+\t    @@\n+\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n+\t    Z\n+\t    -    s/4/A/\n+\t    +    s/4/A/ + new-file\n+\t    Z\n+\t    Z ## file ##\n+\t    Z@@\n+\t    @@\n+\t    Z A\n+\t    Z 6\n+\t    Z 7\n+\t    +\n+\t    + ## new-file (new) ##\n+\t3:  147e64e ! 3:  9a1db4d s/11/B/\n+\t    @@\n+\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n+\t    Z\n+\t    -    s/11/B/\n+\t    +    s/11/B/ + remove file\n+\t    Z\n+\t    Z ## file ##\n+\t    Z@@ A\n+\t    @@\n+\t    Z 12\n+\t    Z 13\n+\t    Z 14\n+\t    +\n+\t    + ## new-file (deleted) ##\n+\t4:  a63e992 = 4:  fea3b5c s/12/B/\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'no commits on one side' '\n \tgit commit --amend -m \"new message\" &&\n \tgit range-diff master HEAD@{1} HEAD\n@@ -197,9 +276,9 @@ test_expect_success 'changed message' '\n \t    Z\n \t    +    Also a silly comment here!\n \t    +\n-\t    Z diff --git a/file b/file\n-\t    Z --- a/file\n-\t    Z +++ b/file\n+\t    Z ## file ##\n+\t    Z@@\n+\t    Z 1\n \t3:  147e64e = 3:  b9cb956 s/11/B/\n \t4:  a63e992 = 4:  8add5f1 s/12/B/\n \tEOF\n@@ -216,9 +295,9 @@ test_expect_success 'dual-coloring' '\n \t:     <RESET>\n \t:    <REVERSE><GREEN>+<RESET><BOLD>    Also a silly comment here!<RESET>\n \t:    <REVERSE><GREEN>+<RESET>\n-\t:      diff --git a/file b/file<RESET>\n-\t:      --- a/file<RESET>\n-\t:      +++ b/file<RESET>\n+\t:      ## file ##<RESET>\n+\t:    <CYAN> @@<RESET>\n+\t:      1<RESET>\n \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n \t:    <REVERSE><CYAN>@@<RESET>\n \t:      9<RESET>\ndiff --git a/t/t3206/history.export b/t/t3206/history.export\nindex b8ffff0940..7bb3814962 100644\n--- a/t/t3206/history.export\n+++ b/t/t3206/history.export\n@@ -22,8 +22,8 @@ data 51\n 19\n 20\n \n-reset refs/heads/removed\n-commit refs/heads/removed\n+reset refs/heads/renamed-file\n+commit refs/heads/renamed-file\n mark :2\n author Thomas Rast <trast@inf.ethz.ch> 1374424921 +0200\n committer Thomas Rast <trast@inf.ethz.ch> 1374484724 +0200\n@@ -599,6 +599,82 @@ s/12/B/\n from :46\n M 100644 :28 file\n \n-reset refs/heads/removed\n-from :47\n+commit refs/heads/added-removed\n+mark :48\n+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574151 +0100\n+data 7\n+s/5/A/\n+from :2\n+M 100644 :3 file\n+\n+blob\n+mark :49\n+data 0\n+\n+commit refs/heads/added-removed\n+mark :50\n+author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n+data 18\n+s/4/A/ + new-file\n+from :48\n+M 100644 :5 file\n+M 100644 :49 new-file\n+\n+commit refs/heads/added-removed\n+mark :51\n+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n+data 22\n+s/11/B/ + remove file\n+from :50\n+M 100644 :7 file\n+D new-file\n+\n+commit refs/heads/added-removed\n+mark :52\n+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n+data 8\n+s/12/B/\n+from :51\n+M 100644 :9 file\n+\n+commit refs/heads/renamed-file\n+mark :53\n+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574309 +0100\n+data 7\n+s/5/A/\n+from :2\n+M 100644 :3 file\n+\n+commit refs/heads/renamed-file\n+mark :54\n+author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574312 +0100\n+data 21\n+s/4/A/ + rename file\n+from :53\n+D file\n+M 100644 :5 renamed-file\n+\n+commit refs/heads/renamed-file\n+mark :55\n+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574319 +0100\n+data 8\n+s/11/B/\n+from :54\n+M 100644 :7 renamed-file\n+\n+commit refs/heads/renamed-file\n+mark :56\n+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574319 +0100\n+data 8\n+s/12/B/\n+from :55\n+M 100644 :9 renamed-file\n \n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378644","messageId":"20190705170630.27500-14-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 13/14] range-diff: add filename to inner diff","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:29Z","receivedAt":"2019-07-05T17:07:17Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"In a range-diff it's not always clear which file a certain funcname of\nthe inner diff belongs to, because the diff header (or section header\nas added in a previous commit) is not always visible in the\nrange-diff.\n\nAdd the filename to the inner diffs header, so it's always visible to\nusers.\n\nThis also allows us to add the filename + the funcname to the outer\ndiffs hunk headers using a custom userdiff pattern, which will be done\nin the next commit.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c          | 14 ++++++++++++--\n t/t3206-range-diff.sh | 16 ++++++++++------\n 2 files changed, 22 insertions(+), 8 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex cc01f7f573..09cb1ddbb1 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -46,7 +46,7 @@ static int read_patches(const char *range, struct string_list *list)\n \tstruct strbuf buf = STRBUF_INIT, file = STRBUF_INIT;\n \tstruct patch_util *util = NULL;\n \tint in_header = 1;\n-\tchar *line;\n+\tchar *line, *current_filename = NULL;\n \tint offset, len;\n \tsize_t size;\n \n@@ -120,6 +120,12 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\telse\n \t\t\t\tstrbuf_addstr(&buf, patch.new_name);\n \n+\t\t\tfree(current_filename);\n+\t\t\tif (patch.is_delete > 0)\n+\t\t\t\tcurrent_filename = xstrdup(patch.old_name);\n+\t\t\telse\n+\t\t\t\tcurrent_filename = xstrdup(patch.new_name);\n+\n \t\t\tif (patch.new_mode && patch.old_mode &&\n \t\t\t    patch.old_mode != patch.new_mode)\n \t\t\t\tstrbuf_addf(&buf, \" (mode change %06o => %06o)\",\n@@ -141,7 +147,10 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t} else if (skip_prefix(line, \"@@ \", &p)) {\n \t\t\tif (!(p = strstr(p, \"@@\")))\n \t\t\t\tdie(_(\"invalid hunk header in inner diff\"));\n-\t\t\tstrbuf_addstr(&buf, p);\n+\t\t\tstrbuf_addstr(&buf, \"@@\");\n+\t\t\tif (current_filename && p[2])\n+\t\t\t\tstrbuf_addf(&buf, \" %s:\", current_filename);\n+\t\t\tstrbuf_addstr(&buf, p + 2);\n \t\t} else if (!line[0])\n \t\t\t/*\n \t\t\t * A completely blank (not ' \\n', which is context)\n@@ -172,6 +181,7 @@ static int read_patches(const char *range, struct string_list *list)\n \tif (util)\n \t\tstring_list_append(list, buf.buf)->util = util;\n \tstrbuf_release(&buf);\n+\tfree(current_filename);\n \n \tif (finish_command(&cp))\n \t\treturn -1;\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex c277756057..d4de270979 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -110,7 +110,7 @@ test_expect_success 'changed commit' '\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n \t    @@\n-\t     @@ A\n+\t     @@ file: A\n \t      9\n \t      10\n \t    - B\n@@ -169,7 +169,7 @@ test_expect_success 'changed commit with sm config' '\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n \t    @@\n-\t     @@ A\n+\t     @@ file: A\n \t      9\n \t      10\n \t    - B\n@@ -203,20 +203,24 @@ test_expect_success 'renamed file' '\n \t    Z    s/11/B/\n \t    Z\n \t    - ## file ##\n+\t    -@@ file: A\n \t    + ## renamed-file ##\n-\t    Z@@ A\n+\t    +@@ renamed-file: A\n \t    Z 8\n \t    Z 9\n+\t    Z 10\n \t4:  a63e992 ! 4:  1e6226b s/12/B/\n \t    @@\n \t    Z\n \t    Z    s/12/B/\n \t    Z\n \t    - ## file ##\n+\t    -@@ file: A\n \t    + ## renamed-file ##\n-\t    Z@@ A\n+\t    +@@ renamed-file: A\n \t    Z 9\n \t    Z 10\n+\t    Z B\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -248,7 +252,7 @@ test_expect_success 'file added and later removed' '\n \t    +    s/11/B/ + remove file\n \t    Z\n \t    Z ## file ##\n-\t    Z@@ A\n+\t    Z@@ file: A\n \t    @@\n \t    Z 12\n \t    Z 13\n@@ -310,7 +314,7 @@ test_expect_success 'dual-coloring' '\n \t:      14<RESET>\n \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n \t:    <REVERSE><CYAN>@@<RESET>\n-\t:    <CYAN> @@ A<RESET>\n+\t:    <CYAN> @@ file: A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n \t:    <REVERSE><RED>-<RESET><FAINT> BB<RESET>\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378645","messageId":"20190705170630.27500-15-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v2 14/14] range-diff: add headers to the outer hunk header","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-05T17:06:30Z","receivedAt":"2019-07-05T17:07:18Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Add the section headers/hunk headers we introduced in the previous\ncommits to the outer diff's hunk headers.  This makes it easier to\nunderstand which change we are actually looking at.  For example an\nouter hunk header might now look like:\n\n    @@  Documentation/config/interactive.txt\n\nwhile previously it would have only been\n\n    @@\n\nwhich doesn't give a lot of context for the change that follows.\n\nFor completeness also add section headers for the commit metadata and\nthe commit message, although they are arguably less important.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c          |  9 ++++++---\n t/t3206-range-diff.sh | 41 ++++++++++++++++++++++-------------------\n 2 files changed, 28 insertions(+), 22 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 09cb1ddbb1..f43b229031 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -134,8 +134,10 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\tstrbuf_addstr(&buf, \" ##\");\n \t\t} else if (in_header) {\n \t\t\tif (starts_with(line, \"Author: \")) {\n+\t\t\t\tstrbuf_addstr(&buf, \" ## Metadata ##\\n\");\n \t\t\t\tstrbuf_addstr(&buf, line);\n \t\t\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n+\t\t\t\tstrbuf_addstr(&buf, \" ## Commit message ##\\n\");\n \t\t\t} else if (starts_with(line, \"    \")) {\n \t\t\t\tp = line + len - 2;\n \t\t\t\twhile (isspace(*p) && p >= line)\n@@ -396,8 +398,9 @@ static void output_pair_header(struct diff_options *diffopt,\n \tfwrite(buf->buf, buf->len, 1, diffopt->file);\n }\n \n-static struct userdiff_driver no_func_name = {\n-\t.funcname = { \"$^\", 0 }\n+static struct userdiff_driver section_headers = {\n+\t.funcname = { \"^ ## (.*) ##$\\n\"\n+\t\t      \"^.?@@ (.*)$\", REG_EXTENDED }\n };\n \n static struct diff_filespec *get_filespec(const char *name, const char *p)\n@@ -409,7 +412,7 @@ static struct diff_filespec *get_filespec(const char *name, const char *p)\n \tspec->size = strlen(p);\n \tspec->should_munmap = 0;\n \tspec->is_stdin = 1;\n-\tspec->driver = &no_func_name;\n+\tspec->driver = &section_headers;\n \n \treturn spec;\n }\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex d4de270979..ec548654ce 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -99,7 +99,7 @@ test_expect_success 'changed commit' '\n \t1:  4de457d = 1:  a4b3333 s/5/A/\n \t2:  fccce22 = 2:  f51d370 s/4/A/\n \t3:  147e64e ! 3:  0559556 s/11/B/\n-\t    @@\n+\t    @@ file: A\n \t      9\n \t      10\n \t     -11\n@@ -109,7 +109,7 @@ test_expect_success 'changed commit' '\n \t      13\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n-\t    @@\n+\t    @@ file\n \t     @@ file: A\n \t      9\n \t      10\n@@ -158,7 +158,7 @@ test_expect_success 'changed commit with sm config' '\n \t1:  4de457d = 1:  a4b3333 s/5/A/\n \t2:  fccce22 = 2:  f51d370 s/4/A/\n \t3:  147e64e ! 3:  0559556 s/11/B/\n-\t    @@\n+\t    @@ file: A\n \t      9\n \t      10\n \t     -11\n@@ -168,7 +168,7 @@ test_expect_success 'changed commit with sm config' '\n \t      13\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n-\t    @@\n+\t    @@ file\n \t     @@ file: A\n \t      9\n \t      10\n@@ -186,9 +186,10 @@ test_expect_success 'renamed file' '\n \tsed s/Z/\\ /g >expected <<-EOF &&\n \t1:  4de457d = 1:  f258d75 s/5/A/\n \t2:  fccce22 ! 2:  017b62d s/4/A/\n-\t    @@\n+\t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n+\t    Z ## Commit message ##\n \t    -    s/4/A/\n \t    +    s/4/A/ + rename file\n \t    Z\n@@ -198,8 +199,8 @@ test_expect_success 'renamed file' '\n \t    Z 1\n \t    Z 2\n \t3:  147e64e ! 3:  3ce7af6 s/11/B/\n-\t    @@\n-\t    Z\n+\t    @@ Metadata\n+\t    Z ## Commit message ##\n \t    Z    s/11/B/\n \t    Z\n \t    - ## file ##\n@@ -210,8 +211,8 @@ test_expect_success 'renamed file' '\n \t    Z 9\n \t    Z 10\n \t4:  a63e992 ! 4:  1e6226b s/12/B/\n-\t    @@\n-\t    Z\n+\t    @@ Metadata\n+\t    Z ## Commit message ##\n \t    Z    s/12/B/\n \t    Z\n \t    - ## file ##\n@@ -230,30 +231,32 @@ test_expect_success 'file added and later removed' '\n \tsed s/Z/\\ /g >expected <<-EOF &&\n \t1:  4de457d = 1:  096b1ba s/5/A/\n \t2:  fccce22 ! 2:  d92e698 s/4/A/\n-\t    @@\n+\t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n+\t    Z ## Commit message ##\n \t    -    s/4/A/\n \t    +    s/4/A/ + new-file\n \t    Z\n \t    Z ## file ##\n \t    Z@@\n-\t    @@\n+\t    @@ file\n \t    Z A\n \t    Z 6\n \t    Z 7\n \t    +\n \t    + ## new-file (new) ##\n \t3:  147e64e ! 3:  9a1db4d s/11/B/\n-\t    @@\n+\t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n+\t    Z ## Commit message ##\n \t    -    s/11/B/\n \t    +    s/11/B/ + remove file\n \t    Z\n \t    Z ## file ##\n \t    Z@@ file: A\n-\t    @@\n+\t    @@ file: A\n \t    Z 12\n \t    Z 13\n \t    Z 14\n@@ -274,8 +277,8 @@ test_expect_success 'changed message' '\n \tsed s/Z/\\ /g >expected <<-EOF &&\n \t1:  4de457d = 1:  f686024 s/5/A/\n \t2:  fccce22 ! 2:  4ab067d s/4/A/\n-\t    @@\n-\t    Z\n+\t    @@ Metadata\n+\t    Z ## Commit message ##\n \t    Z    s/4/A/\n \t    Z\n \t    +    Also a silly comment here!\n@@ -293,8 +296,8 @@ test_expect_success 'dual-coloring' '\n \tsed -e \"s|^:||\" >expect <<-\\EOF &&\n \t:<YELLOW>1:  a4b3333 = 1:  f686024 s/5/A/<RESET>\n \t:<RED>2:  f51d370 <RESET><YELLOW>!<RESET><GREEN> 2:  4ab067d<RESET><YELLOW> s/4/A/<RESET>\n-\t:    <REVERSE><CYAN>@@<RESET>\n-\t:     <RESET>\n+\t:    <REVERSE><CYAN>@@<RESET> <RESET>Metadata<RESET>\n+\t:      ## Commit message ##<RESET>\n \t:         s/4/A/<RESET>\n \t:     <RESET>\n \t:    <REVERSE><GREEN>+<RESET><BOLD>    Also a silly comment here!<RESET>\n@@ -303,7 +306,7 @@ test_expect_success 'dual-coloring' '\n \t:    <CYAN> @@<RESET>\n \t:      1<RESET>\n \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n-\t:    <REVERSE><CYAN>@@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET> <RESET>file: A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n \t:    <RED> -11<RESET>\n@@ -313,7 +316,7 @@ test_expect_success 'dual-coloring' '\n \t:      13<RESET>\n \t:      14<RESET>\n \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n-\t:    <REVERSE><CYAN>@@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET> <RESET>file<RESET>\n \t:    <CYAN> @@ file: A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378647","messageId":"nycvar.QRO.7.76.6.1907052047260.44@tvgsbejvaqbjf.bet","threadId":"51436","inReplyTo":"20190705170630.27500-8-t.gummerer@gmail.com","subject":"Re: [PATCH v2 07/14] apply: make parse_git_header public","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-07-05T18:48:48Z","receivedAt":"2019-07-05T18:48:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Thomas,\n\nOn Fri, 5 Jul 2019, Thomas Gummerer wrote:\n\n> Make parse_git_header a \"public\" function in apply.h, so we can re-use\n> it in range-diff in a subsequent commit.\n\nThis function probably wants a different name when it becomes public, as\nit was relatively obvious inside `apply.c` that it was talking about a Git\n_diff_ header. Maybe `parse_git_diff_header()`?\n\n> @@ -127,6 +161,20 @@ int init_apply_state(struct apply_state *state,\n>  void clear_apply_state(struct apply_state *state);\n>  int check_apply_state(struct apply_state *state, int force_apply);\n>\n> +/*\n> + * Parse a get header, starting at line.  Fills the relevant metadata\n\ns/get/git/\n\nCiao,\nDscho\n\n> + * information in 'struct patch'.\n> + *\n> + * Returns -1 on failure, the length of the parsed header otherwise.\n> + */\n> +int parse_git_header(struct strbuf *root,\n> +\t\t     int *linenr,\n> +\t\t     int p_value,\n> +\t\t     const char *line,\n> +\t\t     int len,\n> +\t\t     unsigned int size,\n> +\t\t     struct patch *patch);\n> +\n>  /*\n>   * Some aspects of the apply behavior are controlled by the following\n>   * bits in the \"options\" parameter passed to apply_all_patches().\n> --\n> 2.22.0.510.g264f2c817a\n>\n>\n"},{"id":"378648","messageId":"nycvar.QRO.7.76.6.1907052048570.44@tvgsbejvaqbjf.bet","threadId":"51436","inReplyTo":"20190705170630.27500-7-t.gummerer@gmail.com","subject":"Re: [PATCH v2 06/14] apply: only pass required data to gitdiff_* functions","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-07-05T18:51:25Z","receivedAt":"2019-07-05T18:51:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Thomas,\n\nOn Fri, 5 Jul 2019, Thomas Gummerer wrote:\n\n> Currently the 'gitdiff_*()' functions take 'struct apply_state' as\n> parameter, even though they only needs the root, linenr and p_value\n> from that struct.\n>\n> These functions are in the callchain of 'parse_git_header()', which we\n> want to make more generally useful in a subsequent commit.  To make\n> that happen we only want to pass in the required data to\n> 'parse_git_header()', and not the whole 'struct apply_state', and thus\n> we want functions in the callchain of 'parse_git_header()' to only\n> take arguments they really need.\n\nThis commit message follows the exact pattern of the previous patches (and\nthey were pretty convincing to me), but...\n\n> diff --git a/apply.c b/apply.c\n> index 3cd4e3d3b3..468f1d3fee 100644\n> --- a/apply.c\n> +++ b/apply.c\n> @@ -22,6 +22,12 @@\n>  #include \"rerere.h\"\n>  #include \"apply.h\"\n>\n> +struct parse_git_header_state {\n> +\tstruct strbuf *root;\n> +\tint linenr;\n> +\tint p_value;\n> +};\n> +\n>  static void git_apply_config(void)\n>  {\n>  \tgit_config_get_string_const(\"apply.whitespace\", &apply_default_whitespace);\n> @@ -914,7 +920,7 @@ static int parse_traditional_patch(struct apply_state *state,\n>  \treturn 0;\n>  }\n>\n> -static int gitdiff_hdrend(struct apply_state *state,\n> +static int gitdiff_hdrend(struct parse_git_header_state *state,\n\n... here we pass a different, newly-invented struct instead of passing the\nrelevant information explicitly.\n\nMy guess is that the code would look horribly verbose if we started\npassing three instead of one parameter? If that is the case, I think a\nlittle side note in the commit message might be warranted.\n\nCiao,\nDscho\n"},{"id":"378650","messageId":"nycvar.QRO.7.76.6.1907052052060.44@tvgsbejvaqbjf.bet","threadId":"51436","inReplyTo":"20190705170630.27500-10-t.gummerer@gmail.com","subject":"Re: [PATCH v2 09/14] range-diff: split lines manually","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-07-05T19:05:14Z","receivedAt":"2019-07-05T19:04:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Thomas,\n\n\nOn Fri, 5 Jul 2019, Thomas Gummerer wrote:\n\n> Currently range-diff uses the 'strbuf_getline()' function for doing\n> its line by line processing.  In a future patch we want to do parts of\n> that parsing using the 'parse_git_header()' function, which does\n\nIf you like my suggestion in patch 7/14, this commit message needs to talk\nabout the new name, too.\n\n> requires reading parts of the input from that function, which doesn't\n\ns/requires/require/\n\n> use strbufs.\n>\n> Switch range-diff to do our own line by line parsing, so we can re-use\n> the parse_git_header function later.\n>\n> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n> ---\n>\n> Longer term it might be better to have both range-diff and apply code\n> use strbufs.  However I didn't feel it's worth making that change for\n> this patch series.\n\nMakes sense.\n\n>  range-diff.c | 69 +++++++++++++++++++++++++++++-----------------------\n>  1 file changed, 39 insertions(+), 30 deletions(-)\n>\n> diff --git a/range-diff.c b/range-diff.c\n> index 9242b8975f..916afa44c0 100644\n> --- a/range-diff.c\n> +++ b/range-diff.c\n> @@ -24,6 +24,17 @@ struct patch_util {\n>  \tstruct object_id oid;\n>  };\n>\n> +static unsigned long linelen(const char *buffer, unsigned long size)\n\nShouldn't this be `size_t`?\n\n> +{\n> +\tunsigned long len = 0;\n\nLikewise.\n\n> +\twhile (size--) {\n> +\t\tlen++;\n> +\t\tif (*buffer++ == '\\n')\n> +\t\t\tbreak;\n> +\t}\n> +\treturn len;\n\nHow about\n\n\tconst char *eol = memchr(buffer, '\\n', size);\n\n\treturn !eol ? size : eol + 1 - buffer;\n\ninstead?\n\nFor an extra brownie point, you could even rename this function to\n`find_end_of_line()` and replace the LF by a NUL:\n\n\tif (!eol)\n\t\treturn size;\n\n\t*eol = '\\0';\n\treturn eol + 1 - buffer;\n\n> +}\n> +\n>  /*\n>   * Reads the patches into a string list, with the `util` field being populated\n>   * as struct object_id (will need to be free()d).\n> @@ -31,10 +42,12 @@ struct patch_util {\n>  static int read_patches(const char *range, struct string_list *list)\n>  {\n>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n> -\tFILE *in;\n> -\tstruct strbuf buf = STRBUF_INIT, line = STRBUF_INIT;\n> +\tstruct strbuf buf = STRBUF_INIT, file = STRBUF_INIT;\n\nThis puzzled me. I'd like to suggest s/file/contents/\n\n>  \tstruct patch_util *util = NULL;\n>  \tint in_header = 1;\n> +\tchar *line;\n> +\tint offset, len;\n> +\tsize_t size;\n>\n>  \targv_array_pushl(&cp.args, \"log\", \"--no-color\", \"-p\", \"--no-merges\",\n>  \t\t\t\"--reverse\", \"--date-order\", \"--decorate=no\",\n> @@ -54,17 +67,15 @@ static int read_patches(const char *range, struct string_list *list)\n>\n>  \tif (start_command(&cp))\n>  \t\treturn error_errno(_(\"could not start `log`\"));\n> -\tin = fdopen(cp.out, \"r\");\n> -\tif (!in) {\n> -\t\terror_errno(_(\"could not read `log` output\"));\n> -\t\tfinish_command(&cp);\n> -\t\treturn -1;\n> -\t}\n> +\tstrbuf_read(&file, cp.out, 0);\n\nShouldn't we handle a negative return value here, erroring out with \"could\nnot read `log` output\" as before?\n\n>\n> -\twhile (strbuf_getline(&line, in) != EOF) {\n> +\tline = strbuf_detach(&file, &size);\n\nI strongly suspect this to leak, given that `line` is subsequently\nadvanced, and there is no backup copy.\n\nMaybe\n\n\tline = file.buf;\n\tsize = file.len;\n\nwould make more sense here?\n\n> +\tfor (offset = 0; size > 0; offset += len, size -= len, line += len) {\n>  \t\tconst char *p;\n>\n> -\t\tif (skip_prefix(line.buf, \"commit \", &p)) {\n> +\t\tlen = linelen(line, size);\n> +\t\tline[len - 1] = '\\0';\n> +\t\tif (skip_prefix(line, \"commit \", &p)) {\n>  \t\t\tif (util) {\n>  \t\t\t\tstring_list_append(list, buf.buf)->util = util;\n>  \t\t\t\tstrbuf_reset(&buf);\n> @@ -75,8 +86,6 @@ static int read_patches(const char *range, struct string_list *list)\n>  \t\t\t\tfree(util);\n>  \t\t\t\tstring_list_clear(list, 1);\n>  \t\t\t\tstrbuf_release(&buf);\n> -\t\t\t\tstrbuf_release(&line);\n> -\t\t\t\tfclose(in);\n\nWe should release the file contents in `file` (or `contents`, if you like\nmy suggestions) here.\n\n>  \t\t\t\tfinish_command(&cp);\n>  \t\t\t\treturn -1;\n>  \t\t\t}\n> @@ -85,26 +94,28 @@ static int read_patches(const char *range, struct string_list *list)\n>  \t\t\tcontinue;\n>  \t\t}\n>\n> -\t\tif (starts_with(line.buf, \"diff --git\")) {\n> +\t\tif (starts_with(line, \"diff --git\")) {\n>  \t\t\tin_header = 0;\n>  \t\t\tstrbuf_addch(&buf, '\\n');\n>  \t\t\tif (!util->diff_offset)\n>  \t\t\t\tutil->diff_offset = buf.len;\n>  \t\t\tstrbuf_addch(&buf, ' ');\n> -\t\t\tstrbuf_addbuf(&buf, &line);\n> +\t\t\tstrbuf_addstr(&buf, line);\n>  \t\t} else if (in_header) {\n> -\t\t\tif (starts_with(line.buf, \"Author: \")) {\n> -\t\t\t\tstrbuf_addbuf(&buf, &line);\n> +\t\t\tif (starts_with(line, \"Author: \")) {\n> +\t\t\t\tstrbuf_addstr(&buf, line);\n>  \t\t\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n> -\t\t\t} else if (starts_with(line.buf, \"    \")) {\n> -\t\t\t\tstrbuf_rtrim(&line);\n> -\t\t\t\tstrbuf_addbuf(&buf, &line);\n> +\t\t\t} else if (starts_with(line, \"    \")) {\n> +\t\t\t\tp = line + len - 2;\n> +\t\t\t\twhile (isspace(*p) && p >= line)\n> +\t\t\t\t\tp--;\n> +\t\t\t\tstrbuf_add(&buf, line, p - line + 1);\n>  \t\t\t\tstrbuf_addch(&buf, '\\n');\n>  \t\t\t}\n>  \t\t\tcontinue;\n> -\t\t} else if (starts_with(line.buf, \"@@ \"))\n> +\t\t} else if (starts_with(line, \"@@ \"))\n>  \t\t\tstrbuf_addstr(&buf, \"@@\");\n> -\t\telse if (!line.buf[0] || starts_with(line.buf, \"index \"))\n> +\t\telse if (!line[0] || starts_with(line, \"index \"))\n>  \t\t\t/*\n>  \t\t\t * A completely blank (not ' \\n', which is context)\n>  \t\t\t * line is not valid in a diff.  We skip it\n> @@ -117,25 +128,23 @@ static int read_patches(const char *range, struct string_list *list)\n>  \t\t\t * we are not interested.\n>  \t\t\t */\n>  \t\t\tcontinue;\n> -\t\telse if (line.buf[0] == '>') {\n> +\t\telse if (line[0] == '>') {\n>  \t\t\tstrbuf_addch(&buf, '+');\n> -\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n> -\t\t} else if (line.buf[0] == '<') {\n> +\t\t\tstrbuf_addstr(&buf, line + 1);\n> +\t\t} else if (line[0] == '<') {\n>  \t\t\tstrbuf_addch(&buf, '-');\n> -\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n> -\t\t} else if (line.buf[0] == '#') {\n> +\t\t\tstrbuf_addstr(&buf, line + 1);\n> +\t\t} else if (line[0] == '#') {\n>  \t\t\tstrbuf_addch(&buf, ' ');\n> -\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n> +\t\t\tstrbuf_addstr(&buf, line + 1);\n>  \t\t} else {\n>  \t\t\tstrbuf_addch(&buf, ' ');\n> -\t\t\tstrbuf_addbuf(&buf, &line);\n> +\t\t\tstrbuf_addstr(&buf, line);\n>  \t\t}\n>\n>  \t\tstrbuf_addch(&buf, '\\n');\n>  \t\tutil->diffsize++;\n>  \t}\n> -\tfclose(in);\n> -\tstrbuf_release(&line);\n\nWe should release the file contents we previously read via `strbuf_read()` here.\n\nCiao,\nDscho\n\n>\n>  \tif (util)\n>  \t\tstring_list_append(list, buf.buf)->util = util;\n> --\n> 2.22.0.510.g264f2c817a\n>\n>\n"},{"id":"378651","messageId":"nycvar.QRO.7.76.6.1907052106430.44@tvgsbejvaqbjf.bet","threadId":"51436","inReplyTo":"20190705170630.27500-11-t.gummerer@gmail.com","subject":"Re: [PATCH v2 10/14] range-diff: don't remove funcname from inner diff","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-07-05T19:09:18Z","receivedAt":"2019-07-05T19:08:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Thomas,\n\nOn Fri, 5 Jul 2019, Thomas Gummerer wrote:\n\n> When postprocessing the inner diff in range-diff, we currently replace\n> the whole hunk header line with just \"@@\".  This matches how 'git\n> tbdiff' used to handle hunk headers as well.\n\nRight, that's why I did it this way: `tbdiff` did it.\n\n> Most likely this is being done because line numbers in the hunk header\n> are not relevant without other changes.\n\nYep, and even worse, it also leads to uninteresting differences.\n\n> They can for example easily change if a range is rebased, and lines are\n> added/removed before a change that we actually care about in our ranges.\n\nExactly.\n\n> However it can still be useful to have the function name that 'git\n> diff' extracts as additional context for the change.\n>\n> Note that it is not guaranteed that the hunk header actually shows up\n> in the range-diff, and this change only aims to improve the case where\n> a hunk header would already be included in the final output.\n\nVery important paragraph here, thank you!\n\n> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n> ---\n>  range-diff.c          | 8 +++++---\n>  t/t3206-range-diff.sh | 6 +++---\n>  2 files changed, 8 insertions(+), 6 deletions(-)\n>\n> diff --git a/range-diff.c b/range-diff.c\n> index 916afa44c0..484b1ec5a9 100644\n> --- a/range-diff.c\n> +++ b/range-diff.c\n> @@ -113,9 +113,11 @@ static int read_patches(const char *range, struct string_list *list)\n>  \t\t\t\tstrbuf_addch(&buf, '\\n');\n>  \t\t\t}\n>  \t\t\tcontinue;\n> -\t\t} else if (starts_with(line, \"@@ \"))\n> -\t\t\tstrbuf_addstr(&buf, \"@@\");\n> -\t\telse if (!line[0] || starts_with(line, \"index \"))\n> +\t\t} else if (skip_prefix(line, \"@@ \", &p)) {\n> +\t\t\tif (!(p = strstr(p, \"@@\")))\n> +\t\t\t\tdie(_(\"invalid hunk header in inner diff\"));\n\nThat's a bit more strict than the preimage. How about\n\n\t\t\tp = strstr(p, \"@@\");\n\t\t\tstrbuf_addstr(&buf, p ? p : \"@@\");\n\ninstead?\n\nThe rest of the patch looks fine to me,\nDscho\n\n> +\t\t\tstrbuf_addstr(&buf, p);\n> +\t\t} else if (!line[0] || starts_with(line, \"index \"))\n>  \t\t\t/*\n>  \t\t\t * A completely blank (not ' \\n', which is context)\n>  \t\t\t * line is not valid in a diff.  We skip it\n> diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\n> index 048feaf6dd..aebd4e3693 100755\n> --- a/t/t3206-range-diff.sh\n> +++ b/t/t3206-range-diff.sh\n> @@ -110,7 +110,7 @@ test_expect_success 'changed commit' '\n>  \t      14\n>  \t4:  a63e992 ! 4:  d966c5c s/12/B/\n>  \t    @@ -8,7 +8,7 @@\n> -\t     @@\n> +\t     @@ A\n>  \t      9\n>  \t      10\n>  \t    - B\n> @@ -169,7 +169,7 @@ test_expect_success 'changed commit with sm config' '\n>  \t      14\n>  \t4:  a63e992 ! 4:  d966c5c s/12/B/\n>  \t    @@ -8,7 +8,7 @@\n> -\t     @@\n> +\t     @@ A\n>  \t      9\n>  \t      10\n>  \t    - B\n> @@ -231,7 +231,7 @@ test_expect_success 'dual-coloring' '\n>  \t:      14<RESET>\n>  \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n>  \t:    <REVERSE><CYAN>@@ -8,7 +8,7 @@<RESET>\n> -\t:    <CYAN> @@<RESET>\n> +\t:    <CYAN> @@ A<RESET>\n>  \t:      9<RESET>\n>  \t:      10<RESET>\n>  \t:    <REVERSE><RED>-<RESET><FAINT> BB<RESET>\n> --\n> 2.22.0.510.g264f2c817a\n>\n>\n"},{"id":"378652","messageId":"nycvar.QRO.7.76.6.1907052114480.44@tvgsbejvaqbjf.bet","threadId":"51436","inReplyTo":"20190705170630.27500-13-t.gummerer@gmail.com","subject":"Re: [PATCH v2 12/14] range-diff: add section header instead of diff header","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-07-05T19:35:22Z","receivedAt":"2019-07-05T19:35:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Thomas,\n\nOn Fri, 5 Jul 2019, Thomas Gummerer wrote:\n\n> Currently range-diff keeps the diff header of the inner diff\n> intact (apart from stripping lines starting with index).  This diff\n> header is somewhat useful, especially when files get different\n> names in different ranges.\n>\n> However there is no real need to keep the whole diff header for that.\n> The main reason we currently do that is probably because it is easy to\n> do.\n>\n> Introduce a new range diff hunk header, that's enclosed by \"##\",\n> similar to how line numbers in diff hunks are enclosed by \"@@\", and\n> give human readable information of what exactly happened to the file,\n> including the file name.\n>\n> This improves the readability of the range-diff by giving more concise\n> information to the users.  For example if a file was renamed in one\n> iteration, but not in another, the diff of the headers would be quite\n> noisy.  However the diff of a single line is concise and should be\n> easier to understand.\n>\n> Additionaly, this allows us to add these range diff section headers to\n\ns/Additionaly/Additionally/\n\n> the outer diffs hunk headers using a custom userdiff pattern, which\n> should help making the range-diff more readable.\n\nMakes sense.\n\n> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n> ---\n>  range-diff.c           | 35 ++++++++++++----\n>  t/t3206-range-diff.sh  | 91 +++++++++++++++++++++++++++++++++++++++---\n>  t/t3206/history.export | 84 ++++++++++++++++++++++++++++++++++++--\n>  3 files changed, 193 insertions(+), 17 deletions(-)\n>\n> diff --git a/range-diff.c b/range-diff.c\n> index b31fbab026..cc01f7f573 100644\n> --- a/range-diff.c\n> +++ b/range-diff.c\n> @@ -10,6 +10,7 @@\n>  #include \"commit.h\"\n>  #include \"pretty.h\"\n>  #include \"userdiff.h\"\n> +#include \"apply.h\"\n>\n>  struct patch_util {\n>  \t/* For the search for an exact match */\n> @@ -95,12 +96,36 @@ static int read_patches(const char *range, struct string_list *list)\n>  \t\t}\n>\n>  \t\tif (starts_with(line, \"diff --git\")) {\n> +\t\t\tstruct patch patch;\n\nIf you append ` = { 0 }` (or ` = { NULL }`, depending on the first field\nof that struct, you don't need that `memset()` later.\n\n> +\t\t\tstruct strbuf root = STRBUF_INIT;\n> +\t\t\tint linenr = 0;\n> +\n>  \t\t\tin_header = 0;\n>  \t\t\tstrbuf_addch(&buf, '\\n');\n>  \t\t\tif (!util->diff_offset)\n>  \t\t\t\tutil->diff_offset = buf.len;\n> -\t\t\tstrbuf_addch(&buf, ' ');\n> -\t\t\tstrbuf_addstr(&buf, line);\n> +\t\t\tmemset(&patch, 0, sizeof(patch));\n> +\t\t\tline[len - 1] = '\\n';\n\nI guess `parse_git_header()` cannot handle lines ending in a NUL?\n\n> +\t\t\tlen = parse_git_header(&root, &linenr, 1, line,\n> +\t\t\t\t\t       len, size, &patch);\n> +\t\t\tif (len < 0)\n> +\t\t\t\tdie(_(\"could not parse git header\"));\n\nMaybe include the line's contents, like ` '%.*s'\", (int)len, line`?\n\n> +\t\t\tstrbuf_addstr(&buf, \" ## \");\n> +\t\t\tif (patch.is_new > 0)\n> +\t\t\t\tstrbuf_addf(&buf, \"%s (new)\", patch.new_name);\n> +\t\t\telse if (patch.is_delete > 0)\n> +\t\t\t\tstrbuf_addf(&buf, \"%s (deleted)\", patch.old_name);\n> +\t\t\telse if (patch.is_rename)\n> +\t\t\t\tstrbuf_addf(&buf, \"%s => %s\", patch.old_name, patch.new_name);\n> +\t\t\telse\n> +\t\t\t\tstrbuf_addstr(&buf, patch.new_name);\n> +\n> +\t\t\tif (patch.new_mode && patch.old_mode &&\n> +\t\t\t    patch.old_mode != patch.new_mode)\n> +\t\t\t\tstrbuf_addf(&buf, \" (mode change %06o => %06o)\",\n> +\t\t\t\t\t    patch.old_mode, patch.new_mode);\n> +\n> +\t\t\tstrbuf_addstr(&buf, \" ##\");\n>  \t\t} else if (in_header) {\n>  \t\t\tif (starts_with(line, \"Author: \")) {\n>  \t\t\t\tstrbuf_addstr(&buf, line);\n> @@ -117,17 +142,13 @@ static int read_patches(const char *range, struct string_list *list)\n>  \t\t\tif (!(p = strstr(p, \"@@\")))\n>  \t\t\t\tdie(_(\"invalid hunk header in inner diff\"));\n>  \t\t\tstrbuf_addstr(&buf, p);\n> -\t\t} else if (!line[0] || starts_with(line, \"index \"))\n> +\t\t} else if (!line[0])\n>  \t\t\t/*\n>  \t\t\t * A completely blank (not ' \\n', which is context)\n>  \t\t\t * line is not valid in a diff.  We skip it\n>  \t\t\t * silently, because this neatly handles the blank\n>  \t\t\t * separator line between commits in git-log\n>  \t\t\t * output.\n> -\t\t\t *\n> -\t\t\t * We also want to ignore the diff's `index` lines\n> -\t\t\t * because they contain exact blob hashes in which\n> -\t\t\t * we are not interested.\n\nOh, are we interested in them again?\n\n>  \t\t\t */\n>  \t\t\tcontinue;\n>  \t\telse if (line[0] == '>') {\n> diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\n> index 9f89af7178..c277756057 100755\n> --- a/t/t3206-range-diff.sh\n> +++ b/t/t3206-range-diff.sh\n> @@ -181,6 +181,85 @@ test_expect_success 'changed commit with sm config' '\n>  \ttest_cmp expected actual\n>  '\n>\n> +test_expect_success 'renamed file' '\n> +\tgit range-diff --no-color --submodule=log topic...renamed-file >actual &&\n> +\tsed s/Z/\\ /g >expected <<-EOF &&\n> +\t1:  4de457d = 1:  f258d75 s/5/A/\n> +\t2:  fccce22 ! 2:  017b62d s/4/A/\n> +\t    @@\n> +\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n> +\t    Z\n> +\t    -    s/4/A/\n> +\t    +    s/4/A/ + rename file\n> +\t    Z\n> +\t    - ## file ##\n> +\t    + ## file => renamed-file ##\n\nI guess there is no good way to suppress the `- ## file ##` line in this\ncase? It is a bit distracting...\n\n> +\t    Z@@\n> +\t    Z 1\n> +\t    Z 2\n> +\t3:  147e64e ! 3:  3ce7af6 s/11/B/\n> +\t    @@\n> +\t    Z\n> +\t    Z    s/11/B/\n> +\t    Z\n> +\t    - ## file ##\n> +\t    + ## renamed-file ##\n> +\t    Z@@ A\n> +\t    Z 8\n> +\t    Z 9\n> +\t4:  a63e992 ! 4:  1e6226b s/12/B/\n> +\t    @@\n> +\t    Z\n> +\t    Z    s/12/B/\n> +\t    Z\n> +\t    - ## file ##\n> +\t    + ## renamed-file ##\n> +\t    Z@@ A\n> +\t    Z 9\n> +\t    Z 10\n> +\tEOF\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'file added and later removed' '\n> +\tgit range-diff --no-color --submodule=log topic...added-removed >actual &&\n> +\tsed s/Z/\\ /g >expected <<-EOF &&\n> +\t1:  4de457d = 1:  096b1ba s/5/A/\n> +\t2:  fccce22 ! 2:  d92e698 s/4/A/\n> +\t    @@\n> +\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n> +\t    Z\n> +\t    -    s/4/A/\n> +\t    +    s/4/A/ + new-file\n> +\t    Z\n> +\t    Z ## file ##\n> +\t    Z@@\n> +\t    @@\n> +\t    Z A\n> +\t    Z 6\n> +\t    Z 7\n> +\t    +\n> +\t    + ## new-file (new) ##\n> +\t3:  147e64e ! 3:  9a1db4d s/11/B/\n> +\t    @@\n> +\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n> +\t    Z\n> +\t    -    s/11/B/\n> +\t    +    s/11/B/ + remove file\n> +\t    Z\n> +\t    Z ## file ##\n> +\t    Z@@ A\n> +\t    @@\n> +\t    Z 12\n> +\t    Z 13\n> +\t    Z 14\n> +\t    +\n> +\t    + ## new-file (deleted) ##\n> +\t4:  a63e992 = 4:  fea3b5c s/12/B/\n> +\tEOF\n> +\ttest_cmp expected actual\n> +'\n> +\n>  test_expect_success 'no commits on one side' '\n>  \tgit commit --amend -m \"new message\" &&\n>  \tgit range-diff master HEAD@{1} HEAD\n> @@ -197,9 +276,9 @@ test_expect_success 'changed message' '\n>  \t    Z\n>  \t    +    Also a silly comment here!\n>  \t    +\n> -\t    Z diff --git a/file b/file\n> -\t    Z --- a/file\n> -\t    Z +++ b/file\n> +\t    Z ## file ##\n> +\t    Z@@\n> +\t    Z 1\n>  \t3:  147e64e = 3:  b9cb956 s/11/B/\n>  \t4:  a63e992 = 4:  8add5f1 s/12/B/\n>  \tEOF\n> @@ -216,9 +295,9 @@ test_expect_success 'dual-coloring' '\n>  \t:     <RESET>\n>  \t:    <REVERSE><GREEN>+<RESET><BOLD>    Also a silly comment here!<RESET>\n>  \t:    <REVERSE><GREEN>+<RESET>\n> -\t:      diff --git a/file b/file<RESET>\n> -\t:      --- a/file<RESET>\n> -\t:      +++ b/file<RESET>\n> +\t:      ## file ##<RESET>\n> +\t:    <CYAN> @@<RESET>\n> +\t:      1<RESET>\n\nI am a bit confused where these last two lines come from all of a\nsudden... They were not there before, and I do not see any code change in\nthis patch that would be responsible for them, either...\n\nCould you help me understand?\n\n>  \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n>  \t:    <REVERSE><CYAN>@@<RESET>\n>  \t:      9<RESET>\n> diff --git a/t/t3206/history.export b/t/t3206/history.export\n> index b8ffff0940..7bb3814962 100644\n> --- a/t/t3206/history.export\n> +++ b/t/t3206/history.export\n> @@ -22,8 +22,8 @@ data 51\n>  19\n>  20\n>\n> -reset refs/heads/removed\n> -commit refs/heads/removed\n> +reset refs/heads/renamed-file\n> +commit refs/heads/renamed-file\n\nHmm. Is the `removed` ref no longer required by the 'removed a commit'\ntest case?\n\n>  mark :2\n>  author Thomas Rast <trast@inf.ethz.ch> 1374424921 +0200\n>  committer Thomas Rast <trast@inf.ethz.ch> 1374484724 +0200\n> @@ -599,6 +599,82 @@ s/12/B/\n>  from :46\n>  M 100644 :28 file\n>\n> -reset refs/heads/removed\n> -from :47\n> +commit refs/heads/added-removed\n> +mark :48\n> +author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200\n> +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574151 +0100\n\nNeat ;-)\n\n> +data 7\n> +s/5/A/\n> +from :2\n> +M 100644 :3 file\n> +\n> +blob\n> +mark :49\n> +data 0\n> +\n> +commit refs/heads/added-removed\n> +mark :50\n> +author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200\n> +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n> +data 18\n> +s/4/A/ + new-file\n> +from :48\n> +M 100644 :5 file\n> +M 100644 :49 new-file\n> +\n> +commit refs/heads/added-removed\n> +mark :51\n> +author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200\n> +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n> +data 22\n> +s/11/B/ + remove file\n> +from :50\n> +M 100644 :7 file\n> +D new-file\n> +\n> +commit refs/heads/added-removed\n> +mark :52\n> +author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200\n> +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n> +data 8\n> +s/12/B/\n> +from :51\n> +M 100644 :9 file\n> +\n> +commit refs/heads/renamed-file\n> +mark :53\n> +author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200\n> +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574309 +0100\n> +data 7\n> +s/5/A/\n> +from :2\n> +M 100644 :3 file\n> +\n> +commit refs/heads/renamed-file\n> +mark :54\n> +author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200\n> +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574312 +0100\n> +data 21\n> +s/4/A/ + rename file\n> +from :53\n> +D file\n> +M 100644 :5 renamed-file\n> +\n> +commit refs/heads/renamed-file\n> +mark :55\n> +author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200\n> +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574319 +0100\n> +data 8\n> +s/11/B/\n> +from :54\n> +M 100644 :7 renamed-file\n> +\n> +commit refs/heads/renamed-file\n> +mark :56\n> +author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200\n> +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574319 +0100\n> +data 8\n> +s/12/B/\n> +from :55\n> +M 100644 :9 renamed-file\n\nI have to admit that I allowed myself not to study this script too\nclosely, trusting that the range-diff explains better what commit history\nit creates than the fast-import script.\n\nThanks,\nDscho\n\n>\n> --\n> 2.22.0.510.g264f2c817a\n>\n>\n"},{"id":"378653","messageId":"nycvar.QRO.7.76.6.1907052138370.44@tvgsbejvaqbjf.bet","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"Re: [PATCH v2 00/14] output improvements for git range-diff","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-07-05T19:48:16Z","receivedAt":"2019-07-05T19:47:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Thomas,\n\nOn Fri, 5 Jul 2019, Thomas Gummerer wrote:\n\n> It's been quite a while since I sent the RFC [1] (thanks all for the\n> comments on that), and the series changed shapes quite a bit since the\n> last round.\n>\n> Since it's been such a long time, just to remind everyone, the goal of\n> this series is to make the range-diff output clearer, by showing\n> information about the filenames to which the current diff belongs.\n\nThank you for that reminder ;-)\n\n> In the previous round, we did this using \"section headers\" that\n> include information about the current file and adding that to the\n> outer diff's hunk headers.\n>\n> In this round we still keep the section headers (with slightly more\n> information), but in addition we also add the filename to the inner\n> diff hunk headers.  In the outer diff hunk headers we then display\n> either the section header or the inner diff hunk header using a\n> userdiff pattern.\n\n\nI like this idea!\n\n> In terms of code changes the biggest change is that we're now re-using\n> the 'parse_git_header' function from the apply code to parse the diff\n> headers, instead of trying to parse them with some hacky parsing code\n> in range-diff.c.  This way we are sure that the diff headers are\n> properly parsed.\n\nYep, very good.\n\n> I had also considered just outputting the section headers directly\n> from 'git log', but then decided against that.  Parsing the headers\n> allows a future enhancement of range-diff, where we would allow\n> parsing an mbox file [2].\n\nThanks you for your consideration; I still would like to have the option\nat some stage to compare a patch series from public-inbox.org/git to the\ncommits in `pu`, without having to fiddle with finding a valid base commit\nto apply the patches on.\n\n> I split the \"only pass required data\" commits up, in the hopes of\n> making them easier to review, but I'm also happy to squash them if\n> people feel like that makes it easier to review them.\n\nI found it very easy to review in the current form, thank you for putting\nin the extra effort.\n\n> An added advantage of this is that we're also getting rid of things\n> like the similarity index, which are not important in the range-diff,\n> and are thus not represented in the \"section header\".\n>\n> One thing that did not change is that the new/deleted strings are not\n> translated in this version either.  This is similar to the regular\n> diff output, where we also don't translate these.  We can still\n> consider translating them in the future though.\n>\n> [1]: https://public-inbox.org/git/20190414210933.20875-1-t.gummerer@gmail.com/\n> [2]: https://github.com/gitgitgadget/git/issues/207\n>\n> I'm including the range-diff between this version of the series and\n> the RFC, and a diff between the range diff and the range-diff without\n> these changes below.  Probably not useful in reviewing the code, but\n> good to show off the changes made in this series.\n\nIndeed!\n\nI very much like the idea, and the current iteration. I offered a couple\nof minor suggestions, in the hope that you find them helpful.\n\nThank you,\nDscho\n"},{"id":"378675","messageId":"20190708112415.GB16825@hank.intra.tgummerer.com","threadId":"51436","inReplyTo":"nycvar.QRO.7.76.6.1907052052060.44@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v2 09/14] range-diff: split lines manually","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T11:24:15Z","receivedAt":"2019-07-08T11:24:32Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 07/05, Johannes Schindelin wrote:\n> Hi Thomas,\n> \n> \n> On Fri, 5 Jul 2019, Thomas Gummerer wrote:\n> \n> > Currently range-diff uses the 'strbuf_getline()' function for doing\n> > its line by line processing.  In a future patch we want to do parts of\n> > that parsing using the 'parse_git_header()' function, which does\n> \n> If you like my suggestion in patch 7/14, this commit message needs to talk\n> about the new name, too.\n\nThanks for the reminder here!  I do indeed like the new name, but\nwould probably have forgotten to change it in the commit message here.\n\n> > requires reading parts of the input from that function, which doesn't\n> \n> s/requires/require/\n> \n> > use strbufs.\n> >\n> > Switch range-diff to do our own line by line parsing, so we can re-use\n> > the parse_git_header function later.\n> >\n> > Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n> > ---\n> >\n> > Longer term it might be better to have both range-diff and apply code\n> > use strbufs.  However I didn't feel it's worth making that change for\n> > this patch series.\n> \n> Makes sense.\n> \n> >  range-diff.c | 69 +++++++++++++++++++++++++++++-----------------------\n> >  1 file changed, 39 insertions(+), 30 deletions(-)\n> >\n> > diff --git a/range-diff.c b/range-diff.c\n> > index 9242b8975f..916afa44c0 100644\n> > --- a/range-diff.c\n> > +++ b/range-diff.c\n> > @@ -24,6 +24,17 @@ struct patch_util {\n> >  \tstruct object_id oid;\n> >  };\n> >\n> > +static unsigned long linelen(const char *buffer, unsigned long size)\n> \n> Shouldn't this be `size_t`?\n> \n> > +{\n> > +\tunsigned long len = 0;\n> \n> Likewise.\n> \n> > +\twhile (size--) {\n> > +\t\tlen++;\n> > +\t\tif (*buffer++ == '\\n')\n> > +\t\t\tbreak;\n> > +\t}\n> > +\treturn len;\n> \n> How about\n> \n> \tconst char *eol = memchr(buffer, '\\n', size);\n> \n> \treturn !eol ? size : eol + 1 - buffer;\n> \n> instead?\n> \n> For an extra brownie point, you could even rename this function to\n> `find_end_of_line()` and replace the LF by a NUL:\n> \n> \tif (!eol)\n> \t\treturn size;\n> \n> \t*eol = '\\0';\n> \treturn eol + 1 - buffer;\n\nI like this, thank you!\n\n> > +}\n> > +\n> >  /*\n> >   * Reads the patches into a string list, with the `util` field being populated\n> >   * as struct object_id (will need to be free()d).\n> > @@ -31,10 +42,12 @@ struct patch_util {\n> >  static int read_patches(const char *range, struct string_list *list)\n> >  {\n> >  \tstruct child_process cp = CHILD_PROCESS_INIT;\n> > -\tFILE *in;\n> > -\tstruct strbuf buf = STRBUF_INIT, line = STRBUF_INIT;\n> > +\tstruct strbuf buf = STRBUF_INIT, file = STRBUF_INIT;\n> \n> This puzzled me. I'd like to suggest s/file/contents/\n\nThanks, will change.\n\n> >  \tstruct patch_util *util = NULL;\n> >  \tint in_header = 1;\n> > +\tchar *line;\n> > +\tint offset, len;\n> > +\tsize_t size;\n> >\n> >  \targv_array_pushl(&cp.args, \"log\", \"--no-color\", \"-p\", \"--no-merges\",\n> >  \t\t\t\"--reverse\", \"--date-order\", \"--decorate=no\",\n> > @@ -54,17 +67,15 @@ static int read_patches(const char *range, struct string_list *list)\n> >\n> >  \tif (start_command(&cp))\n> >  \t\treturn error_errno(_(\"could not start `log`\"));\n> > -\tin = fdopen(cp.out, \"r\");\n> > -\tif (!in) {\n> > -\t\terror_errno(_(\"could not read `log` output\"));\n> > -\t\tfinish_command(&cp);\n> > -\t\treturn -1;\n> > -\t}\n> > +\tstrbuf_read(&file, cp.out, 0);\n> \n> Shouldn't we handle a negative return value here, erroring out with \"could\n> not read `log` output\" as before?\n\nYeah, that was an oversight, we should definitely still handle errors\nhere.\n\n> >\n> > -\twhile (strbuf_getline(&line, in) != EOF) {\n> > +\tline = strbuf_detach(&file, &size);\n> \n> I strongly suspect this to leak, given that `line` is subsequently\n> advanced, and there is no backup copy.\n> \n> Maybe\n> \n> \tline = file.buf;\n> \tsize = file.len;\n> \n> would make more sense here?\n\nHmm good point, that makes more sense indeed.\n\n> > +\tfor (offset = 0; size > 0; offset += len, size -= len, line += len) {\n> >  \t\tconst char *p;\n> >\n> > -\t\tif (skip_prefix(line.buf, \"commit \", &p)) {\n> > +\t\tlen = linelen(line, size);\n> > +\t\tline[len - 1] = '\\0';\n> > +\t\tif (skip_prefix(line, \"commit \", &p)) {\n> >  \t\t\tif (util) {\n> >  \t\t\t\tstring_list_append(list, buf.buf)->util = util;\n> >  \t\t\t\tstrbuf_reset(&buf);\n> > @@ -75,8 +86,6 @@ static int read_patches(const char *range, struct string_list *list)\n> >  \t\t\t\tfree(util);\n> >  \t\t\t\tstring_list_clear(list, 1);\n> >  \t\t\t\tstrbuf_release(&buf);\n> > -\t\t\t\tstrbuf_release(&line);\n> > -\t\t\t\tfclose(in);\n> \n> We should release the file contents in `file` (or `contents`, if you like\n> my suggestions) here.\n\nYeah, I thought it was no longer necessary because of the\n'strbuf_detach()' earlier, but that obviously leaks in a different way\nas you pointed out.  Will release 'contents' here and below. \n\n> >  \t\t\t\tfinish_command(&cp);\n> >  \t\t\t\treturn -1;\n> >  \t\t\t}\n> > @@ -85,26 +94,28 @@ static int read_patches(const char *range, struct string_list *list)\n> >  \t\t\tcontinue;\n> >  \t\t}\n> >\n> > -\t\tif (starts_with(line.buf, \"diff --git\")) {\n> > +\t\tif (starts_with(line, \"diff --git\")) {\n> >  \t\t\tin_header = 0;\n> >  \t\t\tstrbuf_addch(&buf, '\\n');\n> >  \t\t\tif (!util->diff_offset)\n> >  \t\t\t\tutil->diff_offset = buf.len;\n> >  \t\t\tstrbuf_addch(&buf, ' ');\n> > -\t\t\tstrbuf_addbuf(&buf, &line);\n> > +\t\t\tstrbuf_addstr(&buf, line);\n> >  \t\t} else if (in_header) {\n> > -\t\t\tif (starts_with(line.buf, \"Author: \")) {\n> > -\t\t\t\tstrbuf_addbuf(&buf, &line);\n> > +\t\t\tif (starts_with(line, \"Author: \")) {\n> > +\t\t\t\tstrbuf_addstr(&buf, line);\n> >  \t\t\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n> > -\t\t\t} else if (starts_with(line.buf, \"    \")) {\n> > -\t\t\t\tstrbuf_rtrim(&line);\n> > -\t\t\t\tstrbuf_addbuf(&buf, &line);\n> > +\t\t\t} else if (starts_with(line, \"    \")) {\n> > +\t\t\t\tp = line + len - 2;\n> > +\t\t\t\twhile (isspace(*p) && p >= line)\n> > +\t\t\t\t\tp--;\n> > +\t\t\t\tstrbuf_add(&buf, line, p - line + 1);\n> >  \t\t\t\tstrbuf_addch(&buf, '\\n');\n> >  \t\t\t}\n> >  \t\t\tcontinue;\n> > -\t\t} else if (starts_with(line.buf, \"@@ \"))\n> > +\t\t} else if (starts_with(line, \"@@ \"))\n> >  \t\t\tstrbuf_addstr(&buf, \"@@\");\n> > -\t\telse if (!line.buf[0] || starts_with(line.buf, \"index \"))\n> > +\t\telse if (!line[0] || starts_with(line, \"index \"))\n> >  \t\t\t/*\n> >  \t\t\t * A completely blank (not ' \\n', which is context)\n> >  \t\t\t * line is not valid in a diff.  We skip it\n> > @@ -117,25 +128,23 @@ static int read_patches(const char *range, struct string_list *list)\n> >  \t\t\t * we are not interested.\n> >  \t\t\t */\n> >  \t\t\tcontinue;\n> > -\t\telse if (line.buf[0] == '>') {\n> > +\t\telse if (line[0] == '>') {\n> >  \t\t\tstrbuf_addch(&buf, '+');\n> > -\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n> > -\t\t} else if (line.buf[0] == '<') {\n> > +\t\t\tstrbuf_addstr(&buf, line + 1);\n> > +\t\t} else if (line[0] == '<') {\n> >  \t\t\tstrbuf_addch(&buf, '-');\n> > -\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n> > -\t\t} else if (line.buf[0] == '#') {\n> > +\t\t\tstrbuf_addstr(&buf, line + 1);\n> > +\t\t} else if (line[0] == '#') {\n> >  \t\t\tstrbuf_addch(&buf, ' ');\n> > -\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n> > +\t\t\tstrbuf_addstr(&buf, line + 1);\n> >  \t\t} else {\n> >  \t\t\tstrbuf_addch(&buf, ' ');\n> > -\t\t\tstrbuf_addbuf(&buf, &line);\n> > +\t\t\tstrbuf_addstr(&buf, line);\n> >  \t\t}\n> >\n> >  \t\tstrbuf_addch(&buf, '\\n');\n> >  \t\tutil->diffsize++;\n> >  \t}\n> > -\tfclose(in);\n> > -\tstrbuf_release(&line);\n> \n> We should release the file contents we previously read via `strbuf_read()` here.\n> \n> Ciao,\n> Dscho\n> \n> >\n> >  \tif (util)\n> >  \t\tstring_list_append(list, buf.buf)->util = util;\n> > --\n> > 2.22.0.510.g264f2c817a\n> >\n> >\n"},{"id":"378676","messageId":"20190708114422.GC16825@hank.intra.tgummerer.com","threadId":"51436","inReplyTo":"nycvar.QRO.7.76.6.1907052114480.44@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v2 12/14] range-diff: add section header instead of diff header","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T11:44:22Z","receivedAt":"2019-07-08T11:44:29Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 07/05, Johannes Schindelin wrote:\n> >  range-diff.c           | 35 ++++++++++++----\n> >  t/t3206-range-diff.sh  | 91 +++++++++++++++++++++++++++++++++++++++---\n> >  t/t3206/history.export | 84 ++++++++++++++++++++++++++++++++++++--\n> >  3 files changed, 193 insertions(+), 17 deletions(-)\n> >\n> > diff --git a/range-diff.c b/range-diff.c\n> > index b31fbab026..cc01f7f573 100644\n> > --- a/range-diff.c\n> > +++ b/range-diff.c\n> > @@ -10,6 +10,7 @@\n> >  #include \"commit.h\"\n> >  #include \"pretty.h\"\n> >  #include \"userdiff.h\"\n> > +#include \"apply.h\"\n> >\n> >  struct patch_util {\n> >  \t/* For the search for an exact match */\n> > @@ -95,12 +96,36 @@ static int read_patches(const char *range, struct string_list *list)\n> >  \t\t}\n> >\n> >  \t\tif (starts_with(line, \"diff --git\")) {\n> > +\t\t\tstruct patch patch;\n> \n> If you append ` = { 0 }` (or ` = { NULL }`, depending on the first field\n> of that struct, you don't need that `memset()` later.\n\nThanks!\n\n> > +\t\t\tstruct strbuf root = STRBUF_INIT;\n> > +\t\t\tint linenr = 0;\n> > +\n> >  \t\t\tin_header = 0;\n> >  \t\t\tstrbuf_addch(&buf, '\\n');\n> >  \t\t\tif (!util->diff_offset)\n> >  \t\t\t\tutil->diff_offset = buf.len;\n> > -\t\t\tstrbuf_addch(&buf, ' ');\n> > -\t\t\tstrbuf_addstr(&buf, line);\n> > +\t\t\tmemset(&patch, 0, sizeof(patch));\n> > +\t\t\tline[len - 1] = '\\n';\n> \n> I guess `parse_git_header()` cannot handle lines ending in a NUL?\n\nYeah, it doesn't deal well with them.  We might be able to change\nthat, but I haven't looked into that tbh.\n\n> > +\t\t\tlen = parse_git_header(&root, &linenr, 1, line,\n> > +\t\t\t\t\t       len, size, &patch);\n> > +\t\t\tif (len < 0)\n> > +\t\t\t\tdie(_(\"could not parse git header\"));\n> \n> Maybe include the line's contents, like ` '%.*s'\", (int)len, line`?\n\nWill do.\n\n> > @@ -117,17 +142,13 @@ static int read_patches(const char *range, struct string_list *list)\n> >  \t\t\tif (!(p = strstr(p, \"@@\")))\n> >  \t\t\t\tdie(_(\"invalid hunk header in inner diff\"));\n> >  \t\t\tstrbuf_addstr(&buf, p);\n> > -\t\t} else if (!line[0] || starts_with(line, \"index \"))\n> > +\t\t} else if (!line[0])\n> >  \t\t\t/*\n> >  \t\t\t * A completely blank (not ' \\n', which is context)\n> >  \t\t\t * line is not valid in a diff.  We skip it\n> >  \t\t\t * silently, because this neatly handles the blank\n> >  \t\t\t * separator line between commits in git-log\n> >  \t\t\t * output.\n> > -\t\t\t *\n> > -\t\t\t * We also want to ignore the diff's `index` lines\n> > -\t\t\t * because they contain exact blob hashes in which\n> > -\t\t\t * we are not interested.\n> \n> Oh, are we interested in them again?\n\nNot really, but we no longer need to ignore them explicitly here.  The\n'index' lines are just another part of the diff header, which we parse\nusing 'parse_git_diff_header()' now, and then replace completely.\n\nPreviously we singled that line from the git diff headers out, because\nthey would result in a particularly noisy range diff, as the blob\nhashes would change in a lot of cases, but are not really\ninteresting.  We could have ignored things such as the \"similarity\nindex\" as well, as for example a 1% change in the similarity wouldn't\nmatter that much, but I assume we just didn't because that's much less\nlikely to change between different versions of a patch series.\n\n> >  \t\t\t */\n> >  \t\t\tcontinue;\n> >  \t\telse if (line[0] == '>') {\n> > diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\n> > index 9f89af7178..c277756057 100755\n> > --- a/t/t3206-range-diff.sh\n> > +++ b/t/t3206-range-diff.sh\n> > @@ -181,6 +181,85 @@ test_expect_success 'changed commit with sm config' '\n> >  \ttest_cmp expected actual\n> >  '\n> >\n> > +test_expect_success 'renamed file' '\n> > +\tgit range-diff --no-color --submodule=log topic...renamed-file >actual &&\n> > +\tsed s/Z/\\ /g >expected <<-EOF &&\n> > +\t1:  4de457d = 1:  f258d75 s/5/A/\n> > +\t2:  fccce22 ! 2:  017b62d s/4/A/\n> > +\t    @@\n> > +\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n> > +\t    Z\n> > +\t    -    s/4/A/\n> > +\t    +    s/4/A/ + rename file\n> > +\t    Z\n> > +\t    - ## file ##\n> > +\t    + ## file => renamed-file ##\n> \n> I guess there is no good way to suppress the `- ## file ##` line in this\n> case? It is a bit distracting...\n\nNo, I can't think of a good way.  I'm also not sure it would be right\nto remove it.  In this case it means that in the previous version this\nwas only called 'file', while in the new version it was renamend in\nthis patch to 'renamed-file', so it does give some useful information.\n\nNot sure how else we could represent that.  If we just had a \n'## file => renamed-file ##' section header, I'd expect that 'file'\nhas been renamed to 'renamed-file' in both versions.\n\n> > @@ -216,9 +295,9 @@ test_expect_success 'dual-coloring' '\n> >  \t:     <RESET>\n> >  \t:    <REVERSE><GREEN>+<RESET><BOLD>    Also a silly comment here!<RESET>\n> >  \t:    <REVERSE><GREEN>+<RESET>\n> > -\t:      diff --git a/file b/file<RESET>\n> > -\t:      --- a/file<RESET>\n> > -\t:      +++ b/file<RESET>\n> > +\t:      ## file ##<RESET>\n> > +\t:    <CYAN> @@<RESET>\n> > +\t:      1<RESET>\n> \n> I am a bit confused where these last two lines come from all of a\n> sudden... They were not there before, and I do not see any code change in\n> this patch that would be responsible for them, either...\n> \n> Could you help me understand?\n\nSure.  The actual change (in the range-diff) here is that \"Also a\nsilly comment here!\" was added to the commit message.  The diff header\nis context lines after that.\n\nWe now replace the diff header with the new \"section header\", which is\nonly a single line, so we get a couple of additional lines of the\ncontext of the subsequent inner diff.  \n\n\n> >  \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n> >  \t:    <REVERSE><CYAN>@@<RESET>\n> >  \t:      9<RESET>\n> > diff --git a/t/t3206/history.export b/t/t3206/history.export\n> > index b8ffff0940..7bb3814962 100644\n> > --- a/t/t3206/history.export\n> > +++ b/t/t3206/history.export\n> > @@ -22,8 +22,8 @@ data 51\n> >  19\n> >  20\n> >\n> > -reset refs/heads/removed\n> > -commit refs/heads/removed\n> > +reset refs/heads/renamed-file\n> > +commit refs/heads/renamed-file\n> \n> Hmm. Is the `removed` ref no longer required by the 'removed a commit'\n> test case?\n\nIt is, and it still exists.  I'm not entirely familar with the format\nfor fast-export/fast-import scripts.  What I did was just fast-import\nthe existing script, create the new refs that were required for the\ntests and then fast-export'ed it again.\n\nSo not sure exactly why this changed, but the 'removed' ref still\nexists :)\n\n> >  mark :2\n> >  author Thomas Rast <trast@inf.ethz.ch> 1374424921 +0200\n> >  committer Thomas Rast <trast@inf.ethz.ch> 1374484724 +0200\n> > @@ -599,6 +599,82 @@ s/12/B/\n> >  from :46\n> >  M 100644 :28 file\n> >\n> > -reset refs/heads/removed\n> > -from :47\n> > +commit refs/heads/added-removed\n> > +mark :48\n> > +author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200\n> > +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574151 +0100\n> \n> Neat ;-)\n> \n> > +data 7\n> > +s/5/A/\n> > +from :2\n> > +M 100644 :3 file\n> > +\n> > +blob\n> > +mark :49\n> > +data 0\n> > +\n> > +commit refs/heads/added-removed\n> > +mark :50\n> > +author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200\n> > +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n> > +data 18\n> > +s/4/A/ + new-file\n> > +from :48\n> > +M 100644 :5 file\n> > +M 100644 :49 new-file\n> > +\n> > +commit refs/heads/added-removed\n> > +mark :51\n> > +author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200\n> > +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n> > +data 22\n> > +s/11/B/ + remove file\n> > +from :50\n> > +M 100644 :7 file\n> > +D new-file\n> > +\n> > +commit refs/heads/added-removed\n> > +mark :52\n> > +author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200\n> > +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n> > +data 8\n> > +s/12/B/\n> > +from :51\n> > +M 100644 :9 file\n> > +\n> > +commit refs/heads/renamed-file\n> > +mark :53\n> > +author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200\n> > +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574309 +0100\n> > +data 7\n> > +s/5/A/\n> > +from :2\n> > +M 100644 :3 file\n> > +\n> > +commit refs/heads/renamed-file\n> > +mark :54\n> > +author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200\n> > +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574312 +0100\n> > +data 21\n> > +s/4/A/ + rename file\n> > +from :53\n> > +D file\n> > +M 100644 :5 renamed-file\n> > +\n> > +commit refs/heads/renamed-file\n> > +mark :55\n> > +author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200\n> > +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574319 +0100\n> > +data 8\n> > +s/11/B/\n> > +from :54\n> > +M 100644 :7 renamed-file\n> > +\n> > +commit refs/heads/renamed-file\n> > +mark :56\n> > +author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200\n> > +committer Thomas Gummerer <t.gummerer@gmail.com> 1556574319 +0100\n> > +data 8\n> > +s/12/B/\n> > +from :55\n> > +M 100644 :9 renamed-file\n> \n> I have to admit that I allowed myself not to study this script too\n> closely, trusting that the range-diff explains better what commit history\n> it creates than the fast-import script.\n> \n> Thanks,\n> Dscho\n> \n> >\n> > --\n> > 2.22.0.510.g264f2c817a\n> >\n> >\n"},{"id":"378677","messageId":"20190708114546.GD16825@hank.intra.tgummerer.com","threadId":"51436","inReplyTo":"nycvar.QRO.7.76.6.1907052138370.44@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v2 00/14] output improvements for git range-diff","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T11:45:46Z","receivedAt":"2019-07-08T11:45:51Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 07/05, Johannes Schindelin wrote:\n> Hi Thomas,\n> \n> On Fri, 5 Jul 2019, Thomas Gummerer wrote:\n> \n> > It's been quite a while since I sent the RFC [1] (thanks all for the\n> > comments on that), and the series changed shapes quite a bit since the\n> > last round.\n> >\n> > Since it's been such a long time, just to remind everyone, the goal of\n> > this series is to make the range-diff output clearer, by showing\n> > information about the filenames to which the current diff belongs.\n> \n> Thank you for that reminder ;-)\n> \n> > In the previous round, we did this using \"section headers\" that\n> > include information about the current file and adding that to the\n> > outer diff's hunk headers.\n> >\n> > In this round we still keep the section headers (with slightly more\n> > information), but in addition we also add the filename to the inner\n> > diff hunk headers.  In the outer diff hunk headers we then display\n> > either the section header or the inner diff hunk header using a\n> > userdiff pattern.\n> \n> \n> I like this idea!\n> \n> > In terms of code changes the biggest change is that we're now re-using\n> > the 'parse_git_header' function from the apply code to parse the diff\n> > headers, instead of trying to parse them with some hacky parsing code\n> > in range-diff.c.  This way we are sure that the diff headers are\n> > properly parsed.\n> \n> Yep, very good.\n> \n> > I had also considered just outputting the section headers directly\n> > from 'git log', but then decided against that.  Parsing the headers\n> > allows a future enhancement of range-diff, where we would allow\n> > parsing an mbox file [2].\n> \n> Thanks you for your consideration; I still would like to have the option\n> at some stage to compare a patch series from public-inbox.org/git to the\n> commits in `pu`, without having to fiddle with finding a valid base commit\n> to apply the patches on.\n\nYeah, I would like that as well ;)\n\n> > I split the \"only pass required data\" commits up, in the hopes of\n> > making them easier to review, but I'm also happy to squash them if\n> > people feel like that makes it easier to review them.\n> \n> I found it very easy to review in the current form, thank you for putting\n> in the extra effort.\n> \n> > An added advantage of this is that we're also getting rid of things\n> > like the similarity index, which are not important in the range-diff,\n> > and are thus not represented in the \"section header\".\n> >\n> > One thing that did not change is that the new/deleted strings are not\n> > translated in this version either.  This is similar to the regular\n> > diff output, where we also don't translate these.  We can still\n> > consider translating them in the future though.\n> >\n> > [1]: https://public-inbox.org/git/20190414210933.20875-1-t.gummerer@gmail.com/\n> > [2]: https://github.com/gitgitgadget/git/issues/207\n> >\n> > I'm including the range-diff between this version of the series and\n> > the RFC, and a diff between the range diff and the range-diff without\n> > these changes below.  Probably not useful in reviewing the code, but\n> > good to show off the changes made in this series.\n> \n> Indeed!\n> \n> I very much like the idea, and the current iteration. I offered a couple\n> of minor suggestions, in the hope that you find them helpful.\n\nThanks for your review!  I did find the suggestions very helpful\nindeed :)\n"},{"id":"378679","messageId":"nycvar.QRO.7.76.6.1907081507540.44@tvgsbejvaqbjf.bet","threadId":"51436","inReplyTo":"20190708114422.GC16825@hank.intra.tgummerer.com","subject":"Re: [PATCH v2 12/14] range-diff: add section header instead of diff header","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-07-08T13:12:57Z","receivedAt":"2019-07-08T13:12:31Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Thomas,\n\nOn Mon, 8 Jul 2019, Thomas Gummerer wrote:\n\n> On 07/05, Johannes Schindelin wrote:\n>\n> > >  \t\t\t */\n> > >  \t\t\tcontinue;\n> > >  \t\telse if (line[0] == '>') {\n> > > diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\n> > > index 9f89af7178..c277756057 100755\n> > > --- a/t/t3206-range-diff.sh\n> > > +++ b/t/t3206-range-diff.sh\n> > > @@ -181,6 +181,85 @@ test_expect_success 'changed commit with sm config' '\n> > >  \ttest_cmp expected actual\n> > >  '\n> > >\n> > > +test_expect_success 'renamed file' '\n> > > +\tgit range-diff --no-color --submodule=log topic...renamed-file >actual &&\n> > > +\tsed s/Z/\\ /g >expected <<-EOF &&\n> > > +\t1:  4de457d = 1:  f258d75 s/5/A/\n> > > +\t2:  fccce22 ! 2:  017b62d s/4/A/\n> > > +\t    @@\n> > > +\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n> > > +\t    Z\n> > > +\t    -    s/4/A/\n> > > +\t    +    s/4/A/ + rename file\n> > > +\t    Z\n> > > +\t    - ## file ##\n> > > +\t    + ## file => renamed-file ##\n> >\n> > I guess there is no good way to suppress the `- ## file ##` line in this\n> > case? It is a bit distracting...\n>\n> No, I can't think of a good way.  I'm also not sure it would be right\n> to remove it.  In this case it means that in the previous version this\n> was only called 'file', while in the new version it was renamend in\n> this patch to 'renamed-file', so it does give some useful information.\n\nOh, I misunderstood! You're right, this is useful information, and I just\nhave to learn how to read that variant of the range-diffs.\n\nIn other words: please leave this part of your patch series as-is.\n\n> > > @@ -216,9 +295,9 @@ test_expect_success 'dual-coloring' '\n> > >  \t:     <RESET>\n> > >  \t:    <REVERSE><GREEN>+<RESET><BOLD>    Also a silly comment here!<RESET>\n> > >  \t:    <REVERSE><GREEN>+<RESET>\n> > > -\t:      diff --git a/file b/file<RESET>\n> > > -\t:      --- a/file<RESET>\n> > > -\t:      +++ b/file<RESET>\n> > > +\t:      ## file ##<RESET>\n> > > +\t:    <CYAN> @@<RESET>\n> > > +\t:      1<RESET>\n> >\n> > I am a bit confused where these last two lines come from all of a\n> > sudden... They were not there before, and I do not see any code change in\n> > this patch that would be responsible for them, either...\n> >\n> > Could you help me understand?\n>\n> Sure.  The actual change (in the range-diff) here is that \"Also a\n> silly comment here!\" was added to the commit message.  The diff header\n> is context lines after that.\n>\n> We now replace the diff header with the new \"section header\", which is\n> only a single line, so we get a couple of additional lines of the\n> context of the subsequent inner diff.\n\nYou know what? This is my typical mistake when reading uncolored\nrange-diffs: _of course_ I missed that this is talking about context\nlines. I really thought they were added by the new iteration. My bad. And\nthanks for explaining this patiently to me.\n\n> > >  \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n> > >  \t:    <REVERSE><CYAN>@@<RESET>\n> > >  \t:      9<RESET>\n> > > diff --git a/t/t3206/history.export b/t/t3206/history.export\n> > > index b8ffff0940..7bb3814962 100644\n> > > --- a/t/t3206/history.export\n> > > +++ b/t/t3206/history.export\n> > > @@ -22,8 +22,8 @@ data 51\n> > >  19\n> > >  20\n> > >\n> > > -reset refs/heads/removed\n> > > -commit refs/heads/removed\n> > > +reset refs/heads/renamed-file\n> > > +commit refs/heads/renamed-file\n> >\n> > Hmm. Is the `removed` ref no longer required by the 'removed a commit'\n> > test case?\n>\n> It is, and it still exists.  I'm not entirely familar with the format\n> for fast-export/fast-import scripts.  What I did was just fast-import\n> the existing script, create the new refs that were required for the\n> tests and then fast-export'ed it again.\n>\n> So not sure exactly why this changed, but the 'removed' ref still\n> exists :)\n\nRight, it would still exist because earlier parts of the script would have\ncreated that ref (otherwise `reset refs/heads/removed` would have failed).\n\nYour strategy to update the script sounds like the best way to go, it just\nruns afoul of topological ordering that somehow makes this patch look as\nif you had removed parts of the commit history. I believe you when you say\nthat you didn't, of course.\n\nThanks,\nDscho\n"},{"id":"378680","messageId":"20190708163315.29912-1-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190705170630.27500-1-t.gummerer@gmail.com","subject":"[PATCH v3 00/14] output improvements for git range-diff","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:01Z","receivedAt":"2019-07-08T16:37:55Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Thanks Dscho for the review of the previous round [1].  This rounds\naddresses all the comments from that review.\n\nIn particular\n\n- update commit messages where necessary.\n- rename the function in apply.c to 'parse_git_diff_header()'\n- code cleanups in 09/14\n- fix a memory leak introduced in 09/14\n- be less strict about parsing hunk headers, so the new code isn't\n  more strict than it was before\n- give more information when we are unable to parse the git diff\n  header\n\n[1]: https://public-inbox.org/git/20190705170630.27500-1-t.gummerer@gmail.com/\n\nThomas Gummerer (14):\n  apply: replace marc.info link with public-inbox\n  apply: only pass required data to skip_tree_prefix\n  apply: only pass required data to git_header_name\n  apply: only pass required data to check_header_line\n  apply: only pass required data to find_name_*\n  apply: only pass required data to gitdiff_* functions\n  apply: make parse_git_header public\n  range-diff: fix function parameter indentation\n  range-diff: split lines manually\n  range-diff: don't remove funcname from inner diff\n  range-diff: suppress line count in outer diff\n  range-diff: add section header instead of diff header\n  range-diff: add filename to inner diff\n  range-diff: add headers to the outer hunk header\n\n apply.c                | 186 ++++++++++++++++++-----------------------\n apply.h                |  48 +++++++++++\n diff.c                 |   5 +-\n diff.h                 |   1 +\n range-diff.c           | 124 +++++++++++++++++++--------\n t/t3206-range-diff.sh  | 124 ++++++++++++++++++++++-----\n t/t3206/history.export |  84 ++++++++++++++++++-\n 7 files changed, 409 insertions(+), 163 deletions(-)\n\nRange-diff against v2:\n 1:  ef2245edda =  1:  ef2245edda apply: replace marc.info link with public-inbox\n 2:  94578fa45c =  2:  94578fa45c apply: only pass required data to skip_tree_prefix\n 3:  988269a68e =  3:  988269a68e apply: only pass required data to git_header_name\n 4:  a2c1ef3f5f =  4:  a2c1ef3f5f apply: only pass required data to check_header_line\n 5:  0f4cfe21cb =  5:  0f4cfe21cb apply: only pass required data to find_name_*\n 6:  7f1d7a4569 !  6:  07a271518d apply: only pass required data to gitdiff_* functions\n    @@ Commit message\n         we want functions in the callchain of 'parse_git_header()' to only\n         take arguments they really need.\n     \n    +    As these functions are called in a loop using their function pointers,\n    +    each function needs to be passed all the parameters even if only one\n    +    of the functions actually needs it.  We therefore pass this data along\n    +    in a struct to avoid adding too many unused parameters to each\n    +    function and making the code very verbose in the process.\n    +\n         Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      ## apply.c ##\n 7:  a5af8b0845 !  7:  9cb6732a5f apply: make parse_git_header public\n    @@ apply.c: struct fragment {\n      {\n      \twhile (list) {\n     @@ apply.c: static int check_header_line(int linenr, struct patch *patch)\n    + \treturn 0;\n      }\n      \n    - /* Verify that we recognize the lines following a git header */\n    +-/* Verify that we recognize the lines following a git header */\n     -static int parse_git_header(struct apply_state *state,\n     -\t\t\t    const char *line,\n     -\t\t\t    int len,\n     -\t\t\t    unsigned int size,\n     -\t\t\t    struct patch *patch)\n    -+int parse_git_header(struct strbuf *root,\n    -+\t\t     int *linenr,\n    -+\t\t     int p_value,\n    -+\t\t     const char *line,\n    -+\t\t     int len,\n    -+\t\t     unsigned int size,\n    -+\t\t     struct patch *patch)\n    ++int parse_git_diff_header(struct strbuf *root,\n    ++\t\t\t  int *linenr,\n    ++\t\t\t  int p_value,\n    ++\t\t\t  const char *line,\n    ++\t\t\t  int len,\n    ++\t\t\t  unsigned int size,\n    ++\t\t\t  struct patch *patch)\n      {\n      \tunsigned long offset;\n      \tstruct parse_git_header_state parse_hdr_state;\n    @@ apply.c: static int find_header(struct apply_state *state,\n      \t\t */\n      \t\tif (!memcmp(\"diff --git \", line, 11)) {\n     -\t\t\tint git_hdr_len = parse_git_header(state, line, len, size, patch);\n    -+\t\t\tint git_hdr_len = parse_git_header(&state->root, &state->linenr,\n    -+\t\t\t\t\t\t\t   state->p_value, line, len,\n    -+\t\t\t\t\t\t\t   size, patch);\n    ++\t\t\tint git_hdr_len = parse_git_diff_header(&state->root, &state->linenr,\n    ++\t\t\t\t\t\t\t\tstate->p_value, line, len,\n    ++\t\t\t\t\t\t\t\tsize, patch);\n      \t\t\tif (git_hdr_len < 0)\n      \t\t\t\treturn -128;\n      \t\t\tif (git_hdr_len <= len)\n    @@ apply.h: int init_apply_state(struct apply_state *state,\n      int check_apply_state(struct apply_state *state, int force_apply);\n      \n     +/*\n    -+ * Parse a get header, starting at line.  Fills the relevant metadata\n    -+ * information in 'struct patch'.\n    ++ * Parse a git diff header, starting at line.  Fills the relevant\n    ++ * metadata information in 'struct patch'.\n     + *\n     + * Returns -1 on failure, the length of the parsed header otherwise.\n     + */\n    -+int parse_git_header(struct strbuf *root,\n    -+\t\t     int *linenr,\n    -+\t\t     int p_value,\n    -+\t\t     const char *line,\n    -+\t\t     int len,\n    -+\t\t     unsigned int size,\n    -+\t\t     struct patch *patch);\n    ++int parse_git_diff_header(struct strbuf *root,\n    ++\t\t\t  int *linenr,\n    ++\t\t\t  int p_value,\n    ++\t\t\t  const char *line,\n    ++\t\t\t  int len,\n    ++\t\t\t  unsigned int size,\n    ++\t\t\t  struct patch *patch);\n     +\n      /*\n       * Some aspects of the apply behavior are controlled by the following\n 8:  1f25bb1002 =  8:  76a11ce995 range-diff: fix function parameter indentation\n 9:  01ed0f2a6a !  9:  6f70e7faa6 range-diff: split lines manually\n    @@ Commit message\n     \n         Currently range-diff uses the 'strbuf_getline()' function for doing\n         its line by line processing.  In a future patch we want to do parts of\n    -    that parsing using the 'parse_git_header()' function, which does\n    -    requires reading parts of the input from that function, which doesn't\n    -    use strbufs.\n    +    that parsing using the 'parse_git_diff_header()' function.  That\n    +    function does its own line by line reading of the input, and doesn't\n    +    use strbufs.  This doesn't match with how we do the line-by-line\n    +    processing in range-diff currently.\n     \n         Switch range-diff to do our own line by line parsing, so we can re-use\n    -    the parse_git_header function later.\n    +    the 'parse_git_diff_header()' function later.\n     \n         Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n    @@ range-diff.c: struct patch_util {\n      \tstruct object_id oid;\n      };\n      \n    -+static unsigned long linelen(const char *buffer, unsigned long size)\n    ++static size_t find_end_of_line(char *buffer, unsigned long size)\n     +{\n    -+\tunsigned long len = 0;\n    -+\twhile (size--) {\n    -+\t\tlen++;\n    -+\t\tif (*buffer++ == '\\n')\n    -+\t\t\tbreak;\n    -+\t}\n    -+\treturn len;\n    ++\tchar *eol = memchr(buffer, '\\n', size);\n    ++\n    ++\tif (!eol)\n    ++\t\treturn size;\n    ++\n    ++\t*eol = '\\0';\n    ++\treturn eol + 1 - buffer;\n     +}\n     +\n      /*\n    @@ range-diff.c: struct patch_util {\n      \tstruct child_process cp = CHILD_PROCESS_INIT;\n     -\tFILE *in;\n     -\tstruct strbuf buf = STRBUF_INIT, line = STRBUF_INIT;\n    -+\tstruct strbuf buf = STRBUF_INIT, file = STRBUF_INIT;\n    ++\tstruct strbuf buf = STRBUF_INIT, contents = STRBUF_INIT;\n      \tstruct patch_util *util = NULL;\n      \tint in_header = 1;\n     +\tchar *line;\n    @@ range-diff.c: static int read_patches(const char *range, struct string_list *lis\n      \t\treturn error_errno(_(\"could not start `log`\"));\n     -\tin = fdopen(cp.out, \"r\");\n     -\tif (!in) {\n    --\t\terror_errno(_(\"could not read `log` output\"));\n    --\t\tfinish_command(&cp);\n    --\t\treturn -1;\n    --\t}\n    -+\tstrbuf_read(&file, cp.out, 0);\n    ++\tif (strbuf_read(&contents, cp.out, 0) < 0) {\n    + \t\terror_errno(_(\"could not read `log` output\"));\n    + \t\tfinish_command(&cp);\n    + \t\treturn -1;\n    + \t}\n      \n     -\twhile (strbuf_getline(&line, in) != EOF) {\n    -+\tline = strbuf_detach(&file, &size);\n    ++\tline = contents.buf;\n    ++\tsize = contents.len;\n     +\tfor (offset = 0; size > 0; offset += len, size -= len, line += len) {\n      \t\tconst char *p;\n      \n     -\t\tif (skip_prefix(line.buf, \"commit \", &p)) {\n    -+\t\tlen = linelen(line, size);\n    ++\t\tlen = find_end_of_line(line, size);\n     +\t\tline[len - 1] = '\\0';\n     +\t\tif (skip_prefix(line, \"commit \", &p)) {\n      \t\t\tif (util) {\n    @@ range-diff.c: static int read_patches(const char *range, struct string_list *lis\n      \t\t\t\tstrbuf_release(&buf);\n     -\t\t\t\tstrbuf_release(&line);\n     -\t\t\t\tfclose(in);\n    ++\t\t\t\tstrbuf_release(&contents);\n      \t\t\t\tfinish_command(&cp);\n      \t\t\t\treturn -1;\n      \t\t\t}\n    @@ range-diff.c: static int read_patches(const char *range, struct string_list *lis\n      \t}\n     -\tfclose(in);\n     -\tstrbuf_release(&line);\n    ++\tstrbuf_release(&contents);\n      \n      \tif (util)\n      \t\tstring_list_append(list, buf.buf)->util = util;\n10:  044a79868b ! 10:  6618cdff2c range-diff: don't remove funcname from inner diff\n    @@ range-diff.c: static int read_patches(const char *range, struct string_list *lis\n     -\t\t\tstrbuf_addstr(&buf, \"@@\");\n     -\t\telse if (!line[0] || starts_with(line, \"index \"))\n     +\t\t} else if (skip_prefix(line, \"@@ \", &p)) {\n    -+\t\t\tif (!(p = strstr(p, \"@@\")))\n    -+\t\t\t\tdie(_(\"invalid hunk header in inner diff\"));\n    -+\t\t\tstrbuf_addstr(&buf, p);\n    ++\t\t\tp = strstr(p, \"@@\");\n    ++\t\t\tstrbuf_addstr(&buf, p ? p : \"@@\");\n     +\t\t} else if (!line[0] || starts_with(line, \"index \"))\n      \t\t\t/*\n      \t\t\t * A completely blank (not ' \\n', which is context)\n11:  69654fe76d = 11:  2667df4fa5 range-diff: suppress line count in outer diff\n12:  c38f929b9a ! 12:  47cd8c6733 range-diff: add section header instead of diff header\n    @@ Commit message\n         noisy.  However the diff of a single line is concise and should be\n         easier to understand.\n     \n    -    Additionaly, this allows us to add these range diff section headers to\n    +    Additionally, this allows us to add these range diff section headers to\n         the outer diffs hunk headers using a custom userdiff pattern, which\n         should help making the range-diff more readable.\n     \n    @@ range-diff.c: static int read_patches(const char *range, struct string_list *lis\n      \t\t}\n      \n      \t\tif (starts_with(line, \"diff --git\")) {\n    -+\t\t\tstruct patch patch;\n    ++\t\t\tstruct patch patch = { 0 };\n     +\t\t\tstruct strbuf root = STRBUF_INIT;\n     +\t\t\tint linenr = 0;\n     +\n    @@ range-diff.c: static int read_patches(const char *range, struct string_list *lis\n      \t\t\t\tutil->diff_offset = buf.len;\n     -\t\t\tstrbuf_addch(&buf, ' ');\n     -\t\t\tstrbuf_addstr(&buf, line);\n    -+\t\t\tmemset(&patch, 0, sizeof(patch));\n     +\t\t\tline[len - 1] = '\\n';\n    -+\t\t\tlen = parse_git_header(&root, &linenr, 1, line,\n    -+\t\t\t\t\t       len, size, &patch);\n    ++\t\t\tlen = parse_git_diff_header(&root, &linenr, 1, line,\n    ++\t\t\t\t\t\t    len, size, &patch);\n     +\t\t\tif (len < 0)\n    -+\t\t\t\tdie(_(\"could not parse git header\"));\n    ++\t\t\t\tdie(_(\"could not parse git header '%.*s'\"), (int)len, line);\n     +\t\t\tstrbuf_addstr(&buf, \" ## \");\n     +\t\t\tif (patch.is_new > 0)\n     +\t\t\t\tstrbuf_addf(&buf, \"%s (new)\", patch.new_name);\n    @@ range-diff.c: static int read_patches(const char *range, struct string_list *lis\n      \t\t\tif (starts_with(line, \"Author: \")) {\n      \t\t\t\tstrbuf_addstr(&buf, line);\n     @@ range-diff.c: static int read_patches(const char *range, struct string_list *list)\n    - \t\t\tif (!(p = strstr(p, \"@@\")))\n    - \t\t\t\tdie(_(\"invalid hunk header in inner diff\"));\n    - \t\t\tstrbuf_addstr(&buf, p);\n    + \t\t} else if (skip_prefix(line, \"@@ \", &p)) {\n    + \t\t\tp = strstr(p, \"@@\");\n    + \t\t\tstrbuf_addstr(&buf, p ? p : \"@@\");\n     -\t\t} else if (!line[0] || starts_with(line, \"index \"))\n     +\t\t} else if (!line[0])\n      \t\t\t/*\n13:  6df03ecdcf ! 13:  f67fd5dd9a range-diff: add filename to inner diff\n    @@ Commit message\n     \n      ## range-diff.c ##\n     @@ range-diff.c: static int read_patches(const char *range, struct string_list *list)\n    - \tstruct strbuf buf = STRBUF_INIT, file = STRBUF_INIT;\n    + \tstruct strbuf buf = STRBUF_INIT, contents = STRBUF_INIT;\n      \tstruct patch_util *util = NULL;\n      \tint in_header = 1;\n     -\tchar *line;\n    @@ range-diff.c: static int read_patches(const char *range, struct string_list *lis\n      \t\t\t    patch.old_mode != patch.new_mode)\n      \t\t\t\tstrbuf_addf(&buf, \" (mode change %06o => %06o)\",\n     @@ range-diff.c: static int read_patches(const char *range, struct string_list *list)\n    + \t\t\tcontinue;\n      \t\t} else if (skip_prefix(line, \"@@ \", &p)) {\n    - \t\t\tif (!(p = strstr(p, \"@@\")))\n    - \t\t\t\tdie(_(\"invalid hunk header in inner diff\"));\n    --\t\t\tstrbuf_addstr(&buf, p);\n    + \t\t\tp = strstr(p, \"@@\");\n    +-\t\t\tstrbuf_addstr(&buf, p ? p : \"@@\");\n     +\t\t\tstrbuf_addstr(&buf, \"@@\");\n     +\t\t\tif (current_filename && p[2])\n     +\t\t\t\tstrbuf_addf(&buf, \" %s:\", current_filename);\n    -+\t\t\tstrbuf_addstr(&buf, p + 2);\n    ++\t\t\tif (p)\n    ++\t\t\t\tstrbuf_addstr(&buf, p + 2);\n      \t\t} else if (!line[0])\n      \t\t\t/*\n      \t\t\t * A completely blank (not ' \\n', which is context)\n14:  5ceef49035 = 14:  812893a5dc range-diff: add headers to the outer hunk header\n\n-- \n2.22.0.510.g264f2c817a\n"},{"id":"378681","messageId":"20190708163315.29912-2-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 01/14] apply: replace marc.info link with public-inbox","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:02Z","receivedAt":"2019-07-08T16:37:56Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"public-inbox.org links include the whole message ID by default.  This\nmeans the message can still be found even if the site goes away, which\nis not the case with the marc.info link.  Replace the marc.info link\nwith a more future proof one.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/apply.c b/apply.c\nindex f15afa9f6a..599cf8956f 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -478,7 +478,7 @@ static char *find_name_gnu(struct apply_state *state,\n \n \t/*\n \t * Proposed \"new-style\" GNU patch/diff format; see\n-\t * http://marc.info/?l=git&m=112927316408690&w=2\n+\t * https://public-inbox.org/git/7vll0wvb2a.fsf@assigned-by-dhcp.cox.net/\n \t */\n \tif (unquote_c_style(&name, line, NULL)) {\n \t\tstrbuf_release(&name);\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378682","messageId":"20190708163315.29912-3-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 02/14] apply: only pass required data to skip_tree_prefix","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:03Z","receivedAt":"2019-07-08T16:37:57Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'skip_tree_prefix()' function takes 'struct apply_state'\nas parameter, even though it only needs the p_value from that struct.\n\nThis function is in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 18 +++++++++---------\n 1 file changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 599cf8956f..fc7083fcbc 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -1137,17 +1137,17 @@ static int gitdiff_unrecognized(struct apply_state *state,\n  * Skip p_value leading components from \"line\"; as we do not accept\n  * absolute paths, return NULL in that case.\n  */\n-static const char *skip_tree_prefix(struct apply_state *state,\n+static const char *skip_tree_prefix(int p_value,\n \t\t\t\t    const char *line,\n \t\t\t\t    int llen)\n {\n \tint nslash;\n \tint i;\n \n-\tif (!state->p_value)\n+\tif (!p_value)\n \t\treturn (llen && line[0] == '/') ? NULL : line;\n \n-\tnslash = state->p_value;\n+\tnslash = p_value;\n \tfor (i = 0; i < llen; i++) {\n \t\tint ch = line[i];\n \t\tif (ch == '/' && --nslash <= 0)\n@@ -1184,7 +1184,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\tgoto free_and_fail1;\n \n \t\t/* strip the a/b prefix including trailing slash */\n-\t\tcp = skip_tree_prefix(state, first.buf, first.len);\n+\t\tcp = skip_tree_prefix(state->p_value, first.buf, first.len);\n \t\tif (!cp)\n \t\t\tgoto free_and_fail1;\n \t\tstrbuf_remove(&first, 0, cp - first.buf);\n@@ -1201,7 +1201,7 @@ static char *git_header_name(struct apply_state *state,\n \t\tif (*second == '\"') {\n \t\t\tif (unquote_c_style(&sp, second, NULL))\n \t\t\t\tgoto free_and_fail1;\n-\t\t\tcp = skip_tree_prefix(state, sp.buf, sp.len);\n+\t\t\tcp = skip_tree_prefix(state->p_value, sp.buf, sp.len);\n \t\t\tif (!cp)\n \t\t\t\tgoto free_and_fail1;\n \t\t\t/* They must match, otherwise ignore */\n@@ -1212,7 +1212,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t}\n \n \t\t/* unquoted second */\n-\t\tcp = skip_tree_prefix(state, second, line + llen - second);\n+\t\tcp = skip_tree_prefix(state->p_value, second, line + llen - second);\n \t\tif (!cp)\n \t\t\tgoto free_and_fail1;\n \t\tif (line + llen - cp != first.len ||\n@@ -1227,7 +1227,7 @@ static char *git_header_name(struct apply_state *state,\n \t}\n \n \t/* unquoted first name */\n-\tname = skip_tree_prefix(state, line, llen);\n+\tname = skip_tree_prefix(state->p_value, line, llen);\n \tif (!name)\n \t\treturn NULL;\n \n@@ -1243,7 +1243,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\tif (unquote_c_style(&sp, second, NULL))\n \t\t\t\tgoto free_and_fail2;\n \n-\t\t\tnp = skip_tree_prefix(state, sp.buf, sp.len);\n+\t\t\tnp = skip_tree_prefix(state->p_value, sp.buf, sp.len);\n \t\t\tif (!np)\n \t\t\t\tgoto free_and_fail2;\n \n@@ -1287,7 +1287,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\t */\n \t\t\tif (!name[len + 1])\n \t\t\t\treturn NULL; /* no postimage name */\n-\t\t\tsecond = skip_tree_prefix(state, name + len + 1,\n+\t\t\tsecond = skip_tree_prefix(state->p_value, name + len + 1,\n \t\t\t\t\t\t  line_len - (len + 1));\n \t\t\tif (!second)\n \t\t\t\treturn NULL;\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378683","messageId":"20190708163315.29912-4-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 03/14] apply: only pass required data to git_header_name","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:04Z","receivedAt":"2019-07-08T16:37:58Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'git_header_name()' function takes 'struct apply_state'\nas parameter, even though it only needs the p_value from that struct.\n\nThis function is in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex fc7083fcbc..ac668e754d 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -1164,7 +1164,7 @@ static const char *skip_tree_prefix(int p_value,\n  * creation or deletion of an empty file.  In any of these cases,\n  * both sides are the same name under a/ and b/ respectively.\n  */\n-static char *git_header_name(struct apply_state *state,\n+static char *git_header_name(int p_value,\n \t\t\t     const char *line,\n \t\t\t     int llen)\n {\n@@ -1184,7 +1184,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\tgoto free_and_fail1;\n \n \t\t/* strip the a/b prefix including trailing slash */\n-\t\tcp = skip_tree_prefix(state->p_value, first.buf, first.len);\n+\t\tcp = skip_tree_prefix(p_value, first.buf, first.len);\n \t\tif (!cp)\n \t\t\tgoto free_and_fail1;\n \t\tstrbuf_remove(&first, 0, cp - first.buf);\n@@ -1201,7 +1201,7 @@ static char *git_header_name(struct apply_state *state,\n \t\tif (*second == '\"') {\n \t\t\tif (unquote_c_style(&sp, second, NULL))\n \t\t\t\tgoto free_and_fail1;\n-\t\t\tcp = skip_tree_prefix(state->p_value, sp.buf, sp.len);\n+\t\t\tcp = skip_tree_prefix(p_value, sp.buf, sp.len);\n \t\t\tif (!cp)\n \t\t\t\tgoto free_and_fail1;\n \t\t\t/* They must match, otherwise ignore */\n@@ -1212,7 +1212,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t}\n \n \t\t/* unquoted second */\n-\t\tcp = skip_tree_prefix(state->p_value, second, line + llen - second);\n+\t\tcp = skip_tree_prefix(p_value, second, line + llen - second);\n \t\tif (!cp)\n \t\t\tgoto free_and_fail1;\n \t\tif (line + llen - cp != first.len ||\n@@ -1227,7 +1227,7 @@ static char *git_header_name(struct apply_state *state,\n \t}\n \n \t/* unquoted first name */\n-\tname = skip_tree_prefix(state->p_value, line, llen);\n+\tname = skip_tree_prefix(p_value, line, llen);\n \tif (!name)\n \t\treturn NULL;\n \n@@ -1243,7 +1243,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\tif (unquote_c_style(&sp, second, NULL))\n \t\t\t\tgoto free_and_fail2;\n \n-\t\t\tnp = skip_tree_prefix(state->p_value, sp.buf, sp.len);\n+\t\t\tnp = skip_tree_prefix(p_value, sp.buf, sp.len);\n \t\t\tif (!np)\n \t\t\t\tgoto free_and_fail2;\n \n@@ -1287,7 +1287,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\t */\n \t\t\tif (!name[len + 1])\n \t\t\t\treturn NULL; /* no postimage name */\n-\t\t\tsecond = skip_tree_prefix(state->p_value, name + len + 1,\n+\t\t\tsecond = skip_tree_prefix(p_value, name + len + 1,\n \t\t\t\t\t\t  line_len - (len + 1));\n \t\t\tif (!second)\n \t\t\t\treturn NULL;\n@@ -1333,7 +1333,7 @@ static int parse_git_header(struct apply_state *state,\n \t * or removing or adding empty files), so we get\n \t * the default name from the header.\n \t */\n-\tpatch->def_name = git_header_name(state, line, len);\n+\tpatch->def_name = git_header_name(state->p_value, line, len);\n \tif (patch->def_name && state->root.len) {\n \t\tchar *s = xstrfmt(\"%s%s\", state->root.buf, patch->def_name);\n \t\tfree(patch->def_name);\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378686","messageId":"20190708163315.29912-5-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 04/14] apply: only pass required data to check_header_line","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:05Z","receivedAt":"2019-07-08T16:38:00Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'check_header_line()' function takes 'struct\napply_state' as parameter, even though it only needs the linenr from\nthat struct.\n\nThis function is in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex ac668e754d..1602fd5db0 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -1302,15 +1302,15 @@ static char *git_header_name(int p_value,\n \t}\n }\n \n-static int check_header_line(struct apply_state *state, struct patch *patch)\n+static int check_header_line(int linenr, struct patch *patch)\n {\n \tint extensions = (patch->is_delete == 1) + (patch->is_new == 1) +\n \t\t\t (patch->is_rename == 1) + (patch->is_copy == 1);\n \tif (extensions > 1)\n \t\treturn error(_(\"inconsistent header lines %d and %d\"),\n-\t\t\t     patch->extension_linenr, state->linenr);\n+\t\t\t     patch->extension_linenr, linenr);\n \tif (extensions && !patch->extension_linenr)\n-\t\tpatch->extension_linenr = state->linenr;\n+\t\tpatch->extension_linenr = linenr;\n \treturn 0;\n }\n \n@@ -1380,7 +1380,7 @@ static int parse_git_header(struct apply_state *state,\n \t\t\tres = p->fn(state, line + oplen, patch);\n \t\t\tif (res < 0)\n \t\t\t\treturn -1;\n-\t\t\tif (check_header_line(state, patch))\n+\t\t\tif (check_header_line(state->linenr, patch))\n \t\t\t\treturn -1;\n \t\t\tif (res > 0)\n \t\t\t\treturn offset;\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378685","messageId":"20190708163315.29912-6-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 05/14] apply: only pass required data to find_name_*","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:06Z","receivedAt":"2019-07-08T16:38:03Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'find_name_*()' functions take 'struct apply_state' as\nparameter, even though they only need the 'root' member from that\nstruct.\n\nThese functions are in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 48 ++++++++++++++++++++++++------------------------\n 1 file changed, 24 insertions(+), 24 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 1602fd5db0..3cd4e3d3b3 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -469,7 +469,7 @@ static char *squash_slash(char *name)\n \treturn name;\n }\n \n-static char *find_name_gnu(struct apply_state *state,\n+static char *find_name_gnu(struct strbuf *root,\n \t\t\t   const char *line,\n \t\t\t   int p_value)\n {\n@@ -495,8 +495,8 @@ static char *find_name_gnu(struct apply_state *state,\n \t}\n \n \tstrbuf_remove(&name, 0, cp - name.buf);\n-\tif (state->root.len)\n-\t\tstrbuf_insert(&name, 0, state->root.buf, state->root.len);\n+\tif (root->len)\n+\t\tstrbuf_insert(&name, 0, root->buf, root->len);\n \treturn squash_slash(strbuf_detach(&name, NULL));\n }\n \n@@ -659,7 +659,7 @@ static size_t diff_timestamp_len(const char *line, size_t len)\n \treturn line + len - end;\n }\n \n-static char *find_name_common(struct apply_state *state,\n+static char *find_name_common(struct strbuf *root,\n \t\t\t      const char *line,\n \t\t\t      const char *def,\n \t\t\t      int p_value,\n@@ -702,30 +702,30 @@ static char *find_name_common(struct apply_state *state,\n \t\t\treturn squash_slash(xstrdup(def));\n \t}\n \n-\tif (state->root.len) {\n-\t\tchar *ret = xstrfmt(\"%s%.*s\", state->root.buf, len, start);\n+\tif (root->len) {\n+\t\tchar *ret = xstrfmt(\"%s%.*s\", root->buf, len, start);\n \t\treturn squash_slash(ret);\n \t}\n \n \treturn squash_slash(xmemdupz(start, len));\n }\n \n-static char *find_name(struct apply_state *state,\n+static char *find_name(struct strbuf *root,\n \t\t       const char *line,\n \t\t       char *def,\n \t\t       int p_value,\n \t\t       int terminate)\n {\n \tif (*line == '\"') {\n-\t\tchar *name = find_name_gnu(state, line, p_value);\n+\t\tchar *name = find_name_gnu(root, line, p_value);\n \t\tif (name)\n \t\t\treturn name;\n \t}\n \n-\treturn find_name_common(state, line, def, p_value, NULL, terminate);\n+\treturn find_name_common(root, line, def, p_value, NULL, terminate);\n }\n \n-static char *find_name_traditional(struct apply_state *state,\n+static char *find_name_traditional(struct strbuf *root,\n \t\t\t\t   const char *line,\n \t\t\t\t   char *def,\n \t\t\t\t   int p_value)\n@@ -734,7 +734,7 @@ static char *find_name_traditional(struct apply_state *state,\n \tsize_t date_len;\n \n \tif (*line == '\"') {\n-\t\tchar *name = find_name_gnu(state, line, p_value);\n+\t\tchar *name = find_name_gnu(root, line, p_value);\n \t\tif (name)\n \t\t\treturn name;\n \t}\n@@ -742,10 +742,10 @@ static char *find_name_traditional(struct apply_state *state,\n \tlen = strchrnul(line, '\\n') - line;\n \tdate_len = diff_timestamp_len(line, len);\n \tif (!date_len)\n-\t\treturn find_name_common(state, line, def, p_value, NULL, TERM_TAB);\n+\t\treturn find_name_common(root, line, def, p_value, NULL, TERM_TAB);\n \tlen -= date_len;\n \n-\treturn find_name_common(state, line, def, p_value, line + len, 0);\n+\treturn find_name_common(root, line, def, p_value, line + len, 0);\n }\n \n /*\n@@ -759,7 +759,7 @@ static int guess_p_value(struct apply_state *state, const char *nameline)\n \n \tif (is_dev_null(nameline))\n \t\treturn -1;\n-\tname = find_name_traditional(state, nameline, NULL, 0);\n+\tname = find_name_traditional(&state->root, nameline, NULL, 0);\n \tif (!name)\n \t\treturn -1;\n \tcp = strchr(name, '/');\n@@ -883,17 +883,17 @@ static int parse_traditional_patch(struct apply_state *state,\n \tif (is_dev_null(first)) {\n \t\tpatch->is_new = 1;\n \t\tpatch->is_delete = 0;\n-\t\tname = find_name_traditional(state, second, NULL, state->p_value);\n+\t\tname = find_name_traditional(&state->root, second, NULL, state->p_value);\n \t\tpatch->new_name = name;\n \t} else if (is_dev_null(second)) {\n \t\tpatch->is_new = 0;\n \t\tpatch->is_delete = 1;\n-\t\tname = find_name_traditional(state, first, NULL, state->p_value);\n+\t\tname = find_name_traditional(&state->root, first, NULL, state->p_value);\n \t\tpatch->old_name = name;\n \t} else {\n \t\tchar *first_name;\n-\t\tfirst_name = find_name_traditional(state, first, NULL, state->p_value);\n-\t\tname = find_name_traditional(state, second, first_name, state->p_value);\n+\t\tfirst_name = find_name_traditional(&state->root, first, NULL, state->p_value);\n+\t\tname = find_name_traditional(&state->root, second, first_name, state->p_value);\n \t\tfree(first_name);\n \t\tif (has_epoch_timestamp(first)) {\n \t\t\tpatch->is_new = 1;\n@@ -940,7 +940,7 @@ static int gitdiff_verify_name(struct apply_state *state,\n \t\t\t       int side)\n {\n \tif (!*name && !isnull) {\n-\t\t*name = find_name(state, line, NULL, state->p_value, TERM_TAB);\n+\t\t*name = find_name(&state->root, line, NULL, state->p_value, TERM_TAB);\n \t\treturn 0;\n \t}\n \n@@ -949,7 +949,7 @@ static int gitdiff_verify_name(struct apply_state *state,\n \t\tif (isnull)\n \t\t\treturn error(_(\"git apply: bad git-diff - expected /dev/null, got %s on line %d\"),\n \t\t\t\t     *name, state->linenr);\n-\t\tanother = find_name(state, line, NULL, state->p_value, TERM_TAB);\n+\t\tanother = find_name(&state->root, line, NULL, state->p_value, TERM_TAB);\n \t\tif (!another || strcmp(another, *name)) {\n \t\t\tfree(another);\n \t\t\treturn error((side == DIFF_NEW_NAME) ?\n@@ -1032,7 +1032,7 @@ static int gitdiff_copysrc(struct apply_state *state,\n {\n \tpatch->is_copy = 1;\n \tfree(patch->old_name);\n-\tpatch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->old_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n@@ -1042,7 +1042,7 @@ static int gitdiff_copydst(struct apply_state *state,\n {\n \tpatch->is_copy = 1;\n \tfree(patch->new_name);\n-\tpatch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->new_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n@@ -1052,7 +1052,7 @@ static int gitdiff_renamesrc(struct apply_state *state,\n {\n \tpatch->is_rename = 1;\n \tfree(patch->old_name);\n-\tpatch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->old_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n@@ -1062,7 +1062,7 @@ static int gitdiff_renamedst(struct apply_state *state,\n {\n \tpatch->is_rename = 1;\n \tfree(patch->new_name);\n-\tpatch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->new_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378688","messageId":"20190708163315.29912-7-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 06/14] apply: only pass required data to gitdiff_* functions","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:07Z","receivedAt":"2019-07-08T16:38:03Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'gitdiff_*()' functions take 'struct apply_state' as\nparameter, even though they only needs the root, linenr and p_value\nfrom that struct.\n\nThese functions are in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nAs these functions are called in a loop using their function pointers,\neach function needs to be passed all the parameters even if only one\nof the functions actually needs it.  We therefore pass this data along\nin a struct to avoid adding too many unused parameters to each\nfunction and making the code very verbose in the process.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 59 ++++++++++++++++++++++++++++++++++-----------------------\n 1 file changed, 35 insertions(+), 24 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 3cd4e3d3b3..468f1d3fee 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -22,6 +22,12 @@\n #include \"rerere.h\"\n #include \"apply.h\"\n \n+struct parse_git_header_state {\n+\tstruct strbuf *root;\n+\tint linenr;\n+\tint p_value;\n+};\n+\n static void git_apply_config(void)\n {\n \tgit_config_get_string_const(\"apply.whitespace\", &apply_default_whitespace);\n@@ -914,7 +920,7 @@ static int parse_traditional_patch(struct apply_state *state,\n \treturn 0;\n }\n \n-static int gitdiff_hdrend(struct apply_state *state,\n+static int gitdiff_hdrend(struct parse_git_header_state *state,\n \t\t\t  const char *line,\n \t\t\t  struct patch *patch)\n {\n@@ -933,14 +939,14 @@ static int gitdiff_hdrend(struct apply_state *state,\n #define DIFF_OLD_NAME 0\n #define DIFF_NEW_NAME 1\n \n-static int gitdiff_verify_name(struct apply_state *state,\n+static int gitdiff_verify_name(struct parse_git_header_state *state,\n \t\t\t       const char *line,\n \t\t\t       int isnull,\n \t\t\t       char **name,\n \t\t\t       int side)\n {\n \tif (!*name && !isnull) {\n-\t\t*name = find_name(&state->root, line, NULL, state->p_value, TERM_TAB);\n+\t\t*name = find_name(state->root, line, NULL, state->p_value, TERM_TAB);\n \t\treturn 0;\n \t}\n \n@@ -949,7 +955,7 @@ static int gitdiff_verify_name(struct apply_state *state,\n \t\tif (isnull)\n \t\t\treturn error(_(\"git apply: bad git-diff - expected /dev/null, got %s on line %d\"),\n \t\t\t\t     *name, state->linenr);\n-\t\tanother = find_name(&state->root, line, NULL, state->p_value, TERM_TAB);\n+\t\tanother = find_name(state->root, line, NULL, state->p_value, TERM_TAB);\n \t\tif (!another || strcmp(another, *name)) {\n \t\t\tfree(another);\n \t\t\treturn error((side == DIFF_NEW_NAME) ?\n@@ -965,7 +971,7 @@ static int gitdiff_verify_name(struct apply_state *state,\n \treturn 0;\n }\n \n-static int gitdiff_oldname(struct apply_state *state,\n+static int gitdiff_oldname(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n@@ -974,7 +980,7 @@ static int gitdiff_oldname(struct apply_state *state,\n \t\t\t\t   DIFF_OLD_NAME);\n }\n \n-static int gitdiff_newname(struct apply_state *state,\n+static int gitdiff_newname(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n@@ -992,21 +998,21 @@ static int parse_mode_line(const char *line, int linenr, unsigned int *mode)\n \treturn 0;\n }\n \n-static int gitdiff_oldmode(struct apply_state *state,\n+static int gitdiff_oldmode(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n \treturn parse_mode_line(line, state->linenr, &patch->old_mode);\n }\n \n-static int gitdiff_newmode(struct apply_state *state,\n+static int gitdiff_newmode(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n \treturn parse_mode_line(line, state->linenr, &patch->new_mode);\n }\n \n-static int gitdiff_delete(struct apply_state *state,\n+static int gitdiff_delete(struct parse_git_header_state *state,\n \t\t\t  const char *line,\n \t\t\t  struct patch *patch)\n {\n@@ -1016,7 +1022,7 @@ static int gitdiff_delete(struct apply_state *state,\n \treturn gitdiff_oldmode(state, line, patch);\n }\n \n-static int gitdiff_newfile(struct apply_state *state,\n+static int gitdiff_newfile(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n@@ -1026,47 +1032,47 @@ static int gitdiff_newfile(struct apply_state *state,\n \treturn gitdiff_newmode(state, line, patch);\n }\n \n-static int gitdiff_copysrc(struct apply_state *state,\n+static int gitdiff_copysrc(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n \tpatch->is_copy = 1;\n \tfree(patch->old_name);\n-\tpatch->old_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->old_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-static int gitdiff_copydst(struct apply_state *state,\n+static int gitdiff_copydst(struct parse_git_header_state *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n \tpatch->is_copy = 1;\n \tfree(patch->new_name);\n-\tpatch->new_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->new_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-static int gitdiff_renamesrc(struct apply_state *state,\n+static int gitdiff_renamesrc(struct parse_git_header_state *state,\n \t\t\t     const char *line,\n \t\t\t     struct patch *patch)\n {\n \tpatch->is_rename = 1;\n \tfree(patch->old_name);\n-\tpatch->old_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->old_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-static int gitdiff_renamedst(struct apply_state *state,\n+static int gitdiff_renamedst(struct parse_git_header_state *state,\n \t\t\t     const char *line,\n \t\t\t     struct patch *patch)\n {\n \tpatch->is_rename = 1;\n \tfree(patch->new_name);\n-\tpatch->new_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->new_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-static int gitdiff_similarity(struct apply_state *state,\n+static int gitdiff_similarity(struct parse_git_header_state *state,\n \t\t\t      const char *line,\n \t\t\t      struct patch *patch)\n {\n@@ -1076,7 +1082,7 @@ static int gitdiff_similarity(struct apply_state *state,\n \treturn 0;\n }\n \n-static int gitdiff_dissimilarity(struct apply_state *state,\n+static int gitdiff_dissimilarity(struct parse_git_header_state *state,\n \t\t\t\t const char *line,\n \t\t\t\t struct patch *patch)\n {\n@@ -1086,7 +1092,7 @@ static int gitdiff_dissimilarity(struct apply_state *state,\n \treturn 0;\n }\n \n-static int gitdiff_index(struct apply_state *state,\n+static int gitdiff_index(struct parse_git_header_state *state,\n \t\t\t const char *line,\n \t\t\t struct patch *patch)\n {\n@@ -1126,7 +1132,7 @@ static int gitdiff_index(struct apply_state *state,\n  * This is normal for a diff that doesn't change anything: we'll fall through\n  * into the next diff. Tell the parser to break out.\n  */\n-static int gitdiff_unrecognized(struct apply_state *state,\n+static int gitdiff_unrecognized(struct parse_git_header_state *state,\n \t\t\t\tconst char *line,\n \t\t\t\tstruct patch *patch)\n {\n@@ -1322,6 +1328,7 @@ static int parse_git_header(struct apply_state *state,\n \t\t\t    struct patch *patch)\n {\n \tunsigned long offset;\n+\tstruct parse_git_header_state parse_hdr_state;\n \n \t/* A git diff has explicit new/delete information, so we don't guess */\n \tpatch->is_new = 0;\n@@ -1343,10 +1350,14 @@ static int parse_git_header(struct apply_state *state,\n \tline += len;\n \tsize -= len;\n \tstate->linenr++;\n+\tparse_hdr_state.root = &state->root;\n+\tparse_hdr_state.linenr = state->linenr;\n+\tparse_hdr_state.p_value = state->p_value;\n+\n \tfor (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) {\n \t\tstatic const struct opentry {\n \t\t\tconst char *str;\n-\t\t\tint (*fn)(struct apply_state *, const char *, struct patch *);\n+\t\t\tint (*fn)(struct parse_git_header_state *, const char *, struct patch *);\n \t\t} optable[] = {\n \t\t\t{ \"@@ -\", gitdiff_hdrend },\n \t\t\t{ \"--- \", gitdiff_oldname },\n@@ -1377,7 +1388,7 @@ static int parse_git_header(struct apply_state *state,\n \t\t\tint res;\n \t\t\tif (len < oplen || memcmp(p->str, line, oplen))\n \t\t\t\tcontinue;\n-\t\t\tres = p->fn(state, line + oplen, patch);\n+\t\t\tres = p->fn(&parse_hdr_state, line + oplen, patch);\n \t\t\tif (res < 0)\n \t\t\t\treturn -1;\n \t\t\tif (check_header_line(state->linenr, patch))\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378689","messageId":"20190708163315.29912-8-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 07/14] apply: make parse_git_header public","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:08Z","receivedAt":"2019-07-08T16:38:05Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Make parse_git_header a \"public\" function in apply.h, so we can re-use\nit in range-diff in a subsequent commit.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 69 ++++++++++++++++-----------------------------------------\n apply.h | 48 +++++++++++++++++++++++++++++++++++++++\n 2 files changed, 67 insertions(+), 50 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 468f1d3fee..32b5b072ee 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -207,40 +207,6 @@ struct fragment {\n #define BINARY_DELTA_DEFLATED\t1\n #define BINARY_LITERAL_DEFLATED 2\n \n-/*\n- * This represents a \"patch\" to a file, both metainfo changes\n- * such as creation/deletion, filemode and content changes represented\n- * as a series of fragments.\n- */\n-struct patch {\n-\tchar *new_name, *old_name, *def_name;\n-\tunsigned int old_mode, new_mode;\n-\tint is_new, is_delete;\t/* -1 = unknown, 0 = false, 1 = true */\n-\tint rejected;\n-\tunsigned ws_rule;\n-\tint lines_added, lines_deleted;\n-\tint score;\n-\tint extension_linenr; /* first line specifying delete/new/rename/copy */\n-\tunsigned int is_toplevel_relative:1;\n-\tunsigned int inaccurate_eof:1;\n-\tunsigned int is_binary:1;\n-\tunsigned int is_copy:1;\n-\tunsigned int is_rename:1;\n-\tunsigned int recount:1;\n-\tunsigned int conflicted_threeway:1;\n-\tunsigned int direct_to_threeway:1;\n-\tunsigned int crlf_in_old:1;\n-\tstruct fragment *fragments;\n-\tchar *result;\n-\tsize_t resultsize;\n-\tchar old_oid_prefix[GIT_MAX_HEXSZ + 1];\n-\tchar new_oid_prefix[GIT_MAX_HEXSZ + 1];\n-\tstruct patch *next;\n-\n-\t/* three-way fallback result */\n-\tstruct object_id threeway_stage[3];\n-};\n-\n static void free_fragment_list(struct fragment *list)\n {\n \twhile (list) {\n@@ -1320,12 +1286,13 @@ static int check_header_line(int linenr, struct patch *patch)\n \treturn 0;\n }\n \n-/* Verify that we recognize the lines following a git header */\n-static int parse_git_header(struct apply_state *state,\n-\t\t\t    const char *line,\n-\t\t\t    int len,\n-\t\t\t    unsigned int size,\n-\t\t\t    struct patch *patch)\n+int parse_git_diff_header(struct strbuf *root,\n+\t\t\t  int *linenr,\n+\t\t\t  int p_value,\n+\t\t\t  const char *line,\n+\t\t\t  int len,\n+\t\t\t  unsigned int size,\n+\t\t\t  struct patch *patch)\n {\n \tunsigned long offset;\n \tstruct parse_git_header_state parse_hdr_state;\n@@ -1340,21 +1307,21 @@ static int parse_git_header(struct apply_state *state,\n \t * or removing or adding empty files), so we get\n \t * the default name from the header.\n \t */\n-\tpatch->def_name = git_header_name(state->p_value, line, len);\n-\tif (patch->def_name && state->root.len) {\n-\t\tchar *s = xstrfmt(\"%s%s\", state->root.buf, patch->def_name);\n+\tpatch->def_name = git_header_name(p_value, line, len);\n+\tif (patch->def_name && root->len) {\n+\t\tchar *s = xstrfmt(\"%s%s\", root->buf, patch->def_name);\n \t\tfree(patch->def_name);\n \t\tpatch->def_name = s;\n \t}\n \n \tline += len;\n \tsize -= len;\n-\tstate->linenr++;\n-\tparse_hdr_state.root = &state->root;\n-\tparse_hdr_state.linenr = state->linenr;\n-\tparse_hdr_state.p_value = state->p_value;\n+\t(*linenr)++;\n+\tparse_hdr_state.root = root;\n+\tparse_hdr_state.linenr = *linenr;\n+\tparse_hdr_state.p_value = p_value;\n \n-\tfor (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) {\n+\tfor (offset = len ; size > 0 ; offset += len, size -= len, line += len, (*linenr)++) {\n \t\tstatic const struct opentry {\n \t\t\tconst char *str;\n \t\t\tint (*fn)(struct parse_git_header_state *, const char *, struct patch *);\n@@ -1391,7 +1358,7 @@ static int parse_git_header(struct apply_state *state,\n \t\t\tres = p->fn(&parse_hdr_state, line + oplen, patch);\n \t\t\tif (res < 0)\n \t\t\t\treturn -1;\n-\t\t\tif (check_header_line(state->linenr, patch))\n+\t\t\tif (check_header_line(*linenr, patch))\n \t\t\t\treturn -1;\n \t\t\tif (res > 0)\n \t\t\t\treturn offset;\n@@ -1572,7 +1539,9 @@ static int find_header(struct apply_state *state,\n \t\t * or mode change, so we handle that specially\n \t\t */\n \t\tif (!memcmp(\"diff --git \", line, 11)) {\n-\t\t\tint git_hdr_len = parse_git_header(state, line, len, size, patch);\n+\t\t\tint git_hdr_len = parse_git_diff_header(&state->root, &state->linenr,\n+\t\t\t\t\t\t\t\tstate->p_value, line, len,\n+\t\t\t\t\t\t\t\tsize, patch);\n \t\t\tif (git_hdr_len < 0)\n \t\t\t\treturn -128;\n \t\t\tif (git_hdr_len <= len)\ndiff --git a/apply.h b/apply.h\nindex 5948348133..ade50f66c5 100644\n--- a/apply.h\n+++ b/apply.h\n@@ -117,6 +117,40 @@ struct apply_state {\n \tint applied_after_fixing_ws;\n };\n \n+/*\n+ * This represents a \"patch\" to a file, both metainfo changes\n+ * such as creation/deletion, filemode and content changes represented\n+ * as a series of fragments.\n+ */\n+struct patch {\n+\tchar *new_name, *old_name, *def_name;\n+\tunsigned int old_mode, new_mode;\n+\tint is_new, is_delete;\t/* -1 = unknown, 0 = false, 1 = true */\n+\tint rejected;\n+\tunsigned ws_rule;\n+\tint lines_added, lines_deleted;\n+\tint score;\n+\tint extension_linenr; /* first line specifying delete/new/rename/copy */\n+\tunsigned int is_toplevel_relative:1;\n+\tunsigned int inaccurate_eof:1;\n+\tunsigned int is_binary:1;\n+\tunsigned int is_copy:1;\n+\tunsigned int is_rename:1;\n+\tunsigned int recount:1;\n+\tunsigned int conflicted_threeway:1;\n+\tunsigned int direct_to_threeway:1;\n+\tunsigned int crlf_in_old:1;\n+\tstruct fragment *fragments;\n+\tchar *result;\n+\tsize_t resultsize;\n+\tchar old_oid_prefix[GIT_MAX_HEXSZ + 1];\n+\tchar new_oid_prefix[GIT_MAX_HEXSZ + 1];\n+\tstruct patch *next;\n+\n+\t/* three-way fallback result */\n+\tstruct object_id threeway_stage[3];\n+};\n+\n int apply_parse_options(int argc, const char **argv,\n \t\t\tstruct apply_state *state,\n \t\t\tint *force_apply, int *options,\n@@ -127,6 +161,20 @@ int init_apply_state(struct apply_state *state,\n void clear_apply_state(struct apply_state *state);\n int check_apply_state(struct apply_state *state, int force_apply);\n \n+/*\n+ * Parse a get header, starting at line.  Fills the relevant metadata\n+ * information in 'struct patch'.\n+ *\n+ * Returns -1 on failure, the length of the parsed header otherwise.\n+ */\n+int parse_git_diff_header(struct strbuf *root,\n+\t\t\t  int *linenr,\n+\t\t\t  int p_value,\n+\t\t\t  const char *line,\n+\t\t\t  int len,\n+\t\t\t  unsigned int size,\n+\t\t\t  struct patch *patch);\n+\n /*\n  * Some aspects of the apply behavior are controlled by the following\n  * bits in the \"options\" parameter passed to apply_all_patches().\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378684","messageId":"20190708163315.29912-10-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 09/14] range-diff: split lines manually","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:10Z","receivedAt":"2019-07-08T16:38:08Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently range-diff uses the 'strbuf_getline()' function for doing\nits line by line processing.  In a future patch we want to do parts of\nthat parsing using the 'parse_git_diff_header()' function.  That\nfunction does its own line by line reading of the input, and doesn't\nuse strbufs.  This doesn't match with how we do the line-by-line\nprocessing in range-diff currently.\n\nSwitch range-diff to do our own line by line parsing, so we can re-use\nthe 'parse_git_diff_header()' function later.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c | 68 ++++++++++++++++++++++++++++++++--------------------\n 1 file changed, 42 insertions(+), 26 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 9242b8975f..784fac301b 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -24,6 +24,17 @@ struct patch_util {\n \tstruct object_id oid;\n };\n \n+static size_t find_end_of_line(char *buffer, unsigned long size)\n+{\n+\tchar *eol = memchr(buffer, '\\n', size);\n+\n+\tif (!eol)\n+\t\treturn size;\n+\n+\t*eol = '\\0';\n+\treturn eol + 1 - buffer;\n+}\n+\n /*\n  * Reads the patches into a string list, with the `util` field being populated\n  * as struct object_id (will need to be free()d).\n@@ -31,10 +42,12 @@ struct patch_util {\n static int read_patches(const char *range, struct string_list *list)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tFILE *in;\n-\tstruct strbuf buf = STRBUF_INIT, line = STRBUF_INIT;\n+\tstruct strbuf buf = STRBUF_INIT, contents = STRBUF_INIT;\n \tstruct patch_util *util = NULL;\n \tint in_header = 1;\n+\tchar *line;\n+\tint offset, len;\n+\tsize_t size;\n \n \targv_array_pushl(&cp.args, \"log\", \"--no-color\", \"-p\", \"--no-merges\",\n \t\t\t\"--reverse\", \"--date-order\", \"--decorate=no\",\n@@ -54,17 +67,20 @@ static int read_patches(const char *range, struct string_list *list)\n \n \tif (start_command(&cp))\n \t\treturn error_errno(_(\"could not start `log`\"));\n-\tin = fdopen(cp.out, \"r\");\n-\tif (!in) {\n+\tif (strbuf_read(&contents, cp.out, 0) < 0) {\n \t\terror_errno(_(\"could not read `log` output\"));\n \t\tfinish_command(&cp);\n \t\treturn -1;\n \t}\n \n-\twhile (strbuf_getline(&line, in) != EOF) {\n+\tline = contents.buf;\n+\tsize = contents.len;\n+\tfor (offset = 0; size > 0; offset += len, size -= len, line += len) {\n \t\tconst char *p;\n \n-\t\tif (skip_prefix(line.buf, \"commit \", &p)) {\n+\t\tlen = find_end_of_line(line, size);\n+\t\tline[len - 1] = '\\0';\n+\t\tif (skip_prefix(line, \"commit \", &p)) {\n \t\t\tif (util) {\n \t\t\t\tstring_list_append(list, buf.buf)->util = util;\n \t\t\t\tstrbuf_reset(&buf);\n@@ -75,8 +91,7 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\t\tfree(util);\n \t\t\t\tstring_list_clear(list, 1);\n \t\t\t\tstrbuf_release(&buf);\n-\t\t\t\tstrbuf_release(&line);\n-\t\t\t\tfclose(in);\n+\t\t\t\tstrbuf_release(&contents);\n \t\t\t\tfinish_command(&cp);\n \t\t\t\treturn -1;\n \t\t\t}\n@@ -85,26 +100,28 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\tcontinue;\n \t\t}\n \n-\t\tif (starts_with(line.buf, \"diff --git\")) {\n+\t\tif (starts_with(line, \"diff --git\")) {\n \t\t\tin_header = 0;\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\t\tif (!util->diff_offset)\n \t\t\t\tutil->diff_offset = buf.len;\n \t\t\tstrbuf_addch(&buf, ' ');\n-\t\t\tstrbuf_addbuf(&buf, &line);\n+\t\t\tstrbuf_addstr(&buf, line);\n \t\t} else if (in_header) {\n-\t\t\tif (starts_with(line.buf, \"Author: \")) {\n-\t\t\t\tstrbuf_addbuf(&buf, &line);\n+\t\t\tif (starts_with(line, \"Author: \")) {\n+\t\t\t\tstrbuf_addstr(&buf, line);\n \t\t\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n-\t\t\t} else if (starts_with(line.buf, \"    \")) {\n-\t\t\t\tstrbuf_rtrim(&line);\n-\t\t\t\tstrbuf_addbuf(&buf, &line);\n+\t\t\t} else if (starts_with(line, \"    \")) {\n+\t\t\t\tp = line + len - 2;\n+\t\t\t\twhile (isspace(*p) && p >= line)\n+\t\t\t\t\tp--;\n+\t\t\t\tstrbuf_add(&buf, line, p - line + 1);\n \t\t\t\tstrbuf_addch(&buf, '\\n');\n \t\t\t}\n \t\t\tcontinue;\n-\t\t} else if (starts_with(line.buf, \"@@ \"))\n+\t\t} else if (starts_with(line, \"@@ \"))\n \t\t\tstrbuf_addstr(&buf, \"@@\");\n-\t\telse if (!line.buf[0] || starts_with(line.buf, \"index \"))\n+\t\telse if (!line[0] || starts_with(line, \"index \"))\n \t\t\t/*\n \t\t\t * A completely blank (not ' \\n', which is context)\n \t\t\t * line is not valid in a diff.  We skip it\n@@ -117,25 +134,24 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\t * we are not interested.\n \t\t\t */\n \t\t\tcontinue;\n-\t\telse if (line.buf[0] == '>') {\n+\t\telse if (line[0] == '>') {\n \t\t\tstrbuf_addch(&buf, '+');\n-\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n-\t\t} else if (line.buf[0] == '<') {\n+\t\t\tstrbuf_addstr(&buf, line + 1);\n+\t\t} else if (line[0] == '<') {\n \t\t\tstrbuf_addch(&buf, '-');\n-\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n-\t\t} else if (line.buf[0] == '#') {\n+\t\t\tstrbuf_addstr(&buf, line + 1);\n+\t\t} else if (line[0] == '#') {\n \t\t\tstrbuf_addch(&buf, ' ');\n-\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n+\t\t\tstrbuf_addstr(&buf, line + 1);\n \t\t} else {\n \t\t\tstrbuf_addch(&buf, ' ');\n-\t\t\tstrbuf_addbuf(&buf, &line);\n+\t\t\tstrbuf_addstr(&buf, line);\n \t\t}\n \n \t\tstrbuf_addch(&buf, '\\n');\n \t\tutil->diffsize++;\n \t}\n-\tfclose(in);\n-\tstrbuf_release(&line);\n+\tstrbuf_release(&contents);\n \n \tif (util)\n \t\tstring_list_append(list, buf.buf)->util = util;\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378687","messageId":"20190708163315.29912-9-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 08/14] range-diff: fix function parameter indentation","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:09Z","receivedAt":"2019-07-08T16:38:08Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Fix the indentation of the function parameters for a couple of\nfunctions, to match the style in the rest of the file.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 48b0e1b4ce..9242b8975f 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -148,7 +148,7 @@ static int read_patches(const char *range, struct string_list *list)\n }\n \n static int patch_util_cmp(const void *dummy, const struct patch_util *a,\n-\t\t     const struct patch_util *b, const char *keydata)\n+\t\t\t  const struct patch_util *b, const char *keydata)\n {\n \treturn strcmp(a->diff, keydata ? keydata : b->diff);\n }\n@@ -373,7 +373,7 @@ static struct diff_filespec *get_filespec(const char *name, const char *p)\n }\n \n static void patch_diff(const char *a, const char *b,\n-\t\t\t      struct diff_options *diffopt)\n+\t\t       struct diff_options *diffopt)\n {\n \tdiff_queue(&diff_queued_diff,\n \t\t   get_filespec(\"a\", a), get_filespec(\"b\", b));\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378690","messageId":"20190708163315.29912-11-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 10/14] range-diff: don't remove funcname from inner diff","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:11Z","receivedAt":"2019-07-08T16:38:10Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"When postprocessing the inner diff in range-diff, we currently replace\nthe whole hunk header line with just \"@@\".  This matches how 'git\ntbdiff' used to handle hunk headers as well.\n\nMost likely this is being done because line numbers in the hunk header\nare not relevant without other changes.  They can for example easily\nchange if a range is rebased, and lines are added/removed before a\nchange that we actually care about in our ranges.\n\nHowever it can still be useful to have the function name that 'git\ndiff' extracts as additional context for the change.\n\nNote that it is not guaranteed that the hunk header actually shows up\nin the range-diff, and this change only aims to improve the case where\na hunk header would already be included in the final output.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c          | 7 ++++---\n t/t3206-range-diff.sh | 6 +++---\n 2 files changed, 7 insertions(+), 6 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 784fac301b..a5202d8b6c 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -119,9 +119,10 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\t\tstrbuf_addch(&buf, '\\n');\n \t\t\t}\n \t\t\tcontinue;\n-\t\t} else if (starts_with(line, \"@@ \"))\n-\t\t\tstrbuf_addstr(&buf, \"@@\");\n-\t\telse if (!line[0] || starts_with(line, \"index \"))\n+\t\t} else if (skip_prefix(line, \"@@ \", &p)) {\n+\t\t\tp = strstr(p, \"@@\");\n+\t\t\tstrbuf_addstr(&buf, p ? p : \"@@\");\n+\t\t} else if (!line[0] || starts_with(line, \"index \"))\n \t\t\t/*\n \t\t\t * A completely blank (not ' \\n', which is context)\n \t\t\t * line is not valid in a diff.  We skip it\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex 048feaf6dd..aebd4e3693 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -110,7 +110,7 @@ test_expect_success 'changed commit' '\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n \t    @@ -8,7 +8,7 @@\n-\t     @@\n+\t     @@ A\n \t      9\n \t      10\n \t    - B\n@@ -169,7 +169,7 @@ test_expect_success 'changed commit with sm config' '\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n \t    @@ -8,7 +8,7 @@\n-\t     @@\n+\t     @@ A\n \t      9\n \t      10\n \t    - B\n@@ -231,7 +231,7 @@ test_expect_success 'dual-coloring' '\n \t:      14<RESET>\n \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n \t:    <REVERSE><CYAN>@@ -8,7 +8,7 @@<RESET>\n-\t:    <CYAN> @@<RESET>\n+\t:    <CYAN> @@ A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n \t:    <REVERSE><RED>-<RESET><FAINT> BB<RESET>\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378691","messageId":"20190708163315.29912-12-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 11/14] range-diff: suppress line count in outer diff","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:12Z","receivedAt":"2019-07-08T16:38:12Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"The line count in the outer diff's hunk headers of a range diff is not\nall that interesting.  It merely shows how far along the inner diff\nare on both sides.  That number is of no use for human readers, and\nrange-diffs are not meant to be machine readable.\n\nIn a subsequent commit we're going to add some more contextual\ninformation such as the filename corresponding to the diff to the hunk\nheaders.  Remove the unnecessary information, and just keep the \"@@\"\nto indicate that a new hunk of the outer diff is starting.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n diff.c                |  5 ++++-\n diff.h                |  1 +\n range-diff.c          |  1 +\n t/t3206-range-diff.sh | 16 ++++++++--------\n 4 files changed, 14 insertions(+), 9 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex ec5c095199..9c28ff0a92 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1672,7 +1672,10 @@ static void emit_hunk_header(struct emit_callback *ecbdata,\n \tif (ecbdata->opt->flags.dual_color_diffed_diffs)\n \t\tstrbuf_addstr(&msgbuf, reverse);\n \tstrbuf_addstr(&msgbuf, frag);\n-\tstrbuf_add(&msgbuf, line, ep - line);\n+\tif (ecbdata->opt->flags.suppress_hunk_header_line_count)\n+\t\tstrbuf_add(&msgbuf, atat, sizeof(atat));\n+\telse\n+\t\tstrbuf_add(&msgbuf, line, ep - line);\n \tstrbuf_addstr(&msgbuf, reset);\n \n \t/*\ndiff --git a/diff.h b/diff.h\nindex c9db9825bb..49913049f9 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -98,6 +98,7 @@ struct diff_flags {\n \tunsigned stat_with_summary;\n \tunsigned suppress_diff_headers;\n \tunsigned dual_color_diffed_diffs;\n+\tunsigned suppress_hunk_header_line_count;\n };\n \n static inline void diff_flags_or(struct diff_flags *a,\ndiff --git a/range-diff.c b/range-diff.c\nindex a5202d8b6c..f4a90b33b8 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -486,6 +486,7 @@ int show_range_diff(const char *range1, const char *range2,\n \t\t\topts.output_format = DIFF_FORMAT_PATCH;\n \t\topts.flags.suppress_diff_headers = 1;\n \t\topts.flags.dual_color_diffed_diffs = dual_color;\n+\t\topts.flags.suppress_hunk_header_line_count = 1;\n \t\topts.output_prefix = output_prefix_cb;\n \t\tstrbuf_addstr(&indent, \"    \");\n \t\topts.output_prefix_data = &indent;\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex aebd4e3693..9f89af7178 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -99,7 +99,7 @@ test_expect_success 'changed commit' '\n \t1:  4de457d = 1:  a4b3333 s/5/A/\n \t2:  fccce22 = 2:  f51d370 s/4/A/\n \t3:  147e64e ! 3:  0559556 s/11/B/\n-\t    @@ -10,7 +10,7 @@\n+\t    @@\n \t      9\n \t      10\n \t     -11\n@@ -109,7 +109,7 @@ test_expect_success 'changed commit' '\n \t      13\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n-\t    @@ -8,7 +8,7 @@\n+\t    @@\n \t     @@ A\n \t      9\n \t      10\n@@ -158,7 +158,7 @@ test_expect_success 'changed commit with sm config' '\n \t1:  4de457d = 1:  a4b3333 s/5/A/\n \t2:  fccce22 = 2:  f51d370 s/4/A/\n \t3:  147e64e ! 3:  0559556 s/11/B/\n-\t    @@ -10,7 +10,7 @@\n+\t    @@\n \t      9\n \t      10\n \t     -11\n@@ -168,7 +168,7 @@ test_expect_success 'changed commit with sm config' '\n \t      13\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n-\t    @@ -8,7 +8,7 @@\n+\t    @@\n \t     @@ A\n \t      9\n \t      10\n@@ -191,7 +191,7 @@ test_expect_success 'changed message' '\n \tsed s/Z/\\ /g >expected <<-EOF &&\n \t1:  4de457d = 1:  f686024 s/5/A/\n \t2:  fccce22 ! 2:  4ab067d s/4/A/\n-\t    @@ -2,6 +2,8 @@\n+\t    @@\n \t    Z\n \t    Z    s/4/A/\n \t    Z\n@@ -210,7 +210,7 @@ test_expect_success 'dual-coloring' '\n \tsed -e \"s|^:||\" >expect <<-\\EOF &&\n \t:<YELLOW>1:  a4b3333 = 1:  f686024 s/5/A/<RESET>\n \t:<RED>2:  f51d370 <RESET><YELLOW>!<RESET><GREEN> 2:  4ab067d<RESET><YELLOW> s/4/A/<RESET>\n-\t:    <REVERSE><CYAN>@@ -2,6 +2,8 @@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET>\n \t:     <RESET>\n \t:         s/4/A/<RESET>\n \t:     <RESET>\n@@ -220,7 +220,7 @@ test_expect_success 'dual-coloring' '\n \t:      --- a/file<RESET>\n \t:      +++ b/file<RESET>\n \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n-\t:    <REVERSE><CYAN>@@ -10,7 +10,7 @@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n \t:    <RED> -11<RESET>\n@@ -230,7 +230,7 @@ test_expect_success 'dual-coloring' '\n \t:      13<RESET>\n \t:      14<RESET>\n \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n-\t:    <REVERSE><CYAN>@@ -8,7 +8,7 @@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET>\n \t:    <CYAN> @@ A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378692","messageId":"20190708163315.29912-13-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 12/14] range-diff: add section header instead of diff header","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:13Z","receivedAt":"2019-07-08T16:38:14Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently range-diff keeps the diff header of the inner diff\nintact (apart from stripping lines starting with index).  This diff\nheader is somewhat useful, especially when files get different\nnames in different ranges.\n\nHowever there is no real need to keep the whole diff header for that.\nThe main reason we currently do that is probably because it is easy to\ndo.\n\nIntroduce a new range diff hunk header, that's enclosed by \"##\",\nsimilar to how line numbers in diff hunks are enclosed by \"@@\", and\ngive human readable information of what exactly happened to the file,\nincluding the file name.\n\nThis improves the readability of the range-diff by giving more concise\ninformation to the users.  For example if a file was renamed in one\niteration, but not in another, the diff of the headers would be quite\nnoisy.  However the diff of a single line is concise and should be\neasier to understand.\n\nAdditionally, this allows us to add these range diff section headers to\nthe outer diffs hunk headers using a custom userdiff pattern, which\nshould help making the range-diff more readable.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c           | 34 ++++++++++++----\n t/t3206-range-diff.sh  | 91 +++++++++++++++++++++++++++++++++++++++---\n t/t3206/history.export | 84 ++++++++++++++++++++++++++++++++++++--\n 3 files changed, 192 insertions(+), 17 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex f4a90b33b8..5f64380fe4 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -10,6 +10,7 @@\n #include \"commit.h\"\n #include \"pretty.h\"\n #include \"userdiff.h\"\n+#include \"apply.h\"\n \n struct patch_util {\n \t/* For the search for an exact match */\n@@ -101,12 +102,35 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t}\n \n \t\tif (starts_with(line, \"diff --git\")) {\n+\t\t\tstruct patch patch = { 0 };\n+\t\t\tstruct strbuf root = STRBUF_INIT;\n+\t\t\tint linenr = 0;\n+\n \t\t\tin_header = 0;\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\t\tif (!util->diff_offset)\n \t\t\t\tutil->diff_offset = buf.len;\n-\t\t\tstrbuf_addch(&buf, ' ');\n-\t\t\tstrbuf_addstr(&buf, line);\n+\t\t\tline[len - 1] = '\\n';\n+\t\t\tlen = parse_git_diff_header(&root, &linenr, 1, line,\n+\t\t\t\t\t\t    len, size, &patch);\n+\t\t\tif (len < 0)\n+\t\t\t\tdie(_(\"could not parse git header '%.*s'\"), (int)len, line);\n+\t\t\tstrbuf_addstr(&buf, \" ## \");\n+\t\t\tif (patch.is_new > 0)\n+\t\t\t\tstrbuf_addf(&buf, \"%s (new)\", patch.new_name);\n+\t\t\telse if (patch.is_delete > 0)\n+\t\t\t\tstrbuf_addf(&buf, \"%s (deleted)\", patch.old_name);\n+\t\t\telse if (patch.is_rename)\n+\t\t\t\tstrbuf_addf(&buf, \"%s => %s\", patch.old_name, patch.new_name);\n+\t\t\telse\n+\t\t\t\tstrbuf_addstr(&buf, patch.new_name);\n+\n+\t\t\tif (patch.new_mode && patch.old_mode &&\n+\t\t\t    patch.old_mode != patch.new_mode)\n+\t\t\t\tstrbuf_addf(&buf, \" (mode change %06o => %06o)\",\n+\t\t\t\t\t    patch.old_mode, patch.new_mode);\n+\n+\t\t\tstrbuf_addstr(&buf, \" ##\");\n \t\t} else if (in_header) {\n \t\t\tif (starts_with(line, \"Author: \")) {\n \t\t\t\tstrbuf_addstr(&buf, line);\n@@ -122,17 +146,13 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t} else if (skip_prefix(line, \"@@ \", &p)) {\n \t\t\tp = strstr(p, \"@@\");\n \t\t\tstrbuf_addstr(&buf, p ? p : \"@@\");\n-\t\t} else if (!line[0] || starts_with(line, \"index \"))\n+\t\t} else if (!line[0])\n \t\t\t/*\n \t\t\t * A completely blank (not ' \\n', which is context)\n \t\t\t * line is not valid in a diff.  We skip it\n \t\t\t * silently, because this neatly handles the blank\n \t\t\t * separator line between commits in git-log\n \t\t\t * output.\n-\t\t\t *\n-\t\t\t * We also want to ignore the diff's `index` lines\n-\t\t\t * because they contain exact blob hashes in which\n-\t\t\t * we are not interested.\n \t\t\t */\n \t\t\tcontinue;\n \t\telse if (line[0] == '>') {\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex 9f89af7178..c277756057 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -181,6 +181,85 @@ test_expect_success 'changed commit with sm config' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'renamed file' '\n+\tgit range-diff --no-color --submodule=log topic...renamed-file >actual &&\n+\tsed s/Z/\\ /g >expected <<-EOF &&\n+\t1:  4de457d = 1:  f258d75 s/5/A/\n+\t2:  fccce22 ! 2:  017b62d s/4/A/\n+\t    @@\n+\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n+\t    Z\n+\t    -    s/4/A/\n+\t    +    s/4/A/ + rename file\n+\t    Z\n+\t    - ## file ##\n+\t    + ## file => renamed-file ##\n+\t    Z@@\n+\t    Z 1\n+\t    Z 2\n+\t3:  147e64e ! 3:  3ce7af6 s/11/B/\n+\t    @@\n+\t    Z\n+\t    Z    s/11/B/\n+\t    Z\n+\t    - ## file ##\n+\t    + ## renamed-file ##\n+\t    Z@@ A\n+\t    Z 8\n+\t    Z 9\n+\t4:  a63e992 ! 4:  1e6226b s/12/B/\n+\t    @@\n+\t    Z\n+\t    Z    s/12/B/\n+\t    Z\n+\t    - ## file ##\n+\t    + ## renamed-file ##\n+\t    Z@@ A\n+\t    Z 9\n+\t    Z 10\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'file added and later removed' '\n+\tgit range-diff --no-color --submodule=log topic...added-removed >actual &&\n+\tsed s/Z/\\ /g >expected <<-EOF &&\n+\t1:  4de457d = 1:  096b1ba s/5/A/\n+\t2:  fccce22 ! 2:  d92e698 s/4/A/\n+\t    @@\n+\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n+\t    Z\n+\t    -    s/4/A/\n+\t    +    s/4/A/ + new-file\n+\t    Z\n+\t    Z ## file ##\n+\t    Z@@\n+\t    @@\n+\t    Z A\n+\t    Z 6\n+\t    Z 7\n+\t    +\n+\t    + ## new-file (new) ##\n+\t3:  147e64e ! 3:  9a1db4d s/11/B/\n+\t    @@\n+\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n+\t    Z\n+\t    -    s/11/B/\n+\t    +    s/11/B/ + remove file\n+\t    Z\n+\t    Z ## file ##\n+\t    Z@@ A\n+\t    @@\n+\t    Z 12\n+\t    Z 13\n+\t    Z 14\n+\t    +\n+\t    + ## new-file (deleted) ##\n+\t4:  a63e992 = 4:  fea3b5c s/12/B/\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'no commits on one side' '\n \tgit commit --amend -m \"new message\" &&\n \tgit range-diff master HEAD@{1} HEAD\n@@ -197,9 +276,9 @@ test_expect_success 'changed message' '\n \t    Z\n \t    +    Also a silly comment here!\n \t    +\n-\t    Z diff --git a/file b/file\n-\t    Z --- a/file\n-\t    Z +++ b/file\n+\t    Z ## file ##\n+\t    Z@@\n+\t    Z 1\n \t3:  147e64e = 3:  b9cb956 s/11/B/\n \t4:  a63e992 = 4:  8add5f1 s/12/B/\n \tEOF\n@@ -216,9 +295,9 @@ test_expect_success 'dual-coloring' '\n \t:     <RESET>\n \t:    <REVERSE><GREEN>+<RESET><BOLD>    Also a silly comment here!<RESET>\n \t:    <REVERSE><GREEN>+<RESET>\n-\t:      diff --git a/file b/file<RESET>\n-\t:      --- a/file<RESET>\n-\t:      +++ b/file<RESET>\n+\t:      ## file ##<RESET>\n+\t:    <CYAN> @@<RESET>\n+\t:      1<RESET>\n \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n \t:    <REVERSE><CYAN>@@<RESET>\n \t:      9<RESET>\ndiff --git a/t/t3206/history.export b/t/t3206/history.export\nindex b8ffff0940..7bb3814962 100644\n--- a/t/t3206/history.export\n+++ b/t/t3206/history.export\n@@ -22,8 +22,8 @@ data 51\n 19\n 20\n \n-reset refs/heads/removed\n-commit refs/heads/removed\n+reset refs/heads/renamed-file\n+commit refs/heads/renamed-file\n mark :2\n author Thomas Rast <trast@inf.ethz.ch> 1374424921 +0200\n committer Thomas Rast <trast@inf.ethz.ch> 1374484724 +0200\n@@ -599,6 +599,82 @@ s/12/B/\n from :46\n M 100644 :28 file\n \n-reset refs/heads/removed\n-from :47\n+commit refs/heads/added-removed\n+mark :48\n+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574151 +0100\n+data 7\n+s/5/A/\n+from :2\n+M 100644 :3 file\n+\n+blob\n+mark :49\n+data 0\n+\n+commit refs/heads/added-removed\n+mark :50\n+author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n+data 18\n+s/4/A/ + new-file\n+from :48\n+M 100644 :5 file\n+M 100644 :49 new-file\n+\n+commit refs/heads/added-removed\n+mark :51\n+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n+data 22\n+s/11/B/ + remove file\n+from :50\n+M 100644 :7 file\n+D new-file\n+\n+commit refs/heads/added-removed\n+mark :52\n+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n+data 8\n+s/12/B/\n+from :51\n+M 100644 :9 file\n+\n+commit refs/heads/renamed-file\n+mark :53\n+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574309 +0100\n+data 7\n+s/5/A/\n+from :2\n+M 100644 :3 file\n+\n+commit refs/heads/renamed-file\n+mark :54\n+author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574312 +0100\n+data 21\n+s/4/A/ + rename file\n+from :53\n+D file\n+M 100644 :5 renamed-file\n+\n+commit refs/heads/renamed-file\n+mark :55\n+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574319 +0100\n+data 8\n+s/11/B/\n+from :54\n+M 100644 :7 renamed-file\n+\n+commit refs/heads/renamed-file\n+mark :56\n+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574319 +0100\n+data 8\n+s/12/B/\n+from :55\n+M 100644 :9 renamed-file\n \n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378693","messageId":"20190708163315.29912-15-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 14/14] range-diff: add headers to the outer hunk header","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:15Z","receivedAt":"2019-07-08T16:38:16Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Add the section headers/hunk headers we introduced in the previous\ncommits to the outer diff's hunk headers.  This makes it easier to\nunderstand which change we are actually looking at.  For example an\nouter hunk header might now look like:\n\n    @@  Documentation/config/interactive.txt\n\nwhile previously it would have only been\n\n    @@\n\nwhich doesn't give a lot of context for the change that follows.\n\nFor completeness also add section headers for the commit metadata and\nthe commit message, although they are arguably less important.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c          |  9 ++++++---\n t/t3206-range-diff.sh | 41 ++++++++++++++++++++++-------------------\n 2 files changed, 28 insertions(+), 22 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 7a96a587f1..ba1e9a4265 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -139,8 +139,10 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\tstrbuf_addstr(&buf, \" ##\");\n \t\t} else if (in_header) {\n \t\t\tif (starts_with(line, \"Author: \")) {\n+\t\t\t\tstrbuf_addstr(&buf, \" ## Metadata ##\\n\");\n \t\t\t\tstrbuf_addstr(&buf, line);\n \t\t\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n+\t\t\t\tstrbuf_addstr(&buf, \" ## Commit message ##\\n\");\n \t\t\t} else if (starts_with(line, \"    \")) {\n \t\t\t\tp = line + len - 2;\n \t\t\t\twhile (isspace(*p) && p >= line)\n@@ -402,8 +404,9 @@ static void output_pair_header(struct diff_options *diffopt,\n \tfwrite(buf->buf, buf->len, 1, diffopt->file);\n }\n \n-static struct userdiff_driver no_func_name = {\n-\t.funcname = { \"$^\", 0 }\n+static struct userdiff_driver section_headers = {\n+\t.funcname = { \"^ ## (.*) ##$\\n\"\n+\t\t      \"^.?@@ (.*)$\", REG_EXTENDED }\n };\n \n static struct diff_filespec *get_filespec(const char *name, const char *p)\n@@ -415,7 +418,7 @@ static struct diff_filespec *get_filespec(const char *name, const char *p)\n \tspec->size = strlen(p);\n \tspec->should_munmap = 0;\n \tspec->is_stdin = 1;\n-\tspec->driver = &no_func_name;\n+\tspec->driver = &section_headers;\n \n \treturn spec;\n }\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex d4de270979..ec548654ce 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -99,7 +99,7 @@ test_expect_success 'changed commit' '\n \t1:  4de457d = 1:  a4b3333 s/5/A/\n \t2:  fccce22 = 2:  f51d370 s/4/A/\n \t3:  147e64e ! 3:  0559556 s/11/B/\n-\t    @@\n+\t    @@ file: A\n \t      9\n \t      10\n \t     -11\n@@ -109,7 +109,7 @@ test_expect_success 'changed commit' '\n \t      13\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n-\t    @@\n+\t    @@ file\n \t     @@ file: A\n \t      9\n \t      10\n@@ -158,7 +158,7 @@ test_expect_success 'changed commit with sm config' '\n \t1:  4de457d = 1:  a4b3333 s/5/A/\n \t2:  fccce22 = 2:  f51d370 s/4/A/\n \t3:  147e64e ! 3:  0559556 s/11/B/\n-\t    @@\n+\t    @@ file: A\n \t      9\n \t      10\n \t     -11\n@@ -168,7 +168,7 @@ test_expect_success 'changed commit with sm config' '\n \t      13\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n-\t    @@\n+\t    @@ file\n \t     @@ file: A\n \t      9\n \t      10\n@@ -186,9 +186,10 @@ test_expect_success 'renamed file' '\n \tsed s/Z/\\ /g >expected <<-EOF &&\n \t1:  4de457d = 1:  f258d75 s/5/A/\n \t2:  fccce22 ! 2:  017b62d s/4/A/\n-\t    @@\n+\t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n+\t    Z ## Commit message ##\n \t    -    s/4/A/\n \t    +    s/4/A/ + rename file\n \t    Z\n@@ -198,8 +199,8 @@ test_expect_success 'renamed file' '\n \t    Z 1\n \t    Z 2\n \t3:  147e64e ! 3:  3ce7af6 s/11/B/\n-\t    @@\n-\t    Z\n+\t    @@ Metadata\n+\t    Z ## Commit message ##\n \t    Z    s/11/B/\n \t    Z\n \t    - ## file ##\n@@ -210,8 +211,8 @@ test_expect_success 'renamed file' '\n \t    Z 9\n \t    Z 10\n \t4:  a63e992 ! 4:  1e6226b s/12/B/\n-\t    @@\n-\t    Z\n+\t    @@ Metadata\n+\t    Z ## Commit message ##\n \t    Z    s/12/B/\n \t    Z\n \t    - ## file ##\n@@ -230,30 +231,32 @@ test_expect_success 'file added and later removed' '\n \tsed s/Z/\\ /g >expected <<-EOF &&\n \t1:  4de457d = 1:  096b1ba s/5/A/\n \t2:  fccce22 ! 2:  d92e698 s/4/A/\n-\t    @@\n+\t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n+\t    Z ## Commit message ##\n \t    -    s/4/A/\n \t    +    s/4/A/ + new-file\n \t    Z\n \t    Z ## file ##\n \t    Z@@\n-\t    @@\n+\t    @@ file\n \t    Z A\n \t    Z 6\n \t    Z 7\n \t    +\n \t    + ## new-file (new) ##\n \t3:  147e64e ! 3:  9a1db4d s/11/B/\n-\t    @@\n+\t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n+\t    Z ## Commit message ##\n \t    -    s/11/B/\n \t    +    s/11/B/ + remove file\n \t    Z\n \t    Z ## file ##\n \t    Z@@ file: A\n-\t    @@\n+\t    @@ file: A\n \t    Z 12\n \t    Z 13\n \t    Z 14\n@@ -274,8 +277,8 @@ test_expect_success 'changed message' '\n \tsed s/Z/\\ /g >expected <<-EOF &&\n \t1:  4de457d = 1:  f686024 s/5/A/\n \t2:  fccce22 ! 2:  4ab067d s/4/A/\n-\t    @@\n-\t    Z\n+\t    @@ Metadata\n+\t    Z ## Commit message ##\n \t    Z    s/4/A/\n \t    Z\n \t    +    Also a silly comment here!\n@@ -293,8 +296,8 @@ test_expect_success 'dual-coloring' '\n \tsed -e \"s|^:||\" >expect <<-\\EOF &&\n \t:<YELLOW>1:  a4b3333 = 1:  f686024 s/5/A/<RESET>\n \t:<RED>2:  f51d370 <RESET><YELLOW>!<RESET><GREEN> 2:  4ab067d<RESET><YELLOW> s/4/A/<RESET>\n-\t:    <REVERSE><CYAN>@@<RESET>\n-\t:     <RESET>\n+\t:    <REVERSE><CYAN>@@<RESET> <RESET>Metadata<RESET>\n+\t:      ## Commit message ##<RESET>\n \t:         s/4/A/<RESET>\n \t:     <RESET>\n \t:    <REVERSE><GREEN>+<RESET><BOLD>    Also a silly comment here!<RESET>\n@@ -303,7 +306,7 @@ test_expect_success 'dual-coloring' '\n \t:    <CYAN> @@<RESET>\n \t:      1<RESET>\n \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n-\t:    <REVERSE><CYAN>@@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET> <RESET>file: A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n \t:    <RED> -11<RESET>\n@@ -313,7 +316,7 @@ test_expect_success 'dual-coloring' '\n \t:      13<RESET>\n \t:      14<RESET>\n \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n-\t:    <REVERSE><CYAN>@@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET> <RESET>file<RESET>\n \t:    <CYAN> @@ file: A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378694","messageId":"20190708163315.29912-14-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v3 13/14] range-diff: add filename to inner diff","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-08T16:33:14Z","receivedAt":"2019-07-08T16:38:17Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"In a range-diff it's not always clear which file a certain funcname of\nthe inner diff belongs to, because the diff header (or section header\nas added in a previous commit) is not always visible in the\nrange-diff.\n\nAdd the filename to the inner diffs header, so it's always visible to\nusers.\n\nThis also allows us to add the filename + the funcname to the outer\ndiffs hunk headers using a custom userdiff pattern, which will be done\nin the next commit.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c          | 15 +++++++++++++--\n t/t3206-range-diff.sh | 16 ++++++++++------\n 2 files changed, 23 insertions(+), 8 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 5f64380fe4..7a96a587f1 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -46,7 +46,7 @@ static int read_patches(const char *range, struct string_list *list)\n \tstruct strbuf buf = STRBUF_INIT, contents = STRBUF_INIT;\n \tstruct patch_util *util = NULL;\n \tint in_header = 1;\n-\tchar *line;\n+\tchar *line, *current_filename = NULL;\n \tint offset, len;\n \tsize_t size;\n \n@@ -125,6 +125,12 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\telse\n \t\t\t\tstrbuf_addstr(&buf, patch.new_name);\n \n+\t\t\tfree(current_filename);\n+\t\t\tif (patch.is_delete > 0)\n+\t\t\t\tcurrent_filename = xstrdup(patch.old_name);\n+\t\t\telse\n+\t\t\t\tcurrent_filename = xstrdup(patch.new_name);\n+\n \t\t\tif (patch.new_mode && patch.old_mode &&\n \t\t\t    patch.old_mode != patch.new_mode)\n \t\t\t\tstrbuf_addf(&buf, \" (mode change %06o => %06o)\",\n@@ -145,7 +151,11 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\tcontinue;\n \t\t} else if (skip_prefix(line, \"@@ \", &p)) {\n \t\t\tp = strstr(p, \"@@\");\n-\t\t\tstrbuf_addstr(&buf, p ? p : \"@@\");\n+\t\t\tstrbuf_addstr(&buf, \"@@\");\n+\t\t\tif (current_filename && p[2])\n+\t\t\t\tstrbuf_addf(&buf, \" %s:\", current_filename);\n+\t\t\tif (p)\n+\t\t\t\tstrbuf_addstr(&buf, p + 2);\n \t\t} else if (!line[0])\n \t\t\t/*\n \t\t\t * A completely blank (not ' \\n', which is context)\n@@ -177,6 +187,7 @@ static int read_patches(const char *range, struct string_list *list)\n \tif (util)\n \t\tstring_list_append(list, buf.buf)->util = util;\n \tstrbuf_release(&buf);\n+\tfree(current_filename);\n \n \tif (finish_command(&cp))\n \t\treturn -1;\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex c277756057..d4de270979 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -110,7 +110,7 @@ test_expect_success 'changed commit' '\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n \t    @@\n-\t     @@ A\n+\t     @@ file: A\n \t      9\n \t      10\n \t    - B\n@@ -169,7 +169,7 @@ test_expect_success 'changed commit with sm config' '\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n \t    @@\n-\t     @@ A\n+\t     @@ file: A\n \t      9\n \t      10\n \t    - B\n@@ -203,20 +203,24 @@ test_expect_success 'renamed file' '\n \t    Z    s/11/B/\n \t    Z\n \t    - ## file ##\n+\t    -@@ file: A\n \t    + ## renamed-file ##\n-\t    Z@@ A\n+\t    +@@ renamed-file: A\n \t    Z 8\n \t    Z 9\n+\t    Z 10\n \t4:  a63e992 ! 4:  1e6226b s/12/B/\n \t    @@\n \t    Z\n \t    Z    s/12/B/\n \t    Z\n \t    - ## file ##\n+\t    -@@ file: A\n \t    + ## renamed-file ##\n-\t    Z@@ A\n+\t    +@@ renamed-file: A\n \t    Z 9\n \t    Z 10\n+\t    Z B\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -248,7 +252,7 @@ test_expect_success 'file added and later removed' '\n \t    +    s/11/B/ + remove file\n \t    Z\n \t    Z ## file ##\n-\t    Z@@ A\n+\t    Z@@ file: A\n \t    @@\n \t    Z 12\n \t    Z 13\n@@ -310,7 +314,7 @@ test_expect_success 'dual-coloring' '\n \t:      14<RESET>\n \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n \t:    <REVERSE><CYAN>@@<RESET>\n-\t:    <CYAN> @@ A<RESET>\n+\t:    <CYAN> @@ file: A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n \t:    <REVERSE><RED>-<RESET><FAINT> BB<RESET>\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378731","messageId":"xmqqimsbuhdy.fsf@gitster-ct.c.googlers.com","threadId":"51436","inReplyTo":"20190708163315.29912-8-t.gummerer@gmail.com","subject":"Re: [PATCH v3 07/14] apply: make parse_git_header public","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-07-09T19:39:37Z","receivedAt":"2019-07-09T19:39:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Gummerer <t.gummerer@gmail.com> writes:\n\n> Make parse_git_header a \"public\" function in apply.h, so we can re-use\n> it in range-diff in a subsequent commit.\n>\n> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n> ---\n\nThanks for these refactoring patches on \"apply\" machinery in the\nearly part of the series.  I noticed two small things, though.\n\n - The apply_state instance *does* represent a state and various\n   fields get updated as we read and process the patch.  The smaller\n   structure you invented, on the other hand, does not carry any\n   \"state\" at all.  Even its \"linenr\" field does not get incremented\n   as we read/process---you create a new copy to take a snapshot of\n   the current state from apply_state.  parse_git_header_data may\n   have been a name that reflects the nature of the structure\n   better.\n\n - I wonder if it makes the concept clearer if you did not create a\n   new instance outside the apply_state, but instead replaced the\n   three fields in the apply_state with an instance of this new\n   structure.  When you call an API function with shrunk interface,\n   you'd pass a pointer to a field inside the apply_state instance,\n   instead of copying three fields manually.\n\nBut other than that, I think these patches are generally moving bits\nin the right direction.\n\nI do not have strong opinions on the later part of the series on\nrange-diff proper.\n\nThanks.\n"},{"id":"378737","messageId":"20190709212359.GD15477@hank.intra.tgummerer.com","threadId":"51436","inReplyTo":"xmqqimsbuhdy.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v3 07/14] apply: make parse_git_header public","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-09T21:23:59Z","receivedAt":"2019-07-09T21:24:04Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 07/09, Junio C Hamano wrote:\n> Thomas Gummerer <t.gummerer@gmail.com> writes:\n> \n> > Make parse_git_header a \"public\" function in apply.h, so we can re-use\n> > it in range-diff in a subsequent commit.\n\nEek, I just noticed that I forgot updating the name here.  This and\nthe Subject should say 'parse_git_diff_header()' now, instead of\nparse_git_header of course.  Will fix that in the reroll.\n\n> > Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n> > ---\n> \n> Thanks for these refactoring patches on \"apply\" machinery in the\n> early part of the series.  I noticed two small things, though.\n> \n>  - The apply_state instance *does* represent a state and various\n>    fields get updated as we read and process the patch.  The smaller\n>    structure you invented, on the other hand, does not carry any\n>    \"state\" at all.  Even its \"linenr\" field does not get incremented\n>    as we read/process---you create a new copy to take a snapshot of\n>    the current state from apply_state.  parse_git_header_data may\n>    have been a name that reflects the nature of the structure\n>    better.\n\nYeah, I think that's better.  Will change, thanks!\n\nMaybe it would be even better to name it 'struct gitdiff_data', as\nit's really only used for those few functions?\n\n>  - I wonder if it makes the concept clearer if you did not create a\n>    new instance outside the apply_state, but instead replaced the\n>    three fields in the apply_state with an instance of this new\n>    structure.  When you call an API function with shrunk interface,\n>    you'd pass a pointer to a field inside the apply_state instance,\n>    instead of copying three fields manually.\n\nI had considered that.  However I struggled to come up with a name\nthat makes sense in both as an interface to 'parse_git_diff_header()',\nand inside 'struct apply_state'.  'linenr' is not specific to parsing\ngit diff headers (or even parsing any type of diff header), but is\nused all over the apply code.  So 'parse_git_header_data' doesn't make\nsense as a name anymore (and gets complicated to explain to the\nreaders of the code I think).\n\nAt that point the name should also be <something>_state again, because\nwe do update the linenr inside 'parse_git_diff_header()', just not\ninside any of the 'gitdiff_*' functions, though that is only a minor\npoint.\n\nSo unless there's a good name for this struct that I couldn't think\nof, I think it's better to pass in the variables separately to\n'parse_git_diff_header()', and then pass the struct just to the\n'gitdiff_*' functions, as it's done currently.\n\n> But other than that, I think these patches are generally moving bits\n> in the right direction.\n\nThanks for the review!\n\n> I do not have strong opinions on the later part of the series on\n> range-diff proper.\n> \n> Thanks.\n"},{"id":"378748","messageId":"xmqqa7dmvlmn.fsf@gitster-ct.c.googlers.com","threadId":"51436","inReplyTo":"20190709212359.GD15477@hank.intra.tgummerer.com","subject":"Re: [PATCH v3 07/14] apply: make parse_git_header public","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-07-09T23:22:40Z","receivedAt":"2019-07-09T23:22:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Gummerer <t.gummerer@gmail.com> writes:\n\n> Maybe it would be even better to name it 'struct gitdiff_data', as\n> it's really only used for those few functions?\n\nIs it really the case where \"these three are only used by the\ncodepath you made public\"?  If so, I agree that \"gitdiff_data\" is a\nperfectly good name for it.\n\nI however had an impression that it is the oppposite, i.e. \"the\ncodepath you made public only needs these three, but these three are\nused by other (still private) parts, too.\"  If this is the case,\nthen \"gitdiff_data\" is a misnomer, if we were to embed an instance\ninside apply_state.\n\nIt seems that it is not a good idea to do such embedding, and if\nthat is the case, \"gitdiff_data\" is a fine for the three-field\nstruct.\n\nThanks.\n\n"},{"id":"378758","messageId":"20190710084852.GE15477@hank.intra.tgummerer.com","threadId":"51436","inReplyTo":"xmqqa7dmvlmn.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v3 07/14] apply: make parse_git_header public","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-10T08:48:52Z","receivedAt":"2019-07-10T08:48:58Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 07/09, Junio C Hamano wrote:\n> Thomas Gummerer <t.gummerer@gmail.com> writes:\n> \n> > Maybe it would be even better to name it 'struct gitdiff_data', as\n> > it's really only used for those few functions?\n> \n> Is it really the case where \"these three are only used by the\n> codepath you made public\"?  If so, I agree that \"gitdiff_data\" is a\n> perfectly good name for it.\n> \n> I however had an impression that it is the oppposite, i.e. \"the\n> codepath you made public only needs these three, but these three are\n> used by other (still private) parts, too.\"  If this is the case,\n> then \"gitdiff_data\" is a misnomer, if we were to embed an instance\n> inside apply_state.\n\nYeah, that's correct.  What I meant was that since we're only using\nthis struct for the private 'gitdiff_*()' functions, which are called\nfrom 'parse_git_diff_header()', 'struct gitdiff_data' would be a\nbetter name than 'struct parse_git_diff_header_data'.\n\nI do agree that it wouldn't be a good name if we were to embed it\ninside 'struct apply_state', and as mentioned in the previous email\nI'd have a hard time coming up with a good name if we were to do that.\n\n> It seems that it is not a good idea to do such embedding, and if\n> that is the case, \"gitdiff_data\" is a fine for the three-field\n> struct.\n\nYeah, I think that's the best way forward, thanks.\n\n> Thanks.\n"},{"id":"378812","messageId":"20190711160851.14380-1-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190708163315.29912-1-t.gummerer@gmail.com","subject":"[PATCH v4 00/14] output improvements for git range-diff","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:37Z","receivedAt":"2019-07-11T16:09:04Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Thanks Junio for the comment on the previous round [1].  This round\nreanmes the struct we're using in apply.c to 'struct gitdiff_data',\nand updates the commit message of 7/14 to reflect the new name of the\nrenamed function.\n\n[1]: https://public-inbox.org/git/20190708163315.29912-1-t.gummerer@gmail.com/\n\nThomas Gummerer (14):\n  apply: replace marc.info link with public-inbox\n  apply: only pass required data to skip_tree_prefix\n  apply: only pass required data to git_header_name\n  apply: only pass required data to check_header_line\n  apply: only pass required data to find_name_*\n  apply: only pass required data to gitdiff_* functions\n  apply: make parse_git_diff_header public\n  range-diff: fix function parameter indentation\n  range-diff: split lines manually\n  range-diff: don't remove funcname from inner diff\n  range-diff: suppress line count in outer diff\n  range-diff: add section header instead of diff header\n  range-diff: add filename to inner diff\n  range-diff: add headers to the outer hunk header\n\n apply.c                | 186 ++++++++++++++++++-----------------------\n apply.h                |  48 +++++++++++\n diff.c                 |   5 +-\n diff.h                 |   1 +\n range-diff.c           | 124 +++++++++++++++++++--------\n t/t3206-range-diff.sh  | 124 ++++++++++++++++++++++-----\n t/t3206/history.export |  84 ++++++++++++++++++-\n 7 files changed, 409 insertions(+), 163 deletions(-)\n\nRange-diff against v3:\n 1:  ef2245edda =  1:  ef2245edda apply: replace marc.info link with public-inbox\n 2:  94578fa45c =  2:  94578fa45c apply: only pass required data to skip_tree_prefix\n 3:  988269a68e =  3:  988269a68e apply: only pass required data to git_header_name\n 4:  a2c1ef3f5f =  4:  a2c1ef3f5f apply: only pass required data to check_header_line\n 5:  0f4cfe21cb =  5:  0f4cfe21cb apply: only pass required data to find_name_*\n 6:  07a271518d !  6:  42665e5295 apply: only pass required data to gitdiff_* functions\n    @@ -28,7 +28,7 @@\n      #include \"rerere.h\"\n      #include \"apply.h\"\n      \n    -+struct parse_git_header_state {\n    ++struct gitdiff_data {\n     +\tstruct strbuf *root;\n     +\tint linenr;\n     +\tint p_value;\n    @@ -42,7 +42,7 @@\n      }\n      \n     -static int gitdiff_hdrend(struct apply_state *state,\n    -+static int gitdiff_hdrend(struct parse_git_header_state *state,\n    ++static int gitdiff_hdrend(struct gitdiff_data *state,\n      \t\t\t  const char *line,\n      \t\t\t  struct patch *patch)\n      {\n    @@ -51,7 +51,7 @@\n      #define DIFF_NEW_NAME 1\n      \n     -static int gitdiff_verify_name(struct apply_state *state,\n    -+static int gitdiff_verify_name(struct parse_git_header_state *state,\n    ++static int gitdiff_verify_name(struct gitdiff_data *state,\n      \t\t\t       const char *line,\n      \t\t\t       int isnull,\n      \t\t\t       char **name,\n    @@ -77,7 +77,7 @@\n      }\n      \n     -static int gitdiff_oldname(struct apply_state *state,\n    -+static int gitdiff_oldname(struct parse_git_header_state *state,\n    ++static int gitdiff_oldname(struct gitdiff_data *state,\n      \t\t\t   const char *line,\n      \t\t\t   struct patch *patch)\n      {\n    @@ -86,7 +86,7 @@\n      }\n      \n     -static int gitdiff_newname(struct apply_state *state,\n    -+static int gitdiff_newname(struct parse_git_header_state *state,\n    ++static int gitdiff_newname(struct gitdiff_data *state,\n      \t\t\t   const char *line,\n      \t\t\t   struct patch *patch)\n      {\n    @@ -95,7 +95,7 @@\n      }\n      \n     -static int gitdiff_oldmode(struct apply_state *state,\n    -+static int gitdiff_oldmode(struct parse_git_header_state *state,\n    ++static int gitdiff_oldmode(struct gitdiff_data *state,\n      \t\t\t   const char *line,\n      \t\t\t   struct patch *patch)\n      {\n    @@ -103,7 +103,7 @@\n      }\n      \n     -static int gitdiff_newmode(struct apply_state *state,\n    -+static int gitdiff_newmode(struct parse_git_header_state *state,\n    ++static int gitdiff_newmode(struct gitdiff_data *state,\n      \t\t\t   const char *line,\n      \t\t\t   struct patch *patch)\n      {\n    @@ -111,7 +111,7 @@\n      }\n      \n     -static int gitdiff_delete(struct apply_state *state,\n    -+static int gitdiff_delete(struct parse_git_header_state *state,\n    ++static int gitdiff_delete(struct gitdiff_data *state,\n      \t\t\t  const char *line,\n      \t\t\t  struct patch *patch)\n      {\n    @@ -120,7 +120,7 @@\n      }\n      \n     -static int gitdiff_newfile(struct apply_state *state,\n    -+static int gitdiff_newfile(struct parse_git_header_state *state,\n    ++static int gitdiff_newfile(struct gitdiff_data *state,\n      \t\t\t   const char *line,\n      \t\t\t   struct patch *patch)\n      {\n    @@ -129,7 +129,7 @@\n      }\n      \n     -static int gitdiff_copysrc(struct apply_state *state,\n    -+static int gitdiff_copysrc(struct parse_git_header_state *state,\n    ++static int gitdiff_copysrc(struct gitdiff_data *state,\n      \t\t\t   const char *line,\n      \t\t\t   struct patch *patch)\n      {\n    @@ -141,7 +141,7 @@\n      }\n      \n     -static int gitdiff_copydst(struct apply_state *state,\n    -+static int gitdiff_copydst(struct parse_git_header_state *state,\n    ++static int gitdiff_copydst(struct gitdiff_data *state,\n      \t\t\t   const char *line,\n      \t\t\t   struct patch *patch)\n      {\n    @@ -153,7 +153,7 @@\n      }\n      \n     -static int gitdiff_renamesrc(struct apply_state *state,\n    -+static int gitdiff_renamesrc(struct parse_git_header_state *state,\n    ++static int gitdiff_renamesrc(struct gitdiff_data *state,\n      \t\t\t     const char *line,\n      \t\t\t     struct patch *patch)\n      {\n    @@ -165,7 +165,7 @@\n      }\n      \n     -static int gitdiff_renamedst(struct apply_state *state,\n    -+static int gitdiff_renamedst(struct parse_git_header_state *state,\n    ++static int gitdiff_renamedst(struct gitdiff_data *state,\n      \t\t\t     const char *line,\n      \t\t\t     struct patch *patch)\n      {\n    @@ -177,7 +177,7 @@\n      }\n      \n     -static int gitdiff_similarity(struct apply_state *state,\n    -+static int gitdiff_similarity(struct parse_git_header_state *state,\n    ++static int gitdiff_similarity(struct gitdiff_data *state,\n      \t\t\t      const char *line,\n      \t\t\t      struct patch *patch)\n      {\n    @@ -186,7 +186,7 @@\n      }\n      \n     -static int gitdiff_dissimilarity(struct apply_state *state,\n    -+static int gitdiff_dissimilarity(struct parse_git_header_state *state,\n    ++static int gitdiff_dissimilarity(struct gitdiff_data *state,\n      \t\t\t\t const char *line,\n      \t\t\t\t struct patch *patch)\n      {\n    @@ -195,7 +195,7 @@\n      }\n      \n     -static int gitdiff_index(struct apply_state *state,\n    -+static int gitdiff_index(struct parse_git_header_state *state,\n    ++static int gitdiff_index(struct gitdiff_data *state,\n      \t\t\t const char *line,\n      \t\t\t struct patch *patch)\n      {\n    @@ -204,7 +204,7 @@\n       * into the next diff. Tell the parser to break out.\n       */\n     -static int gitdiff_unrecognized(struct apply_state *state,\n    -+static int gitdiff_unrecognized(struct parse_git_header_state *state,\n    ++static int gitdiff_unrecognized(struct gitdiff_data *state,\n      \t\t\t\tconst char *line,\n      \t\t\t\tstruct patch *patch)\n      {\n    @@ -212,7 +212,7 @@\n      \t\t\t    struct patch *patch)\n      {\n      \tunsigned long offset;\n    -+\tstruct parse_git_header_state parse_hdr_state;\n    ++\tstruct gitdiff_data parse_hdr_state;\n      \n      \t/* A git diff has explicit new/delete information, so we don't guess */\n      \tpatch->is_new = 0;\n    @@ -228,7 +228,7 @@\n      \t\tstatic const struct opentry {\n      \t\t\tconst char *str;\n     -\t\t\tint (*fn)(struct apply_state *, const char *, struct patch *);\n    -+\t\t\tint (*fn)(struct parse_git_header_state *, const char *, struct patch *);\n    ++\t\t\tint (*fn)(struct gitdiff_data *, const char *, struct patch *);\n      \t\t} optable[] = {\n      \t\t\t{ \"@@ -\", gitdiff_hdrend },\n      \t\t\t{ \"--- \", gitdiff_oldname },\n 7:  9cb6732a5f !  7:  3068fda8a9 apply: make parse_git_header public\n    @@ -1,9 +1,12 @@\n     Author: Thomas Gummerer <t.gummerer@gmail.com>\n     \n    -    apply: make parse_git_header public\n    +    apply: make parse_git_diff_header public\n     \n    -    Make parse_git_header a \"public\" function in apply.h, so we can re-use\n    -    it in range-diff in a subsequent commit.\n    +    Make 'parse_git_header()' (renamed to 'parse_git_diff_header()') a\n    +    \"public\" function in apply.h, so we can re-use it in range-diff in a\n    +    subsequent commit.  We're renaming the function to make it clearer in\n    +    other parts of the codebase that we're talking about a diff header and\n    +    not just any header.\n     \n         Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n    @@ -70,7 +73,7 @@\n     +\t\t\t  struct patch *patch)\n      {\n      \tunsigned long offset;\n    - \tstruct parse_git_header_state parse_hdr_state;\n    + \tstruct gitdiff_data parse_hdr_state;\n     @@\n      \t * or removing or adding empty files), so we get\n      \t * the default name from the header.\n    @@ -100,7 +103,7 @@\n     +\tfor (offset = len ; size > 0 ; offset += len, size -= len, line += len, (*linenr)++) {\n      \t\tstatic const struct opentry {\n      \t\t\tconst char *str;\n    - \t\t\tint (*fn)(struct parse_git_header_state *, const char *, struct patch *);\n    + \t\t\tint (*fn)(struct gitdiff_data *, const char *, struct patch *);\n     @@\n      \t\t\tres = p->fn(&parse_hdr_state, line + oplen, patch);\n      \t\t\tif (res < 0)\n 8:  76a11ce995 =  8:  781d054cab range-diff: fix function parameter indentation\n 9:  6f70e7faa6 =  9:  68a2953310 range-diff: split lines manually\n10:  6618cdff2c = 10:  8ae95d053b range-diff: don't remove funcname from inner diff\n11:  2667df4fa5 = 11:  e572510c52 range-diff: suppress line count in outer diff\n12:  47cd8c6733 = 12:  45605db760 range-diff: add section header instead of diff header\n13:  f67fd5dd9a = 13:  89a27fbeaa range-diff: add filename to inner diff\n14:  812893a5dc = 14:  8bee2c525f range-diff: add headers to the outer hunk header\n\n-- \n2.22.0.510.g264f2c817a\n"},{"id":"378813","messageId":"20190711160851.14380-2-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 01/14] apply: replace marc.info link with public-inbox","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:38Z","receivedAt":"2019-07-11T16:09:04Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"public-inbox.org links include the whole message ID by default.  This\nmeans the message can still be found even if the site goes away, which\nis not the case with the marc.info link.  Replace the marc.info link\nwith a more future proof one.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/apply.c b/apply.c\nindex f15afa9f6a..599cf8956f 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -478,7 +478,7 @@ static char *find_name_gnu(struct apply_state *state,\n \n \t/*\n \t * Proposed \"new-style\" GNU patch/diff format; see\n-\t * http://marc.info/?l=git&m=112927316408690&w=2\n+\t * https://public-inbox.org/git/7vll0wvb2a.fsf@assigned-by-dhcp.cox.net/\n \t */\n \tif (unquote_c_style(&name, line, NULL)) {\n \t\tstrbuf_release(&name);\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378814","messageId":"20190711160851.14380-3-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 02/14] apply: only pass required data to skip_tree_prefix","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:39Z","receivedAt":"2019-07-11T16:09:04Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'skip_tree_prefix()' function takes 'struct apply_state'\nas parameter, even though it only needs the p_value from that struct.\n\nThis function is in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 18 +++++++++---------\n 1 file changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 599cf8956f..fc7083fcbc 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -1137,17 +1137,17 @@ static int gitdiff_unrecognized(struct apply_state *state,\n  * Skip p_value leading components from \"line\"; as we do not accept\n  * absolute paths, return NULL in that case.\n  */\n-static const char *skip_tree_prefix(struct apply_state *state,\n+static const char *skip_tree_prefix(int p_value,\n \t\t\t\t    const char *line,\n \t\t\t\t    int llen)\n {\n \tint nslash;\n \tint i;\n \n-\tif (!state->p_value)\n+\tif (!p_value)\n \t\treturn (llen && line[0] == '/') ? NULL : line;\n \n-\tnslash = state->p_value;\n+\tnslash = p_value;\n \tfor (i = 0; i < llen; i++) {\n \t\tint ch = line[i];\n \t\tif (ch == '/' && --nslash <= 0)\n@@ -1184,7 +1184,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\tgoto free_and_fail1;\n \n \t\t/* strip the a/b prefix including trailing slash */\n-\t\tcp = skip_tree_prefix(state, first.buf, first.len);\n+\t\tcp = skip_tree_prefix(state->p_value, first.buf, first.len);\n \t\tif (!cp)\n \t\t\tgoto free_and_fail1;\n \t\tstrbuf_remove(&first, 0, cp - first.buf);\n@@ -1201,7 +1201,7 @@ static char *git_header_name(struct apply_state *state,\n \t\tif (*second == '\"') {\n \t\t\tif (unquote_c_style(&sp, second, NULL))\n \t\t\t\tgoto free_and_fail1;\n-\t\t\tcp = skip_tree_prefix(state, sp.buf, sp.len);\n+\t\t\tcp = skip_tree_prefix(state->p_value, sp.buf, sp.len);\n \t\t\tif (!cp)\n \t\t\t\tgoto free_and_fail1;\n \t\t\t/* They must match, otherwise ignore */\n@@ -1212,7 +1212,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t}\n \n \t\t/* unquoted second */\n-\t\tcp = skip_tree_prefix(state, second, line + llen - second);\n+\t\tcp = skip_tree_prefix(state->p_value, second, line + llen - second);\n \t\tif (!cp)\n \t\t\tgoto free_and_fail1;\n \t\tif (line + llen - cp != first.len ||\n@@ -1227,7 +1227,7 @@ static char *git_header_name(struct apply_state *state,\n \t}\n \n \t/* unquoted first name */\n-\tname = skip_tree_prefix(state, line, llen);\n+\tname = skip_tree_prefix(state->p_value, line, llen);\n \tif (!name)\n \t\treturn NULL;\n \n@@ -1243,7 +1243,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\tif (unquote_c_style(&sp, second, NULL))\n \t\t\t\tgoto free_and_fail2;\n \n-\t\t\tnp = skip_tree_prefix(state, sp.buf, sp.len);\n+\t\t\tnp = skip_tree_prefix(state->p_value, sp.buf, sp.len);\n \t\t\tif (!np)\n \t\t\t\tgoto free_and_fail2;\n \n@@ -1287,7 +1287,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\t */\n \t\t\tif (!name[len + 1])\n \t\t\t\treturn NULL; /* no postimage name */\n-\t\t\tsecond = skip_tree_prefix(state, name + len + 1,\n+\t\t\tsecond = skip_tree_prefix(state->p_value, name + len + 1,\n \t\t\t\t\t\t  line_len - (len + 1));\n \t\t\tif (!second)\n \t\t\t\treturn NULL;\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378815","messageId":"20190711160851.14380-5-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 04/14] apply: only pass required data to check_header_line","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:41Z","receivedAt":"2019-07-11T16:09:08Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'check_header_line()' function takes 'struct\napply_state' as parameter, even though it only needs the linenr from\nthat struct.\n\nThis function is in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex ac668e754d..1602fd5db0 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -1302,15 +1302,15 @@ static char *git_header_name(int p_value,\n \t}\n }\n \n-static int check_header_line(struct apply_state *state, struct patch *patch)\n+static int check_header_line(int linenr, struct patch *patch)\n {\n \tint extensions = (patch->is_delete == 1) + (patch->is_new == 1) +\n \t\t\t (patch->is_rename == 1) + (patch->is_copy == 1);\n \tif (extensions > 1)\n \t\treturn error(_(\"inconsistent header lines %d and %d\"),\n-\t\t\t     patch->extension_linenr, state->linenr);\n+\t\t\t     patch->extension_linenr, linenr);\n \tif (extensions && !patch->extension_linenr)\n-\t\tpatch->extension_linenr = state->linenr;\n+\t\tpatch->extension_linenr = linenr;\n \treturn 0;\n }\n \n@@ -1380,7 +1380,7 @@ static int parse_git_header(struct apply_state *state,\n \t\t\tres = p->fn(state, line + oplen, patch);\n \t\t\tif (res < 0)\n \t\t\t\treturn -1;\n-\t\t\tif (check_header_line(state, patch))\n+\t\t\tif (check_header_line(state->linenr, patch))\n \t\t\t\treturn -1;\n \t\t\tif (res > 0)\n \t\t\t\treturn offset;\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378816","messageId":"20190711160851.14380-4-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 03/14] apply: only pass required data to git_header_name","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:40Z","receivedAt":"2019-07-11T16:09:08Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'git_header_name()' function takes 'struct apply_state'\nas parameter, even though it only needs the p_value from that struct.\n\nThis function is in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex fc7083fcbc..ac668e754d 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -1164,7 +1164,7 @@ static const char *skip_tree_prefix(int p_value,\n  * creation or deletion of an empty file.  In any of these cases,\n  * both sides are the same name under a/ and b/ respectively.\n  */\n-static char *git_header_name(struct apply_state *state,\n+static char *git_header_name(int p_value,\n \t\t\t     const char *line,\n \t\t\t     int llen)\n {\n@@ -1184,7 +1184,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\tgoto free_and_fail1;\n \n \t\t/* strip the a/b prefix including trailing slash */\n-\t\tcp = skip_tree_prefix(state->p_value, first.buf, first.len);\n+\t\tcp = skip_tree_prefix(p_value, first.buf, first.len);\n \t\tif (!cp)\n \t\t\tgoto free_and_fail1;\n \t\tstrbuf_remove(&first, 0, cp - first.buf);\n@@ -1201,7 +1201,7 @@ static char *git_header_name(struct apply_state *state,\n \t\tif (*second == '\"') {\n \t\t\tif (unquote_c_style(&sp, second, NULL))\n \t\t\t\tgoto free_and_fail1;\n-\t\t\tcp = skip_tree_prefix(state->p_value, sp.buf, sp.len);\n+\t\t\tcp = skip_tree_prefix(p_value, sp.buf, sp.len);\n \t\t\tif (!cp)\n \t\t\t\tgoto free_and_fail1;\n \t\t\t/* They must match, otherwise ignore */\n@@ -1212,7 +1212,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t}\n \n \t\t/* unquoted second */\n-\t\tcp = skip_tree_prefix(state->p_value, second, line + llen - second);\n+\t\tcp = skip_tree_prefix(p_value, second, line + llen - second);\n \t\tif (!cp)\n \t\t\tgoto free_and_fail1;\n \t\tif (line + llen - cp != first.len ||\n@@ -1227,7 +1227,7 @@ static char *git_header_name(struct apply_state *state,\n \t}\n \n \t/* unquoted first name */\n-\tname = skip_tree_prefix(state->p_value, line, llen);\n+\tname = skip_tree_prefix(p_value, line, llen);\n \tif (!name)\n \t\treturn NULL;\n \n@@ -1243,7 +1243,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\tif (unquote_c_style(&sp, second, NULL))\n \t\t\t\tgoto free_and_fail2;\n \n-\t\t\tnp = skip_tree_prefix(state->p_value, sp.buf, sp.len);\n+\t\t\tnp = skip_tree_prefix(p_value, sp.buf, sp.len);\n \t\t\tif (!np)\n \t\t\t\tgoto free_and_fail2;\n \n@@ -1287,7 +1287,7 @@ static char *git_header_name(struct apply_state *state,\n \t\t\t */\n \t\t\tif (!name[len + 1])\n \t\t\t\treturn NULL; /* no postimage name */\n-\t\t\tsecond = skip_tree_prefix(state->p_value, name + len + 1,\n+\t\t\tsecond = skip_tree_prefix(p_value, name + len + 1,\n \t\t\t\t\t\t  line_len - (len + 1));\n \t\t\tif (!second)\n \t\t\t\treturn NULL;\n@@ -1333,7 +1333,7 @@ static int parse_git_header(struct apply_state *state,\n \t * or removing or adding empty files), so we get\n \t * the default name from the header.\n \t */\n-\tpatch->def_name = git_header_name(state, line, len);\n+\tpatch->def_name = git_header_name(state->p_value, line, len);\n \tif (patch->def_name && state->root.len) {\n \t\tchar *s = xstrfmt(\"%s%s\", state->root.buf, patch->def_name);\n \t\tfree(patch->def_name);\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378817","messageId":"20190711160851.14380-6-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 05/14] apply: only pass required data to find_name_*","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:42Z","receivedAt":"2019-07-11T16:09:10Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'find_name_*()' functions take 'struct apply_state' as\nparameter, even though they only need the 'root' member from that\nstruct.\n\nThese functions are in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 48 ++++++++++++++++++++++++------------------------\n 1 file changed, 24 insertions(+), 24 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 1602fd5db0..3cd4e3d3b3 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -469,7 +469,7 @@ static char *squash_slash(char *name)\n \treturn name;\n }\n \n-static char *find_name_gnu(struct apply_state *state,\n+static char *find_name_gnu(struct strbuf *root,\n \t\t\t   const char *line,\n \t\t\t   int p_value)\n {\n@@ -495,8 +495,8 @@ static char *find_name_gnu(struct apply_state *state,\n \t}\n \n \tstrbuf_remove(&name, 0, cp - name.buf);\n-\tif (state->root.len)\n-\t\tstrbuf_insert(&name, 0, state->root.buf, state->root.len);\n+\tif (root->len)\n+\t\tstrbuf_insert(&name, 0, root->buf, root->len);\n \treturn squash_slash(strbuf_detach(&name, NULL));\n }\n \n@@ -659,7 +659,7 @@ static size_t diff_timestamp_len(const char *line, size_t len)\n \treturn line + len - end;\n }\n \n-static char *find_name_common(struct apply_state *state,\n+static char *find_name_common(struct strbuf *root,\n \t\t\t      const char *line,\n \t\t\t      const char *def,\n \t\t\t      int p_value,\n@@ -702,30 +702,30 @@ static char *find_name_common(struct apply_state *state,\n \t\t\treturn squash_slash(xstrdup(def));\n \t}\n \n-\tif (state->root.len) {\n-\t\tchar *ret = xstrfmt(\"%s%.*s\", state->root.buf, len, start);\n+\tif (root->len) {\n+\t\tchar *ret = xstrfmt(\"%s%.*s\", root->buf, len, start);\n \t\treturn squash_slash(ret);\n \t}\n \n \treturn squash_slash(xmemdupz(start, len));\n }\n \n-static char *find_name(struct apply_state *state,\n+static char *find_name(struct strbuf *root,\n \t\t       const char *line,\n \t\t       char *def,\n \t\t       int p_value,\n \t\t       int terminate)\n {\n \tif (*line == '\"') {\n-\t\tchar *name = find_name_gnu(state, line, p_value);\n+\t\tchar *name = find_name_gnu(root, line, p_value);\n \t\tif (name)\n \t\t\treturn name;\n \t}\n \n-\treturn find_name_common(state, line, def, p_value, NULL, terminate);\n+\treturn find_name_common(root, line, def, p_value, NULL, terminate);\n }\n \n-static char *find_name_traditional(struct apply_state *state,\n+static char *find_name_traditional(struct strbuf *root,\n \t\t\t\t   const char *line,\n \t\t\t\t   char *def,\n \t\t\t\t   int p_value)\n@@ -734,7 +734,7 @@ static char *find_name_traditional(struct apply_state *state,\n \tsize_t date_len;\n \n \tif (*line == '\"') {\n-\t\tchar *name = find_name_gnu(state, line, p_value);\n+\t\tchar *name = find_name_gnu(root, line, p_value);\n \t\tif (name)\n \t\t\treturn name;\n \t}\n@@ -742,10 +742,10 @@ static char *find_name_traditional(struct apply_state *state,\n \tlen = strchrnul(line, '\\n') - line;\n \tdate_len = diff_timestamp_len(line, len);\n \tif (!date_len)\n-\t\treturn find_name_common(state, line, def, p_value, NULL, TERM_TAB);\n+\t\treturn find_name_common(root, line, def, p_value, NULL, TERM_TAB);\n \tlen -= date_len;\n \n-\treturn find_name_common(state, line, def, p_value, line + len, 0);\n+\treturn find_name_common(root, line, def, p_value, line + len, 0);\n }\n \n /*\n@@ -759,7 +759,7 @@ static int guess_p_value(struct apply_state *state, const char *nameline)\n \n \tif (is_dev_null(nameline))\n \t\treturn -1;\n-\tname = find_name_traditional(state, nameline, NULL, 0);\n+\tname = find_name_traditional(&state->root, nameline, NULL, 0);\n \tif (!name)\n \t\treturn -1;\n \tcp = strchr(name, '/');\n@@ -883,17 +883,17 @@ static int parse_traditional_patch(struct apply_state *state,\n \tif (is_dev_null(first)) {\n \t\tpatch->is_new = 1;\n \t\tpatch->is_delete = 0;\n-\t\tname = find_name_traditional(state, second, NULL, state->p_value);\n+\t\tname = find_name_traditional(&state->root, second, NULL, state->p_value);\n \t\tpatch->new_name = name;\n \t} else if (is_dev_null(second)) {\n \t\tpatch->is_new = 0;\n \t\tpatch->is_delete = 1;\n-\t\tname = find_name_traditional(state, first, NULL, state->p_value);\n+\t\tname = find_name_traditional(&state->root, first, NULL, state->p_value);\n \t\tpatch->old_name = name;\n \t} else {\n \t\tchar *first_name;\n-\t\tfirst_name = find_name_traditional(state, first, NULL, state->p_value);\n-\t\tname = find_name_traditional(state, second, first_name, state->p_value);\n+\t\tfirst_name = find_name_traditional(&state->root, first, NULL, state->p_value);\n+\t\tname = find_name_traditional(&state->root, second, first_name, state->p_value);\n \t\tfree(first_name);\n \t\tif (has_epoch_timestamp(first)) {\n \t\t\tpatch->is_new = 1;\n@@ -940,7 +940,7 @@ static int gitdiff_verify_name(struct apply_state *state,\n \t\t\t       int side)\n {\n \tif (!*name && !isnull) {\n-\t\t*name = find_name(state, line, NULL, state->p_value, TERM_TAB);\n+\t\t*name = find_name(&state->root, line, NULL, state->p_value, TERM_TAB);\n \t\treturn 0;\n \t}\n \n@@ -949,7 +949,7 @@ static int gitdiff_verify_name(struct apply_state *state,\n \t\tif (isnull)\n \t\t\treturn error(_(\"git apply: bad git-diff - expected /dev/null, got %s on line %d\"),\n \t\t\t\t     *name, state->linenr);\n-\t\tanother = find_name(state, line, NULL, state->p_value, TERM_TAB);\n+\t\tanother = find_name(&state->root, line, NULL, state->p_value, TERM_TAB);\n \t\tif (!another || strcmp(another, *name)) {\n \t\t\tfree(another);\n \t\t\treturn error((side == DIFF_NEW_NAME) ?\n@@ -1032,7 +1032,7 @@ static int gitdiff_copysrc(struct apply_state *state,\n {\n \tpatch->is_copy = 1;\n \tfree(patch->old_name);\n-\tpatch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->old_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n@@ -1042,7 +1042,7 @@ static int gitdiff_copydst(struct apply_state *state,\n {\n \tpatch->is_copy = 1;\n \tfree(patch->new_name);\n-\tpatch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->new_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n@@ -1052,7 +1052,7 @@ static int gitdiff_renamesrc(struct apply_state *state,\n {\n \tpatch->is_rename = 1;\n \tfree(patch->old_name);\n-\tpatch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->old_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n@@ -1062,7 +1062,7 @@ static int gitdiff_renamedst(struct apply_state *state,\n {\n \tpatch->is_rename = 1;\n \tfree(patch->new_name);\n-\tpatch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->new_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378818","messageId":"20190711160851.14380-7-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 06/14] apply: only pass required data to gitdiff_* functions","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:43Z","receivedAt":"2019-07-11T16:09:13Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently the 'gitdiff_*()' functions take 'struct apply_state' as\nparameter, even though they only needs the root, linenr and p_value\nfrom that struct.\n\nThese functions are in the callchain of 'parse_git_header()', which we\nwant to make more generally useful in a subsequent commit.  To make\nthat happen we only want to pass in the required data to\n'parse_git_header()', and not the whole 'struct apply_state', and thus\nwe want functions in the callchain of 'parse_git_header()' to only\ntake arguments they really need.\n\nAs these functions are called in a loop using their function pointers,\neach function needs to be passed all the parameters even if only one\nof the functions actually needs it.  We therefore pass this data along\nin a struct to avoid adding too many unused parameters to each\nfunction and making the code very verbose in the process.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 59 ++++++++++++++++++++++++++++++++++-----------------------\n 1 file changed, 35 insertions(+), 24 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 3cd4e3d3b3..034d134dd5 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -22,6 +22,12 @@\n #include \"rerere.h\"\n #include \"apply.h\"\n \n+struct gitdiff_data {\n+\tstruct strbuf *root;\n+\tint linenr;\n+\tint p_value;\n+};\n+\n static void git_apply_config(void)\n {\n \tgit_config_get_string_const(\"apply.whitespace\", &apply_default_whitespace);\n@@ -914,7 +920,7 @@ static int parse_traditional_patch(struct apply_state *state,\n \treturn 0;\n }\n \n-static int gitdiff_hdrend(struct apply_state *state,\n+static int gitdiff_hdrend(struct gitdiff_data *state,\n \t\t\t  const char *line,\n \t\t\t  struct patch *patch)\n {\n@@ -933,14 +939,14 @@ static int gitdiff_hdrend(struct apply_state *state,\n #define DIFF_OLD_NAME 0\n #define DIFF_NEW_NAME 1\n \n-static int gitdiff_verify_name(struct apply_state *state,\n+static int gitdiff_verify_name(struct gitdiff_data *state,\n \t\t\t       const char *line,\n \t\t\t       int isnull,\n \t\t\t       char **name,\n \t\t\t       int side)\n {\n \tif (!*name && !isnull) {\n-\t\t*name = find_name(&state->root, line, NULL, state->p_value, TERM_TAB);\n+\t\t*name = find_name(state->root, line, NULL, state->p_value, TERM_TAB);\n \t\treturn 0;\n \t}\n \n@@ -949,7 +955,7 @@ static int gitdiff_verify_name(struct apply_state *state,\n \t\tif (isnull)\n \t\t\treturn error(_(\"git apply: bad git-diff - expected /dev/null, got %s on line %d\"),\n \t\t\t\t     *name, state->linenr);\n-\t\tanother = find_name(&state->root, line, NULL, state->p_value, TERM_TAB);\n+\t\tanother = find_name(state->root, line, NULL, state->p_value, TERM_TAB);\n \t\tif (!another || strcmp(another, *name)) {\n \t\t\tfree(another);\n \t\t\treturn error((side == DIFF_NEW_NAME) ?\n@@ -965,7 +971,7 @@ static int gitdiff_verify_name(struct apply_state *state,\n \treturn 0;\n }\n \n-static int gitdiff_oldname(struct apply_state *state,\n+static int gitdiff_oldname(struct gitdiff_data *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n@@ -974,7 +980,7 @@ static int gitdiff_oldname(struct apply_state *state,\n \t\t\t\t   DIFF_OLD_NAME);\n }\n \n-static int gitdiff_newname(struct apply_state *state,\n+static int gitdiff_newname(struct gitdiff_data *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n@@ -992,21 +998,21 @@ static int parse_mode_line(const char *line, int linenr, unsigned int *mode)\n \treturn 0;\n }\n \n-static int gitdiff_oldmode(struct apply_state *state,\n+static int gitdiff_oldmode(struct gitdiff_data *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n \treturn parse_mode_line(line, state->linenr, &patch->old_mode);\n }\n \n-static int gitdiff_newmode(struct apply_state *state,\n+static int gitdiff_newmode(struct gitdiff_data *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n \treturn parse_mode_line(line, state->linenr, &patch->new_mode);\n }\n \n-static int gitdiff_delete(struct apply_state *state,\n+static int gitdiff_delete(struct gitdiff_data *state,\n \t\t\t  const char *line,\n \t\t\t  struct patch *patch)\n {\n@@ -1016,7 +1022,7 @@ static int gitdiff_delete(struct apply_state *state,\n \treturn gitdiff_oldmode(state, line, patch);\n }\n \n-static int gitdiff_newfile(struct apply_state *state,\n+static int gitdiff_newfile(struct gitdiff_data *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n@@ -1026,47 +1032,47 @@ static int gitdiff_newfile(struct apply_state *state,\n \treturn gitdiff_newmode(state, line, patch);\n }\n \n-static int gitdiff_copysrc(struct apply_state *state,\n+static int gitdiff_copysrc(struct gitdiff_data *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n \tpatch->is_copy = 1;\n \tfree(patch->old_name);\n-\tpatch->old_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->old_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-static int gitdiff_copydst(struct apply_state *state,\n+static int gitdiff_copydst(struct gitdiff_data *state,\n \t\t\t   const char *line,\n \t\t\t   struct patch *patch)\n {\n \tpatch->is_copy = 1;\n \tfree(patch->new_name);\n-\tpatch->new_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->new_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-static int gitdiff_renamesrc(struct apply_state *state,\n+static int gitdiff_renamesrc(struct gitdiff_data *state,\n \t\t\t     const char *line,\n \t\t\t     struct patch *patch)\n {\n \tpatch->is_rename = 1;\n \tfree(patch->old_name);\n-\tpatch->old_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->old_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-static int gitdiff_renamedst(struct apply_state *state,\n+static int gitdiff_renamedst(struct gitdiff_data *state,\n \t\t\t     const char *line,\n \t\t\t     struct patch *patch)\n {\n \tpatch->is_rename = 1;\n \tfree(patch->new_name);\n-\tpatch->new_name = find_name(&state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n+\tpatch->new_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);\n \treturn 0;\n }\n \n-static int gitdiff_similarity(struct apply_state *state,\n+static int gitdiff_similarity(struct gitdiff_data *state,\n \t\t\t      const char *line,\n \t\t\t      struct patch *patch)\n {\n@@ -1076,7 +1082,7 @@ static int gitdiff_similarity(struct apply_state *state,\n \treturn 0;\n }\n \n-static int gitdiff_dissimilarity(struct apply_state *state,\n+static int gitdiff_dissimilarity(struct gitdiff_data *state,\n \t\t\t\t const char *line,\n \t\t\t\t struct patch *patch)\n {\n@@ -1086,7 +1092,7 @@ static int gitdiff_dissimilarity(struct apply_state *state,\n \treturn 0;\n }\n \n-static int gitdiff_index(struct apply_state *state,\n+static int gitdiff_index(struct gitdiff_data *state,\n \t\t\t const char *line,\n \t\t\t struct patch *patch)\n {\n@@ -1126,7 +1132,7 @@ static int gitdiff_index(struct apply_state *state,\n  * This is normal for a diff that doesn't change anything: we'll fall through\n  * into the next diff. Tell the parser to break out.\n  */\n-static int gitdiff_unrecognized(struct apply_state *state,\n+static int gitdiff_unrecognized(struct gitdiff_data *state,\n \t\t\t\tconst char *line,\n \t\t\t\tstruct patch *patch)\n {\n@@ -1322,6 +1328,7 @@ static int parse_git_header(struct apply_state *state,\n \t\t\t    struct patch *patch)\n {\n \tunsigned long offset;\n+\tstruct gitdiff_data parse_hdr_state;\n \n \t/* A git diff has explicit new/delete information, so we don't guess */\n \tpatch->is_new = 0;\n@@ -1343,10 +1350,14 @@ static int parse_git_header(struct apply_state *state,\n \tline += len;\n \tsize -= len;\n \tstate->linenr++;\n+\tparse_hdr_state.root = &state->root;\n+\tparse_hdr_state.linenr = state->linenr;\n+\tparse_hdr_state.p_value = state->p_value;\n+\n \tfor (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) {\n \t\tstatic const struct opentry {\n \t\t\tconst char *str;\n-\t\t\tint (*fn)(struct apply_state *, const char *, struct patch *);\n+\t\t\tint (*fn)(struct gitdiff_data *, const char *, struct patch *);\n \t\t} optable[] = {\n \t\t\t{ \"@@ -\", gitdiff_hdrend },\n \t\t\t{ \"--- \", gitdiff_oldname },\n@@ -1377,7 +1388,7 @@ static int parse_git_header(struct apply_state *state,\n \t\t\tint res;\n \t\t\tif (len < oplen || memcmp(p->str, line, oplen))\n \t\t\t\tcontinue;\n-\t\t\tres = p->fn(state, line + oplen, patch);\n+\t\t\tres = p->fn(&parse_hdr_state, line + oplen, patch);\n \t\t\tif (res < 0)\n \t\t\t\treturn -1;\n \t\t\tif (check_header_line(state->linenr, patch))\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378819","messageId":"20190711160851.14380-9-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 08/14] range-diff: fix function parameter indentation","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:45Z","receivedAt":"2019-07-11T16:09:16Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Fix the indentation of the function parameters for a couple of\nfunctions, to match the style in the rest of the file.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 48b0e1b4ce..9242b8975f 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -148,7 +148,7 @@ static int read_patches(const char *range, struct string_list *list)\n }\n \n static int patch_util_cmp(const void *dummy, const struct patch_util *a,\n-\t\t     const struct patch_util *b, const char *keydata)\n+\t\t\t  const struct patch_util *b, const char *keydata)\n {\n \treturn strcmp(a->diff, keydata ? keydata : b->diff);\n }\n@@ -373,7 +373,7 @@ static struct diff_filespec *get_filespec(const char *name, const char *p)\n }\n \n static void patch_diff(const char *a, const char *b,\n-\t\t\t      struct diff_options *diffopt)\n+\t\t       struct diff_options *diffopt)\n {\n \tdiff_queue(&diff_queued_diff,\n \t\t   get_filespec(\"a\", a), get_filespec(\"b\", b));\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378821","messageId":"20190711160851.14380-8-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 07/14] apply: make parse_git_diff_header public","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:44Z","receivedAt":"2019-07-11T16:09:19Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Make 'parse_git_header()' (renamed to 'parse_git_diff_header()') a\n\"public\" function in apply.h, so we can re-use it in range-diff in a\nsubsequent commit.  We're renaming the function to make it clearer in\nother parts of the codebase that we're talking about a diff header and\nnot just any header.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n apply.c | 69 ++++++++++++++++-----------------------------------------\n apply.h | 48 +++++++++++++++++++++++++++++++++++++++\n 2 files changed, 67 insertions(+), 50 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 034d134dd5..d68a6acad7 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -207,40 +207,6 @@ struct fragment {\n #define BINARY_DELTA_DEFLATED\t1\n #define BINARY_LITERAL_DEFLATED 2\n \n-/*\n- * This represents a \"patch\" to a file, both metainfo changes\n- * such as creation/deletion, filemode and content changes represented\n- * as a series of fragments.\n- */\n-struct patch {\n-\tchar *new_name, *old_name, *def_name;\n-\tunsigned int old_mode, new_mode;\n-\tint is_new, is_delete;\t/* -1 = unknown, 0 = false, 1 = true */\n-\tint rejected;\n-\tunsigned ws_rule;\n-\tint lines_added, lines_deleted;\n-\tint score;\n-\tint extension_linenr; /* first line specifying delete/new/rename/copy */\n-\tunsigned int is_toplevel_relative:1;\n-\tunsigned int inaccurate_eof:1;\n-\tunsigned int is_binary:1;\n-\tunsigned int is_copy:1;\n-\tunsigned int is_rename:1;\n-\tunsigned int recount:1;\n-\tunsigned int conflicted_threeway:1;\n-\tunsigned int direct_to_threeway:1;\n-\tunsigned int crlf_in_old:1;\n-\tstruct fragment *fragments;\n-\tchar *result;\n-\tsize_t resultsize;\n-\tchar old_oid_prefix[GIT_MAX_HEXSZ + 1];\n-\tchar new_oid_prefix[GIT_MAX_HEXSZ + 1];\n-\tstruct patch *next;\n-\n-\t/* three-way fallback result */\n-\tstruct object_id threeway_stage[3];\n-};\n-\n static void free_fragment_list(struct fragment *list)\n {\n \twhile (list) {\n@@ -1320,12 +1286,13 @@ static int check_header_line(int linenr, struct patch *patch)\n \treturn 0;\n }\n \n-/* Verify that we recognize the lines following a git header */\n-static int parse_git_header(struct apply_state *state,\n-\t\t\t    const char *line,\n-\t\t\t    int len,\n-\t\t\t    unsigned int size,\n-\t\t\t    struct patch *patch)\n+int parse_git_diff_header(struct strbuf *root,\n+\t\t\t  int *linenr,\n+\t\t\t  int p_value,\n+\t\t\t  const char *line,\n+\t\t\t  int len,\n+\t\t\t  unsigned int size,\n+\t\t\t  struct patch *patch)\n {\n \tunsigned long offset;\n \tstruct gitdiff_data parse_hdr_state;\n@@ -1340,21 +1307,21 @@ static int parse_git_header(struct apply_state *state,\n \t * or removing or adding empty files), so we get\n \t * the default name from the header.\n \t */\n-\tpatch->def_name = git_header_name(state->p_value, line, len);\n-\tif (patch->def_name && state->root.len) {\n-\t\tchar *s = xstrfmt(\"%s%s\", state->root.buf, patch->def_name);\n+\tpatch->def_name = git_header_name(p_value, line, len);\n+\tif (patch->def_name && root->len) {\n+\t\tchar *s = xstrfmt(\"%s%s\", root->buf, patch->def_name);\n \t\tfree(patch->def_name);\n \t\tpatch->def_name = s;\n \t}\n \n \tline += len;\n \tsize -= len;\n-\tstate->linenr++;\n-\tparse_hdr_state.root = &state->root;\n-\tparse_hdr_state.linenr = state->linenr;\n-\tparse_hdr_state.p_value = state->p_value;\n+\t(*linenr)++;\n+\tparse_hdr_state.root = root;\n+\tparse_hdr_state.linenr = *linenr;\n+\tparse_hdr_state.p_value = p_value;\n \n-\tfor (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) {\n+\tfor (offset = len ; size > 0 ; offset += len, size -= len, line += len, (*linenr)++) {\n \t\tstatic const struct opentry {\n \t\t\tconst char *str;\n \t\t\tint (*fn)(struct gitdiff_data *, const char *, struct patch *);\n@@ -1391,7 +1358,7 @@ static int parse_git_header(struct apply_state *state,\n \t\t\tres = p->fn(&parse_hdr_state, line + oplen, patch);\n \t\t\tif (res < 0)\n \t\t\t\treturn -1;\n-\t\t\tif (check_header_line(state->linenr, patch))\n+\t\t\tif (check_header_line(*linenr, patch))\n \t\t\t\treturn -1;\n \t\t\tif (res > 0)\n \t\t\t\treturn offset;\n@@ -1572,7 +1539,9 @@ static int find_header(struct apply_state *state,\n \t\t * or mode change, so we handle that specially\n \t\t */\n \t\tif (!memcmp(\"diff --git \", line, 11)) {\n-\t\t\tint git_hdr_len = parse_git_header(state, line, len, size, patch);\n+\t\t\tint git_hdr_len = parse_git_diff_header(&state->root, &state->linenr,\n+\t\t\t\t\t\t\t\tstate->p_value, line, len,\n+\t\t\t\t\t\t\t\tsize, patch);\n \t\t\tif (git_hdr_len < 0)\n \t\t\t\treturn -128;\n \t\t\tif (git_hdr_len <= len)\ndiff --git a/apply.h b/apply.h\nindex 5948348133..a795193435 100644\n--- a/apply.h\n+++ b/apply.h\n@@ -117,6 +117,40 @@ struct apply_state {\n \tint applied_after_fixing_ws;\n };\n \n+/*\n+ * This represents a \"patch\" to a file, both metainfo changes\n+ * such as creation/deletion, filemode and content changes represented\n+ * as a series of fragments.\n+ */\n+struct patch {\n+\tchar *new_name, *old_name, *def_name;\n+\tunsigned int old_mode, new_mode;\n+\tint is_new, is_delete;\t/* -1 = unknown, 0 = false, 1 = true */\n+\tint rejected;\n+\tunsigned ws_rule;\n+\tint lines_added, lines_deleted;\n+\tint score;\n+\tint extension_linenr; /* first line specifying delete/new/rename/copy */\n+\tunsigned int is_toplevel_relative:1;\n+\tunsigned int inaccurate_eof:1;\n+\tunsigned int is_binary:1;\n+\tunsigned int is_copy:1;\n+\tunsigned int is_rename:1;\n+\tunsigned int recount:1;\n+\tunsigned int conflicted_threeway:1;\n+\tunsigned int direct_to_threeway:1;\n+\tunsigned int crlf_in_old:1;\n+\tstruct fragment *fragments;\n+\tchar *result;\n+\tsize_t resultsize;\n+\tchar old_oid_prefix[GIT_MAX_HEXSZ + 1];\n+\tchar new_oid_prefix[GIT_MAX_HEXSZ + 1];\n+\tstruct patch *next;\n+\n+\t/* three-way fallback result */\n+\tstruct object_id threeway_stage[3];\n+};\n+\n int apply_parse_options(int argc, const char **argv,\n \t\t\tstruct apply_state *state,\n \t\t\tint *force_apply, int *options,\n@@ -127,6 +161,20 @@ int init_apply_state(struct apply_state *state,\n void clear_apply_state(struct apply_state *state);\n int check_apply_state(struct apply_state *state, int force_apply);\n \n+/*\n+ * Parse a git diff header, starting at line.  Fills the relevant\n+ * metadata information in 'struct patch'.\n+ *\n+ * Returns -1 on failure, the length of the parsed header otherwise.\n+ */\n+int parse_git_diff_header(struct strbuf *root,\n+\t\t\t  int *linenr,\n+\t\t\t  int p_value,\n+\t\t\t  const char *line,\n+\t\t\t  int len,\n+\t\t\t  unsigned int size,\n+\t\t\t  struct patch *patch);\n+\n /*\n  * Some aspects of the apply behavior are controlled by the following\n  * bits in the \"options\" parameter passed to apply_all_patches().\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378820","messageId":"20190711160851.14380-10-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 09/14] range-diff: split lines manually","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:46Z","receivedAt":"2019-07-11T16:09:20Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently range-diff uses the 'strbuf_getline()' function for doing\nits line by line processing.  In a future patch we want to do parts of\nthat parsing using the 'parse_git_diff_header()' function.  That\nfunction does its own line by line reading of the input, and doesn't\nuse strbufs.  This doesn't match with how we do the line-by-line\nprocessing in range-diff currently.\n\nSwitch range-diff to do our own line by line parsing, so we can re-use\nthe 'parse_git_diff_header()' function later.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c | 68 ++++++++++++++++++++++++++++++++--------------------\n 1 file changed, 42 insertions(+), 26 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 9242b8975f..784fac301b 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -24,6 +24,17 @@ struct patch_util {\n \tstruct object_id oid;\n };\n \n+static size_t find_end_of_line(char *buffer, unsigned long size)\n+{\n+\tchar *eol = memchr(buffer, '\\n', size);\n+\n+\tif (!eol)\n+\t\treturn size;\n+\n+\t*eol = '\\0';\n+\treturn eol + 1 - buffer;\n+}\n+\n /*\n  * Reads the patches into a string list, with the `util` field being populated\n  * as struct object_id (will need to be free()d).\n@@ -31,10 +42,12 @@ struct patch_util {\n static int read_patches(const char *range, struct string_list *list)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tFILE *in;\n-\tstruct strbuf buf = STRBUF_INIT, line = STRBUF_INIT;\n+\tstruct strbuf buf = STRBUF_INIT, contents = STRBUF_INIT;\n \tstruct patch_util *util = NULL;\n \tint in_header = 1;\n+\tchar *line;\n+\tint offset, len;\n+\tsize_t size;\n \n \targv_array_pushl(&cp.args, \"log\", \"--no-color\", \"-p\", \"--no-merges\",\n \t\t\t\"--reverse\", \"--date-order\", \"--decorate=no\",\n@@ -54,17 +67,20 @@ static int read_patches(const char *range, struct string_list *list)\n \n \tif (start_command(&cp))\n \t\treturn error_errno(_(\"could not start `log`\"));\n-\tin = fdopen(cp.out, \"r\");\n-\tif (!in) {\n+\tif (strbuf_read(&contents, cp.out, 0) < 0) {\n \t\terror_errno(_(\"could not read `log` output\"));\n \t\tfinish_command(&cp);\n \t\treturn -1;\n \t}\n \n-\twhile (strbuf_getline(&line, in) != EOF) {\n+\tline = contents.buf;\n+\tsize = contents.len;\n+\tfor (offset = 0; size > 0; offset += len, size -= len, line += len) {\n \t\tconst char *p;\n \n-\t\tif (skip_prefix(line.buf, \"commit \", &p)) {\n+\t\tlen = find_end_of_line(line, size);\n+\t\tline[len - 1] = '\\0';\n+\t\tif (skip_prefix(line, \"commit \", &p)) {\n \t\t\tif (util) {\n \t\t\t\tstring_list_append(list, buf.buf)->util = util;\n \t\t\t\tstrbuf_reset(&buf);\n@@ -75,8 +91,7 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\t\tfree(util);\n \t\t\t\tstring_list_clear(list, 1);\n \t\t\t\tstrbuf_release(&buf);\n-\t\t\t\tstrbuf_release(&line);\n-\t\t\t\tfclose(in);\n+\t\t\t\tstrbuf_release(&contents);\n \t\t\t\tfinish_command(&cp);\n \t\t\t\treturn -1;\n \t\t\t}\n@@ -85,26 +100,28 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\tcontinue;\n \t\t}\n \n-\t\tif (starts_with(line.buf, \"diff --git\")) {\n+\t\tif (starts_with(line, \"diff --git\")) {\n \t\t\tin_header = 0;\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\t\tif (!util->diff_offset)\n \t\t\t\tutil->diff_offset = buf.len;\n \t\t\tstrbuf_addch(&buf, ' ');\n-\t\t\tstrbuf_addbuf(&buf, &line);\n+\t\t\tstrbuf_addstr(&buf, line);\n \t\t} else if (in_header) {\n-\t\t\tif (starts_with(line.buf, \"Author: \")) {\n-\t\t\t\tstrbuf_addbuf(&buf, &line);\n+\t\t\tif (starts_with(line, \"Author: \")) {\n+\t\t\t\tstrbuf_addstr(&buf, line);\n \t\t\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n-\t\t\t} else if (starts_with(line.buf, \"    \")) {\n-\t\t\t\tstrbuf_rtrim(&line);\n-\t\t\t\tstrbuf_addbuf(&buf, &line);\n+\t\t\t} else if (starts_with(line, \"    \")) {\n+\t\t\t\tp = line + len - 2;\n+\t\t\t\twhile (isspace(*p) && p >= line)\n+\t\t\t\t\tp--;\n+\t\t\t\tstrbuf_add(&buf, line, p - line + 1);\n \t\t\t\tstrbuf_addch(&buf, '\\n');\n \t\t\t}\n \t\t\tcontinue;\n-\t\t} else if (starts_with(line.buf, \"@@ \"))\n+\t\t} else if (starts_with(line, \"@@ \"))\n \t\t\tstrbuf_addstr(&buf, \"@@\");\n-\t\telse if (!line.buf[0] || starts_with(line.buf, \"index \"))\n+\t\telse if (!line[0] || starts_with(line, \"index \"))\n \t\t\t/*\n \t\t\t * A completely blank (not ' \\n', which is context)\n \t\t\t * line is not valid in a diff.  We skip it\n@@ -117,25 +134,24 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\t * we are not interested.\n \t\t\t */\n \t\t\tcontinue;\n-\t\telse if (line.buf[0] == '>') {\n+\t\telse if (line[0] == '>') {\n \t\t\tstrbuf_addch(&buf, '+');\n-\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n-\t\t} else if (line.buf[0] == '<') {\n+\t\t\tstrbuf_addstr(&buf, line + 1);\n+\t\t} else if (line[0] == '<') {\n \t\t\tstrbuf_addch(&buf, '-');\n-\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n-\t\t} else if (line.buf[0] == '#') {\n+\t\t\tstrbuf_addstr(&buf, line + 1);\n+\t\t} else if (line[0] == '#') {\n \t\t\tstrbuf_addch(&buf, ' ');\n-\t\t\tstrbuf_add(&buf, line.buf + 1, line.len - 1);\n+\t\t\tstrbuf_addstr(&buf, line + 1);\n \t\t} else {\n \t\t\tstrbuf_addch(&buf, ' ');\n-\t\t\tstrbuf_addbuf(&buf, &line);\n+\t\t\tstrbuf_addstr(&buf, line);\n \t\t}\n \n \t\tstrbuf_addch(&buf, '\\n');\n \t\tutil->diffsize++;\n \t}\n-\tfclose(in);\n-\tstrbuf_release(&line);\n+\tstrbuf_release(&contents);\n \n \tif (util)\n \t\tstring_list_append(list, buf.buf)->util = util;\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378822","messageId":"20190711160851.14380-11-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 10/14] range-diff: don't remove funcname from inner diff","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:47Z","receivedAt":"2019-07-11T16:09:22Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"When postprocessing the inner diff in range-diff, we currently replace\nthe whole hunk header line with just \"@@\".  This matches how 'git\ntbdiff' used to handle hunk headers as well.\n\nMost likely this is being done because line numbers in the hunk header\nare not relevant without other changes.  They can for example easily\nchange if a range is rebased, and lines are added/removed before a\nchange that we actually care about in our ranges.\n\nHowever it can still be useful to have the function name that 'git\ndiff' extracts as additional context for the change.\n\nNote that it is not guaranteed that the hunk header actually shows up\nin the range-diff, and this change only aims to improve the case where\na hunk header would already be included in the final output.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c          | 7 ++++---\n t/t3206-range-diff.sh | 6 +++---\n 2 files changed, 7 insertions(+), 6 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 784fac301b..a5202d8b6c 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -119,9 +119,10 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\t\tstrbuf_addch(&buf, '\\n');\n \t\t\t}\n \t\t\tcontinue;\n-\t\t} else if (starts_with(line, \"@@ \"))\n-\t\t\tstrbuf_addstr(&buf, \"@@\");\n-\t\telse if (!line[0] || starts_with(line, \"index \"))\n+\t\t} else if (skip_prefix(line, \"@@ \", &p)) {\n+\t\t\tp = strstr(p, \"@@\");\n+\t\t\tstrbuf_addstr(&buf, p ? p : \"@@\");\n+\t\t} else if (!line[0] || starts_with(line, \"index \"))\n \t\t\t/*\n \t\t\t * A completely blank (not ' \\n', which is context)\n \t\t\t * line is not valid in a diff.  We skip it\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex 048feaf6dd..aebd4e3693 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -110,7 +110,7 @@ test_expect_success 'changed commit' '\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n \t    @@ -8,7 +8,7 @@\n-\t     @@\n+\t     @@ A\n \t      9\n \t      10\n \t    - B\n@@ -169,7 +169,7 @@ test_expect_success 'changed commit with sm config' '\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n \t    @@ -8,7 +8,7 @@\n-\t     @@\n+\t     @@ A\n \t      9\n \t      10\n \t    - B\n@@ -231,7 +231,7 @@ test_expect_success 'dual-coloring' '\n \t:      14<RESET>\n \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n \t:    <REVERSE><CYAN>@@ -8,7 +8,7 @@<RESET>\n-\t:    <CYAN> @@<RESET>\n+\t:    <CYAN> @@ A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n \t:    <REVERSE><RED>-<RESET><FAINT> BB<RESET>\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378824","messageId":"20190711160851.14380-13-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 12/14] range-diff: add section header instead of diff header","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:49Z","receivedAt":"2019-07-11T16:09:24Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Currently range-diff keeps the diff header of the inner diff\nintact (apart from stripping lines starting with index).  This diff\nheader is somewhat useful, especially when files get different\nnames in different ranges.\n\nHowever there is no real need to keep the whole diff header for that.\nThe main reason we currently do that is probably because it is easy to\ndo.\n\nIntroduce a new range diff hunk header, that's enclosed by \"##\",\nsimilar to how line numbers in diff hunks are enclosed by \"@@\", and\ngive human readable information of what exactly happened to the file,\nincluding the file name.\n\nThis improves the readability of the range-diff by giving more concise\ninformation to the users.  For example if a file was renamed in one\niteration, but not in another, the diff of the headers would be quite\nnoisy.  However the diff of a single line is concise and should be\neasier to understand.\n\nAdditionally, this allows us to add these range diff section headers to\nthe outer diffs hunk headers using a custom userdiff pattern, which\nshould help making the range-diff more readable.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c           | 34 ++++++++++++----\n t/t3206-range-diff.sh  | 91 +++++++++++++++++++++++++++++++++++++++---\n t/t3206/history.export | 84 ++++++++++++++++++++++++++++++++++++--\n 3 files changed, 192 insertions(+), 17 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex f4a90b33b8..5f64380fe4 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -10,6 +10,7 @@\n #include \"commit.h\"\n #include \"pretty.h\"\n #include \"userdiff.h\"\n+#include \"apply.h\"\n \n struct patch_util {\n \t/* For the search for an exact match */\n@@ -101,12 +102,35 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t}\n \n \t\tif (starts_with(line, \"diff --git\")) {\n+\t\t\tstruct patch patch = { 0 };\n+\t\t\tstruct strbuf root = STRBUF_INIT;\n+\t\t\tint linenr = 0;\n+\n \t\t\tin_header = 0;\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\t\tif (!util->diff_offset)\n \t\t\t\tutil->diff_offset = buf.len;\n-\t\t\tstrbuf_addch(&buf, ' ');\n-\t\t\tstrbuf_addstr(&buf, line);\n+\t\t\tline[len - 1] = '\\n';\n+\t\t\tlen = parse_git_diff_header(&root, &linenr, 1, line,\n+\t\t\t\t\t\t    len, size, &patch);\n+\t\t\tif (len < 0)\n+\t\t\t\tdie(_(\"could not parse git header '%.*s'\"), (int)len, line);\n+\t\t\tstrbuf_addstr(&buf, \" ## \");\n+\t\t\tif (patch.is_new > 0)\n+\t\t\t\tstrbuf_addf(&buf, \"%s (new)\", patch.new_name);\n+\t\t\telse if (patch.is_delete > 0)\n+\t\t\t\tstrbuf_addf(&buf, \"%s (deleted)\", patch.old_name);\n+\t\t\telse if (patch.is_rename)\n+\t\t\t\tstrbuf_addf(&buf, \"%s => %s\", patch.old_name, patch.new_name);\n+\t\t\telse\n+\t\t\t\tstrbuf_addstr(&buf, patch.new_name);\n+\n+\t\t\tif (patch.new_mode && patch.old_mode &&\n+\t\t\t    patch.old_mode != patch.new_mode)\n+\t\t\t\tstrbuf_addf(&buf, \" (mode change %06o => %06o)\",\n+\t\t\t\t\t    patch.old_mode, patch.new_mode);\n+\n+\t\t\tstrbuf_addstr(&buf, \" ##\");\n \t\t} else if (in_header) {\n \t\t\tif (starts_with(line, \"Author: \")) {\n \t\t\t\tstrbuf_addstr(&buf, line);\n@@ -122,17 +146,13 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t} else if (skip_prefix(line, \"@@ \", &p)) {\n \t\t\tp = strstr(p, \"@@\");\n \t\t\tstrbuf_addstr(&buf, p ? p : \"@@\");\n-\t\t} else if (!line[0] || starts_with(line, \"index \"))\n+\t\t} else if (!line[0])\n \t\t\t/*\n \t\t\t * A completely blank (not ' \\n', which is context)\n \t\t\t * line is not valid in a diff.  We skip it\n \t\t\t * silently, because this neatly handles the blank\n \t\t\t * separator line between commits in git-log\n \t\t\t * output.\n-\t\t\t *\n-\t\t\t * We also want to ignore the diff's `index` lines\n-\t\t\t * because they contain exact blob hashes in which\n-\t\t\t * we are not interested.\n \t\t\t */\n \t\t\tcontinue;\n \t\telse if (line[0] == '>') {\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex 9f89af7178..c277756057 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -181,6 +181,85 @@ test_expect_success 'changed commit with sm config' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'renamed file' '\n+\tgit range-diff --no-color --submodule=log topic...renamed-file >actual &&\n+\tsed s/Z/\\ /g >expected <<-EOF &&\n+\t1:  4de457d = 1:  f258d75 s/5/A/\n+\t2:  fccce22 ! 2:  017b62d s/4/A/\n+\t    @@\n+\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n+\t    Z\n+\t    -    s/4/A/\n+\t    +    s/4/A/ + rename file\n+\t    Z\n+\t    - ## file ##\n+\t    + ## file => renamed-file ##\n+\t    Z@@\n+\t    Z 1\n+\t    Z 2\n+\t3:  147e64e ! 3:  3ce7af6 s/11/B/\n+\t    @@\n+\t    Z\n+\t    Z    s/11/B/\n+\t    Z\n+\t    - ## file ##\n+\t    + ## renamed-file ##\n+\t    Z@@ A\n+\t    Z 8\n+\t    Z 9\n+\t4:  a63e992 ! 4:  1e6226b s/12/B/\n+\t    @@\n+\t    Z\n+\t    Z    s/12/B/\n+\t    Z\n+\t    - ## file ##\n+\t    + ## renamed-file ##\n+\t    Z@@ A\n+\t    Z 9\n+\t    Z 10\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'file added and later removed' '\n+\tgit range-diff --no-color --submodule=log topic...added-removed >actual &&\n+\tsed s/Z/\\ /g >expected <<-EOF &&\n+\t1:  4de457d = 1:  096b1ba s/5/A/\n+\t2:  fccce22 ! 2:  d92e698 s/4/A/\n+\t    @@\n+\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n+\t    Z\n+\t    -    s/4/A/\n+\t    +    s/4/A/ + new-file\n+\t    Z\n+\t    Z ## file ##\n+\t    Z@@\n+\t    @@\n+\t    Z A\n+\t    Z 6\n+\t    Z 7\n+\t    +\n+\t    + ## new-file (new) ##\n+\t3:  147e64e ! 3:  9a1db4d s/11/B/\n+\t    @@\n+\t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n+\t    Z\n+\t    -    s/11/B/\n+\t    +    s/11/B/ + remove file\n+\t    Z\n+\t    Z ## file ##\n+\t    Z@@ A\n+\t    @@\n+\t    Z 12\n+\t    Z 13\n+\t    Z 14\n+\t    +\n+\t    + ## new-file (deleted) ##\n+\t4:  a63e992 = 4:  fea3b5c s/12/B/\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'no commits on one side' '\n \tgit commit --amend -m \"new message\" &&\n \tgit range-diff master HEAD@{1} HEAD\n@@ -197,9 +276,9 @@ test_expect_success 'changed message' '\n \t    Z\n \t    +    Also a silly comment here!\n \t    +\n-\t    Z diff --git a/file b/file\n-\t    Z --- a/file\n-\t    Z +++ b/file\n+\t    Z ## file ##\n+\t    Z@@\n+\t    Z 1\n \t3:  147e64e = 3:  b9cb956 s/11/B/\n \t4:  a63e992 = 4:  8add5f1 s/12/B/\n \tEOF\n@@ -216,9 +295,9 @@ test_expect_success 'dual-coloring' '\n \t:     <RESET>\n \t:    <REVERSE><GREEN>+<RESET><BOLD>    Also a silly comment here!<RESET>\n \t:    <REVERSE><GREEN>+<RESET>\n-\t:      diff --git a/file b/file<RESET>\n-\t:      --- a/file<RESET>\n-\t:      +++ b/file<RESET>\n+\t:      ## file ##<RESET>\n+\t:    <CYAN> @@<RESET>\n+\t:      1<RESET>\n \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n \t:    <REVERSE><CYAN>@@<RESET>\n \t:      9<RESET>\ndiff --git a/t/t3206/history.export b/t/t3206/history.export\nindex b8ffff0940..7bb3814962 100644\n--- a/t/t3206/history.export\n+++ b/t/t3206/history.export\n@@ -22,8 +22,8 @@ data 51\n 19\n 20\n \n-reset refs/heads/removed\n-commit refs/heads/removed\n+reset refs/heads/renamed-file\n+commit refs/heads/renamed-file\n mark :2\n author Thomas Rast <trast@inf.ethz.ch> 1374424921 +0200\n committer Thomas Rast <trast@inf.ethz.ch> 1374484724 +0200\n@@ -599,6 +599,82 @@ s/12/B/\n from :46\n M 100644 :28 file\n \n-reset refs/heads/removed\n-from :47\n+commit refs/heads/added-removed\n+mark :48\n+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574151 +0100\n+data 7\n+s/5/A/\n+from :2\n+M 100644 :3 file\n+\n+blob\n+mark :49\n+data 0\n+\n+commit refs/heads/added-removed\n+mark :50\n+author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n+data 18\n+s/4/A/ + new-file\n+from :48\n+M 100644 :5 file\n+M 100644 :49 new-file\n+\n+commit refs/heads/added-removed\n+mark :51\n+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n+data 22\n+s/11/B/ + remove file\n+from :50\n+M 100644 :7 file\n+D new-file\n+\n+commit refs/heads/added-removed\n+mark :52\n+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574177 +0100\n+data 8\n+s/12/B/\n+from :51\n+M 100644 :9 file\n+\n+commit refs/heads/renamed-file\n+mark :53\n+author Thomas Rast <trast@inf.ethz.ch> 1374485014 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574309 +0100\n+data 7\n+s/5/A/\n+from :2\n+M 100644 :3 file\n+\n+commit refs/heads/renamed-file\n+mark :54\n+author Thomas Rast <trast@inf.ethz.ch> 1374485024 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574312 +0100\n+data 21\n+s/4/A/ + rename file\n+from :53\n+D file\n+M 100644 :5 renamed-file\n+\n+commit refs/heads/renamed-file\n+mark :55\n+author Thomas Rast <trast@inf.ethz.ch> 1374485036 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574319 +0100\n+data 8\n+s/11/B/\n+from :54\n+M 100644 :7 renamed-file\n+\n+commit refs/heads/renamed-file\n+mark :56\n+author Thomas Rast <trast@inf.ethz.ch> 1374485044 +0200\n+committer Thomas Gummerer <t.gummerer@gmail.com> 1556574319 +0100\n+data 8\n+s/12/B/\n+from :55\n+M 100644 :9 renamed-file\n \n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378823","messageId":"20190711160851.14380-12-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 11/14] range-diff: suppress line count in outer diff","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:48Z","receivedAt":"2019-07-11T16:09:25Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"The line count in the outer diff's hunk headers of a range diff is not\nall that interesting.  It merely shows how far along the inner diff\nare on both sides.  That number is of no use for human readers, and\nrange-diffs are not meant to be machine readable.\n\nIn a subsequent commit we're going to add some more contextual\ninformation such as the filename corresponding to the diff to the hunk\nheaders.  Remove the unnecessary information, and just keep the \"@@\"\nto indicate that a new hunk of the outer diff is starting.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n diff.c                |  5 ++++-\n diff.h                |  1 +\n range-diff.c          |  1 +\n t/t3206-range-diff.sh | 16 ++++++++--------\n 4 files changed, 14 insertions(+), 9 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex ec5c095199..9c28ff0a92 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1672,7 +1672,10 @@ static void emit_hunk_header(struct emit_callback *ecbdata,\n \tif (ecbdata->opt->flags.dual_color_diffed_diffs)\n \t\tstrbuf_addstr(&msgbuf, reverse);\n \tstrbuf_addstr(&msgbuf, frag);\n-\tstrbuf_add(&msgbuf, line, ep - line);\n+\tif (ecbdata->opt->flags.suppress_hunk_header_line_count)\n+\t\tstrbuf_add(&msgbuf, atat, sizeof(atat));\n+\telse\n+\t\tstrbuf_add(&msgbuf, line, ep - line);\n \tstrbuf_addstr(&msgbuf, reset);\n \n \t/*\ndiff --git a/diff.h b/diff.h\nindex c9db9825bb..49913049f9 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -98,6 +98,7 @@ struct diff_flags {\n \tunsigned stat_with_summary;\n \tunsigned suppress_diff_headers;\n \tunsigned dual_color_diffed_diffs;\n+\tunsigned suppress_hunk_header_line_count;\n };\n \n static inline void diff_flags_or(struct diff_flags *a,\ndiff --git a/range-diff.c b/range-diff.c\nindex a5202d8b6c..f4a90b33b8 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -486,6 +486,7 @@ int show_range_diff(const char *range1, const char *range2,\n \t\t\topts.output_format = DIFF_FORMAT_PATCH;\n \t\topts.flags.suppress_diff_headers = 1;\n \t\topts.flags.dual_color_diffed_diffs = dual_color;\n+\t\topts.flags.suppress_hunk_header_line_count = 1;\n \t\topts.output_prefix = output_prefix_cb;\n \t\tstrbuf_addstr(&indent, \"    \");\n \t\topts.output_prefix_data = &indent;\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex aebd4e3693..9f89af7178 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -99,7 +99,7 @@ test_expect_success 'changed commit' '\n \t1:  4de457d = 1:  a4b3333 s/5/A/\n \t2:  fccce22 = 2:  f51d370 s/4/A/\n \t3:  147e64e ! 3:  0559556 s/11/B/\n-\t    @@ -10,7 +10,7 @@\n+\t    @@\n \t      9\n \t      10\n \t     -11\n@@ -109,7 +109,7 @@ test_expect_success 'changed commit' '\n \t      13\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n-\t    @@ -8,7 +8,7 @@\n+\t    @@\n \t     @@ A\n \t      9\n \t      10\n@@ -158,7 +158,7 @@ test_expect_success 'changed commit with sm config' '\n \t1:  4de457d = 1:  a4b3333 s/5/A/\n \t2:  fccce22 = 2:  f51d370 s/4/A/\n \t3:  147e64e ! 3:  0559556 s/11/B/\n-\t    @@ -10,7 +10,7 @@\n+\t    @@\n \t      9\n \t      10\n \t     -11\n@@ -168,7 +168,7 @@ test_expect_success 'changed commit with sm config' '\n \t      13\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n-\t    @@ -8,7 +8,7 @@\n+\t    @@\n \t     @@ A\n \t      9\n \t      10\n@@ -191,7 +191,7 @@ test_expect_success 'changed message' '\n \tsed s/Z/\\ /g >expected <<-EOF &&\n \t1:  4de457d = 1:  f686024 s/5/A/\n \t2:  fccce22 ! 2:  4ab067d s/4/A/\n-\t    @@ -2,6 +2,8 @@\n+\t    @@\n \t    Z\n \t    Z    s/4/A/\n \t    Z\n@@ -210,7 +210,7 @@ test_expect_success 'dual-coloring' '\n \tsed -e \"s|^:||\" >expect <<-\\EOF &&\n \t:<YELLOW>1:  a4b3333 = 1:  f686024 s/5/A/<RESET>\n \t:<RED>2:  f51d370 <RESET><YELLOW>!<RESET><GREEN> 2:  4ab067d<RESET><YELLOW> s/4/A/<RESET>\n-\t:    <REVERSE><CYAN>@@ -2,6 +2,8 @@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET>\n \t:     <RESET>\n \t:         s/4/A/<RESET>\n \t:     <RESET>\n@@ -220,7 +220,7 @@ test_expect_success 'dual-coloring' '\n \t:      --- a/file<RESET>\n \t:      +++ b/file<RESET>\n \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n-\t:    <REVERSE><CYAN>@@ -10,7 +10,7 @@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n \t:    <RED> -11<RESET>\n@@ -230,7 +230,7 @@ test_expect_success 'dual-coloring' '\n \t:      13<RESET>\n \t:      14<RESET>\n \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n-\t:    <REVERSE><CYAN>@@ -8,7 +8,7 @@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET>\n \t:    <CYAN> @@ A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378825","messageId":"20190711160851.14380-14-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 13/14] range-diff: add filename to inner diff","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:50Z","receivedAt":"2019-07-11T16:09:27Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"In a range-diff it's not always clear which file a certain funcname of\nthe inner diff belongs to, because the diff header (or section header\nas added in a previous commit) is not always visible in the\nrange-diff.\n\nAdd the filename to the inner diffs header, so it's always visible to\nusers.\n\nThis also allows us to add the filename + the funcname to the outer\ndiffs hunk headers using a custom userdiff pattern, which will be done\nin the next commit.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c          | 15 +++++++++++++--\n t/t3206-range-diff.sh | 16 ++++++++++------\n 2 files changed, 23 insertions(+), 8 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 5f64380fe4..7a96a587f1 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -46,7 +46,7 @@ static int read_patches(const char *range, struct string_list *list)\n \tstruct strbuf buf = STRBUF_INIT, contents = STRBUF_INIT;\n \tstruct patch_util *util = NULL;\n \tint in_header = 1;\n-\tchar *line;\n+\tchar *line, *current_filename = NULL;\n \tint offset, len;\n \tsize_t size;\n \n@@ -125,6 +125,12 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\telse\n \t\t\t\tstrbuf_addstr(&buf, patch.new_name);\n \n+\t\t\tfree(current_filename);\n+\t\t\tif (patch.is_delete > 0)\n+\t\t\t\tcurrent_filename = xstrdup(patch.old_name);\n+\t\t\telse\n+\t\t\t\tcurrent_filename = xstrdup(patch.new_name);\n+\n \t\t\tif (patch.new_mode && patch.old_mode &&\n \t\t\t    patch.old_mode != patch.new_mode)\n \t\t\t\tstrbuf_addf(&buf, \" (mode change %06o => %06o)\",\n@@ -145,7 +151,11 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\tcontinue;\n \t\t} else if (skip_prefix(line, \"@@ \", &p)) {\n \t\t\tp = strstr(p, \"@@\");\n-\t\t\tstrbuf_addstr(&buf, p ? p : \"@@\");\n+\t\t\tstrbuf_addstr(&buf, \"@@\");\n+\t\t\tif (current_filename && p[2])\n+\t\t\t\tstrbuf_addf(&buf, \" %s:\", current_filename);\n+\t\t\tif (p)\n+\t\t\t\tstrbuf_addstr(&buf, p + 2);\n \t\t} else if (!line[0])\n \t\t\t/*\n \t\t\t * A completely blank (not ' \\n', which is context)\n@@ -177,6 +187,7 @@ static int read_patches(const char *range, struct string_list *list)\n \tif (util)\n \t\tstring_list_append(list, buf.buf)->util = util;\n \tstrbuf_release(&buf);\n+\tfree(current_filename);\n \n \tif (finish_command(&cp))\n \t\treturn -1;\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex c277756057..d4de270979 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -110,7 +110,7 @@ test_expect_success 'changed commit' '\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n \t    @@\n-\t     @@ A\n+\t     @@ file: A\n \t      9\n \t      10\n \t    - B\n@@ -169,7 +169,7 @@ test_expect_success 'changed commit with sm config' '\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n \t    @@\n-\t     @@ A\n+\t     @@ file: A\n \t      9\n \t      10\n \t    - B\n@@ -203,20 +203,24 @@ test_expect_success 'renamed file' '\n \t    Z    s/11/B/\n \t    Z\n \t    - ## file ##\n+\t    -@@ file: A\n \t    + ## renamed-file ##\n-\t    Z@@ A\n+\t    +@@ renamed-file: A\n \t    Z 8\n \t    Z 9\n+\t    Z 10\n \t4:  a63e992 ! 4:  1e6226b s/12/B/\n \t    @@\n \t    Z\n \t    Z    s/12/B/\n \t    Z\n \t    - ## file ##\n+\t    -@@ file: A\n \t    + ## renamed-file ##\n-\t    Z@@ A\n+\t    +@@ renamed-file: A\n \t    Z 9\n \t    Z 10\n+\t    Z B\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -248,7 +252,7 @@ test_expect_success 'file added and later removed' '\n \t    +    s/11/B/ + remove file\n \t    Z\n \t    Z ## file ##\n-\t    Z@@ A\n+\t    Z@@ file: A\n \t    @@\n \t    Z 12\n \t    Z 13\n@@ -310,7 +314,7 @@ test_expect_success 'dual-coloring' '\n \t:      14<RESET>\n \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n \t:    <REVERSE><CYAN>@@<RESET>\n-\t:    <CYAN> @@ A<RESET>\n+\t:    <CYAN> @@ file: A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n \t:    <REVERSE><RED>-<RESET><FAINT> BB<RESET>\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378826","messageId":"20190711160851.14380-15-t.gummerer@gmail.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"[PATCH v4 14/14] range-diff: add headers to the outer hunk header","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-07-11T16:08:51Z","receivedAt":"2019-07-11T16:09:29Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Add the section headers/hunk headers we introduced in the previous\ncommits to the outer diff's hunk headers.  This makes it easier to\nunderstand which change we are actually looking at.  For example an\nouter hunk header might now look like:\n\n    @@  Documentation/config/interactive.txt\n\nwhile previously it would have only been\n\n    @@\n\nwhich doesn't give a lot of context for the change that follows.\n\nFor completeness also add section headers for the commit metadata and\nthe commit message, although they are arguably less important.\n\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n range-diff.c          |  9 ++++++---\n t/t3206-range-diff.sh | 41 ++++++++++++++++++++++-------------------\n 2 files changed, 28 insertions(+), 22 deletions(-)\n\ndiff --git a/range-diff.c b/range-diff.c\nindex 7a96a587f1..ba1e9a4265 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -139,8 +139,10 @@ static int read_patches(const char *range, struct string_list *list)\n \t\t\tstrbuf_addstr(&buf, \" ##\");\n \t\t} else if (in_header) {\n \t\t\tif (starts_with(line, \"Author: \")) {\n+\t\t\t\tstrbuf_addstr(&buf, \" ## Metadata ##\\n\");\n \t\t\t\tstrbuf_addstr(&buf, line);\n \t\t\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n+\t\t\t\tstrbuf_addstr(&buf, \" ## Commit message ##\\n\");\n \t\t\t} else if (starts_with(line, \"    \")) {\n \t\t\t\tp = line + len - 2;\n \t\t\t\twhile (isspace(*p) && p >= line)\n@@ -402,8 +404,9 @@ static void output_pair_header(struct diff_options *diffopt,\n \tfwrite(buf->buf, buf->len, 1, diffopt->file);\n }\n \n-static struct userdiff_driver no_func_name = {\n-\t.funcname = { \"$^\", 0 }\n+static struct userdiff_driver section_headers = {\n+\t.funcname = { \"^ ## (.*) ##$\\n\"\n+\t\t      \"^.?@@ (.*)$\", REG_EXTENDED }\n };\n \n static struct diff_filespec *get_filespec(const char *name, const char *p)\n@@ -415,7 +418,7 @@ static struct diff_filespec *get_filespec(const char *name, const char *p)\n \tspec->size = strlen(p);\n \tspec->should_munmap = 0;\n \tspec->is_stdin = 1;\n-\tspec->driver = &no_func_name;\n+\tspec->driver = &section_headers;\n \n \treturn spec;\n }\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex d4de270979..ec548654ce 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -99,7 +99,7 @@ test_expect_success 'changed commit' '\n \t1:  4de457d = 1:  a4b3333 s/5/A/\n \t2:  fccce22 = 2:  f51d370 s/4/A/\n \t3:  147e64e ! 3:  0559556 s/11/B/\n-\t    @@\n+\t    @@ file: A\n \t      9\n \t      10\n \t     -11\n@@ -109,7 +109,7 @@ test_expect_success 'changed commit' '\n \t      13\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n-\t    @@\n+\t    @@ file\n \t     @@ file: A\n \t      9\n \t      10\n@@ -158,7 +158,7 @@ test_expect_success 'changed commit with sm config' '\n \t1:  4de457d = 1:  a4b3333 s/5/A/\n \t2:  fccce22 = 2:  f51d370 s/4/A/\n \t3:  147e64e ! 3:  0559556 s/11/B/\n-\t    @@\n+\t    @@ file: A\n \t      9\n \t      10\n \t     -11\n@@ -168,7 +168,7 @@ test_expect_success 'changed commit with sm config' '\n \t      13\n \t      14\n \t4:  a63e992 ! 4:  d966c5c s/12/B/\n-\t    @@\n+\t    @@ file\n \t     @@ file: A\n \t      9\n \t      10\n@@ -186,9 +186,10 @@ test_expect_success 'renamed file' '\n \tsed s/Z/\\ /g >expected <<-EOF &&\n \t1:  4de457d = 1:  f258d75 s/5/A/\n \t2:  fccce22 ! 2:  017b62d s/4/A/\n-\t    @@\n+\t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n+\t    Z ## Commit message ##\n \t    -    s/4/A/\n \t    +    s/4/A/ + rename file\n \t    Z\n@@ -198,8 +199,8 @@ test_expect_success 'renamed file' '\n \t    Z 1\n \t    Z 2\n \t3:  147e64e ! 3:  3ce7af6 s/11/B/\n-\t    @@\n-\t    Z\n+\t    @@ Metadata\n+\t    Z ## Commit message ##\n \t    Z    s/11/B/\n \t    Z\n \t    - ## file ##\n@@ -210,8 +211,8 @@ test_expect_success 'renamed file' '\n \t    Z 9\n \t    Z 10\n \t4:  a63e992 ! 4:  1e6226b s/12/B/\n-\t    @@\n-\t    Z\n+\t    @@ Metadata\n+\t    Z ## Commit message ##\n \t    Z    s/12/B/\n \t    Z\n \t    - ## file ##\n@@ -230,30 +231,32 @@ test_expect_success 'file added and later removed' '\n \tsed s/Z/\\ /g >expected <<-EOF &&\n \t1:  4de457d = 1:  096b1ba s/5/A/\n \t2:  fccce22 ! 2:  d92e698 s/4/A/\n-\t    @@\n+\t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n+\t    Z ## Commit message ##\n \t    -    s/4/A/\n \t    +    s/4/A/ + new-file\n \t    Z\n \t    Z ## file ##\n \t    Z@@\n-\t    @@\n+\t    @@ file\n \t    Z A\n \t    Z 6\n \t    Z 7\n \t    +\n \t    + ## new-file (new) ##\n \t3:  147e64e ! 3:  9a1db4d s/11/B/\n-\t    @@\n+\t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n+\t    Z ## Commit message ##\n \t    -    s/11/B/\n \t    +    s/11/B/ + remove file\n \t    Z\n \t    Z ## file ##\n \t    Z@@ file: A\n-\t    @@\n+\t    @@ file: A\n \t    Z 12\n \t    Z 13\n \t    Z 14\n@@ -274,8 +277,8 @@ test_expect_success 'changed message' '\n \tsed s/Z/\\ /g >expected <<-EOF &&\n \t1:  4de457d = 1:  f686024 s/5/A/\n \t2:  fccce22 ! 2:  4ab067d s/4/A/\n-\t    @@\n-\t    Z\n+\t    @@ Metadata\n+\t    Z ## Commit message ##\n \t    Z    s/4/A/\n \t    Z\n \t    +    Also a silly comment here!\n@@ -293,8 +296,8 @@ test_expect_success 'dual-coloring' '\n \tsed -e \"s|^:||\" >expect <<-\\EOF &&\n \t:<YELLOW>1:  a4b3333 = 1:  f686024 s/5/A/<RESET>\n \t:<RED>2:  f51d370 <RESET><YELLOW>!<RESET><GREEN> 2:  4ab067d<RESET><YELLOW> s/4/A/<RESET>\n-\t:    <REVERSE><CYAN>@@<RESET>\n-\t:     <RESET>\n+\t:    <REVERSE><CYAN>@@<RESET> <RESET>Metadata<RESET>\n+\t:      ## Commit message ##<RESET>\n \t:         s/4/A/<RESET>\n \t:     <RESET>\n \t:    <REVERSE><GREEN>+<RESET><BOLD>    Also a silly comment here!<RESET>\n@@ -303,7 +306,7 @@ test_expect_success 'dual-coloring' '\n \t:    <CYAN> @@<RESET>\n \t:      1<RESET>\n \t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n-\t:    <REVERSE><CYAN>@@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET> <RESET>file: A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n \t:    <RED> -11<RESET>\n@@ -313,7 +316,7 @@ test_expect_success 'dual-coloring' '\n \t:      13<RESET>\n \t:      14<RESET>\n \t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n-\t:    <REVERSE><CYAN>@@<RESET>\n+\t:    <REVERSE><CYAN>@@<RESET> <RESET>file<RESET>\n \t:    <CYAN> @@ file: A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n-- \n2.22.0.510.g264f2c817a\n\n"},{"id":"378874","messageId":"fd594a29-0ae9-c06b-6cb9-6ae2804f8f44@ramsayjones.plus.com","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"Re: [PATCH v4 00/14] output improvements for git range-diff","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2019-07-11T22:09:58Z","receivedAt":"2019-07-11T22:10:04Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 11/07/2019 17:08, Thomas Gummerer wrote:\n> Thanks Junio for the comment on the previous round [1].  This round\n> reanmes the struct we're using in apply.c to 'struct gitdiff_data',\n> and updates the commit message of 7/14 to reflect the new name of the\n> renamed function.\n> \n> [1]: https://public-inbox.org/git/20190708163315.29912-1-t.gummerer@gmail.com/\n> \n> Thomas Gummerer (14):\n>   apply: replace marc.info link with public-inbox\n>   apply: only pass required data to skip_tree_prefix\n>   apply: only pass required data to git_header_name\n>   apply: only pass required data to check_header_line\n>   apply: only pass required data to find_name_*\n>   apply: only pass required data to gitdiff_* functions\n>   apply: make parse_git_diff_header public\n>   range-diff: fix function parameter indentation\n>   range-diff: split lines manually\n>   range-diff: don't remove funcname from inner diff\n>   range-diff: suppress line count in outer diff\n>   range-diff: add section header instead of diff header\n>   range-diff: add filename to inner diff\n>   range-diff: add headers to the outer hunk header\n> \n>  apply.c                | 186 ++++++++++++++++++-----------------------\n>  apply.h                |  48 +++++++++++\n>  diff.c                 |   5 +-\n>  diff.h                 |   1 +\n>  range-diff.c           | 124 +++++++++++++++++++--------\n>  t/t3206-range-diff.sh  | 124 ++++++++++++++++++++++-----\n>  t/t3206/history.export |  84 ++++++++++++++++++-\n>  7 files changed, 409 insertions(+), 163 deletions(-)\n\n\nYes, the patch I just sent related to the previous version of\nthis series. However, I believe it still applies to this version\n(looking at the range-diff below).\n\nATB,\nRamsay Jones\n\n> Range-diff against v3:\n>  1:  ef2245edda =  1:  ef2245edda apply: replace marc.info link with public-inbox\n>  2:  94578fa45c =  2:  94578fa45c apply: only pass required data to skip_tree_prefix\n>  3:  988269a68e =  3:  988269a68e apply: only pass required data to git_header_name\n>  4:  a2c1ef3f5f =  4:  a2c1ef3f5f apply: only pass required data to check_header_line\n>  5:  0f4cfe21cb =  5:  0f4cfe21cb apply: only pass required data to find_name_*\n>  6:  07a271518d !  6:  42665e5295 apply: only pass required data to gitdiff_* functions\n>     @@ -28,7 +28,7 @@\n>       #include \"rerere.h\"\n>       #include \"apply.h\"\n>       \n>     -+struct parse_git_header_state {\n>     ++struct gitdiff_data {\n>      +\tstruct strbuf *root;\n>      +\tint linenr;\n>      +\tint p_value;\n>     @@ -42,7 +42,7 @@\n>       }\n>       \n>      -static int gitdiff_hdrend(struct apply_state *state,\n>     -+static int gitdiff_hdrend(struct parse_git_header_state *state,\n>     ++static int gitdiff_hdrend(struct gitdiff_data *state,\n>       \t\t\t  const char *line,\n>       \t\t\t  struct patch *patch)\n>       {\n>     @@ -51,7 +51,7 @@\n>       #define DIFF_NEW_NAME 1\n>       \n>      -static int gitdiff_verify_name(struct apply_state *state,\n>     -+static int gitdiff_verify_name(struct parse_git_header_state *state,\n>     ++static int gitdiff_verify_name(struct gitdiff_data *state,\n>       \t\t\t       const char *line,\n>       \t\t\t       int isnull,\n>       \t\t\t       char **name,\n>     @@ -77,7 +77,7 @@\n>       }\n>       \n>      -static int gitdiff_oldname(struct apply_state *state,\n>     -+static int gitdiff_oldname(struct parse_git_header_state *state,\n>     ++static int gitdiff_oldname(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -86,7 +86,7 @@\n>       }\n>       \n>      -static int gitdiff_newname(struct apply_state *state,\n>     -+static int gitdiff_newname(struct parse_git_header_state *state,\n>     ++static int gitdiff_newname(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -95,7 +95,7 @@\n>       }\n>       \n>      -static int gitdiff_oldmode(struct apply_state *state,\n>     -+static int gitdiff_oldmode(struct parse_git_header_state *state,\n>     ++static int gitdiff_oldmode(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -103,7 +103,7 @@\n>       }\n>       \n>      -static int gitdiff_newmode(struct apply_state *state,\n>     -+static int gitdiff_newmode(struct parse_git_header_state *state,\n>     ++static int gitdiff_newmode(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -111,7 +111,7 @@\n>       }\n>       \n>      -static int gitdiff_delete(struct apply_state *state,\n>     -+static int gitdiff_delete(struct parse_git_header_state *state,\n>     ++static int gitdiff_delete(struct gitdiff_data *state,\n>       \t\t\t  const char *line,\n>       \t\t\t  struct patch *patch)\n>       {\n>     @@ -120,7 +120,7 @@\n>       }\n>       \n>      -static int gitdiff_newfile(struct apply_state *state,\n>     -+static int gitdiff_newfile(struct parse_git_header_state *state,\n>     ++static int gitdiff_newfile(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -129,7 +129,7 @@\n>       }\n>       \n>      -static int gitdiff_copysrc(struct apply_state *state,\n>     -+static int gitdiff_copysrc(struct parse_git_header_state *state,\n>     ++static int gitdiff_copysrc(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -141,7 +141,7 @@\n>       }\n>       \n>      -static int gitdiff_copydst(struct apply_state *state,\n>     -+static int gitdiff_copydst(struct parse_git_header_state *state,\n>     ++static int gitdiff_copydst(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -153,7 +153,7 @@\n>       }\n>       \n>      -static int gitdiff_renamesrc(struct apply_state *state,\n>     -+static int gitdiff_renamesrc(struct parse_git_header_state *state,\n>     ++static int gitdiff_renamesrc(struct gitdiff_data *state,\n>       \t\t\t     const char *line,\n>       \t\t\t     struct patch *patch)\n>       {\n>     @@ -165,7 +165,7 @@\n>       }\n>       \n>      -static int gitdiff_renamedst(struct apply_state *state,\n>     -+static int gitdiff_renamedst(struct parse_git_header_state *state,\n>     ++static int gitdiff_renamedst(struct gitdiff_data *state,\n>       \t\t\t     const char *line,\n>       \t\t\t     struct patch *patch)\n>       {\n>     @@ -177,7 +177,7 @@\n>       }\n>       \n>      -static int gitdiff_similarity(struct apply_state *state,\n>     -+static int gitdiff_similarity(struct parse_git_header_state *state,\n>     ++static int gitdiff_similarity(struct gitdiff_data *state,\n>       \t\t\t      const char *line,\n>       \t\t\t      struct patch *patch)\n>       {\n>     @@ -186,7 +186,7 @@\n>       }\n>       \n>      -static int gitdiff_dissimilarity(struct apply_state *state,\n>     -+static int gitdiff_dissimilarity(struct parse_git_header_state *state,\n>     ++static int gitdiff_dissimilarity(struct gitdiff_data *state,\n>       \t\t\t\t const char *line,\n>       \t\t\t\t struct patch *patch)\n>       {\n>     @@ -195,7 +195,7 @@\n>       }\n>       \n>      -static int gitdiff_index(struct apply_state *state,\n>     -+static int gitdiff_index(struct parse_git_header_state *state,\n>     ++static int gitdiff_index(struct gitdiff_data *state,\n>       \t\t\t const char *line,\n>       \t\t\t struct patch *patch)\n>       {\n>     @@ -204,7 +204,7 @@\n>        * into the next diff. Tell the parser to break out.\n>        */\n>      -static int gitdiff_unrecognized(struct apply_state *state,\n>     -+static int gitdiff_unrecognized(struct parse_git_header_state *state,\n>     ++static int gitdiff_unrecognized(struct gitdiff_data *state,\n>       \t\t\t\tconst char *line,\n>       \t\t\t\tstruct patch *patch)\n>       {\n>     @@ -212,7 +212,7 @@\n>       \t\t\t    struct patch *patch)\n>       {\n>       \tunsigned long offset;\n>     -+\tstruct parse_git_header_state parse_hdr_state;\n>     ++\tstruct gitdiff_data parse_hdr_state;\n>       \n>       \t/* A git diff has explicit new/delete information, so we don't guess */\n>       \tpatch->is_new = 0;\n>     @@ -228,7 +228,7 @@\n>       \t\tstatic const struct opentry {\n>       \t\t\tconst char *str;\n>      -\t\t\tint (*fn)(struct apply_state *, const char *, struct patch *);\n>     -+\t\t\tint (*fn)(struct parse_git_header_state *, const char *, struct patch *);\n>     ++\t\t\tint (*fn)(struct gitdiff_data *, const char *, struct patch *);\n>       \t\t} optable[] = {\n>       \t\t\t{ \"@@ -\", gitdiff_hdrend },\n>       \t\t\t{ \"--- \", gitdiff_oldname },\n>  7:  9cb6732a5f !  7:  3068fda8a9 apply: make parse_git_header public\n>     @@ -1,9 +1,12 @@\n>      Author: Thomas Gummerer <t.gummerer@gmail.com>\n>      \n>     -    apply: make parse_git_header public\n>     +    apply: make parse_git_diff_header public\n>      \n>     -    Make parse_git_header a \"public\" function in apply.h, so we can re-use\n>     -    it in range-diff in a subsequent commit.\n>     +    Make 'parse_git_header()' (renamed to 'parse_git_diff_header()') a\n>     +    \"public\" function in apply.h, so we can re-use it in range-diff in a\n>     +    subsequent commit.  We're renaming the function to make it clearer in\n>     +    other parts of the codebase that we're talking about a diff header and\n>     +    not just any header.\n>      \n>          Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n>      \n>     @@ -70,7 +73,7 @@\n>      +\t\t\t  struct patch *patch)\n>       {\n>       \tunsigned long offset;\n>     - \tstruct parse_git_header_state parse_hdr_state;\n>     + \tstruct gitdiff_data parse_hdr_state;\n>      @@\n>       \t * or removing or adding empty files), so we get\n>       \t * the default name from the header.\n>     @@ -100,7 +103,7 @@\n>      +\tfor (offset = len ; size > 0 ; offset += len, size -= len, line += len, (*linenr)++) {\n>       \t\tstatic const struct opentry {\n>       \t\t\tconst char *str;\n>     - \t\t\tint (*fn)(struct parse_git_header_state *, const char *, struct patch *);\n>     + \t\t\tint (*fn)(struct gitdiff_data *, const char *, struct patch *);\n>      @@\n>       \t\t\tres = p->fn(&parse_hdr_state, line + oplen, patch);\n>       \t\t\tif (res < 0)\n>  8:  76a11ce995 =  8:  781d054cab range-diff: fix function parameter indentation\n>  9:  6f70e7faa6 =  9:  68a2953310 range-diff: split lines manually\n> 10:  6618cdff2c = 10:  8ae95d053b range-diff: don't remove funcname from inner diff\n> 11:  2667df4fa5 = 11:  e572510c52 range-diff: suppress line count in outer diff\n> 12:  47cd8c6733 = 12:  45605db760 range-diff: add section header instead of diff header\n> 13:  f67fd5dd9a = 13:  89a27fbeaa range-diff: add filename to inner diff\n> 14:  812893a5dc = 14:  8bee2c525f range-diff: add headers to the outer hunk header\n> \n"},{"id":"378882","messageId":"nycvar.QRO.7.76.6.1907121244270.47@tvgsbejvaqbjf.bet","threadId":"51436","inReplyTo":"20190711160851.14380-1-t.gummerer@gmail.com","subject":"Re: [PATCH v4 00/14] output improvements for git range-diff","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-07-12T10:44:55Z","receivedAt":"2019-07-12T10:45:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Thomas,\n\nOn Thu, 11 Jul 2019, Thomas Gummerer wrote:\n\n> Thanks Junio for the comment on the previous round [1].  This round\n> reanmes the struct we're using in apply.c to 'struct gitdiff_data',\n> and updates the commit message of 7/14 to reflect the new name of the\n> renamed function.\n\nYep, and the range-diff looks good to me, too.\n\nThank you!\nDscho\n\n>\n> [1]: https://public-inbox.org/git/20190708163315.29912-1-t.gummerer@gmail.com/\n>\n> Thomas Gummerer (14):\n>   apply: replace marc.info link with public-inbox\n>   apply: only pass required data to skip_tree_prefix\n>   apply: only pass required data to git_header_name\n>   apply: only pass required data to check_header_line\n>   apply: only pass required data to find_name_*\n>   apply: only pass required data to gitdiff_* functions\n>   apply: make parse_git_diff_header public\n>   range-diff: fix function parameter indentation\n>   range-diff: split lines manually\n>   range-diff: don't remove funcname from inner diff\n>   range-diff: suppress line count in outer diff\n>   range-diff: add section header instead of diff header\n>   range-diff: add filename to inner diff\n>   range-diff: add headers to the outer hunk header\n>\n>  apply.c                | 186 ++++++++++++++++++-----------------------\n>  apply.h                |  48 +++++++++++\n>  diff.c                 |   5 +-\n>  diff.h                 |   1 +\n>  range-diff.c           | 124 +++++++++++++++++++--------\n>  t/t3206-range-diff.sh  | 124 ++++++++++++++++++++++-----\n>  t/t3206/history.export |  84 ++++++++++++++++++-\n>  7 files changed, 409 insertions(+), 163 deletions(-)\n>\n> Range-diff against v3:\n>  1:  ef2245edda =  1:  ef2245edda apply: replace marc.info link with public-inbox\n>  2:  94578fa45c =  2:  94578fa45c apply: only pass required data to skip_tree_prefix\n>  3:  988269a68e =  3:  988269a68e apply: only pass required data to git_header_name\n>  4:  a2c1ef3f5f =  4:  a2c1ef3f5f apply: only pass required data to check_header_line\n>  5:  0f4cfe21cb =  5:  0f4cfe21cb apply: only pass required data to find_name_*\n>  6:  07a271518d !  6:  42665e5295 apply: only pass required data to gitdiff_* functions\n>     @@ -28,7 +28,7 @@\n>       #include \"rerere.h\"\n>       #include \"apply.h\"\n>\n>     -+struct parse_git_header_state {\n>     ++struct gitdiff_data {\n>      +\tstruct strbuf *root;\n>      +\tint linenr;\n>      +\tint p_value;\n>     @@ -42,7 +42,7 @@\n>       }\n>\n>      -static int gitdiff_hdrend(struct apply_state *state,\n>     -+static int gitdiff_hdrend(struct parse_git_header_state *state,\n>     ++static int gitdiff_hdrend(struct gitdiff_data *state,\n>       \t\t\t  const char *line,\n>       \t\t\t  struct patch *patch)\n>       {\n>     @@ -51,7 +51,7 @@\n>       #define DIFF_NEW_NAME 1\n>\n>      -static int gitdiff_verify_name(struct apply_state *state,\n>     -+static int gitdiff_verify_name(struct parse_git_header_state *state,\n>     ++static int gitdiff_verify_name(struct gitdiff_data *state,\n>       \t\t\t       const char *line,\n>       \t\t\t       int isnull,\n>       \t\t\t       char **name,\n>     @@ -77,7 +77,7 @@\n>       }\n>\n>      -static int gitdiff_oldname(struct apply_state *state,\n>     -+static int gitdiff_oldname(struct parse_git_header_state *state,\n>     ++static int gitdiff_oldname(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -86,7 +86,7 @@\n>       }\n>\n>      -static int gitdiff_newname(struct apply_state *state,\n>     -+static int gitdiff_newname(struct parse_git_header_state *state,\n>     ++static int gitdiff_newname(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -95,7 +95,7 @@\n>       }\n>\n>      -static int gitdiff_oldmode(struct apply_state *state,\n>     -+static int gitdiff_oldmode(struct parse_git_header_state *state,\n>     ++static int gitdiff_oldmode(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -103,7 +103,7 @@\n>       }\n>\n>      -static int gitdiff_newmode(struct apply_state *state,\n>     -+static int gitdiff_newmode(struct parse_git_header_state *state,\n>     ++static int gitdiff_newmode(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -111,7 +111,7 @@\n>       }\n>\n>      -static int gitdiff_delete(struct apply_state *state,\n>     -+static int gitdiff_delete(struct parse_git_header_state *state,\n>     ++static int gitdiff_delete(struct gitdiff_data *state,\n>       \t\t\t  const char *line,\n>       \t\t\t  struct patch *patch)\n>       {\n>     @@ -120,7 +120,7 @@\n>       }\n>\n>      -static int gitdiff_newfile(struct apply_state *state,\n>     -+static int gitdiff_newfile(struct parse_git_header_state *state,\n>     ++static int gitdiff_newfile(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -129,7 +129,7 @@\n>       }\n>\n>      -static int gitdiff_copysrc(struct apply_state *state,\n>     -+static int gitdiff_copysrc(struct parse_git_header_state *state,\n>     ++static int gitdiff_copysrc(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -141,7 +141,7 @@\n>       }\n>\n>      -static int gitdiff_copydst(struct apply_state *state,\n>     -+static int gitdiff_copydst(struct parse_git_header_state *state,\n>     ++static int gitdiff_copydst(struct gitdiff_data *state,\n>       \t\t\t   const char *line,\n>       \t\t\t   struct patch *patch)\n>       {\n>     @@ -153,7 +153,7 @@\n>       }\n>\n>      -static int gitdiff_renamesrc(struct apply_state *state,\n>     -+static int gitdiff_renamesrc(struct parse_git_header_state *state,\n>     ++static int gitdiff_renamesrc(struct gitdiff_data *state,\n>       \t\t\t     const char *line,\n>       \t\t\t     struct patch *patch)\n>       {\n>     @@ -165,7 +165,7 @@\n>       }\n>\n>      -static int gitdiff_renamedst(struct apply_state *state,\n>     -+static int gitdiff_renamedst(struct parse_git_header_state *state,\n>     ++static int gitdiff_renamedst(struct gitdiff_data *state,\n>       \t\t\t     const char *line,\n>       \t\t\t     struct patch *patch)\n>       {\n>     @@ -177,7 +177,7 @@\n>       }\n>\n>      -static int gitdiff_similarity(struct apply_state *state,\n>     -+static int gitdiff_similarity(struct parse_git_header_state *state,\n>     ++static int gitdiff_similarity(struct gitdiff_data *state,\n>       \t\t\t      const char *line,\n>       \t\t\t      struct patch *patch)\n>       {\n>     @@ -186,7 +186,7 @@\n>       }\n>\n>      -static int gitdiff_dissimilarity(struct apply_state *state,\n>     -+static int gitdiff_dissimilarity(struct parse_git_header_state *state,\n>     ++static int gitdiff_dissimilarity(struct gitdiff_data *state,\n>       \t\t\t\t const char *line,\n>       \t\t\t\t struct patch *patch)\n>       {\n>     @@ -195,7 +195,7 @@\n>       }\n>\n>      -static int gitdiff_index(struct apply_state *state,\n>     -+static int gitdiff_index(struct parse_git_header_state *state,\n>     ++static int gitdiff_index(struct gitdiff_data *state,\n>       \t\t\t const char *line,\n>       \t\t\t struct patch *patch)\n>       {\n>     @@ -204,7 +204,7 @@\n>        * into the next diff. Tell the parser to break out.\n>        */\n>      -static int gitdiff_unrecognized(struct apply_state *state,\n>     -+static int gitdiff_unrecognized(struct parse_git_header_state *state,\n>     ++static int gitdiff_unrecognized(struct gitdiff_data *state,\n>       \t\t\t\tconst char *line,\n>       \t\t\t\tstruct patch *patch)\n>       {\n>     @@ -212,7 +212,7 @@\n>       \t\t\t    struct patch *patch)\n>       {\n>       \tunsigned long offset;\n>     -+\tstruct parse_git_header_state parse_hdr_state;\n>     ++\tstruct gitdiff_data parse_hdr_state;\n>\n>       \t/* A git diff has explicit new/delete information, so we don't guess */\n>       \tpatch->is_new = 0;\n>     @@ -228,7 +228,7 @@\n>       \t\tstatic const struct opentry {\n>       \t\t\tconst char *str;\n>      -\t\t\tint (*fn)(struct apply_state *, const char *, struct patch *);\n>     -+\t\t\tint (*fn)(struct parse_git_header_state *, const char *, struct patch *);\n>     ++\t\t\tint (*fn)(struct gitdiff_data *, const char *, struct patch *);\n>       \t\t} optable[] = {\n>       \t\t\t{ \"@@ -\", gitdiff_hdrend },\n>       \t\t\t{ \"--- \", gitdiff_oldname },\n>  7:  9cb6732a5f !  7:  3068fda8a9 apply: make parse_git_header public\n>     @@ -1,9 +1,12 @@\n>      Author: Thomas Gummerer <t.gummerer@gmail.com>\n>\n>     -    apply: make parse_git_header public\n>     +    apply: make parse_git_diff_header public\n>\n>     -    Make parse_git_header a \"public\" function in apply.h, so we can re-use\n>     -    it in range-diff in a subsequent commit.\n>     +    Make 'parse_git_header()' (renamed to 'parse_git_diff_header()') a\n>     +    \"public\" function in apply.h, so we can re-use it in range-diff in a\n>     +    subsequent commit.  We're renaming the function to make it clearer in\n>     +    other parts of the codebase that we're talking about a diff header and\n>     +    not just any header.\n>\n>          Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n>\n>     @@ -70,7 +73,7 @@\n>      +\t\t\t  struct patch *patch)\n>       {\n>       \tunsigned long offset;\n>     - \tstruct parse_git_header_state parse_hdr_state;\n>     + \tstruct gitdiff_data parse_hdr_state;\n>      @@\n>       \t * or removing or adding empty files), so we get\n>       \t * the default name from the header.\n>     @@ -100,7 +103,7 @@\n>      +\tfor (offset = len ; size > 0 ; offset += len, size -= len, line += len, (*linenr)++) {\n>       \t\tstatic const struct opentry {\n>       \t\t\tconst char *str;\n>     - \t\t\tint (*fn)(struct parse_git_header_state *, const char *, struct patch *);\n>     + \t\t\tint (*fn)(struct gitdiff_data *, const char *, struct patch *);\n>      @@\n>       \t\t\tres = p->fn(&parse_hdr_state, line + oplen, patch);\n>       \t\t\tif (res < 0)\n>  8:  76a11ce995 =  8:  781d054cab range-diff: fix function parameter indentation\n>  9:  6f70e7faa6 =  9:  68a2953310 range-diff: split lines manually\n> 10:  6618cdff2c = 10:  8ae95d053b range-diff: don't remove funcname from inner diff\n> 11:  2667df4fa5 = 11:  e572510c52 range-diff: suppress line count in outer diff\n> 12:  47cd8c6733 = 12:  45605db760 range-diff: add section header instead of diff header\n> 13:  f67fd5dd9a = 13:  89a27fbeaa range-diff: add filename to inner diff\n> 14:  812893a5dc = 14:  8bee2c525f range-diff: add headers to the outer hunk header\n>\n> --\n> 2.22.0.510.g264f2c817a\n>\n"}]}