Home > Net >  Why does '$' for funcname in git log -L cause an infinite search?
Why does '$' for funcname in git log -L cause an infinite search?

Time:12-06

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")
}
  1. Start gdb and set a breakpoint at the beginning of the while loop in line-range.c. Arrange to print the value of start 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)
    
  2. 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"
    
  3. 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.

  4. 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 the while 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
  • Related