How do you get git bisect to ignore merged branche

2019-01-21 22:29发布

I'm aware that git bisect is branch-aware by design, so that if between good commit, G, and bad commit, B, you merged in a branch, it needs to take those changes into consideration as well, as the bug may be contained in the branch.

In my case I have a dependency as a side branch and I merge in changes to my main project from time to time. The dependency can be considered a library that has a different way of running, different build-system etc. from my main project, but I still want recent changes from it via merges to the main branch.

The problem is then that while bisecting in this scenario, you end up on non-compilable commits in the commits from the dependency.

I would really just want to consider each branch merge as a single commit while doing the bisection.

A workaround I've found so far is making a list of valid commits G..B with git log --first-parent, and then while bisecting, do git bisect skip if the current commit isn't in that list. That takes a lot of time though (lots of files to checkout/change for each skip).

So the question is: Is there any way of doing --first-parent with git bisect or providing a list of commits i feel are valid to be able to avoid checking out branches I know already are not compilable? How do we only check the commits marked o in the diagram?

G---o---o---o---o---o---o---B  main project branch
   /       /       / 
  x---x---x---x---x            dependency
           \ /
            x'                 dependency project taskbranch

Edit: added diagram for clarity

9条回答
手持菜刀,她持情操
2楼-- · 2019-01-21 22:48

Björn Steinbrink's answer works great, but recently started printing this:

hint: Support for /info/grafts is deprecated
hint: and will be removed in a future Git version.
hint: 
hint: Please use "git replace --convert-graft-file"
hint: to convert the grafts into replace refs.
hint: 
hint: Turn this message off by running
hint: "git config advice.graftFileDeprecated false"

Here's a more modern version of his solution using "git replace" instead of grafts:

git rev-list --first-parent --merges --parents HEAD | \
  while read COMMIT PARENT1 PARENT2; do 
    git replace --graft $COMMIT $PARENT1; 
  done

Unfortunately, it is much slower for large repos (around 3 minutes for 150k commits); git replace doesn't seem to have a bulk-mode yet. You might want to restrict the rev-list to only the commits in scope of your bisect.

To remove the replacements when you are done, you can rm .git/refs/replace/* .

查看更多
老娘就宠你
3楼-- · 2019-01-21 22:53

I thought of one possible solution, but I'm still hoping to find something more elegant:

Mark all second-parents of all merges into the main branch as good

Marking all remote parents of each merge as good will consider all the commits preceeding them as good (and as such skipped by bisect). This solution should also be generic enough to handle multiple merges from multiple branches, leaving only the commits on the main branch.

git rev-list --first-parent --merges --parents GOOD..BAD \
| sed 's/^[^ ]\+ [^ ]\+ //' \
| xargs git bisect good

(replace GOOD and BAD with the relevant commits)

The regex in sed removes the first two commits of each line; the merge commit itself, and the first parent, leaving the rest of the parents (usually just the second one).

Given the history stated in the question, running the oneliner would give you:

G---o---o---o---o---o---o---B  main project branch
   /       /       / 
  G---x---G---x---G            dependency
           \ /
            x'                 dependency project taskbranch

This would make bisect traverse only the commits on the main branch:

    o---o---o---o---o---o

If any of the merged branches are indirectly the cause of the problem, it will be discovered when you test the merge commit via bisect, which could be reason to investigate further on that branch.

查看更多
太酷不给撩
4楼-- · 2019-01-21 22:53

You can make git treat you history as linear using grafts. To linearize the whole first parent history you can use:

git rev-list --first-parent --merges --parents HEAD | cut -d' ' -f1,2 > .git/info/grafts

Just drop the grafts file when you're done with the bisection.

查看更多
爱情/是我丢掉的垃圾
5楼-- · 2019-01-21 22:58

You can instruct git-bisect to only go through the first-parents of the merge commits by running the following command:

git bisect skip $(comm -23 <(git rev-list G | sort) <(git rev-list --first-parent G | sort))
查看更多
\"骚年 ilove
6楼-- · 2019-01-21 23:02

You might be able to use git bisect start --no-checkout to avoid having to actually checkout the commit into the working tree. Then I suspect that you can do git checkout BISECT_HEAD for commits that you actually want to test (i.e. only first parent commits on the main branch). I have not tried this but I hope it would work.

查看更多
爱情/是我丢掉的垃圾
7楼-- · 2019-01-21 23:05

I don't see a one-step method, however, based on your current solution: git bisect skip can take a list of commits to skip. git log branchname will list commits on branch branchname. So this should let you specify the list of commits.

If your dependency and your main code live in different filesystem spaces, you can specify the paths to include with git bisect start. Depending on your code layout, that may be the best option. (Almost certainly is if you have a list of files that may contain the bug!)

The man page has details; the see also there is interesting reading, too.

查看更多
登录 后发表回答