git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: git grep: ^$ false match at end of file

From
Jeff King <peff@peff.net>
Date
Jan 13, 2025, 06:26 UTC
Message-ID
<20250113062601.GD767856@coredump.intra.peff.net>
In-Reply-To
<87r05ahljt.fsf@igel.home>
On Fri, Jan 10, 2025 at 01:59:18PM +0100, Andreas Schwab wrote:
Show 8 quoted lines
> On Jan 10 2025, Jeff King wrote:
> 
> > but it is weird to me that patmatch() will match "^$" to the end of the
> > buffer at all. It is just calling regexec_buf() behind the scenes, so I
> > guess this is just a weird special case there, and may even depend on
> > the regex implementation.
> 
> Shouldn't the matcher be called with REG_NOTEOL in that case?

Perhaps. If regexec_buf() is assuming we are feeding lines, then without REG_NOTEOL it thinks the end of the buffer is the end of a line. Which makes sense, but trips up this case because we are not feeding lines, but rather a whole buffer. So the final newline is not the start of an empty line, but the true end of the buffer.

But what if the buffer doesn't end in a newline? In the example, the file is something like "content\n". But what if it was just "content"? Then the end of the buffer really is the end of a line, isn't it? And REG_NOTEOL would not be appropriate.

So without REG_NOTEOL:
  [this is wrong, per the report]
  $ echo content >file.txt
  $ git grep --no-index -n '^$' file.txt
  file.txt:2:
  [this is right]
  $ printf content >file.txt
  $ git grep --no-index -n '^$' file.txt
  $ echo $?
  1
and with it, like this patch:
diff --git a/grep.c b/grep.c
index 4e155ee9e6..7e3b6d9474 100644
--- a/grep.c
+++ b/grep.c
@@ -1467,7 +1467,7 @@ static int look_ahead(struct grep_opt *opt,
 		int hit;
 		regmatch_t m;
 
-		hit = patmatch(p, bol, bol + *left_p, &m, 0);
+		hit = patmatch(p, bol, bol + *left_p, &m, REG_NOTEOL);
 		if (hit < 0)
 			return -1;
 		if (!hit || m.rm_so < 0 || m.rm_eo < 0)

we get:

  [this is now right]
  $ git grep --no-index -n '^$' file.txt
  $ echo $?
  1

  [and this stays right]
  $ printf content >file.txt
  $ git grep --no-index -n '^$' file.txt
  $ echo $?
  1

but:

  [without REG_NOTEOL, this matches]
  $ printf content >file.txt
  $ git grep --no-index -n 't$' file.txt
  file.txt:1:content

  [but with that flag, it no longer does]
  $ printf content >file.txt
  $ git grep --no-index -n 't$' file.txt
  $ echo $?
  1

So I do think "\n" at the end of the buffer is a special case. Perhaps
we should always omit it, and then leave REG_NOTEOL unset, making the
end of the buffer consistently the end of the final line. Like this,
which no longer matches "^$" but does match "t$":

diff --git a/grep.c b/grep.c
index 4e155ee9e6..c4bb9f1081 100644
--- a/grep.c
+++ b/grep.c
@@ -1646,6 +1646,8 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
 
 	bol = gs->buf;
 	left = gs->size;
+	if (left && gs->buf[left-1] == '\n')
+		left--;
 	while (left) {
 		const char *eol;
 		int hit;

-Peff
Previous: Andreas Schwab
Message 5 of 5 in “git grep: ^$ false match at end of file”
  1. Olly BettsJan 9, 2025
  2. Jeff KingJan 10, 2025
  3. Jeff KingJan 10, 2025
  4. Andreas SchwabJan 10, 2025
  5. Jeff KingJan 13, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.