如何在大型提交中查找单个重大更改

时间:2012-01-25 17:22:19

标签: git

我对lint我的代码做了一些源更改。我没有运行单元测试,后来我发现一个大的提交破坏了代码。 如何返回并找出许多文件中的哪些代码更改导致测试失败?

我可以:

  1. 针对破解(稍后)提交
  2. 区分工作(早期)提交
  3. 将结果保存在补丁文件中
  4. 补丁/测试周期
    1. 将补丁文件的一部分应用于先前的提交
    2. 运行测试
  5. 我希望git的手册不如此。

3 个答案:

答案 0 :(得分:4)

使用git-stash的能力不仅可以隐藏并重新应用更改,还可以使用索引的状态,这是一种不错的方法。它是这样的:

# check out the bad commit
git checkout bad-commit
# and then reset to the commit before, leaving the bad changes in the work tree
git reset HEAD^

# stage the things you want to keep/test first
git add -p
# stash away the rest (keep the staged parts)
git stash --keep-index
# now build/test. if it works, go ahead and commit it
git commit
# bring back the stashed changes
git stash pop
# repeat!
git add -p
git stash --keep-index
# now suppose the broken part is in what you kept, and you want to split it up more
# unstage the changes
git reset
# and then repeat!
git add -p
git stash --keep-index
# if you do this, you'll end up with multiple stashes; you can check on them:
git stash list
git stash show
git stash show -p stash@{1}

使用stashes和测试有一个好处,如果你设法选择一个只是破坏构建的更改的子集,你可以直接弹出存储,然后再试一次。

你可以做一些类似的事情,将提交分成多个,然后在其上运行git bisect,但通常这样做更多,因为在你不进行测试的情况下,更难以知道如何拆分。

当然,现在您知道您应该进行较小的提交。但是我第一次也不总是这么做!

答案 1 :(得分:1)

您可以将提交拆分为几个小提交,然后使用git bisect查找违规提交。最简单的方法是使用交互式rebase

  1. 如果你已经推送了提交,那么用git checkout -b <name>创建一个新分支,否则你就可以了。
  2. 运行git rebase -i <bad commit>^
  3. 编辑器将弹出一行或多行,每行指示一次提交。在违规提交的行上,将第一个单词从pick更改为edit,然后保存并关闭编辑器。
  4. 当你的违规提交的所有更改都被暂存时,Git现在将停在rebase的中间位置。现在我们通过对更改的文件执行git reset,然后单独提交每个更改,将这个大型提交分成更小的提交。
  5. 完成后,执行git rebase --continue以完成rebase程序。
  6. 您现在已将提交拆分为几个较小的提交,您可以更轻松地找出确切的更改。一种非常有效的方法是使用git bisect。它的作用是执行提交的二进制搜索,以便准确找到被破坏的提交,这正是您想要实现的。这是你如何使用它:

    1. 运行git bisect start
    2. 运行git bisect good <good commit>,其中<good commit>是一个哈希/标记/您知道有效的提交。您可以在刚刚进行的第一次拆分提交之前使用提交。
    3. 运行git bisect bad <bad commit>,其中<bad commit>是一个has / tag /您认识的任何提交。您可以使用HEAD,因为您知道它已损坏。
    4. Git现在会检查该范围内的一些提交并要求您进行测试。执行测试,然后分别运行git bisect goodgit bisect bad告诉git它是好还是坏。根据你的说法,Git将继续检查其他提交。
    5. 经过一些步骤后,git会告诉你确切的提交是哪个提交。然后,您可以使用git show <commit>查看其中包含的内容。
    6. 运行git bisect reset退出平分模式。
    7. 要了解有关这些主题的更多信息,请继续阅读:

      1. Git Book about git bisectinteractive rebase
      2. Pro Git有关git bisectinteractive rebase
      3. 的书籍
      4. git手册页(顺便说一句,这很棒!),git rebase --helpgit bisect --help

答案 2 :(得分:1)

我最终基本上做了Jefromi所建议的。我写了这个python代码,命名为git-bisect.py:

#!/usr/bin/env python

from subprocess import Popen, PIPE
from sys import stderr


def run_command(cmd):
    p = Popen(cmd, stdout=PIPE, stderr=PIPE)
    output, errors = p.communicate()
    if p.returncode:
        raise Exception(errors)


def process_all(filename):
    with open(filename) as f:
        all_files = [fn.strip() for fn in f]
    try:
        for i, f in enumerate(all_files, start=1):
            print >> stderr, i, f
            run_command(['git', 'add', f])
            run_command(['git', 'stash', '--keep-index'])
            run_command(['bin/login_test.sh'])
            run_command(['git', 'stash', 'pop'])
    except Exception, exc:
        print >> stderr, exc


if __name__ == '__main__':
    from sys import argv
    for filename in argv[1:]:
        process_all(filename)

并针对受影响的文件列表运行它。

这是我完整的工作流程:

# Get the commit just before things went bad
git checkout 8f5c3d7
# Diff good code against bad, make patch
git diff 8f5c3d7 cb8ddf0 > 8f5c3d7-cb8ddf0.patch
# Apply patch
git apply 8f5c3d7-cb8ddf0.patch

# Now my index has all the changes that went into the bad commit
# but with just a dirty index.

# Run code that finds bad file in commit
python ~/Dropbox/src/git-bisect.py ~/Dropbox/src/bad-commit.txt

然后它一直ch着直到它停在第一个坏文件上。然后我会:

git reset --hard HEAD

并重新应用补丁,将错误文件放在文件列表的末尾并重新开始。很快,我就知道有任何改变会破坏我的构建。