好的,情况是这样的:
几年前,我们对代码库中的多个文件进行了多次更改,并且一次全部提交。在这些更改中的某个地方隐藏了一个错误。使用 git bisect,我很快就能找到罪魁祸首的提交,但该提交中的大量更改让我不那么热情了。
使用 git bisect 找到一个错误的提交是一件轻而易举的事,但是一旦找到,追踪使这一切变得繁荣的单一更改的最佳方法是什么?将受影响的文件一个一个恢复到以前的版本?
除非您非常了解该大型提交中发生的所有更改,否则这可能非常乏味。
通常,一个非常大(坏大)的提交涉及许多不同的更改。您需要做的是在概念上隔离所有这些更改并重写不同的提交。
我建议根据以下 4 个标准分解更改:
[新] 涉及与单独识别的技术级别功能相关的所有代码(与可能涉及多个技术级别功能的用户级别相反)
[RFG] 任何行为不变的变化。保留执行的行为和 API(接口)
[CHG] 任何代表规范/要求变化的实施
[FIX] 任何可能改变行为以使其符合编码背后意图的更改。
然后,git-wise 是你需要做的:
git checkout <bad commit SHA1> -b CULPRIT
这将创建一个“罪魁祸首”分支。我总是将此作为参考,因为您可能需要对以下步骤执行许多繁琐的迭代。作为旁注,沿途保留部分引用会有所帮助(作为分支或标签)。
git reset HEAD^ --mixed
这将撤消提交,就好像提交中的所有更改都作为对先前提交的未暂存更改的补丁应用一样。然后使用
git add --patch
您可以更改这些更改的子集。不要犹豫,使用 [s]plit 选项逐行单独选择更改。有时,您无法避免一个版本来手动选择您想要的更改。并在我上面建议的 NEW,RFG,CHG,FIX 方案中分解为多个提交,并根据需要重写尽可能多的提交。
请注意:
...因为目标是使平分工作。此外,通过 git diff 确保您的新提交与旧提交相同,新的 HEAD 提交与 CULPRIT 相同,以确保您没有引入进一步的更改。
一开始这很痛苦,需要大量练习,但一旦你足够擅长这样做,你将成为整个团队崇拜的调试神,然后可以以 NEW 的形式传播小提交的福音, CHG、RFG 和 FIX。
您始终可以从该提交创建一个分支,将提交拆分为较小的提交,然后从那里继续进行二分。
另外,请记住,这git bisect
只是缩小导致问题的变化的工具。它并不能免除您必须分析问题、定位错误并提出修复的责任。
由于错误提交中只有几个文件,因此我选择了一种有点幼稚的方法来解决它。从接受的答案中得出,这就是我最终要做的事情:
1)准备提交的非暂存版本:
git checkout <bad commit SHA1> -b CULPRIT
git reset HEAD^ --mixed
2)暂存另一个文件:
git add <filename>
3) 暂存剩余文件,保持暂存文件完整:
git stash --keep-index
4)如果错误仍然没有显示:
git stash pop
5) 重复步骤 2、3 和 4 直到出现错误,这意味着之前暂存的文件是罪魁祸首
由于罪魁祸首文件中有几个不相关的更改,我选择继续消除过程,但这次是在代码行(或“hunk”)级别:
6)在罪魁祸首文件中暂存各个代码块:
add --patch <culprit filename>
7) 重复步骤 3 和 4 直到 bug 出现,这意味着之前上演的大块头是罪魁祸首
我认为我可以更优雅地完成此操作并使用 git bisect,但由于错误提交中只有 7 个受影响的文件,我决定手动进行消除。找到错误代码后,我删除了本地 CULPRIT 分支。