Git
章节 ▾ 第二版

7.10 Git 工具 - 使用 Git 调试

使用 Git 调试

除了主要用于版本控制之外,Git 还提供了一些命令来帮助你调试源代码项目。由于 Git 被设计为可以处理几乎任何类型的文件,因此这些工具非常通用,但当出现问题时,它们通常可以帮助你查找错误或罪魁祸首。

文件注释

如果你在代码中发现了一个错误,并且想知道它是什么时候引入的以及原因,那么文件注释通常是你的最佳工具。它会显示最后修改任何文件中的每一行的提交。因此,如果你看到代码中的某个方法有错误,你可以使用 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 的另一个很酷的事情是,它不会显式跟踪文件重命名。它记录快照,然后尝试在事后找出隐式重命名了什么。其中一个有趣的功能是,您还可以要求它找出各种代码移动。如果您将 -C 传递给 git blame,Git 将分析您正在注释的文件,并尝试找出其中的代码片段最初来自何处(如果它们是从其他地方复制的)。例如,假设您将名为 GITServerHandler.m 的文件重构为多个文件,其中一个文件是 GITPackUpload.m。通过使用 -C 选项责备 GITPackUpload.m,您可以看到代码的哪些部分最初来自何处

$ 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 也会告诉您编写这些行的原始提交。

如果您一开始就知道问题所在,注释文件会有所帮助。如果您不知道是什么导致中断,并且自您知道代码正常工作的上次状态以来已经进行了几十次或数百次提交,那么您可能会求助于 git bisect 来获得帮助。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,并显示一些提交信息以及该提交中修改的文件,以便你可以找出可能引入此错误的操作

$ git bisect good
b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit
commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04
Author: PJ Hyett <[email protected]>
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

这是一个强大的工具,可以帮助你在几分钟内检查数百个提交以查找引入的错误。事实上,如果你有一个脚本,当项目正常时退出 0,当项目错误时退出非 0,则可以完全自动化 git bisect。首先,你再次通过提供已知的错误提交和正常提交来告诉它二分查找的范围。你可以通过使用 bisect start 命令列出它们来执行此操作(如果你愿意),首先列出已知的错误提交,然后列出已知的正常提交

$ git bisect start HEAD v1.0
$ git bisect run test-error.sh

这样做会自动在每个检出的提交上运行 test-error.sh,直到 Git 找到第一个错误的提交。你还可以运行类似 makemake tests 的命令,或运行任何为你运行自动化测试的命令。

scroll-to-top