-
1. 起步
-
2. Git 基础
-
3. Git 分支
-
4. 服务器上的 Git
- 4.1 协议
- 4.2 在服务器上部署 Git
- 4.3 生成 SSH 公钥
- 4.4 架设服务器
- 4.5 Git Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 第三方托管服务
- 4.10 小结
-
5. 分布式 Git
-
A1. 附录 A: Git 在其他环境
- A1.1 图形界面
- A1.2 Visual Studio 中的 Git
- A1.3 Visual Studio Code 中的 Git
- A1.4 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中的 Git
- A1.5 Sublime Text 中的 Git
- A1.6 Bash 中的 Git
- A1.7 Zsh 中的 Git
- A1.8 PowerShell 中的 Git
- A1.9 小结
-
A2. 附录 B: 在应用程序中嵌入 Git
-
A3. 附录 C: Git 命令
7.10 Git 工具 - 使用 Git 进行调试
使用 Git 进行调试
除了作为版本控制工具,Git 还提供了一些命令来帮助你调试源代码项目。由于 Git 被设计为可以处理几乎任何类型的内容,这些工具非常通用,但在事情出错时,它们往往能帮你找出 Bug 或罪魁祸首。
文件注解 (File Annotation)
如果你在代码中定位到了一个 Bug,并想知道它是什么时候引入的以及原因是什么,文件注解(File Annotation)通常是你最好的工具。它会显示文件中每一行代码最后一次被修改是在哪次提交中。因此,如果你发现代码中的某个方法存在 Bug,可以使用 git blame 对该文件进行注解,从而确定是哪次提交导致了该行的引入。
下面的示例使用 git blame 来确定 Linux 内核顶层 Makefile 中某些行对应的提交者和提交信息。此外,还使用了 -L 选项将注解输出限制在文件的第 69 行到第 82 行之间:
$ git blame -L 69,82 Makefile
b8b0618cf6fab (Cheng Renquan 2009-05-26 16:03:07 +0800 69) ifeq ("$(origin V)", "command line")
b8b0618cf6fab (Cheng Renquan 2009-05-26 16:03:07 +0800 70) KBUILD_VERBOSE = $(V)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 71) endif
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 72) ifndef KBUILD_VERBOSE
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 73) KBUILD_VERBOSE = 0
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 74) endif
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 75)
066b7ed955808 (Michal Marek 2014-07-04 14:29:30 +0200 76) ifeq ($(KBUILD_VERBOSE),1)
066b7ed955808 (Michal Marek 2014-07-04 14:29:30 +0200 77) quiet =
066b7ed955808 (Michal Marek 2014-07-04 14:29:30 +0200 78) Q =
066b7ed955808 (Michal Marek 2014-07-04 14:29:30 +0200 79) else
066b7ed955808 (Michal Marek 2014-07-04 14:29:30 +0200 80) quiet=quiet_
066b7ed955808 (Michal Marek 2014-07-04 14:29:30 +0200 81) Q = @
066b7ed955808 (Michal Marek 2014-07-04 14:29:30 +0200 82) endif
注意,第一个字段是最后修改该行的提交的 SHA-1 部分哈希值。接下来的两个字段是从该提交中提取的值——作者姓名和提交日期——因此你可以轻松查看是谁在什么时候修改了该行。之后是行号和文件内容。还要注意 ^1da177e4c3f4 提交行,其中 ^ 前缀表示这些行是在仓库的初始提交中引入的,并且此后从未更改过。这有点令人困惑,因为你已经见识过 Git 使用 ^ 修改提交 SHA-1 的至少三种不同方式了,但在本处,它的含义正如上述。
Git 的另一个酷炫之处在于它不会显式跟踪文件重命名。它记录的是快照,然后在事后尝试推断出发生了什么重命名。这项功能的一个有趣之处在于,你可以要求它找出各种代码移动。如果你向 git blame 传递 -C 参数,Git 会分析你正在注解的文件,并尝试推断出其中的代码片段最初来自哪里(如果它们是从其他地方复制过来的)。例如,假设你正在将一个名为 GITServerHandler.m 的文件重构为多个文件,其中一个是 GITPackUpload.m。通过对 GITPackUpload.m 使用带 -C 选项的 blame 命令,你可以查看到代码片段最初的来源。
$ git blame -C -L 141,153 GITPackUpload.m
f344f58d GITServerHandler.m (Scott 2009-01-04 141)
f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC
f344f58d GITServerHandler.m (Scott 2009-01-04 143) {
70befddd GITServerHandler.m (Scott 2009-03-22 144) //NSLog(@"GATHER COMMI
ad11ac80 GITPackUpload.m (Scott 2009-03-24 145)
ad11ac80 GITPackUpload.m (Scott 2009-03-24 146) NSString *parentSha;
ad11ac80 GITPackUpload.m (Scott 2009-03-24 147) GITCommit *commit = [g
ad11ac80 GITPackUpload.m (Scott 2009-03-24 148)
ad11ac80 GITPackUpload.m (Scott 2009-03-24 149) //NSLog(@"GATHER COMMI
ad11ac80 GITPackUpload.m (Scott 2009-03-24 150)
56ef2caf GITServerHandler.m (Scott 2009-01-05 151) if(commit) {
56ef2caf GITServerHandler.m (Scott 2009-01-05 152) [refDict setOb
56ef2caf GITServerHandler.m (Scott 2009-01-05 153)
这非常有用。通常情况下,你会得到复制代码时的那次提交作为原始提交,因为那是你第一次在该文件中触碰这些行。而 Git 可以告诉你最初编写这些行时的原始提交,即使它在另一个文件中。
二分查找 (Binary Search)
如果你一开始就知道问题出在哪里,那么注解文件很有帮助。如果你不知道哪里出错了,而且距离你确定代码正常工作的状态已经过去了数十次甚至数百次提交,那么你很可能会求助于 git bisect。bisect 命令会在你的提交历史中进行二分查找,从而帮助你尽快识别出是哪次提交引入了问题。
假设你刚刚向生产环境发布了代码,收到了关于开发环境中未出现过的 Bug 报告,而你无法想象代码为什么会这样。当你回到代码时,发现可以重现该问题,但无法弄清楚哪里出了错。你可以通过 bisect(二分查找) 来找出问题。首先运行 git bisect start 来启动过程,然后使用 git bisect bad 告诉系统当前的提交是有问题的。接着,你必须使用 git bisect good <good_commit> 告诉系统上一个已知的正常状态是什么。
$ git bisect start
$ git bisect bad
$ git bisect good v1.0
Bisecting: 6 revisions left to test after this
[ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] Error handling on repo
Git 计算出在你标记为最后一个正常提交(v1.0)和当前错误版本之间大约有 12 次提交,并为你检出了中间的那一次。此时,你可以运行测试来查看该提交是否存在问题。如果存在,说明问题是在该中间提交之前引入的;如果不存在,说明问题是在该中间提交之后引入的。事实证明这里没有问题,你可以通过输入 git bisect good 告诉 Git,并继续你的查找旅程。
$ git bisect good
Bisecting: 3 revisions left to test after this
[b047b02ea83310a70fd603dc8cd7a6cd13d15c04] Secure this thing
现在你位于另一个提交上,处于你刚测试的提交和错误提交的中间位置。你再次运行测试,发现这个提交也是坏的,因此使用 git bisect bad 告诉 Git。
$ git bisect bad
Bisecting: 1 revisions left to test after this
[f71ce38690acf49c1f3c9bea38e09d82a5ce6014] Drop exceptions table
这个提交是好的,现在 Git 拥有了确定问题引入位置所需的所有信息。它会告诉你第一个坏提交的 SHA-1,并显示一些提交信息以及在该提交中修改了哪些文件,这样你就可以弄清楚发生了什么从而引入了这个 Bug。
$ git bisect good
b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit
commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04
Author: PJ Hyett <pjhyett@example.com>
Date: Tue Jan 27 14:48:32 2009 -0800
Secure this thing
:040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730
f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M config
当你完成后,应该运行 git bisect reset 将 HEAD 重置到开始之前的位置,否则你会处于一种奇怪的状态。
$ git bisect reset
这是一个强大的工具,可以帮助你在几分钟内检查数百次提交以找出引入的 Bug。事实上,如果你有一个脚本,在项目正常时退出码为 0,在项目损坏时退出码为非 0,你就可以完全自动化 git bisect。首先,再次通过提供已知的坏提交和好提交来告诉它二分查找的范围。如果你愿意,可以在 bisect start 命令中列出它们,第一个列出已知的坏提交,第二个列出已知的好提交。
$ git bisect start HEAD v1.0
$ git bisect run test-error.sh
这样做会自动在每个检出的提交上运行 test-error.sh,直到 Git 找到第一个损坏的提交。你也可以运行类似 make 或 make tests 或任何你用来运行自动化测试的命令。