撤销 Git Filter-branch操作

84

我不小心使用git filter-branch从我的仓库中删除了一个文件:

git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch images/thumb/a.JPG' HEAD

我该如何撤销这个操作?是否可以实现?也就是说,文件被永久删除了吗?


3
现在最重要的问题是什么? - jimh
4个回答

159
当使用 git filter-branch 命令时,会在当前目录下创建一个备份文件。
refs/original/refs/heads/master

如果您在分支“master”中使用了该命令,则可以检查是否在“.git/refs”目录中有备份。鉴于此,您可以使用此备份来恢复您的文件:
git reset --hard refs/original/refs/heads/master

9
我收到了错误信息fatal: Cannot do hard reset with paths. ,所以我先执行了 git reflog 命令,并找到了一行 HEAD@{integer}: filter-branch: rewrite,然后我记录下了这个整数,接着执行了 git reset --hard HEAD@{integer} 命令,之后问题就解决了。 - hermansc
这对我有用。我在git reset --hard HEAD@{integer}之后加入了git push -f - kilojoules
我想使用 git filter-branch 命令从我们的代码库中删除某人意外提交的库所导致的大量行。因此,在图表中,它显示了大约8000行的添加/删除,我认为filter-branch会重写这些行,然后更新图表,但实际上并没有。反而是将所有行数翻倍了。那么,执行此操作是否会将行数恢复到旧状态?还是会再次翻倍? - Arian Faurtosh
是的,这个命令会将你的代码恢复到执行 'filter-branch' 命令之前的状态。 - William Seiti Mizuta
4
感谢您重置我的心脏病发作。在过滤命令删除除文件夹以外的所有内容后,一切都运行得非常顺利。 - jHilscher
显示剩余4条评论

35

可能比直接硬重置回原始主分支更合适的方法是恢复git filter-branch重写的所有参考,并且甚至删除备份参考,以便能够再次调用git filter-branch而不需要--force

for orig_ref in $(git for-each-ref --format="%(refname)" refs/original/); do
    git update-ref "${orig_ref#refs/original/}" $orig_ref
    git update-ref -d $orig_ref  # to also remove backup refs
done

之后:

git reset --hard master

更新。

以下是(可能)更加符合 Git 风格且无需使用 shell 循环执行相同操作的方法:

git for-each-ref --format="update %(refname:lstrip=2) %(objectname)" refs/original/ | git update-ref --stdin
git for-each-ref --format="delete %(refname) %(objectname)" refs/original/ | git update-ref --stdin

5
谢谢。我认为这个脚本应该放在 git-filter-branch 的文档页面上,因为它没有关于如何从备份中恢复的说明。 - user
1
不用谢!我猜你也可以建议改进他们的文档,哈哈。;-) - Eldar Abusalimov
这里的建议怎么样:https://dev59.com/vH_aa4cB1Zd3GeqP2mRD#23483576 - git fetch . +refs/original/*:* - Mr_and_Mrs_D
认为在这样做之后,我仍然需要重新从上游拉取标签。这是否符合预期? - Sam Brightman
@SamBrightman 该手册页没有提到这一点,而我不知道TBH... - Eldar Abusalimov

7
这篇回答由@jthill提供,提供了一种更为简洁的解决方案。
git fetch . +refs/original/*:*

如上述答案所述,如果要恢复当前检出的分支,则可能需要分离HEAD。

要删除refs/original引用,请执行以下操作:

git for-each-ref refs/original --format='delete %(refname) %(objectname)' | git update-ref --stdin

如果你需要恢复多个分支,这绝对是一个更简单的解决方案。完美地运行了。 - Mike Scannell

5

你的旧分支尖端可能会被保存在你的reflog中。然后,你应该能够检出未更改的提交及其所有先前的历史记录。


git reflog... 好耶.. 这个命令已经帮我省了无数次的事. :) - Ccr
是的,这对我解决了问题,而且似乎是最简单的解决方案。我找到了要还原的reflog条目(恰好是HEAD@{5}),然后运行了git reset HEAD@{5}。更多细节可以查看此链接 - jimmy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接