{"thread":{"id":"66359","subject":"[PATCH] line-range: skip trailing empty lines in funcname range","startedAt":"2026-09-21T11:49:41Z","lastAt":"2026-09-25T10:35:31Z","messageCount":4,"participants":["Kristofer Karlsson via GitGitGadget","Junio C Hamano","Kristofer Karlsson"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"552925","messageId":"pull.2234.git.1789991377413.gitgitgadget@gmail.com","threadId":"66359","inReplyTo":null,"subject":"[PATCH] line-range: skip trailing empty lines in funcname range","fromName":"Kristofer Karlsson via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-09-21T11:49:37Z","receivedAt":"2026-09-21T11:49:41Z","isPatch":true,"body":"From: Kristofer Karlsson <krka@spotify.com>\n\ngit log -L :funcname: includes trailing empty lines in the\nfunction range. This boils down to parse_range_funcname()\nextending the range forward until it either reaches end-of-file\nor another function, without skipping empty lines at the\nboundary. Including empty lines is inconsistent with how it\nworks for git grep -W.\n\nFix this by trimming trailing empty lines from the computed range,\nwith the same goal as this fix (though with a different approach):\n\n    8e5dd94e68 (grep: -W: skip trailing empty lines at EOF, too,\n    2024-07-30)\n\nReported-by: Nikita Makarov <n.makarov@yadro.com>\nSigned-off-by: Kristofer Karlsson <krka@spotify.com>\n---\n    line-range: skip trailing empty lines in funcname range\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2234%2Fspkrka%2Fkrka%2Fline-range-trailing-blank-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2234/spkrka/krka/line-range-trailing-blank-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/2234\n\n line-range.c                                  |  5 ++++\n t/t4211-line-log.sh                           | 26 +++++++++++++++++++\n t/t4211/sha1/expect.parallel-change-f-to-main | 18 +++++--------\n t/t4211/sha1/expect.simple-f-to-main          | 12 +++------\n .../sha256/expect.parallel-change-f-to-main   | 18 +++++--------\n t/t4211/sha256/expect.simple-f-to-main        | 12 +++------\n 6 files changed, 51 insertions(+), 40 deletions(-)\n\ndiff --git a/line-range.c b/line-range.c\nindex b99f0d9895..44b52d4e34 100644\n--- a/line-range.c\n+++ b/line-range.c\n@@ -233,6 +233,11 @@ static const char *parse_range_funcname(\n \t\t(*end)++;\n \t}\n \n+\t/* exclude trailing empty lines from the function range */\n+\twhile (*end > *begin + 1 &&\n+\t       nth_line_cb(cb_data, *end - 1)[0] == '\\n')\n+\t\t(*end)--;\n+\n \tregfree(&regexp);\n \tif (xecfg)\n \t\txdiff_clear_find_func(xecfg);\ndiff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh\nindex d0a834ed8f..3f2a889e76 100755\n--- a/t/t4211-line-log.sh\n+++ b/t/t4211-line-log.sh\n@@ -781,6 +781,32 @@ test_expect_success '--summary shows new file on root commit' '\n \ttest_grep \"create mode 100644 file.c\" actual\n '\n \n+test_expect_success '-L :funcname: excludes trailing empty lines at EOF' '\n+\tgit init trailing-empty &&\n+\t(\n+\t\tcd trailing-empty &&\n+\t\ttest_commit --printf --no-tag \"add func.py\" \\\n+\t\t\tfunc.py \"def foo():\\n    return 1\\n\" &&\n+\t\ttest_commit --printf --no-tag \"add trailing empty lines\" \\\n+\t\t\tfunc.py \"def foo():\\n    return 1\\n\\n\\n\\n\" &&\n+\t\tgit log -L :foo:func.py --format=\"%s\" >actual &&\n+\t\ttest_grep ! \"add trailing empty lines\" actual\n+\t)\n+'\n+\n+test_expect_success '-L :funcname: excludes empty lines between functions' '\n+\tgit init empty-between-funcs &&\n+\t(\n+\t\tcd empty-between-funcs &&\n+\t\ttest_commit --printf --no-tag \"add two funcs\" \\\n+\t\t\tfunc.py \"def foo():\\n    return 1\\n\\ndef bar():\\n    return 2\\n\" &&\n+\t\ttest_commit --printf --no-tag \"add empty lines between\" \\\n+\t\t\tfunc.py \"def foo():\\n    return 1\\n\\n\\n\\ndef bar():\\n    return 2\\n\" &&\n+\t\tgit log -L :foo:func.py --format=\"%s\" >actual &&\n+\t\ttest_grep ! \"add empty lines between\" actual\n+\t)\n+'\n+\n test_expect_success 'get_commit_action() does not mutate a not-yet-walked commit' '\n \tgit init peek &&\n \t(\ndiff --git a/t/t4211/sha1/expect.parallel-change-f-to-main b/t/t4211/sha1/expect.parallel-change-f-to-main\nindex 6d7a201036..cb2f6bcbac 100644\n--- a/t/t4211/sha1/expect.parallel-change-f-to-main\n+++ b/t/t4211/sha1/expect.parallel-change-f-to-main\n@@ -15,7 +15,7 @@ diff --git a/b.c b/b.c\n index 5de3ea4..bf79c2f 100644\n --- a/b.c\n +++ b/b.c\n-@@ -4,14 +4,14 @@\n+@@ -4,13 +4,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -30,7 +30,6 @@ index 5de3ea4..bf79c2f 100644\n  /*\n   * This is only an example!\n   */\n- \n \n commit ba227c6632349700fbb957dec2b50f5e2358be3f\n Author: Thomas Rast <trast@inf.ethz.ch>\n@@ -42,7 +41,7 @@ diff --git a/a.c b/a.c\n index 5de3ea4..01b5b65 100644\n --- a/a.c\n +++ b/a.c\n-@@ -4,14 +4,14 @@\n+@@ -4,13 +4,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -57,7 +56,6 @@ index 5de3ea4..01b5b65 100644\n - * This is only an example!\n + * This is only a short example!\n   */\n- \n \n commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -69,7 +67,7 @@ diff --git a/a.c b/a.c\n index e51de13..bdb2bb1 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -84,7 +82,6 @@ index e51de13..bdb2bb1 100644\n - * A comment.\n + * This is only an example!\n   */\n- \n \n commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -96,7 +93,7 @@ diff --git a/a.c b/a.c\n index 3233403..e51de13 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n -int f(int x)\n +long f(long x)\n  {\n@@ -111,7 +108,6 @@ index 3233403..e51de13 100644\n  /*\n   * A comment.\n   */\n- \n \n commit f04fb20f2c77850996cba739709acc6faecc58f7\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -123,7 +119,7 @@ diff --git a/a.c b/a.c\n index 444e415..3233403 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,13 +3,14 @@\n+@@ -3,12 +3,13 @@\n  int f(int x)\n  {\n  \tint s = 0;\n@@ -137,7 +133,6 @@ index 444e415..3233403 100644\n  /*\n   * A comment.\n   */\n- \n \n commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -150,7 +145,7 @@ new file mode 100644\n index 0000000..444e415\n --- /dev/null\n +++ b/a.c\n-@@ -0,0 +3,13 @@\n+@@ -0,0 +3,12 @@\n +int f(int x)\n +{\n +\tint s = 0;\n@@ -163,4 +158,3 @@ index 0000000..444e415\n +/*\n + * A comment.\n + */\n-+\ndiff --git a/t/t4211/sha1/expect.simple-f-to-main b/t/t4211/sha1/expect.simple-f-to-main\nindex cd92100dfc..99404cdc40 100644\n--- a/t/t4211/sha1/expect.simple-f-to-main\n+++ b/t/t4211/sha1/expect.simple-f-to-main\n@@ -8,7 +8,7 @@ diff --git a/a.c b/a.c\n index e51de13..bdb2bb1 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -23,7 +23,6 @@ index e51de13..bdb2bb1 100644\n - * A comment.\n + * This is only an example!\n   */\n- \n \n commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -35,7 +34,7 @@ diff --git a/a.c b/a.c\n index 3233403..e51de13 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n -int f(int x)\n +long f(long x)\n  {\n@@ -50,7 +49,6 @@ index 3233403..e51de13 100644\n  /*\n   * A comment.\n   */\n- \n \n commit f04fb20f2c77850996cba739709acc6faecc58f7\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -62,7 +60,7 @@ diff --git a/a.c b/a.c\n index 444e415..3233403 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,13 +3,14 @@\n+@@ -3,12 +3,13 @@\n  int f(int x)\n  {\n  \tint s = 0;\n@@ -76,7 +74,6 @@ index 444e415..3233403 100644\n  /*\n   * A comment.\n   */\n- \n \n commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -89,7 +86,7 @@ new file mode 100644\n index 0000000..444e415\n --- /dev/null\n +++ b/a.c\n-@@ -0,0 +3,13 @@\n+@@ -0,0 +3,12 @@\n +int f(int x)\n +{\n +\tint s = 0;\n@@ -102,4 +99,3 @@ index 0000000..444e415\n +/*\n + * A comment.\n + */\n-+\ndiff --git a/t/t4211/sha256/expect.parallel-change-f-to-main b/t/t4211/sha256/expect.parallel-change-f-to-main\nindex c93e03bef4..49f1402e46 100644\n--- a/t/t4211/sha256/expect.parallel-change-f-to-main\n+++ b/t/t4211/sha256/expect.parallel-change-f-to-main\n@@ -15,7 +15,7 @@ diff --git a/b.c b/b.c\n index 62c1fc2..69cb69c 100644\n --- a/b.c\n +++ b/b.c\n-@@ -4,14 +4,14 @@\n+@@ -4,13 +4,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -30,7 +30,6 @@ index 62c1fc2..69cb69c 100644\n  /*\n   * This is only an example!\n   */\n- \n \n commit b511694f5337663fbd697622993a5f8e1099eca84be4df313f2b3ee94a098b42\n Author: Thomas Rast <trast@inf.ethz.ch>\n@@ -42,7 +41,7 @@ diff --git a/a.c b/a.c\n index 62c1fc2..e1e8475 100644\n --- a/a.c\n +++ b/a.c\n-@@ -4,14 +4,14 @@\n+@@ -4,13 +4,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -57,7 +56,6 @@ index 62c1fc2..e1e8475 100644\n - * This is only an example!\n + * This is only a short example!\n   */\n- \n \n commit 5a1b3989063d55e71e7685efa3392f133385b4034bddde530dcb5090d8b8b8ca\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -69,7 +67,7 @@ diff --git a/a.c b/a.c\n index 75c0119..3a78aaf 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -84,7 +82,6 @@ index 75c0119..3a78aaf 100644\n - * A comment.\n + * This is only an example!\n   */\n- \n \n commit ccf97b9878189c40a981da50b15713bb80a35755326320ec80900caf22ced46f\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -96,7 +93,7 @@ diff --git a/a.c b/a.c\n index 7a296b9..75c0119 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n -int f(int x)\n +long f(long x)\n  {\n@@ -111,7 +108,6 @@ index 7a296b9..75c0119 100644\n  /*\n   * A comment.\n   */\n- \n \n commit f6434acd34260a6c9f61e96d96bf9a323d330561df5b1ca2631104f82026dfed\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -123,7 +119,7 @@ diff --git a/a.c b/a.c\n index 9f550c3..7a296b9 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,13 +3,14 @@\n+@@ -3,12 +3,13 @@\n  int f(int x)\n  {\n  \tint s = 0;\n@@ -137,7 +133,6 @@ index 9f550c3..7a296b9 100644\n  /*\n   * A comment.\n   */\n- \n \n commit 1dd7e9b2b1699324b53b341e728653b913bc192a14dfea168c5b51f2b3d03592\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -150,7 +145,7 @@ new file mode 100644\n index 0000000..9f550c3\n --- /dev/null\n +++ b/a.c\n-@@ -0,0 +3,13 @@\n+@@ -0,0 +3,12 @@\n +int f(int x)\n +{\n +\tint s = 0;\n@@ -163,4 +158,3 @@ index 0000000..9f550c3\n +/*\n + * A comment.\n + */\n-+\ndiff --git a/t/t4211/sha256/expect.simple-f-to-main b/t/t4211/sha256/expect.simple-f-to-main\nindex e67fa017a7..3dd225947e 100644\n--- a/t/t4211/sha256/expect.simple-f-to-main\n+++ b/t/t4211/sha256/expect.simple-f-to-main\n@@ -8,7 +8,7 @@ diff --git a/a.c b/a.c\n index 75c0119..3a78aaf 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -23,7 +23,6 @@ index 75c0119..3a78aaf 100644\n - * A comment.\n + * This is only an example!\n   */\n- \n \n commit ccf97b9878189c40a981da50b15713bb80a35755326320ec80900caf22ced46f\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -35,7 +34,7 @@ diff --git a/a.c b/a.c\n index 7a296b9..75c0119 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n -int f(int x)\n +long f(long x)\n  {\n@@ -50,7 +49,6 @@ index 7a296b9..75c0119 100644\n  /*\n   * A comment.\n   */\n- \n \n commit f6434acd34260a6c9f61e96d96bf9a323d330561df5b1ca2631104f82026dfed\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -62,7 +60,7 @@ diff --git a/a.c b/a.c\n index 9f550c3..7a296b9 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,13 +3,14 @@\n+@@ -3,12 +3,13 @@\n  int f(int x)\n  {\n  \tint s = 0;\n@@ -76,7 +74,6 @@ index 9f550c3..7a296b9 100644\n  /*\n   * A comment.\n   */\n- \n \n commit 1dd7e9b2b1699324b53b341e728653b913bc192a14dfea168c5b51f2b3d03592\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -89,7 +86,7 @@ new file mode 100644\n index 0000000..9f550c3\n --- /dev/null\n +++ b/a.c\n-@@ -0,0 +3,13 @@\n+@@ -0,0 +3,12 @@\n +int f(int x)\n +{\n +\tint s = 0;\n@@ -102,4 +99,3 @@ index 0000000..9f550c3\n +/*\n + * A comment.\n + */\n-+\n\nbase-commit: d38352cd43ab9745686d697872408bc3249a153f\n-- \ngitgitgadget\n"},{"id":"552942","messageId":"xmqqse32mrzm.fsf@gitster.g","threadId":"66359","inReplyTo":"pull.2234.git.1789991377413.gitgitgadget@gmail.com","subject":"Re: [PATCH] line-range: skip trailing empty lines in funcname range","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-09-21T17:30:53Z","receivedAt":"2026-09-21T17:30:56Z","isPatch":true,"body":"\"Kristofer Karlsson via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> diff --git a/line-range.c b/line-range.c\n> index b99f0d9895..44b52d4e34 100644\n> --- a/line-range.c\n> +++ b/line-range.c\n> @@ -233,6 +233,11 @@ static const char *parse_range_funcname(\n>  \t\t(*end)++;\n>  \t}\n>  \n> +\t/* exclude trailing empty lines from the function range */\n> +\twhile (*end > *begin + 1 &&\n> +\t       nth_line_cb(cb_data, *end - 1)[0] == '\\n')\n> +\t\t(*end)--;\n> +\n\nWhile the new behaviour may mean well, the implementation falls a\nbit short of matching what \"grep -W\" did, doesn't it?  Over there we\ntreat a line with only whitespace characters on it as an empty line\nthat we want to ignore, but the above code does not allow anything\nbefore the LF.  I do not know offhand if the above misbehaves on\nCRLF text files, but if you treated a line with only whitespace\ncharacters on it as an empty line, concerns on CRLF files would also\ndisappear ;-).\n\n\n"},{"id":"552947","messageId":"CAL71e4OW7=WhkTtzifG2fBNp748HvipW5AJiTOPynwXyo-BFaQ@mail.gmail.com","threadId":"66359","inReplyTo":"xmqqse32mrzm.fsf@gitster.g","subject":"Re: [PATCH] line-range: skip trailing empty lines in funcname range","fromName":"Kristofer Karlsson","fromEmail":"krka@spotify.com","sentAt":"2026-09-21T18:58:50Z","receivedAt":"2026-09-21T18:59:02Z","isPatch":true,"body":"On Mon, 21 Sept 2026 at 19:30, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"Kristofer Karlsson via GitGitGadget\" <gitgitgadget@gmail.com>\n> writes:\n>\n> > diff --git a/line-range.c b/line-range.c\n> > index b99f0d9895..44b52d4e34 100644\n> > --- a/line-range.c\n> > +++ b/line-range.c\n> > @@ -233,6 +233,11 @@ static const char *parse_range_funcname(\n> >               (*end)++;\n> >       }\n> >\n> > +     /* exclude trailing empty lines from the function range */\n> > +     while (*end > *begin + 1 &&\n> > +            nth_line_cb(cb_data, *end - 1)[0] == '\\n')\n> > +             (*end)--;\n> > +\n>\n> While the new behaviour may mean well, the implementation falls a\n> bit short of matching what \"grep -W\" did, doesn't it?  Over there we\n> treat a line with only whitespace characters on it as an empty line\n> that we want to ignore, but the above code does not allow anything\n> before the LF.  I do not know offhand if the above misbehaves on\n> CRLF text files, but if you treated a line with only whitespace\n> characters on it as an empty line, concerns on CRLF files would also\n> disappear ;-).\n>\n\nAh yes, a blank line is not the same as an empty line, good point.\n\nI also noticed the old behavior is still in the documentation\nfor man git grep:\n\n    -W, --function-context\n        Show the surrounding text from the previous line\n        containing a function name up to the one before\n        the next function name, effectively showing the whole\n        function in which the match was found.\n\nAnd for man git log (in the -L section):\n\n    If :<funcname> is given in place of <start> and <end>,\n    it is a regular expression that denotes the range from\n    the first funcname line that matches <funcname>, up to\n    the next funcname line.  :<funcname> searches from the\n    end of the previous -L range, if any, otherwise from the\n    start of file.\n\nSo neither of these functions should do any blank-line trimming\naccording to the docs (but the docs can be changed).\n\nSo I suppose the question is what behavior we actually want,\nand if these two use cases should be analogous or not.\n\nThanks,\nKristofer\n"},{"id":"553272","messageId":"pull.2234.v2.git.1790332529050.gitgitgadget@gmail.com","threadId":"66359","inReplyTo":"pull.2234.git.1789991377413.gitgitgadget@gmail.com","subject":"[PATCH v2] line-range: skip trailing blank lines in funcname range","fromName":"Kristofer Karlsson via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-09-25T10:35:29Z","receivedAt":"2026-09-25T10:35:31Z","isPatch":true,"body":"From: Kristofer Karlsson <krka@spotify.com>\n\ngit log -L :funcname: includes trailing blank lines in the\nfunction range. This boils down to parse_range_funcname()\nextending the range forward until it either reaches end-of-file\nor another function, without skipping blank lines at the\nboundary. Including blank lines is inconsistent with how it\nworks for git grep -W.\n\nFix this by trimming trailing blank lines from the computed range,\nwith the same goal as this fix (though with a different approach):\n\n    8e5dd94e68 (grep: -W: skip trailing empty lines at EOF, too,\n    2024-07-30)\n\nA blank line is one that contains only whitespace characters,\nmatching the definition used by grep -W.\n\nReported-by: Nikita Makarov <n.makarov@yadro.com>\nSigned-off-by: Kristofer Karlsson <krka@spotify.com>\n---\n    line-range: skip trailing empty lines in funcname range\n    \n    cc: René Scharfe l.s.r@web.de cc: Nikita Makarov n.makarov@yadro.com cc:\n    Kristofer Karlsson krka@spotify.com\n    \n    Changes since v1:\n    \n     * Filter out all blank lines (only whitespace characters) instead of\n       only filtering empty lines to match how grep -W works. (This also\n       makes CRLF line endings work.)\n     * Updated tests to include whitespace-only lines (spaces, tabs) in\n       addition to empty lines.\n     * Rephrased from \"empty\" to \"blank\" throughout.\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2234%2Fspkrka%2Fkrka%2Fline-range-trailing-blank-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2234/spkrka/krka/line-range-trailing-blank-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/2234\n\nRange-diff vs v1:\n\n 1:  f2e217d968 ! 1:  a55eadf639 line-range: skip trailing empty lines in funcname range\n     @@ Metadata\n      Author: Kristofer Karlsson <krka@spotify.com>\n      \n       ## Commit message ##\n     -    line-range: skip trailing empty lines in funcname range\n     +    line-range: skip trailing blank lines in funcname range\n      \n     -    git log -L :funcname: includes trailing empty lines in the\n     +    git log -L :funcname: includes trailing blank lines in the\n          function range. This boils down to parse_range_funcname()\n          extending the range forward until it either reaches end-of-file\n     -    or another function, without skipping empty lines at the\n     -    boundary. Including empty lines is inconsistent with how it\n     +    or another function, without skipping blank lines at the\n     +    boundary. Including blank lines is inconsistent with how it\n          works for git grep -W.\n      \n     -    Fix this by trimming trailing empty lines from the computed range,\n     +    Fix this by trimming trailing blank lines from the computed range,\n          with the same goal as this fix (though with a different approach):\n      \n              8e5dd94e68 (grep: -W: skip trailing empty lines at EOF, too,\n              2024-07-30)\n      \n     +    A blank line is one that contains only whitespace characters,\n     +    matching the definition used by grep -W.\n     +\n          Reported-by: Nikita Makarov <n.makarov@yadro.com>\n          Signed-off-by: Kristofer Karlsson <krka@spotify.com>\n      \n     @@ line-range.c: static const char *parse_range_funcname(\n       \t\t(*end)++;\n       \t}\n       \n     -+\t/* exclude trailing empty lines from the function range */\n     -+\twhile (*end > *begin + 1 &&\n     -+\t       nth_line_cb(cb_data, *end - 1)[0] == '\\n')\n     ++\t/* exclude trailing blank lines from the function range */\n     ++\twhile (*end > *begin + 1) {\n     ++\t\tconst char *bol = nth_line_cb(cb_data, *end - 1);\n     ++\t\tconst char *eol = nth_line_cb(cb_data, *end);\n     ++\t\twhile (bol < eol && isspace(*bol))\n     ++\t\t\tbol++;\n     ++\t\tif (bol < eol)\n     ++\t\t\tbreak;\n      +\t\t(*end)--;\n     ++\t}\n      +\n       \tregfree(&regexp);\n       \tif (xecfg)\n     @@ t/t4211-line-log.sh: test_expect_success '--summary shows new file on root commi\n       \ttest_grep \"create mode 100644 file.c\" actual\n       '\n       \n     -+test_expect_success '-L :funcname: excludes trailing empty lines at EOF' '\n     -+\tgit init trailing-empty &&\n     ++test_expect_success '-L :funcname: excludes trailing blank lines at EOF' '\n     ++\tgit init trailing-blank &&\n      +\t(\n     -+\t\tcd trailing-empty &&\n     ++\t\tcd trailing-blank &&\n      +\t\ttest_commit --printf --no-tag \"add func.py\" \\\n      +\t\t\tfunc.py \"def foo():\\n    return 1\\n\" &&\n     -+\t\ttest_commit --printf --no-tag \"add trailing empty lines\" \\\n     -+\t\t\tfunc.py \"def foo():\\n    return 1\\n\\n\\n\\n\" &&\n     ++\t\ttest_commit --printf --no-tag \"add trailing blank lines\" \\\n     ++\t\t\tfunc.py \"def foo():\\n    return 1\\n\\n  \\n\\t\\n\" &&\n      +\t\tgit log -L :foo:func.py --format=\"%s\" >actual &&\n     -+\t\ttest_grep ! \"add trailing empty lines\" actual\n     ++\t\ttest_grep ! \"add trailing blank lines\" actual\n      +\t)\n      +'\n      +\n     -+test_expect_success '-L :funcname: excludes empty lines between functions' '\n     -+\tgit init empty-between-funcs &&\n     ++test_expect_success '-L :funcname: excludes blank lines between functions' '\n     ++\tgit init blank-between-funcs &&\n      +\t(\n     -+\t\tcd empty-between-funcs &&\n     ++\t\tcd blank-between-funcs &&\n      +\t\ttest_commit --printf --no-tag \"add two funcs\" \\\n      +\t\t\tfunc.py \"def foo():\\n    return 1\\n\\ndef bar():\\n    return 2\\n\" &&\n     -+\t\ttest_commit --printf --no-tag \"add empty lines between\" \\\n     -+\t\t\tfunc.py \"def foo():\\n    return 1\\n\\n\\n\\ndef bar():\\n    return 2\\n\" &&\n     ++\t\ttest_commit --printf --no-tag \"add blank lines between\" \\\n     ++\t\t\tfunc.py \"def foo():\\n    return 1\\n\\n \\n\\t\\ndef bar():\\n    return 2\\n\" &&\n      +\t\tgit log -L :foo:func.py --format=\"%s\" >actual &&\n     -+\t\ttest_grep ! \"add empty lines between\" actual\n     ++\t\ttest_grep ! \"add blank lines between\" actual\n      +\t)\n      +'\n      +\n\n\n line-range.c                                  | 11 ++++++++\n t/t4211-line-log.sh                           | 26 +++++++++++++++++++\n t/t4211/sha1/expect.parallel-change-f-to-main | 18 +++++--------\n t/t4211/sha1/expect.simple-f-to-main          | 12 +++------\n .../sha256/expect.parallel-change-f-to-main   | 18 +++++--------\n t/t4211/sha256/expect.simple-f-to-main        | 12 +++------\n 6 files changed, 57 insertions(+), 40 deletions(-)\n\ndiff --git a/line-range.c b/line-range.c\nindex b99f0d9895..2dceb1cc0c 100644\n--- a/line-range.c\n+++ b/line-range.c\n@@ -233,6 +233,17 @@ static const char *parse_range_funcname(\n \t\t(*end)++;\n \t}\n \n+\t/* exclude trailing blank lines from the function range */\n+\twhile (*end > *begin + 1) {\n+\t\tconst char *bol = nth_line_cb(cb_data, *end - 1);\n+\t\tconst char *eol = nth_line_cb(cb_data, *end);\n+\t\twhile (bol < eol && isspace(*bol))\n+\t\t\tbol++;\n+\t\tif (bol < eol)\n+\t\t\tbreak;\n+\t\t(*end)--;\n+\t}\n+\n \tregfree(&regexp);\n \tif (xecfg)\n \t\txdiff_clear_find_func(xecfg);\ndiff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh\nindex d0a834ed8f..0b5538b262 100755\n--- a/t/t4211-line-log.sh\n+++ b/t/t4211-line-log.sh\n@@ -781,6 +781,32 @@ test_expect_success '--summary shows new file on root commit' '\n \ttest_grep \"create mode 100644 file.c\" actual\n '\n \n+test_expect_success '-L :funcname: excludes trailing blank lines at EOF' '\n+\tgit init trailing-blank &&\n+\t(\n+\t\tcd trailing-blank &&\n+\t\ttest_commit --printf --no-tag \"add func.py\" \\\n+\t\t\tfunc.py \"def foo():\\n    return 1\\n\" &&\n+\t\ttest_commit --printf --no-tag \"add trailing blank lines\" \\\n+\t\t\tfunc.py \"def foo():\\n    return 1\\n\\n  \\n\\t\\n\" &&\n+\t\tgit log -L :foo:func.py --format=\"%s\" >actual &&\n+\t\ttest_grep ! \"add trailing blank lines\" actual\n+\t)\n+'\n+\n+test_expect_success '-L :funcname: excludes blank lines between functions' '\n+\tgit init blank-between-funcs &&\n+\t(\n+\t\tcd blank-between-funcs &&\n+\t\ttest_commit --printf --no-tag \"add two funcs\" \\\n+\t\t\tfunc.py \"def foo():\\n    return 1\\n\\ndef bar():\\n    return 2\\n\" &&\n+\t\ttest_commit --printf --no-tag \"add blank lines between\" \\\n+\t\t\tfunc.py \"def foo():\\n    return 1\\n\\n \\n\\t\\ndef bar():\\n    return 2\\n\" &&\n+\t\tgit log -L :foo:func.py --format=\"%s\" >actual &&\n+\t\ttest_grep ! \"add blank lines between\" actual\n+\t)\n+'\n+\n test_expect_success 'get_commit_action() does not mutate a not-yet-walked commit' '\n \tgit init peek &&\n \t(\ndiff --git a/t/t4211/sha1/expect.parallel-change-f-to-main b/t/t4211/sha1/expect.parallel-change-f-to-main\nindex 6d7a201036..cb2f6bcbac 100644\n--- a/t/t4211/sha1/expect.parallel-change-f-to-main\n+++ b/t/t4211/sha1/expect.parallel-change-f-to-main\n@@ -15,7 +15,7 @@ diff --git a/b.c b/b.c\n index 5de3ea4..bf79c2f 100644\n --- a/b.c\n +++ b/b.c\n-@@ -4,14 +4,14 @@\n+@@ -4,13 +4,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -30,7 +30,6 @@ index 5de3ea4..bf79c2f 100644\n  /*\n   * This is only an example!\n   */\n- \n \n commit ba227c6632349700fbb957dec2b50f5e2358be3f\n Author: Thomas Rast <trast@inf.ethz.ch>\n@@ -42,7 +41,7 @@ diff --git a/a.c b/a.c\n index 5de3ea4..01b5b65 100644\n --- a/a.c\n +++ b/a.c\n-@@ -4,14 +4,14 @@\n+@@ -4,13 +4,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -57,7 +56,6 @@ index 5de3ea4..01b5b65 100644\n - * This is only an example!\n + * This is only a short example!\n   */\n- \n \n commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -69,7 +67,7 @@ diff --git a/a.c b/a.c\n index e51de13..bdb2bb1 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -84,7 +82,6 @@ index e51de13..bdb2bb1 100644\n - * A comment.\n + * This is only an example!\n   */\n- \n \n commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -96,7 +93,7 @@ diff --git a/a.c b/a.c\n index 3233403..e51de13 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n -int f(int x)\n +long f(long x)\n  {\n@@ -111,7 +108,6 @@ index 3233403..e51de13 100644\n  /*\n   * A comment.\n   */\n- \n \n commit f04fb20f2c77850996cba739709acc6faecc58f7\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -123,7 +119,7 @@ diff --git a/a.c b/a.c\n index 444e415..3233403 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,13 +3,14 @@\n+@@ -3,12 +3,13 @@\n  int f(int x)\n  {\n  \tint s = 0;\n@@ -137,7 +133,6 @@ index 444e415..3233403 100644\n  /*\n   * A comment.\n   */\n- \n \n commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -150,7 +145,7 @@ new file mode 100644\n index 0000000..444e415\n --- /dev/null\n +++ b/a.c\n-@@ -0,0 +3,13 @@\n+@@ -0,0 +3,12 @@\n +int f(int x)\n +{\n +\tint s = 0;\n@@ -163,4 +158,3 @@ index 0000000..444e415\n +/*\n + * A comment.\n + */\n-+\ndiff --git a/t/t4211/sha1/expect.simple-f-to-main b/t/t4211/sha1/expect.simple-f-to-main\nindex cd92100dfc..99404cdc40 100644\n--- a/t/t4211/sha1/expect.simple-f-to-main\n+++ b/t/t4211/sha1/expect.simple-f-to-main\n@@ -8,7 +8,7 @@ diff --git a/a.c b/a.c\n index e51de13..bdb2bb1 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -23,7 +23,6 @@ index e51de13..bdb2bb1 100644\n - * A comment.\n + * This is only an example!\n   */\n- \n \n commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -35,7 +34,7 @@ diff --git a/a.c b/a.c\n index 3233403..e51de13 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n -int f(int x)\n +long f(long x)\n  {\n@@ -50,7 +49,6 @@ index 3233403..e51de13 100644\n  /*\n   * A comment.\n   */\n- \n \n commit f04fb20f2c77850996cba739709acc6faecc58f7\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -62,7 +60,7 @@ diff --git a/a.c b/a.c\n index 444e415..3233403 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,13 +3,14 @@\n+@@ -3,12 +3,13 @@\n  int f(int x)\n  {\n  \tint s = 0;\n@@ -76,7 +74,6 @@ index 444e415..3233403 100644\n  /*\n   * A comment.\n   */\n- \n \n commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -89,7 +86,7 @@ new file mode 100644\n index 0000000..444e415\n --- /dev/null\n +++ b/a.c\n-@@ -0,0 +3,13 @@\n+@@ -0,0 +3,12 @@\n +int f(int x)\n +{\n +\tint s = 0;\n@@ -102,4 +99,3 @@ index 0000000..444e415\n +/*\n + * A comment.\n + */\n-+\ndiff --git a/t/t4211/sha256/expect.parallel-change-f-to-main b/t/t4211/sha256/expect.parallel-change-f-to-main\nindex c93e03bef4..49f1402e46 100644\n--- a/t/t4211/sha256/expect.parallel-change-f-to-main\n+++ b/t/t4211/sha256/expect.parallel-change-f-to-main\n@@ -15,7 +15,7 @@ diff --git a/b.c b/b.c\n index 62c1fc2..69cb69c 100644\n --- a/b.c\n +++ b/b.c\n-@@ -4,14 +4,14 @@\n+@@ -4,13 +4,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -30,7 +30,6 @@ index 62c1fc2..69cb69c 100644\n  /*\n   * This is only an example!\n   */\n- \n \n commit b511694f5337663fbd697622993a5f8e1099eca84be4df313f2b3ee94a098b42\n Author: Thomas Rast <trast@inf.ethz.ch>\n@@ -42,7 +41,7 @@ diff --git a/a.c b/a.c\n index 62c1fc2..e1e8475 100644\n --- a/a.c\n +++ b/a.c\n-@@ -4,14 +4,14 @@\n+@@ -4,13 +4,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -57,7 +56,6 @@ index 62c1fc2..e1e8475 100644\n - * This is only an example!\n + * This is only a short example!\n   */\n- \n \n commit 5a1b3989063d55e71e7685efa3392f133385b4034bddde530dcb5090d8b8b8ca\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -69,7 +67,7 @@ diff --git a/a.c b/a.c\n index 75c0119..3a78aaf 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -84,7 +82,6 @@ index 75c0119..3a78aaf 100644\n - * A comment.\n + * This is only an example!\n   */\n- \n \n commit ccf97b9878189c40a981da50b15713bb80a35755326320ec80900caf22ced46f\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -96,7 +93,7 @@ diff --git a/a.c b/a.c\n index 7a296b9..75c0119 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n -int f(int x)\n +long f(long x)\n  {\n@@ -111,7 +108,6 @@ index 7a296b9..75c0119 100644\n  /*\n   * A comment.\n   */\n- \n \n commit f6434acd34260a6c9f61e96d96bf9a323d330561df5b1ca2631104f82026dfed\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -123,7 +119,7 @@ diff --git a/a.c b/a.c\n index 9f550c3..7a296b9 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,13 +3,14 @@\n+@@ -3,12 +3,13 @@\n  int f(int x)\n  {\n  \tint s = 0;\n@@ -137,7 +133,6 @@ index 9f550c3..7a296b9 100644\n  /*\n   * A comment.\n   */\n- \n \n commit 1dd7e9b2b1699324b53b341e728653b913bc192a14dfea168c5b51f2b3d03592\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -150,7 +145,7 @@ new file mode 100644\n index 0000000..9f550c3\n --- /dev/null\n +++ b/a.c\n-@@ -0,0 +3,13 @@\n+@@ -0,0 +3,12 @@\n +int f(int x)\n +{\n +\tint s = 0;\n@@ -163,4 +158,3 @@ index 0000000..9f550c3\n +/*\n + * A comment.\n + */\n-+\ndiff --git a/t/t4211/sha256/expect.simple-f-to-main b/t/t4211/sha256/expect.simple-f-to-main\nindex e67fa017a7..3dd225947e 100644\n--- a/t/t4211/sha256/expect.simple-f-to-main\n+++ b/t/t4211/sha256/expect.simple-f-to-main\n@@ -8,7 +8,7 @@ diff --git a/a.c b/a.c\n index 75c0119..3a78aaf 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n  long f(long x)\n  {\n  \tint s = 0;\n@@ -23,7 +23,6 @@ index 75c0119..3a78aaf 100644\n - * A comment.\n + * This is only an example!\n   */\n- \n \n commit ccf97b9878189c40a981da50b15713bb80a35755326320ec80900caf22ced46f\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -35,7 +34,7 @@ diff --git a/a.c b/a.c\n index 7a296b9..75c0119 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,14 +3,14 @@\n+@@ -3,13 +3,13 @@\n -int f(int x)\n +long f(long x)\n  {\n@@ -50,7 +49,6 @@ index 7a296b9..75c0119 100644\n  /*\n   * A comment.\n   */\n- \n \n commit f6434acd34260a6c9f61e96d96bf9a323d330561df5b1ca2631104f82026dfed\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -62,7 +60,7 @@ diff --git a/a.c b/a.c\n index 9f550c3..7a296b9 100644\n --- a/a.c\n +++ b/a.c\n-@@ -3,13 +3,14 @@\n+@@ -3,12 +3,13 @@\n  int f(int x)\n  {\n  \tint s = 0;\n@@ -76,7 +74,6 @@ index 9f550c3..7a296b9 100644\n  /*\n   * A comment.\n   */\n- \n \n commit 1dd7e9b2b1699324b53b341e728653b913bc192a14dfea168c5b51f2b3d03592\n Author: Thomas Rast <trast@student.ethz.ch>\n@@ -89,7 +86,7 @@ new file mode 100644\n index 0000000..9f550c3\n --- /dev/null\n +++ b/a.c\n-@@ -0,0 +3,13 @@\n+@@ -0,0 +3,12 @@\n +int f(int x)\n +{\n +\tint s = 0;\n@@ -102,4 +99,3 @@ index 0000000..9f550c3\n +/*\n + * A comment.\n + */\n-+\n\nbase-commit: d38352cd43ab9745686d697872408bc3249a153f\n-- \ngitgitgadget\n"}]}