'git bisect start ...'(man) 和后续的 'git bisect (good|bad)'(man) 命令可能需要很长时间好的和坏的提交之间的修订范围很大并且包含很多合并提交,例如在git.git:
$ git rev-list --count v1.6.0..v2.28.0
44284
$ time git bisect start v2.28.0 v1.6.0
Bisecting: 22141 revisions left to test after this (roughly 15 steps)
[e197c21807dacadc8305250baa0b9228819189d4] unable_to_lock_die(): rename function from unable_to_lock_index_die()
real 0m15.472s
user 0m15.220s
sys 0m0.255s
运行时的大部分时间都花在do_find_bisection() 中,我们试图找到一个尽可能接近坏修订和好修订之间中间点的提交,即一个提交,其中可到达的提交数量在好坏范围是该范围内提交总数的一半。
因此,我们计算该范围内的每个提交在好坏范围内可以达到多少次提交,这对于线性历史来说是快速和容易的,即使线性范围内超过 300k 的提交在我的机器上处理的时间约为 0.3 秒。
唉,处理合并提交并不简单,而且相当昂贵,因为使用的算法似乎是二次的,导致上面显示的运行时间很长。
有趣的是,看看一个额外的提交可以带来多大的不同:
$ git rev-list --count v1.6.0^..v2.28.0
44285
$ time git bisect start v2.28.0 v1.6.0^
Bisecting: 22142 revisions left to test after this (roughly 15 steps)
[565301e41670825ceedf75220f2918ae76831240] Sync with 2.1.2
real 0m5.848s
user 0m5.600s
sys 0m0.252s
差异是由于尝试减少 1c4fea3a40 中添加的运行时的优化之一(“git-rev-list --bisect: optimization”,2007-03-21,Git v1.5.2-rc0 - - merge):
Another small optimization is whenever we find a half-way commit
(that is, a commit that can reach exactly half of the commits),
we stop giving counts to remaining commits, as we will not find
any better commit than we just found.
在第二个 'git bisect start'(man) 命令中,我们恰好在中途发现了一个提交,并且可以提前返回,但在第一种情况下,没有这样的提交,所以我们不能提前返回并最终计算好坏范围内所有提交的可达提交数。
但是,当我们有数千个提交时,找到准确中点并不是那么重要,几个提交或多或少不会对二分产生任何真正的影响。
因此,让我们放松对 halfway() 帮助程序的检查,将提交也考虑在确切中点的大约 0.1% 以内,并将函数相应地重命名为 approx_halfway()。
这将使我们能够在更大的好坏范围内尽早返回,即使在中途点没有提交,从而大大减少上述第一个命令的运行时间,从约 15 秒到 4.901 秒。
此外,即使有一个提交正好在中间点,我们仍然可能会在找到确切的中间点之前偶然发现那个 0.1% 范围内的提交,这使我们能够早一点返回,稍微减少了运行时间第二个命令从 5.848s 到 5.058s。
请注意,此更改不会影响包含约 2000 次或更少提交的好坏范围,因为整数运算导致 0.1% 的容差变为零;但是,如果范围那么小,那么计算所有提交的可达提交已经足够快了。
当然,这可能会改变在每个二等分步骤中选择哪些提交,进而可能会改变找到第一个错误提交所需的二分步骤。
如果必要的二等分步骤的数量经常增加,那么这种更改可能会适得其反,因为在每个步骤中构建和测试可能需要比节省的时间更长的时间。
OTOH,如果减少步数,那将是双赢。
所以我进行了一些测试,看看这种情况发生的频率:随机选择好的和坏的开始修订,至少有 50k 次提交,并在 git.git 之间随机选择第一个错误提交,并使用 'git bisect git merge-base run --is-ancestor HEAD $first_bad_commit' (man) 检查必要的二等分步数。
在使用和不使用此补丁的情况下重复所有这 1000 次后,我发现:
- 146 例比以前多一等分步骤,149 例少一步骤,而其余 705 例中的步骤数没有变化。
因此,在不可忽略的情况下,二分步数确实发生了变化,但从长远来看,平均步数似乎没有变化。
- 第一个 '
git bisect start'(man) 命令在 456 个案例中的速度提高了 3 倍以上,因此这种“在确切的中途点没有提交”的案例似乎很常见,值得关注关于。