gitmatchingbisect

How to use "git bisect run" for locating first commit introducing a string into a file which only exists in some commits


Let's say I want to locate the first (oldest) Git revision which adds the line delta to text file file.

But I want to use git bisect for this rather than searching the output of git log -p because of performance reasons (huge repository).

First create a repository testrepo as a test case for demonstrating the search:

r=testrepo
git init $r && cd $r
dt='2022-12-07 11:30:00 +0100'
an='Guenther Brunthaler' ae='some_username@somewhere.example'
for add in alpha beta gamma delta epsilon zeta eta
do
    echo $add >> file
    git add file
    GIT_AUTHOR_NAME="$an" GIT_COMMITTER_NAME="$an" \
        GIT_AUTHOR_EMAIL="$ae" GIT_COMMITTER_EMAIL="$ae" \
        GIT_AUTHOR_DATE="$dt" GIT_COMMITTER_DATE="$dt" \
        git commit -m "Adding $add"
done
unset r dt am ae

This repository contains a file file to which the next Greek letter name has been appended in every revision.

Now we want to locate the commit which added the line delta.

This should be commit a2444b7, because:

$ git log --oneline | grep delta | tail -n 1
a2444b7 Adding delta

Does not work yet

Use the "git bisect" command like this:

relfile=file added=delta
initial=`git log --graph --pretty=%h | cut -d ' ' -f 2 | tail -n 1`

git bisect reset # optional: abort an already running bisect

git bisect start
git bisect bad HEAD
git bisect good $initial
git bisect run sh -c "test ! -e '${relfile?:}' || grep -qv '${added?:}' '${relfile?:}'"

where $initial is the commit ID of the initial commit, $added is the RegEx for locating the new feature within file $relfile.

The above command should display the ID of the commit introducing the feature:

b0ece99babe9ffb16b7792d4e810cb3cc9571385 is the first bad commit

But obviously, this is the wrong commit!

What can I do in order to make git bisect run report revision a2444b7 as the first bad commit?


Solution

  • The success exit status of grep tells that any lines would be printed printed. Usually, this coincides with whether a line that matches the pattern was found. But when you pass -v, successful exit happens when at least one non-matching line was found. That is not what you want.

    Use ! grep without -v to stop at the correct commit:

    git bisect run sh -c "test ! -e '${relfile?:}' || ! grep -q '${added?:}' '${relfile?:}'"