- 11.4.6 二分查找:git bisect
- bad:[6652a0dce6a5067732c00ef0a220810a7230655e]Add Images for git treeview.
- good:[e80aa7481beda65ae00e35afc4bc4b171f9b0ebf]commit G.
- good:[0cd7f2ea245d90d414e502467ac749f36aa32cc4]commit C.
- good:[212efce1548795a1edb08e3708a50989fcd73cce]Commit D:merge G with H
- bad:[776c5c9da9dcbb7e463c061d965ea47e73853b6e]Commit B:merge D with E and F
- ! /bin/sh
11.4.6 二分查找:git bisect
1.使用二分查找
在下面的试验中定义坏提交的依据很简单,如果在doc目录中包含文件B.txt,则此版本是“坏”的。(这个示例太简陋,不要见笑,聪明的读者可以直接通过doc/B.txt文件就可追溯到B提交。)
下面开始通过手动测试(查找doc/B.txt存在与否),借助Git二分查找定位“问题”版本,具体操作步骤如下。
(1)首先确认工作在master分支。
$cd/path/to/my/workspace/gitdemo-commit-tree/
$git checkout master
Already on 'master'
(2)开始二分查找。
$git bisect start
(3)当前版本已经是“坏提交”,因为存在文件doc/B.txt。而G版本是“好提交”,因为不存在文件doc/B.txt。
$git cat-file-t master:doc/B.txt
blob
$git cat-file-t G:doc/B.txt
fatal:Not a valid object name G:doc/B.txt
(4)将当前版本(HEAD)标记为“坏提交”,将G版本标记为“好提交”。
$git bisect bad
$git bisect good G
Bisecting:5 revisions left to test after this(roughly 2 steps)
[0cd7f2ea245d90d414e502467ac749f36aa32cc4]commit C.
(5)自动定位到C提交。没有文件doc/B.txt,也是一个好提交。
$git describe
C
$ls doc/B.txt
ls:无法访问doc/B.txt:没有那个文件或目录
(6)标记当前版本(C提交)为“好提交”。
$git bisect good
Bisecting:3 revisions left to test after this(roughly 2 steps)
[212efce1548795a1edb08e3708a50989fcd73cce]Commit D:merge G with H
(7)现在定位到D版本,这也是一个“好提交”。
$git describe
D
$ls doc/B.txt
ls:无法访问doc/B.txt:没有那个文件或目录
(8)标记当前版本(D提交)为“好提交”。
$git bisect good
Bisecting:1 revision left to test after this(roughly 1 step)
[776c5c9da9dcbb7e463c061d965ea47e73853b6e]Commit B:merge D with E and F
(9)现在定位到B版本,这是一个“坏提交”。
$git bisect bad
Bisecting:0 revisions left to test after this(roughly 0 steps)
[83be36956c007d7bfffe13805dd2081839fd3603]commit E.
(10)现在定位到E版本,这是一个“好提交”。当标记E为好提交之后,输出显示已经成功定位到引入坏提交的最接近的版本。
$git bisect good
776c5c9da9dcbb7e463c061d965ea47e73853b6e is the first bad commit
(11)最终定位的坏提交用引用refs/bisect/bad标识。可以用如下方法切换到该版本。
$git checkout bisect/bad
Previous HEAD position was 83be369…commit E.
HEAD is now at 776c5c9…Commit B:merge D with E and F
(12)当对"Bug"定位和修复后,撤销二分查找在版本库中遗留的临时文件和引用。撤销二分查找后,版本库切换回执行二分查找之前所在的分支。
$git bisect reset
Previous HEAD position was 776c5c9…Commit B:merge D with E and F
Switched to branch 'master'
2.把“好提交”标记成了“坏提交”该怎么办?
在执行二分查找的过程中,一不小心就有可能犯错,将“好提交”标记为“坏提交”,或者相反。这将导致前面的查找过程也前功尽弃。为此,Git的二分查找提供了一个恢复查找进度的办法,具体操作过程如下。
(1)例如对提交E,本来是一个“好版本”却被错误的标记为“坏版本”。
$git bisect bad
83be36956c007d7bfffe13805dd2081839fd3603 is the first bad commit
(2)用git bisect log命令查看二分查找的日志记录。
把二分查找的日志保存在一个文件中。
$git bisect log>logfile
(3)编辑这个文件,删除记录了错误动作的行。
以井号(#)开始的行是注释。
$cat logfile
bad:[6652a0dce6a5067732c00ef0a220810a7230655e]Add Images for git treeview.
good:[e80aa7481beda65ae00e35afc4bc4b171f9b0ebf]commit G.
git bisect start 'master' 'G'
good:[0cd7f2ea245d90d414e502467ac749f36aa32cc4]commit C.
git bisect good 0cd7f2ea245d90d414e502467ac749f36aa32cc4
good:[212efce1548795a1edb08e3708a50989fcd73cce]Commit D:merge G with H
git bisect good 212efce1548795a1edb08e3708a50989fcd73cce
bad:[776c5c9da9dcbb7e463c061d965ea47e73853b6e]Commit B:merge D with E and F
git bisect bad 776c5c9da9dcbb7e463c061d965ea47e73853b6e
(4)结束正在进行的出错的二分查找。
$git bisect reset
Previous HEAD position was 83be369…commit E.
Switched to branch 'master'
(5)通过日志文件恢复进度,重启二分查找。
$git bisect replay logfile
We are not bisecting.
Bisecting:5 revisions left to test after this(roughly 2 steps)
[0cd7f2ea245d90d414e502467ac749f36aa32cc4]commit C.
Bisecting:0 revisions left to test after this(roughly 0 steps)
[83be36956c007d7bfffe13805dd2081839fd3603]commit E.
(6)再一次回到了提交E,这一次不要标记错了哦。
$git describe
E
$git bisect good
776c5c9da9dcbb7e463c061d965ea47e73853b6e is the first bad commit
3.二分查找使用自动化测试
Git的二分查找命令支持run子命令,可以运行一个自动化测试脚本,实现自动的二分查找。
如果脚本的退出码是0,正在测试的版本是一个“好版本”。
如果脚本的退出码是125,正在测试的版本被跳过。
如果脚本的退出码是1到127(125除外),正在测试的版本是一个“坏版本”。
为本例写一个自动化测试太简单了,无非就是判断文件是否存在,存在则返回错误码1,不存在则返回错误码0。测试脚本good-or-bad.sh如下:
! /bin/sh
[-f doc/B.txt]&&exit 1
exit 0
用此脚本实现自动化二分查找的过程非常简单,具体操作步骤如下。
(1)从已知的坏版本master和好版本G开始新一轮的二分查找。
$git bisect start master G
Bisecting:5 revisions left to test after this(roughly 2 steps)
[0cd7f2ea245d90d414e502467ac749f36aa32cc4]commit C.
(2)自动化测试,使用脚本good-or-bad.sh。
$git bisect run sh good-or-bad.sh
running sh good-or-bad.sh
Bisecting:3 revisions left to test after this(roughly 2 steps)
[212efce1548795a1edb08e3708a50989fcd73cce]Commit D:merge G with H
running sh good-or-bad.sh
Bisecting:1 revision left to test after this(roughly 1 step)
[776c5c9da9dcbb7e463c061d965ea47e73853b6e]Commit B:merge D with E and F
running sh good-or-bad.sh
Bisecting:0 revisions left to test after this(roughly 0 steps)
[83be36956c007d7bfffe13805dd2081839fd3603]commit E.
running sh good-or-bad.sh
776c5c9da9dcbb7e463c061d965ea47e73853b6e is the first bad commit
bisect run success
(3)定位到的“坏版本”是B。
$git describe refs/bisect/bad
B