从 git 存储库中删除文件(历史记录)

Posted

技术标签:

【中文标题】从 git 存储库中删除文件(历史记录)【英文标题】:Remove file from git repository (history) 【发布时间】:2011-01-10 23:56:11 【问题描述】:

(已解决,见问题正文底部) 找这个找了很久,到现在有的是:

http://dound.com/2009/04/git-forever-remove-files-or-folders-from-history/ 和 http://progit.org/book/ch9-7.html

几乎相同的方法,但它们都将对象留在包文件中......卡住了。 我尝试了什么:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch file_name'
rm -Rf .git/refs/original
rm -Rf .git/logs/
git gc

包里还有文件,我是这样知道的:

git verify-pack -v .git/objects/pack/pack-3f8c0...bb.idx | sort -k 3 -n | tail -3

还有这个:

git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch file_name" HEAD
rm -rf .git/refs/original/ && git reflog expire --all &&  git gc --aggressive --prune

同样...

尝试了git clone 技巧,它删除了一些文件(其中约 3000 个),但最大的文件仍然存在......

我在存储库中有一些大型遗留文件,约 200M,我真的不希望它们在那里...而且我不想将存储库重置为 0 :(

解决方案: 这是删除文件的最短方法:

    检查 .git/packed-refs - 我的问题是我有一个远程存储库的 refs/remotes/origin/master 行,删除它,否则 git 不会删除这些文件 (可选) git verify-pack -v .git/objects/pack/#pack-name.idx | sort -k 3 -n | tail -5 - 检查最大的文件 (可选) git rev-list --objects --all | grep a0d770a97ff0fac0be1d777b32cc67fe69eb9a98 - 检查这些文件是什么 git filter-branch --index-filter 'git rm --cached --ignore-unmatch file_names' - 从所有修订中删除文件 rm -rf .git/refs/original/ - 删除 git 的备份 git reflog expire --all --expire='0 days' - 使所有松散的对象失效 git fsck --full --unreachable - 检查是否有松散的物体 git repack -A -d - 重新包装 git prune - 最终移除这些对象

【问题讨论】:

可能重复:***.com/questions/2100907/…***.com/questions/872565/… zneak - 我的问题在标题中。 gbacon - 尝试了这些,文件仍然保留在包文件中...... 如果您查看重复中引用的文章,它会显示如何在删除有问题的文件后压缩您的对象存储。 这是救命稻草。心理提示:始终将潜在的巨大 *.log 文件添加到 .gitignore。在此之后从 800mb 的 repo 变成了 6mb。 第二步和第三步合二为一for i in `git verify-pack -v .git/objects/pack/#pack-name.idx | sort -k 3 -n | tail -5` ; do git rev-list --objects --all | grep $(echo $i | sed 's/ .*//g') ; done 【参考方案1】:

见:How do I remove sensitive files from git’s history

如果文件在 rev 中不存在,上述操作将失败。在这种情况下,'--ignore-unmatch' 开关将修复它:

git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch <filename>' HEAD

然后,将所有松散的对象从存储库中取出:

git gc --prune='0 days ago'

【讨论】:

是的,这个试过了,包里的文件还在,大小也没有太大变化…… 我刚做了一个git沙箱,试了一下。这里也不好。让我们看看我能弄清楚什么。 答案中的那个? :) 和我发布的一样,它仍然将文件留在包中......尝试一个 git 沙箱,执行 git gc 以便它会打包文件,然后运行它...... 哦,松散的物体?往上看。我倾向于让它们在两周内被垃圾收集(gc 的默认值);杀死所有个松散的对象就像清空垃圾箱——我失去了找回我不小心删除的任何东西的机会。 :) 也试过这个...删除了一些文件,但最大的仍然存在...【参考方案2】:

git gc 之后仍然很大的 git repo 大小有多种原因,因为它是does not remove all loose objects。

我在“reduce the git repository size”中详细说明了这些原因

但在您的情况下测试的一个技巧是 clone your "cleaned" Git repo 并查看克隆是否具有适当的大小。

('"cleaned" repo' 是你应用filter-branch,然后是gcprune)

【讨论】:

是的,已经测试过了,现在再次测试,它减少了 2k 的存储库:) 并且文件仍然存在...... 奇怪的是git count-objects -v -&gt; count: 0, size: 0, in-pack: 10021, packs: 1, size-pack: 244547, prune-packable: 0, garbage: 0 但是:git clone test1 test2 -&gt; Checking out files: 100% (8509/8509), done【参考方案3】:

如果不能访问您的存储库数据,我不能肯定地说,但我相信在您运行 git filter-branch 之前,可能有一个或多个打包的引用仍在引用旧提交。这可以解释为什么 git fsck --full --unreachable 不会将大 blob 称为无法访问的对象,即使您已过期 reflog 并删除了原始(未打包的)refs。

这是我要做的(在完成git filter-branchgit gc 之后):

1) 确保原始 refs 已消失:

rm -rf .git/refs/original

2) 使所有 reflog 条目过期:

git reflog expire --all --expire='0 days'

3) 检查旧打包的 refs

这可能会很棘手,具体取决于您拥有多少打包的 ref。我不知道任何可以自动执行此操作的 Git 命令,因此我认为您必须手动执行此操作。备份.git/packed-refs。现在编辑.git/packed-refs。检查旧的参考(特别是,看看它是否包含来自.git/refs/original 的任何参考)。如果您发现任何不需要的旧文件,请将它们删除(删除该引用的行)。

清理完packed-refs 文件后,查看git fsck 是否注意到无法访问的对象:

git fsck --full --unreachable

如果可行,并且git fsck 现在报告您的大 blob 无法访问,您可以继续下一步。

4) 重新打包打包的存档

git repack -A -d

这将确保无法访问的对象被解包并保持解包。

5) 修剪松散(无法访问)的对象

git prune

应该这样做。 Git 确实应该有更好的方法来管理打包的引用。也许有更好的方法,我不知道。在没有更好的方法的情况下,手动编辑 packed-refs 文件可能是唯一的方法。

【讨论】:

耶!!!我爱你 !问题出在打包的引用文件中,有 refs/remotes/origin/master 从我在某个服务器上备份它的时候开始......一旦我删除它,它就开始消失了......谢谢! (用完整的解决方案更新问题主体)【参考方案4】:

我试图摆脱历史上的一个大文件,上面的答案在一定程度上是有效的。关键是:如果你有标签,它们就不起作用。如果可以从标签访问包含大文件的提交,那么您需要调整 filter-branches 命令:

git filter-branch --tag-name-filter cat \
--index-filter 'git rm --cached --ignore-unmatch huge_file_name' -- \
--all --tags

【讨论】:

【参考方案5】:

我遇到了同样的问题,我在 github 上找到了一个很棒的 tutorial,它逐步解释了如何删除您意外提交的文件。

这里是 Cupcake 建议的程序的一个小总结。

如果您有一个名为 file_to_remove 的文件要从历史记录中删除:

cd path_to_parent_dir

git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch file_to_remove' \
  --prune-empty --tag-name-filter cat -- --all

【讨论】:

在 Stack Overflow 上非常不鼓励仅链接的答案,因为如果将来链接断开,那么答案将变得毫无用处。请考虑在您的答案中总结链接中包含的相关信息。【参考方案6】:

我发现这对于删除整个文件夹很有帮助,因为上述内容并没有真正帮助我:https://help.github.com/articles/remove-sensitive-data。

我用过:

git filter-branch -f --force \
--index-filter 'git rm -rf --cached --ignore-unmatch folder/sub-folder' \
--prune-empty --tag-name-filter cat -- --all

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

【讨论】:

【参考方案7】:

这应该包含在 Git Extras (https://github.com/visionmedia/git-extras) 中的 git obliterate 命令中。

git obliterate <filename>

【讨论】:

【参考方案8】:

我建议使用BFG Repo-Cleaner,它是git-filter-branch 的更简单、更快速的替代方案,专为重写 Git 历史文件而设计。它使您的生活在这里变得更轻松的一种方法是它实际上默认处理 all 引用(所有标签、分支、像 refs/remotes/origin/master 之类的东西),但它也是 10-50x更快。

您应该在这里仔细按照以下步骤操作:http://rtyley.github.com/bfg-repo-cleaner/#usage - 但核心位是这样的:下载BFG's jar(需要 Java 6 或更高版本)并运行以下命令:

$ java -jar bfg.jar  --delete-files file_name  my-repo.git

任何名为file_name 的文件(不在您的最新 提交中)都将从您的存储库历史记录中完全删除。然后您可以使用git gc 清除死数据:

$ git gc --prune=now --aggressive

BFG 通常比 git-filter-branch 更易于使用 - 选项是围绕这两个常见用例量身定制的:

删除 疯狂的大文件 删除密码、凭据和其他私人数据

全面披露:我是 BFG Repo-Cleaner 的作者。

【讨论】:

推送后这是否还会清除远程仓库中的私有数据? @ThomasLauria 是的,相同的清理后的 ref 在推送时被推送到远程仓库 - rtyley.github.io/bfg-repo-cleaner/#usage 的说明应该涵盖它。如果您可以控制远程仓库,您还可以在推送后对其运行“git gc --prune=now --aggressive”,以确保死对象也立即从中删除。 @RobertoTyley 这可能导致两个提交在历史记录中相继出现并且具有相同的树(如果其中一个提交只添加了已删除的文件)。您是否知道一种从提交历史记录中删除此类提交的简单方法,因为它们似乎是人为的? @RobertoTyley 我认为这涉及另一个问题。在我描述的情况下,只涉及一个存储库。但是git filter-branch --prune-empty 似乎是我的问题的解决方案(虽然使用其他工具,请让我知道 BFG Repo-Cleaner 是否可以这样做)。

以上是关于从 git 存储库中删除文件(历史记录)的主要内容,如果未能解决你的问题,请参考以下文章

从 Git 历史记录中删除大文件

github 强制删除历史记录

从 git/GitHub 的历史记录中删除文件夹及其内容

仅从 git 提交中删除文件

如何从 git 存储库中删除文件而不实际删除文件 [重复]

如何将文件从一个文件夹移动到同一git存储库中的另一个文件夹保留历史记录[重复]