如何使用git bisect找到第一个良好的提交?

113

我有以下问题:

  • master 版本正常工作
  • master 之前的最后一个标签版本(假设为last)存在一个错误
  • 同事需要为其 last 版本修补该错误

好的,让我们向朋友 git bisect 询问修复错误的版本:

git bisect start
git bisect bad last
git bisect good master

但这样是行不通的:

有些好的版本不是坏版本的祖先。
在这种情况下,git bisect 无法正常工作。
也许您混淆了好的和坏的版本?

有什么提示可以克服这个问题吗?我在文档中错过了什么吗?


1
我正在运行 git bisect run ... 来自动执行二分查找。因此,我没有机会只是交换“好”和“坏”这两个词(那太明显了)。如何使用 run 找到第一个良好的版本? - Daniel Böhmer
@DanielBöhmer:你需要在运行脚本时交换术语,是吗? - eckes
1
git bisect run 运行的脚本的退出代码是 goodbad 而不是字符串。请参见我刚刚发布的答案。 - Daniel Böhmer
@DanielBöhmer:那么,在这种情况下,你将不得不反转返回代码,不是吗? - eckes
正确,这就是我回答中所描述的。 - Daniel Böhmer
5个回答

125

从git 2.7版本开始,你可以使用参数--term-old和--term-new。

例如,你可以这样识别修复问题的提交:

git bisect start --term-new=fixed --term-old=unfixed
git bisect fixed master
git bisect unfixed $some-old-sha1

当你测试时,根据情况说出git bisect fixedgit bisect unfixed

旧的回答,适用于Git 2.7版本之前

为什么不创建一些别名,而是暂时训练自己认为坏意味着好,好意味着坏呢? 在~/.gitconfig中添加以下内容:

[alias]
        bisect-fixed = bisect bad
        bisect-unfixed = bisect good

你可以这样开始识别一个解决问题的提交:

$ git bisect start
$ git bisect-fixed master
$ git bisect-unfixed $some-old-sha1

在测试时,根据情况使用git bisect-fixedgit bisect-unfixed


6
顺便提一下,git 不允许您为子命令创建别名。因此需要使用破折号。如果将来有可能实现该功能,希望有人能更新这个答案。 - Michael Wolf
3
即使你使用别名,Git 的输出仍然不会改变,因此它仍会报告 "foo 是第一个错误提交",因此似乎仍然需要进行临时训练,不是吗? - ThomasW
2
公正的观点。(点赞了你的评论。)尽管如此,希望至少减轻一点额外的认知负荷,作为程序员,我们已经有很多负担了。 - Michael Wolf
1
@JonasKölker,这是个好主意。我使用了答案中建议的别名,以及您推荐的bisect-after = bisect badbisect-before = bisect good。现在我可以使用任何一组别名。在使用几次后,我们将看到我更喜欢哪一组。 - Gabriel Staples
2
这真的有效吗?我已经尝试过了,但它没有起作用,因为 git bisect 仍然假定成功的退出代码意味着“向前移动”,而失败的退出代码意味着“向后移动”,这只有在旧提交意味着成功,新提交意味着失败时才有效。我只能通过与一个脚本结合使用来使其工作,该脚本会还原构建的退出代码,例如这个 - quiram
显示剩余4条评论

50

我只想“欺骗”git,交换好与坏的意义。

换句话说,把“坏”的定义为不展现问题的内容,因此这不是基于它来创建补丁的“好”版本。

“好”和“坏”本来就是相当主观的概念,对吧? :)

git bisect start
git bisect good last
git bisect bad master

2
嗯,如果你想一想,其实没有普遍意义上的好或坏(甚至在宗教中也可能如此)...这只取决于你的目的。这样做并不算作欺骗 - 但也许 Git 的罪过(继续谈论宗教话题:D)是选择一个有争议的术语而不是更中立的“目标”/“起源”等。但是,哲学确实可以令人费解;-) - inger
这一定是我第一次听说错误可以是“好的”。 - MarcH
1
这就是我在发现这个问题之前所做的。我不会再这样做了。记住,在整个二分法出错之前只需要一个错误的答案。不要纠结于此。 - proski

28

如果你像我一样使用Perl的prove命令(运行自动测试)来运行git bisect run,那么你不可能只是简单地交换goodbad。测试的成功将被报告为退出代码。

我找到了一个有效的Bash语法来否定git bisect run运行的程序的退出代码:

git bisect start
git bisect bad HEAD                 # last revision known to PASS the tests
git bisect good $LAST_FAIL_REVISION # last revision known to FAIL the tests
git bisect run bash -c "! prove"

这使我第一次修改后 通过 了由 prove 运行的测试。


3
我同意,我宁愿不修改我的测试用例,所以这样很完美。 - seanlinsley
如果你想要找到包含某段文本的第一个提交,这个方法也非常有效 - 只需要将最后一行改为 git bisect run bash -c "! git grep 'some text to find' " 即可。 - Andy
1
@Andy,我认为查询日志比检查旧版本要快得多。git log -p -S '要查找的某些文本' 显示添加/删除该文本的所有差异,所以很可能是您想要的提交。 - Daniel Böhmer

11

现在Git允许你在未定义oldnew的情况下使用它们。你需要调用git bisect start命令,然后再通过适当的调用开始二分。

git bisect old <rev>
git bisect new <rev>

https://git-scm.com/docs/git-bisect#_alternate_terms

@MarcH 建议应该实现的基本上就是这个。


1
这是最相关的答案(适用于现代git)。根据您分享的链接,启动命令应该是:git bisect start --term-new fixed --term-old broken - Sam Protsenko
是的。这些选项是什么时候引入的?我想更新我的答案。 - Michael Wolf
@MichaelWolf 它们出现在版本2.7.0中。 - GKFX

6

Git 别名是个好主意,但是术语“fixed”和“unfixed”与“good”和“bad”一样存在问题:它们不能同时与回归和进展兼容。很容易找到适用于双方的词汇:只需从原始二分搜索术语中选择中性的词汇即可,不带任何关于什么是好或坏的先入之见。例如:

git config --global alias.bisect-high 'bisect bad'
git config --global alias.bisect-low  'bisect good'

使用这些中性词语,您始终可以键入:git bisect-high(或git bisect-upper,或git-bisect max,...任选!)无论您是在寻找回归还是修复。

很遗憾,git bisect的开发人员不能简单地重用任何现有的术语。一般来说,用户界面不是Git的关注点:http://stevebennett.me/2012/02/24/10-things-i-hate-about-git/


一些补丁系列正在进行优化,以允许git bisect使用任意一对术语而不是good和bad。... - MarcH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接