{"thread":{"id":"65528","subject":"git grep bug with --column and --only-matching","startedAt":"2026-04-21T05:04:09Z","lastAt":"2026-05-04T13:10:14Z","messageCount":5,"participants":["Brandon Chinn","René Scharfe","Phillip Wood"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"542007","messageId":"CAGANf=dkRgFp+bEkB5f8QBeiR3m+3WE8sKqT9vKstkGHqbxA3A@mail.gmail.com","threadId":"65528","inReplyTo":null,"subject":"git grep bug with --column and --only-matching","fromName":"Brandon Chinn","fromEmail":"brandonchinn178@gmail.com","sentAt":"2026-04-21T05:03:56Z","receivedAt":"2026-04-21T05:04:09Z","isPatch":false,"body":"I'm encountering a bug when using `git grep` with both `--column` and\n`--only-matching`, is this a known limitation?\n\nRepro:\n\n```\n$ echo 'x   x   x' > repro.txt\n\n$ grep -bo x repro.txt\n0:x\n4:x\n8:x\n\n$ git grep --no-index -o -n --column x repro.txt\nrepro.txt:1:  1:x\nrepro.txt:1:  2:x\nrepro.txt:1:  6:x\n```\n\n[System Info]\ngit version:\ngit version 2.51.1\ncpu: arm64\nno commit associated with this build\nsizeof-long: 8\nsizeof-size_t: 8\nshell-path: /bin/sh\nfeature: fsmonitor--daemon\nlibcurl: 8.7.1\nzlib: 1.2.12\nSHA-1: SHA1_DC\nSHA-256: SHA256_BLK\ndefault-ref-format: files\ndefault-hash: sha1\nuname: Darwin 25.3.0 Darwin Kernel Version 25.3.0: Wed Jan 28 20:56:34\nPST 2026; root:xnu-12377.91.3~2/RELEASE_ARM64_T8112 arm64\ncompiler info: clang: 17.0.0 (clang-1700.0.13.3)\nlibc info: no libc information available\n$SHELL (typically, interactive shell): /bin/zsh\n"},{"id":"542067","messageId":"3ce1906a-85e3-4fb1-9ebc-a5639f3194c9@web.de","threadId":"65528","inReplyTo":"CAGANf=dkRgFp+bEkB5f8QBeiR3m+3WE8sKqT9vKstkGHqbxA3A@mail.gmail.com","subject":"Re: git grep bug with --column and --only-matching","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2026-04-21T20:33:04Z","receivedAt":"2026-04-21T20:33:09Z","isPatch":false,"body":"On 4/21/26 7:03 AM, Brandon Chinn wrote:\n> I'm encountering a bug when using `git grep` with both `--column` and\n> `--only-matching`, is this a known limitation?\n> \n> Repro:\n> \n> ```\n> $ echo 'x   x   x' > repro.txt\n> \n> $ grep -bo x repro.txt\n> 0:x\n> 4:x\n> 8:x\n> \n> $ git grep --no-index -o -n --column x repro.txt\n> repro.txt:1:  1:x\n> repro.txt:1:  2:x\n> repro.txt:1:  6:x\n\nThe first column value matches (1-based for git grep, 0-based for\ngrep(1)), the remaining ones are different.  grep(1) shows the offset\nlike for the first match, but git grep adds the relative position of the\nend of the previous match.\n\nI don't know how to use the resulting numbers and I agree that it seems\nlike a bug -- showing the column of each match within its line makes\nmore sense for an option named --column.\n\nHowever, the original submission of this feature in\nhttps://lore.kernel.org/git/cover.1529961706.git.me@ttaylorr.com/\ngave similar examples in its commit message and its tests, called\nthem \"as one would expect\" and was not challenged on that, so I may be\nmissing something.\n\nHere's its first example with underlined matches, a ruler and the\nintended output (without unnecessary headers):\n\n  (`man gitcvs-migration` or `git help cvs-migration` if git is\n        ^^^                   ^^^                        ^^^\n           1111111111222222222233333333334444444444555555555566  \n  123456789 123456789 123456789 123456789 123456789 123456789 1\n           \n  7:git\n  16:git\n  38:git\n\nAnd here's the last line of the test file from t7810 with underlined\nmatches (the other four lines are very similar), a ruler and the\nexpected output (unncessary headers removed):\n\n  foo_mmap bar mmap baz\n      ^^^^     ^^^^\n           111111111122\n  123456789 123456789 1\n\n  5:mmap\n  13:mmap\n\nIf we wanted to show the column of matches 2 and beyond then we could do\nsomething like this:\n\n\ndiff --git a/grep.c b/grep.c\nindex c7e1dc1e0e..a54e5d86a9 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -1267,6 +1267,7 @@ static void show_line(struct grep_opt *opt,\n \t\tregmatch_t match;\n \t\tenum grep_context ctx = GREP_CONTEXT_BODY;\n \t\tint eflags = 0;\n+\t\tconst char *start = bol;\n \n \t\tif (want_color(opt->color)) {\n \t\t\tif (sign == ':')\n@@ -1285,6 +1286,7 @@ static void show_line(struct grep_opt *opt,\n \t\t\tif (match.rm_so == match.rm_eo)\n \t\t\t\tbreak;\n \n+\t\t\tcno = bol - start + match.rm_so + 1;\n \t\t\tif (opt->only_matching)\n \t\t\t\tshow_line_header(opt, name, lno, cno, sign);\n \t\t\telse\n@@ -1294,7 +1296,6 @@ static void show_line(struct grep_opt *opt,\n \t\t\tif (opt->only_matching)\n \t\t\t\topt->output(opt, \"\\n\", 1);\n \t\t\tbol += match.rm_eo;\n-\t\t\tcno += match.rm_eo;\n \t\t\trest -= match.rm_eo;\n \t\t\teflags = REG_NOTBOL;\n \t\t}\ndiff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\nindex 64ac4f04ee..bd439563d6 100755\n--- a/t/t7810-grep.sh\n+++ b/t/t7810-grep.sh\n@@ -322,11 +322,11 @@ do\n \t\t${HC}file:1:5:mmap\n \t\t${HC}file:2:5:mmap\n \t\t${HC}file:3:5:mmap\n-\t\t${HC}file:3:13:mmap\n+\t\t${HC}file:3:14:mmap\n \t\t${HC}file:4:5:mmap\n-\t\t${HC}file:4:13:mmap\n+\t\t${HC}file:4:14:mmap\n \t\t${HC}file:5:5:mmap\n-\t\t${HC}file:5:13:mmap\n+\t\t${HC}file:5:14:mmap\n \t\tEOF\n \t\tgit grep --column -n -o -e mmap $H >actual &&\n \t\ttest_cmp expected actual\n\n"},{"id":"542193","messageId":"033622e1-1f61-4f9d-b56f-fafc7850d275@gmail.com","threadId":"65528","inReplyTo":"3ce1906a-85e3-4fb1-9ebc-a5639f3194c9@web.de","subject":"Re: git grep bug with --column and --only-matching","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-04-23T09:44:31Z","receivedAt":"2026-04-23T09:44:35Z","isPatch":false,"body":"On 21/04/2026 21:33, René Scharfe wrote:\n> On 4/21/26 7:03 AM, Brandon Chinn wrote:\n>> I'm encountering a bug when using `git grep` with both `--column` and\n>> `--only-matching`, is this a known limitation?\n>>\n>> Repro:\n>>\n>> ```\n>> $ echo 'x   x   x' > repro.txt\n>>\n>> $ grep -bo x repro.txt\n>> 0:x\n>> 4:x\n>> 8:x\n>>\n>> $ git grep --no-index -o -n --column x repro.txt\n>> repro.txt:1:  1:x\n>> repro.txt:1:  2:x\n>> repro.txt:1:  6:x\n> \n> The first column value matches (1-based for git grep, 0-based for\n> grep(1)), the remaining ones are different.  grep(1) shows the offset\n> like for the first match, but git grep adds the relative position of the\n> end of the previous match.\n\nI'm having a hard time understanding what git is doing. Looking at your \nexample with the ruler\n\n>    (`man gitcvs-migration` or `git help cvs-migration` if git is\n>          ^^^                   ^^^                        ^^^\n>             1111111111222222222233333333334444444444555555555566\n>    123456789 123456789 123456789 123456789 123456789 123456789 1\n>             \n>    7:git\n>    16:git\n>    38:git\n\nThe first match ends at column 9 and the second match starts at column \n29 so how do we end up with 16 as the \"column\" of the second match when \nthe difference between them is 20?\n\n> I don't know how to use the resulting numbers and I agree that it seems\n> like a bug -- showing the column of each match within its line makes\n> more sense for an option named --column.\n\nIndeed\n\n> However, the original submission of this feature in\n> https://lore.kernel.org/git/cover.1529961706.git.me@ttaylorr.com/\n> gave similar examples in its commit message and its tests, called\n> them \"as one would expect\" and was not challenged on that, so I may be\n> missing something.\n\nI wonder how hard anyone looked at the examples and tests, the \ndiscussion seemed to have focused on other things.\n\nThanks\n\nPhillip\n\n> Here's its first example with underlined matches, a ruler and the\n> intended output (without unnecessary headers):\n> \n>    (`man gitcvs-migration` or `git help cvs-migration` if git is\n>          ^^^                   ^^^                        ^^^\n>             1111111111222222222233333333334444444444555555555566\n>    123456789 123456789 123456789 123456789 123456789 123456789 1\n>             \n>    7:git\n>    16:git\n>    38:git\n> \n> And here's the last line of the test file from t7810 with underlined\n> matches (the other four lines are very similar), a ruler and the\n> expected output (unncessary headers removed):\n> \n>    foo_mmap bar mmap baz\n>        ^^^^     ^^^^\n>             111111111122\n>    123456789 123456789 1\n> \n>    5:mmap\n>    13:mmap\n> \n> If we wanted to show the column of matches 2 and beyond then we could do\n> something like this:\n> \n> \n> diff --git a/grep.c b/grep.c\n> index c7e1dc1e0e..a54e5d86a9 100644\n> --- a/grep.c\n> +++ b/grep.c\n> @@ -1267,6 +1267,7 @@ static void show_line(struct grep_opt *opt,\n>   \t\tregmatch_t match;\n>   \t\tenum grep_context ctx = GREP_CONTEXT_BODY;\n>   \t\tint eflags = 0;\n> +\t\tconst char *start = bol;\n>   \n>   \t\tif (want_color(opt->color)) {\n>   \t\t\tif (sign == ':')\n> @@ -1285,6 +1286,7 @@ static void show_line(struct grep_opt *opt,\n>   \t\t\tif (match.rm_so == match.rm_eo)\n>   \t\t\t\tbreak;\n>   \n> +\t\t\tcno = bol - start + match.rm_so + 1;\n>   \t\t\tif (opt->only_matching)\n>   \t\t\t\tshow_line_header(opt, name, lno, cno, sign);\n>   \t\t\telse\n> @@ -1294,7 +1296,6 @@ static void show_line(struct grep_opt *opt,\n>   \t\t\tif (opt->only_matching)\n>   \t\t\t\topt->output(opt, \"\\n\", 1);\n>   \t\t\tbol += match.rm_eo;\n> -\t\t\tcno += match.rm_eo;\n>   \t\t\trest -= match.rm_eo;\n>   \t\t\teflags = REG_NOTBOL;\n>   \t\t}\n> diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\n> index 64ac4f04ee..bd439563d6 100755\n> --- a/t/t7810-grep.sh\n> +++ b/t/t7810-grep.sh\n> @@ -322,11 +322,11 @@ do\n>   \t\t${HC}file:1:5:mmap\n>   \t\t${HC}file:2:5:mmap\n>   \t\t${HC}file:3:5:mmap\n> -\t\t${HC}file:3:13:mmap\n> +\t\t${HC}file:3:14:mmap\n>   \t\t${HC}file:4:5:mmap\n> -\t\t${HC}file:4:13:mmap\n> +\t\t${HC}file:4:14:mmap\n>   \t\t${HC}file:5:5:mmap\n> -\t\t${HC}file:5:13:mmap\n> +\t\t${HC}file:5:14:mmap\n>   \t\tEOF\n>   \t\tgit grep --column -n -o -e mmap $H >actual &&\n>   \t\ttest_cmp expected actual\n> \n> \n\n"},{"id":"542275","messageId":"9bd69678-f04b-41d2-ad74-a386820d34c8@web.de","threadId":"65528","inReplyTo":"CAGANf=dkRgFp+bEkB5f8QBeiR3m+3WE8sKqT9vKstkGHqbxA3A@mail.gmail.com","subject":"[PATCH] grep: fix --column --only-match for 2nd and later matches","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2026-04-24T21:04:27Z","receivedAt":"2026-04-24T21:09:42Z","isPatch":true,"body":"\"git grep --column --only-match\" shows the 1-based column number of the\nfirst match on each line, but confusing numbers for further matches.\nExample:\n\n   $ echo 123456789012345678901234567890 >file\n   $ for d in 1 2 3 4 5 6 7 8 9 0\n     do\n       git grep --no-index --column --only-matching $d file |\n       awk -v FS=: -v l=$d: '{l = l sprintf(\"%3s\", $2)} END {print l}'\n     done\n   1:  1  2 12\n   2:  2  4 14\n   3:  3  6 16\n   4:  4  8 18\n   5:  5 10 20\n   6:  6 12 22\n   7:  7 14 24\n   8:  8 16 26\n   9:  9 18 28\n   0: 10 20 30\n\nReport the column number of each match instead:\n\n   $ for d in 1 2 3 4 5 6 7 8 9 0\n     do\n       ./git grep --no-index --column --only-matching $d file |\n       awk -v FS=: -v l=$d: '{l = l sprintf(\"%3s\", $2)} END {print l}'\n     done\n   1:  1 11 21\n   2:  2 12 22\n   3:  3 13 23\n   4:  4 14 24\n   5:  5 15 25\n   6:  6 16 26\n   7:  7 17 27\n   8:  8 18 28\n   9:  9 19 29\n   0: 10 20 30\n\nWe need to adjust the test in t7810 as well.  The file it uses has the\nfollowing five lines; I add a line highlighting the matches and a ruler\nat the bottom here, to make it easier to see that the second \"mmap\"\nindeed starts at column 14:\n\nfoo mmap bar\nfoo_mmap bar\nfoo_mmap bar mmap\nfoo mmap bar_mmap\nfoo_mmap bar mmap baz\n    ====     ====\n123456789 123456789 1\n\nReported-by: Brandon Chinn <brandonchinn178@gmail.com>\nSigned-off-by: René Scharfe <l.s.r@web.de>\n---\n grep.c          | 3 ++-\n t/t7810-grep.sh | 6 +++---\n 2 files changed, 5 insertions(+), 4 deletions(-)\n\ndiff --git a/grep.c b/grep.c\nindex c7e1dc1e0e..a54e5d86a9 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -1267,6 +1267,7 @@ static void show_line(struct grep_opt *opt,\n \t\tregmatch_t match;\n \t\tenum grep_context ctx = GREP_CONTEXT_BODY;\n \t\tint eflags = 0;\n+\t\tconst char *start = bol;\n \n \t\tif (want_color(opt->color)) {\n \t\t\tif (sign == ':')\n@@ -1285,6 +1286,7 @@ static void show_line(struct grep_opt *opt,\n \t\t\tif (match.rm_so == match.rm_eo)\n \t\t\t\tbreak;\n \n+\t\t\tcno = bol - start + match.rm_so + 1;\n \t\t\tif (opt->only_matching)\n \t\t\t\tshow_line_header(opt, name, lno, cno, sign);\n \t\t\telse\n@@ -1294,7 +1296,6 @@ static void show_line(struct grep_opt *opt,\n \t\t\tif (opt->only_matching)\n \t\t\t\topt->output(opt, \"\\n\", 1);\n \t\t\tbol += match.rm_eo;\n-\t\t\tcno += match.rm_eo;\n \t\t\trest -= match.rm_eo;\n \t\t\teflags = REG_NOTBOL;\n \t\t}\ndiff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\nindex 64ac4f04ee..bd439563d6 100755\n--- a/t/t7810-grep.sh\n+++ b/t/t7810-grep.sh\n@@ -322,11 +322,11 @@ do\n \t\t${HC}file:1:5:mmap\n \t\t${HC}file:2:5:mmap\n \t\t${HC}file:3:5:mmap\n-\t\t${HC}file:3:13:mmap\n+\t\t${HC}file:3:14:mmap\n \t\t${HC}file:4:5:mmap\n-\t\t${HC}file:4:13:mmap\n+\t\t${HC}file:4:14:mmap\n \t\t${HC}file:5:5:mmap\n-\t\t${HC}file:5:13:mmap\n+\t\t${HC}file:5:14:mmap\n \t\tEOF\n \t\tgit grep --column -n -o -e mmap $H >actual &&\n \t\ttest_cmp expected actual\n-- \n2.54.0\n"},{"id":"542668","messageId":"a7819ca3-7e57-4ba1-94e3-aab427fad678@gmail.com","threadId":"65528","inReplyTo":"9bd69678-f04b-41d2-ad74-a386820d34c8@web.de","subject":"Re: [PATCH] grep: fix --column --only-match for 2nd and later matches","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-05-04T13:10:11Z","receivedAt":"2026-05-04T13:10:14Z","isPatch":true,"body":"Hi René\n\nOn 24/04/2026 22:04, René Scharfe wrote:\n> diff --git a/grep.c b/grep.c\n> index c7e1dc1e0e..a54e5d86a9 100644\n> --- a/grep.c\n> +++ b/grep.c\n> @@ -1267,6 +1267,7 @@ static void show_line(struct grep_opt *opt,\n>   \t\tregmatch_t match;\n>   \t\tenum grep_context ctx = GREP_CONTEXT_BODY;\n>   \t\tint eflags = 0;\n> +\t\tconst char *start = bol;\n\nHere we save a pointer to the start of the line\n\n>   \t\tif (want_color(opt->color)) {\n>   \t\t\tif (sign == ':')\n> @@ -1285,6 +1286,7 @@ static void show_line(struct grep_opt *opt,\n>   \t\t\tif (match.rm_so == match.rm_eo)\n>   \t\t\t\tbreak;\n>   \n> +\t\t\tcno = bol - start + match.rm_so + 1;\n\nand then we calculate the column number relative to that.\n\nThat looks good, thanks for fixing it\n\nPhillip\n\n>   \t\t\tif (opt->only_matching)\n>   \t\t\t\tshow_line_header(opt, name, lno, cno, sign);\n>   \t\t\telse\n> @@ -1294,7 +1296,6 @@ static void show_line(struct grep_opt *opt,\n>   \t\t\tif (opt->only_matching)\n>   \t\t\t\topt->output(opt, \"\\n\", 1);\n>   \t\t\tbol += match.rm_eo;\n> -\t\t\tcno += match.rm_eo;\n>   \t\t\trest -= match.rm_eo;\n>   \t\t\teflags = REG_NOTBOL;\n>   \t\t}\n> diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\n> index 64ac4f04ee..bd439563d6 100755\n> --- a/t/t7810-grep.sh\n> +++ b/t/t7810-grep.sh\n> @@ -322,11 +322,11 @@ do\n>   \t\t${HC}file:1:5:mmap\n>   \t\t${HC}file:2:5:mmap\n>   \t\t${HC}file:3:5:mmap\n> -\t\t${HC}file:3:13:mmap\n> +\t\t${HC}file:3:14:mmap\n>   \t\t${HC}file:4:5:mmap\n> -\t\t${HC}file:4:13:mmap\n> +\t\t${HC}file:4:14:mmap\n>   \t\t${HC}file:5:5:mmap\n> -\t\t${HC}file:5:13:mmap\n> +\t\t${HC}file:5:14:mmap\n>   \t\tEOF\n>   \t\tgit grep --column -n -o -e mmap $H >actual &&\n>   \t\ttest_cmp expected actual\n\n"}]}