5

I have a repository with a complicated tree of branches and merges, and I want to use git bisect to find when a bug was introduced.

I have a good commit and bad commit to start the bisect, where the good commit is an ancestor of the bad one.

I'd expect git bisect to go to commits which have the good commit as an ancestor, but it doesn't (using git 2.21.0).

Currently I'm doing the bisect manually by keeping a list of commits and using git rev-list --ancestry-path GOOD..BAD and picking a commit in the middle. Is there a way to automate this with git bisect? Does it have a flag to stay in the ancestry path?

Reasoning for bisecting in the ancestry path first

The features relating to the bug one is searching may not exist in all branches, making the check irrelevant there. They should exist in the ancestry path though.

Once bisecting the ancestry path is done, one would probably get a merge commit as the blame, and they would know the bug originates from that branch. This already teaches one a lot about the bug.

To continue bisecting over the branch which introduced the bug, one needs to test not each commit on its own (as they don't necessarily have the features needed for the bug to surface), but one should merge each one with the last good commit and then check if the bug exists. Then one can pinpoint a specific commit, that when merged with the good commit, introduces the bug.

Note that I've done this process several times and it was very useful to me, and I'm merely looking for methods to make it more convinient.

yairchu
  • 23,680
  • 7
  • 69
  • 109

2 Answers2

2

git bisect doesn't have an option to follow the ancestry path. Such an option isn't generally useful for several reasons:

  • Most often, users don't know for certain where the regression was introduced. Consequently, complex options to control the path traversal are unlikely to be used.
  • In most cases, you'll omit large numbers of possible branches because you'll skip over them. If you had 1024 individual branches with a single commit merged into your master branch, git bisect would omit half of those from even being considered after the first test and it would still take only twelve runs to find the problematic commit.
  • Frequently, the problematic commit is on a merged-in branch and is not on the ancestry path. This is true for most cases where you don't have a linear history (i.e., merge-based workflows).

Unless you have specific knowledge about your repository such that commits not on the ancestry path will produce false positives or false negatives, it's usually fine to just let git rebase do its thing. Since its behavior is O(log N), the additional costs tend to be negligible. You can, however, use git bisect skip RANGE to specify a range to skip if need be.

If you can automate the testing of your branch with a shell script or command, you can use git bisect run with that shell script. It should exit 0 if the commit is good, 125 if the commit should be skipped, and any other code between 1 and 127 inclusive if the commit is bad. This can be used to avoid checking commits that are not suitable for whatever reason.

bk2204
  • 64,793
  • 6
  • 84
  • 100
  • I've added a "reasoning" section to my answer, to explain why such an option (should actually be the default imho) is useful. – yairchu Apr 09 '20 at 08:25
  • I've updated my answer to explain how you can use `git bisect run` to do what you want if you can script your test. My answer still stands: Git doesn't have this feature, and upstream will likely tell you what I've mentioned as to why it doesn't. – bk2204 Apr 09 '20 at 23:17
0

You can do this easily with the existing bisect, instead of git bisect good agoodone do

git bisect good $(git rev-list \
        --ancestry-path --boundary agoodone..thebadone | sed -n s/-//p)
jthill
  • 55,082
  • 5
  • 77
  • 137
  • I don't understand, wouldn't that mark all the ancestry path as good? – yairchu Apr 09 '20 at 15:24
  • Whoops, deleted my previous comment, I misread your comment completely. No, the sed picks only the boundary commits, all the commits that rev-list didn't examine further because they are not on the ancestry path. So this just explicitly supplies the boundary commits that the ancestry path option finds – jthill Apr 09 '20 at 19:56