如何让 git bisect 忽略合并的分支?
我知道 git bisect 在设计上是分支感知的,所以如果在好的提交 G 和坏的提交 B 之间合并到一个分支中,它也需要考虑这些更改,因为错误可能会发生包含在分支中。
就我而言,我有一个依赖项作为侧分支,并且我不时合并对我的主项目的更改。该依赖项可以被认为是一个与我的主项目具有不同运行方式、不同构建系统等的库,但我仍然希望通过合并到主分支来对其进行最近的更改。
问题是,在这种情况下进行平分时,您最终会在依赖项的提交中出现不可编译的提交。
我真的只想在进行二分时将每个分支合并视为单个提交。
到目前为止,我发现的解决方法是使用 git log --first-parent 制作有效提交 G..B 的列表,然后在二等分时,如果当前提交不在该列表中,则执行 git bisect skip 。但这需要花费很多时间(每次跳过都要签出/更改大量文件)。
所以问题是:是否有任何方法可以使用 git bisect 执行 --first-parent 或提供我认为有效的提交列表,以避免检查我已经知道不可编译的分支?我们如何只检查图中标记为 o 的提交?
G---o---o---o---o---o---o---B main project branch / / / x---x---x---x---x dependency \ / x' dependency project taskbranch
编辑:为了清晰起见添加了图表
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
是的:使用 Git 2.29 (Q4 2020),“
git bisect
"(man) 学习“--first-parent
”选项来查找沿第一个父链的第一个断裂点。请参阅提交 ad464a4、提交 e8861ff, 提交 be5fe20、提交 0fe305a、提交 15a4802(2020 年 8 月 7 日),作者:Aaron Lipman (
alipman88
)。(由 Junio C Hamano --
gitster
-- 合并于 提交 47f0f94,2020 年 8 月 17 日)git bisect
现在包含在其Yes: with Git 2.29 (Q4 2020), "
git bisect
"(man) learns the "--first-parent
" option to find the first breakage along the first-parent chain.See commit ad464a4, commit e8861ff, commit be5fe20, commit 0fe305a, commit 15a4802 (07 Aug 2020) by Aaron Lipman (
alipman88
).(Merged by Junio C Hamano --
gitster
-- in commit 47f0f94, 17 Aug 2020)git bisect
now includes in its man page:我想到了一个可能的解决方案,但我仍然希望找到更优雅的解决方案:
将所有合并到主分支的所有第二父级标记为良好
将每个合并的所有远程父级标记为良好意愿认为它们之前的所有提交都是好的(因此被二等分跳过)。该解决方案还应该足够通用,能够处理来自多个分支的多个合并,只保留主分支上的提交。
(用相关提交替换 GOOD 和 BAD)
sed 中的正则表达式删除每行的前两个提交;合并提交本身和第一个父级,留下其余的父级(通常只是第二个)。
鉴于问题中所述的历史记录,运行单行代码会给您带来:
这将使 bisect 仅遍历主分支上的提交:
如果任何合并分支间接导致问题,当您通过二等分测试合并提交,这可能是进一步调查该分支的原因。
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 preceding 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.
(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 one-liner would give you:
This would make bisect traverse only the commits on the main branch:
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.
我也一直在寻找类似的东西。据我所知, git rev-list --bisect --first-parent 似乎可以做你想做的事情,而 rev-list 的文档意味着
- -bisect
选项是 bisect 内部使用的选项 - 但是让 git bisect 将该标志添加到其对 rev-list 的调用中似乎不那么简单:bisect 命令是由 shell 实现的脚本 git-bisect,它反过来使用内置命令 bisect--helper 来实际执行有趣的部分(“计算,显示和结帐”说评论......),显然基于一堆.git/ 中的魔法状态文件。这似乎是 rev-list 命令重用了 bisect--helper 中的代码,而不是您可能期望的相反。
因此,我认为您必须扩展 bisect--helper 代码的提交过滤才能做到这一点。
作为一种解决方法,类似这样的方法可能会起作用:在 bisect 为您检查某些内容后,使用 git rev-list --bisect --first-parent 重置为不同的内容,测试它并将其标记为良好/bad/skip 并从那里继续。
I've been looking for something like this too. As far as I've got is that
git rev-list --bisect --first-parent
seems to do what you want to, and the docs for rev-list implies that the--bisect
option is what bisect uses internally - but gettinggit bisect
to add that flag to its call(s) to rev-list seems less trivial:The bisect command is implemented by a shell script git-bisect, which in turn uses a builtin command
bisect--helper
to actually do the interesting part ("computation, display and checkout" says the comment...), apparently based on a bunch of magic state files in .git/. And it seems to be the rev-list command that is reusing code from bisect--helper rather than the other way around as you might expect.So, you'd have to extend the bisect--helper code's commit filtering to do it, I think.
As a workaround, something like this might work: after bisect checks something out for you, reset to a different one using
git rev-list --bisect --first-parent
, test that and mark it good/bad/skip and continue from there.如果历史记录看起来像:
其中 L 是坏的,B 是好的,并且您想忽略 DEFG 分支,那么运行
where B、C、G 和 L 是各自的 shas 似乎可以满足您的要求。
If the history looks like:
where L is bad, B is good, and you want to ignore the DEFG branch, then running
where B,C,G,and L are the respective shas seems to do what you want.
你可以使用移植让 git 将你的历史视为线性。要线性化整个第一个父级历史记录,您可以使用:
完成二等分后,只需删除移植文件即可。
You can make git treat you history as linear using grafts. To linearize the whole first parent history you can use:
Just drop the grafts file when you're done with the bisection.
Björn Steinbrink 的答案效果很好,但最近开始打印这个:
这是他的解决方案的更现代版本,使用“git Replace”而不是移植:
不幸的是,对于大型存储库来说,它要慢得多(150k 提交大约需要 3 分钟);
git Replace
似乎还没有批量模式。您可能希望将修订列表限制为仅二等分范围内的提交。要在完成后删除替换内容,您可以
rm .git/refs/replace/*
。Björn Steinbrink's answer works great, but recently started printing this:
Here's a more modern version of his solution using "git replace" instead of grafts:
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/*
.您可以通过运行以下命令来指示 git-bisect 仅遍历合并提交的第一个父级:
You can instruct git-bisect to only go through the first-parents of the merge commits by running the following command:
因此,合并提交的第一个父级始终是同一分支的假设并不总是正确的。例如,如果您离开主题分支并将 master 合并到它以获取最新信息(因此对于此合并提交,第一个父级是主题分支),然后签出 master 并将主题合并回它,您会得到快进合并,只会将 master 移动到将第一个父级作为主题分支的合并提交。这似乎是做作的,但它实际上是一个非常正常的工作流程 - 我总是将 master 合并到我的分支中,这样我合并回 master 将是一个微不足道的合并(即,可快速转发)(对不起 James,总是忘记重新设置它) 。
我发现有一种方法可以帮助确定哪个父级是您的分支 - 合并提交注释本身。默认情况下,git 会编写一个合并提交注释,说明哪个分支被合并,您可以使用它来推断哪个父分支是您感兴趣的分支,只要执行合并提交的人没有覆盖此合并提交注释。
所以我尝试了一下,它似乎对我有用。我编写了一个 Python 脚本来帮助在 github 上完成此操作。如果您运行此脚本,它将尝试向后跟踪并跟踪您的分支,并发出提交 ID 列表,这些提交 ID 是合并到您的分支中的分支的提示。有了这个列表,您可以将它们赋予“git bisect good”,然后 bisect 将忽略二分中合并分支上的所有提交,从而实现所需的结果。
So, the assumption that the first parent of a merge commit is always the same branch isn't always correct. For instance, if you are off on a topic branch and merge master to it to get up to date (so for this merge commit, the first parent is the topic branch) and then checkout master and merge topic back to it, you get a fast forward merge, which just moves master to the merge commit that had first parent as your topic branch. This may seem contrived, but its actually a pretty normal workflow - I always merge master into my branch so that my merge back to master will be a trivial merge (i.e., fast forward-able) (Sorry James, always forget to rebase it).
There is one way that I've found to help figure out which parent is your branch - the merge commit comment itself. By default, git composes a merge commit comment that says which branch was merged and you can use this to deduce which parent is the branch you are interested in, so long as the person doing the merge commit didn't overwrite this merge commit comment.
So I tried this out and it seems to work for me. I wrote a Python script to help do this on github. If you run this script, it will try to trace backwards and follow your branch and emit a list of commit ids that are the tips of the branches that are merged into your branch. With this list, you can give these to "git bisect good" and bisect will then omit all of the commits on the merged branches from your bisection, achieving the desired result.
但是,根据您当前的解决方案,我没有看到一站式方法:
git bisect skip 可以获取要跳过的提交列表。
git logbranchname 将列出分支branchname 上的提交。
所以这应该让您指定提交列表。
如果您的依赖项和主代码位于不同的文件系统空间中,您可以指定要包含在 git bisect start 中的路径。根据您的代码布局,这可能是最佳选择。 (几乎可以肯定的是,如果您有可能包含该错误的文件列表!)
手册页有详细信息;看看也有有趣的阅读。
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.
您也许可以使用 git bisect start --no-checkout 来避免实际将提交检出到工作树中。然后我怀疑您可以对您真正想要测试的提交(即仅主分支上的第一个父提交)执行 git checkout BISECT_HEAD 。我还没有尝试过这个,但我希望它会起作用。
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 dogit 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.