Git 专题 11 —— position
程序遇到bug的时候,我们需要快速定位。
定位有两种,第一种是定位bug在哪个提交上,第二种是定位特定文件的某一行是谁最近提交的。
bisect
有时候我们发现程序有bug,但是回退几个版本都不解决问题。说明这个bug是一次很老的提交导致的,也不知道当时怎么就没察觉。
那怎么办呢?继续一个一个版本的回退?
估计Linus Torvalds
会鄙视你吧。
为了专注于工作,不分心来鄙视你,Linus Torvalds
在git中内置了一套定位bug的命令。
大家都玩过猜数字游戏吧。主持人悄悄写下一个数,给大家一个数字区间,然后大家轮流开始切割,谁切到主持人写的那个数就要自罚三杯了。
对,这就是二分法。git利用二分法定位bug的命令是git bisect
。
使用
假设目前的git项目历史是这样的。
C0 -- C1 -- C2 -- C3 -- C4 -- C5 -- C6 -- C7 -- C8 -- C9(HEAD -> master)
这里面有一次commit藏了一个bug,但幸运的是,你不知道是哪一次。
运行git bisect start
命令,后跟你要定位的区间中最新的commit和最老的commit。
$ git bisect start HEAD C0
Bisecting: 4 revisions left to test after this (roughly 2 steps)
[ee27077fdfc6c0c9281c1b7f6957ea2b59a461dd] C4
然后你就发现HEAD指针自动的指向了C4
commit。如果范围是奇数位,那取中间就行了,如果范围是偶数位,则取中间更偏老的那个commit,就比如这里的C4
commit。
$ git bisect good
Bisecting: 2 revisions left to test after this (roughly 1 step)
[97cc0e879dc09796bd56cfd7c3a54deb41e447f6] C6
HEAD指针指向C4
commit后,你应该运行一下程序,如果没问题,那说明有bug的提交在它之后。我们只需要告诉git当前commit以及更老的commit都是好的。
然后HEAD指针就自动指向C6
commit。
继续在C6
commit运行程序,结果复现了bug。说明问题就出在C6
commit和C4
commit之间。
$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[a7e09bd3eab7d1e824c0338233f358cafa682af0] C5
将C6
commit标记为bad
之后,HEAD指针自动指向C5
commit。再次运行程序,依然能复现bug。话不多说,标记C5
commit为bad
。
$ git bisect bad
a7e09bd3eab7d1e824c0338233f358cafa682af0 is the first bad commit
因为C4
commit和C5
commit之间已经不需要二分了,git会告诉你,C5
commit是你标记为bad
的最早的commit。问题就应该出在C5
commit上。
git bisect reset
Previous HEAD position was a7e09bd... C5
Switched to branch 'master'
既然找到问题了,那就可以退出git bisect
工具了。
另外,git bisect old
和git bisect good
的效果相同,git bisect new
和git bisect bad
的效果相同,这是因为git考虑到,有时候开发者并不是想定位bug,只是想定位某个commit,这时候用good bad
就会有点别扭。
后悔
git bisect
确实很强大,但如果我已经bisect
若干次,结果不小心把一个good
commit标记为bad
,或者相反,难道我要reset
重来么?
git bisect
还有一个log
命令,我们只需要保存bisect
日志到一个文件,然后擦除文件中标记错误的日志,然后按新的日志重新开始bisect
就好了。
git bisect log > log.txt
该命令的作用是将日志保存到log.txt
文件中。
看看log.txt
文件中的内容。
# bad: [4d5e75c7a9e6e65a168d6a2663e95b19da1e2b21] C9
# good: [c2fa7ca426cac9990ba27466520677bf1780af97] add a.md
git bisect start 'HEAD' 'c2fa7ca426cac9990ba27466520677bf1780af97'
# good: [ee27077fdfc6c0c9281c1b7f6957ea2b59a461dd] C4
git bisect good ee27077fdfc6c0c9281c1b7f6957ea2b59a461dd
# good: [97cc0e879dc09796bd56cfd7c3a54deb41e447f6] C6
git bisect good 97cc0e879dc09796bd56cfd7c3a54deb41e447f6
将标记错误的内容去掉。
# bad: [4d5e75c7a9e6e65a168d6a2663e95b19da1e2b21] C9
# good: [c2fa7ca426cac9990ba27466520677bf1780af97] add a.md
git bisect start 'HEAD' 'c2fa7ca426cac9990ba27466520677bf1780af97'
# good: [ee27077fdfc6c0c9281c1b7f6957ea2b59a461dd] C4
git bisect good ee27077fdfc6c0c9281c1b7f6957ea2b59a461dd
然后运行git bisect replay log.txt
命令。
$ git bisect replay log.txt
Previous HEAD position was ad95ae3... C8
Switched to branch 'master'
Bisecting: 4 revisions left to test after this (roughly 2 steps)
[ee27077fdfc6c0c9281c1b7f6957ea2b59a461dd] C4
Bisecting: 2 revisions left to test after this (roughly 1 step)
[97cc0e879dc09796bd56cfd7c3a54deb41e447f6] C6
git会根据log从头开始重新bisect
,错误的标记就被擦除了。
然后就是重新做人啦。
blame
一个充分协作的项目,每个文件可能都被多个人改动过。当出现问题的时候,大家希望快速的知道,某个文件的某一行是谁最后改动的,以便厘清责任。
git blame
就是这样一个命令。blame
翻译成中文是归咎于
,这个命令就是用来甩锅的。
git blame
只能作用于单个文件。
$ git blame a.md
705d9622 (veedrin 2018-12-25 10:09:04 +0800 1) 第一行
74eff2ee (abby 2018-12-25 10:16:44 +0800 2) 第二行
a65b29bd (bob 2018-12-25 10:17:02 +0800 3) 第三行
ee27077f (veedrin 2018-12-25 10:19:05 +0800 4) 第四行
a7e09bd3 (veedrin 2018-12-25 10:19:19 +0800 5) 第五行
97cc0e87 (veedrin 2018-12-25 10:21:55 +0800 6) 第六行
67029a81 (veedrin 2018-12-25 10:22:15 +0800 7) 第七行
ad95ae3f (zhangsan 2018-12-25 10:23:20 +0800 8) 第八行
4d5e75c7 (lisi 2018-12-25 10:23:37 +0800 9) 第九行
它会把每一行的修改者信息都列出来。
第一部分是commit哈希值,表示这一行的最近一次修改属于该次提交。
第二部分是作者以及修改时间。
第三部分是行的内容。
如果文件太长,我们可以截取部分行。
$ git blame -L 1,5 a.md
705d9622 (veedrin 2018-12-25 10:09:04 +0800 1) 第一行
74eff2ee (abby 2018-12-25 10:16:44 +0800 2) 第二行
a65b29bd (bob 2018-12-25 10:17:02 +0800 3) 第三行
ee27077f (veedrin 2018-12-25 10:19:05 +0800 4) 第四行
a7e09bd3 (veedrin 2018-12-25 10:19:19 +0800 5) 第五行
或者这样写。
$ git blame -L 1,+4 a.md
705d9622 (veedrin 2018-12-25 10:09:04 +0800 1) 第一行
74eff2ee (abby 2018-12-25 10:16:44 +0800 2) 第二行
a65b29bd (bob 2018-12-25 10:17:02 +0800 3) 第三行
ee27077f (veedrin 2018-12-25 10:19:05 +0800 4) 第四行
但是结果不是你预期的那样是吧。1,+4
的确切意思是从1开始,显示4行。
如果有人重名,可以显示邮箱来区分。添加参数-e
或者--show-email
即可。
$ git blame -e a.md
705d9622 (veedrin@qq.com 2018-12-25 10:09:04 +0800 1) 第一行
74eff2ee (abby@qq.com 2018-12-25 10:16:44 +0800 2) 第二行
a65b29bd (bob@qq.com 2018-12-25 10:17:02 +0800 3) 第三行
ee27077f (veedrin@qq.com 2018-12-25 10:19:05 +0800 4) 第四行
a7e09bd3 (veedrin@qq.com 2018-12-25 10:19:19 +0800 5) 第五行
97cc0e87 (veedrin@qq.com 2018-12-25 10:21:55 +0800 6) 第六行
67029a81 (veedrin@qq.com 2018-12-25 10:22:15 +0800 7) 第七行
ad95ae3f (zhangsan@qq.com 2018-12-25 10:23:20 +0800 8) 第八行
4d5e75c7 (lisi@qq.com 2018-12-25 10:23:37 +0800 9) 第九行