Changes made by merge commits not found by `git bisect` or `git log -S --all`

Why does git log --all -S TERM not list the merge commit?

You must add the -m option to it:

git log -m --all -S TERM

In order to understand why a special option is needed in that case, let's first look at the operation of git log -p which must include the changes in the listed history. As you can easily check, by default for merge commits changes are NOT shown. The reason is that a change is the difference between two revisions whereas for a merge commit we have three (or more) revisions. The -m option to git log addresses that problem:

Diff Formatting

...

-m

This flag makes the merge commits show the full diff like regular commits; for each merge parent, a separate log entry and diff is generated. An exception is that only diff against the first parent is shown when --first-parent option is given; in that case, the output represents the changes the merge brought into the then-current branch.

Then, assuming that git log -S works as if by processing the diff returned by git log -p you should admit that by default the merge commits would be excluded from the results. However, fortunately you can combine the -S and -m options in git log and it works as expected.


Solution using git bisect

git bisect start $badCommit $goodCommit
git bisect run bash -c "! git merge-base --is-ancestor $goodCommit HEAD || git grep -q $TERM"
bit bisect reset

Note: this solution use git bisect run for simplicity. You use git bisect in "interactive" mode too. See Explanation section below.

Main idea

The idea is to restrict the commits analyzed by the bisect algorithm to the ones in the ancestry path.

The idea was suggested by CB Bailey in this comment, although I didn't find a way using git rev-list.

Explanation

For the part about git bisect, the explanation is a bit tricky. It is based on how the bisect algorithm works. The algorithm makes the assumption that all ancestors of a good commit are good. Thus, when testing a specific commit in git bisect, you must mark commits that are not descendants of the good commit as good too. This typically not the case when using only a simple grep function. You can tell if commits are descendant of another commit using git merge-base --is-ancestor $goodCommit HEAD (more details on How can I tell if one commit is an ancestor of another commit (or vice-versa)? or checking if a commit is an ancestor of another)

Tags:

Git

Git Bisect