You can search for a filename and function name in git log with git log -L :funcname:filename
.
I ran into an issue where we had been running this search programmatically and the funcname was set to '$', which caused an endless search. (e.g. git log -L :$:somefile.py
)
'$' means end of string in regex, but why does this cause an endless search loop when other regex characters like '^' or '?' don't? What unique effects does the '$' character have?
CodePudding user response:
I was able to reproduce this locally and step through the code using gdb
.
This looks like a bug in the find_funcname_match_regexp
function. It eventually ends up matching $
against the empty string, which matches successfully but causes no changes in the pointers used to mark the position in the file, resulting in an infinite loop.
Here's a walk through of the reproducer. In this example, we're running git log
against the file main.go
which has the following content:
package main
import "fmt"
func main() {
fmt.Println("example repository for demonstrating git log bug")
}
Start gdb and set a breakpoint at the beginning of the
while
loop inline-range.c
. Arrange to print the value ofstart
after each break:(gdb) break line-range.c:140 Breakpoint 1 at 0x5a7a89: file line-range.c, line 140. (gdb) commands Type commands for breakpoint(s) 1, one per line. End with a line saying just "end". >p start >end (gdb)
Run
git log -L :$:main.go
under the control of gdb:(gdb) run log -L :$:main.go Starting program: /home/lars/src/git/git log -L :$:main.go [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". [Detaching after fork from child process 2469232] Breakpoint 1, find_funcname_matching_regexp (xecfg=0x0, start=0x8477c0 "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"example repository for demonstrating git log bug\")\n}\n", regexp=0x7fffffffc020) at line-range.c:140 140 reg_error = regexec(regexp, start, 1, match, 0); $1 = 0x8477c0 "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"example repository for demonstrating git log bug\")\n}\n"
In this output, we can see that
start
is pointing at the beginning of the file:$1 = 0x8477c0 "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"example repository for demonstrating git log bug\")\n}\n"
Skip a few iterations:
(gdb) c 7 Will ignore next 6 crossings of breakpoint 1. Continuing. Breakpoint 1, find_funcname_matching_regexp (xecfg=0x0, start=0x84782b "}\n", regexp=0x7fffffffc020) at line-range.c:140 140 reg_error = regexec(regexp, start, 1, match, 0); $2 = 0x84782b "}\n"
Here we see that
start
now points at the last line in the file.Watch what happens if we iterate a few more times:
(gdb) c Continuing. Breakpoint 1, find_funcname_matching_regexp (xecfg=0x0, start=0x84782d "", regexp=0x7fffffffc020) at line-range.c:140 140 reg_error = regexec(regexp, start, 1, match, 0); $3 = 0x84782d "" (gdb) c Continuing. Breakpoint 1, find_funcname_matching_regexp (xecfg=0x0, start=0x84782d "", regexp=0x7fffffffc020) at line-range.c:140 140 reg_error = regexec(regexp, start, 1, match, 0); $4 = 0x84782d "" (gdb) c Continuing. Breakpoint 1, find_funcname_matching_regexp (xecfg=0x0, start=0x84782d "", regexp=0x7fffffffc020) at line-range.c:140 140 reg_error = regexec(regexp, start, 1, match, 0); $5 = 0x84782d ""
After one more iteration of the loop,
start
now points at the empty string. It keeps this value in every subsequent iteration, and we never break out of thewhile
loop.
I've submitted a patch to git that should correct this behavior.
You can follow the discussion there to see if they like my patch or if they decide there is a more appropriate way to resolve the problem.
With the patched version of the code, we see the following behavior instead:
$ git log -L :$:main.go
fatal: -L parameter '$' starting at line 1: no match