【问题标题】:Removing a folder from a git repo's history doesn't make repo smaller从 git repo 历史记录中删除文件夹不会使 repo 更小
【发布时间】:2017-10-19 11:05:31
【问题描述】:

我阅读了许多相关的问题,但我遇到了以下问题。

this repo中,media/1 Juno-Trumpet中有大文件(在之前的提交中),所以我完全按照answer here删除了这些文件:

git clone https://github.com/alexmacrae/SamplerBox.git
git count-objects -vH

总文件大小:54MB

git filter-branch --tree-filter 'rm -rf "media/1 Juno-Trumpet"' --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo "media/1 Juno-Trumpet/" >> .gitignore
git add .gitignore
git commit -m 'Removing a folder from git history'
git gc
git count-objects -vH

总文件大小:54MB

问题:为什么 repo 的大小没有改变?经过这样的清理后,如何使 repo 大小更小?

【问题讨论】:

  • 您确认文件夹是否真的消失了吗?
  • 它在最后一次提交中不存在(这个文件夹在历史上是旧的),所以它不再显示在文件@LasseV.Karlsen 中。或者是否有一种精确的方法来验证这一点,包括在过去的提交中?
  • 除了检查之前有该文件夹的提交,我不知道,但老实说,我认为 lucanLepus 的答案是你想要的。

标签: git github


【解决方案1】:

仍然包含子目录的旧提交仍然是存储库的一部分,即使它们无法从任何分支访问。

你可以清理它们

git reflog expire --expire=now --all && git gc --prune=now --aggressive

然而,这将清空您的 reflog。 这是必要的,因为您的 reflog 引用的提交不会被垃圾收集

【讨论】:

【解决方案2】:

运行git filter-branch 实际上复制每个被过滤的提交。生成的存储库从未更小——嗯,还没有——而且通常更大。如果幸运或聪明,大部分副本会重复使用大部分原始对象,因此生成的存储库仅比原始存储库大一点点

您可能会问:“那我们为什么要过滤存储库?”事实上,大多数情况下您不应该这样做:对于使用存储库的每个人来说,这都是一个令人头疼的问题(但通常至少是一次性的),因为他们都必须切换到新的过滤存储库。但真正的答案是,过滤后,您可以删除原始(预复制)对象的引用,或者将存储库克隆到新的新克隆中。 p>

原始对象的引用保存在refs/original/ 中的reflogs(特别是HEAD reflog 通常会有它们)。请参阅the git filter-branch documentation 末尾的说明以了解如何删除这些内容,如果您选择(出于某种疯狂的原因)不只是重新克隆过滤后的存储库。

【讨论】:

  • 感谢您的回答。你推荐什么git 命令来完成这个过程?
  • 最好是git clone,使用file://(但要注意分支,因为克隆的克隆只拾取本地分支,而不是任何远程跟踪分支)。或者使用手册页和 lucanLepus 的答案中显示的 git reflog expire 方法。请注意,--aggressive 最初的实现很差,修复一次是为了更好,并且最近才重新修复才明智。
  • 我仍然无法让它工作:see here。任何想法@torek?
  • 此时最好的猜测是您有其他分支或标签或其他引用到达并因此保留旧提交。请注意,即使是流浪的git stash 也可以做到这一点。重新克隆会丢弃 stash,这通常是个好主意,尤其是当您将 --prune-empty--all 结合使用时,因为这往往会破坏 stash。
  • 谢谢。哇,现在@torek 对我来说似乎很复杂。您能否以类似的方式编辑答案或粘贴您的想法than this
【解决方案3】:

只是一个现成的完整版本,基于@lucanLepus 接受的答案。

假设我是 userA,我想从 Github 上的存储库中从历史记录中完全删除文件夹 media/1 Juno-Trumpet/(在最近的提交中不再存在,但在很久以前的提交中)。

注意:此特定存储库具有原始分支 mastersfzwifi,以及标记 v1.0。为了避免需要知道这一点,我在这里使用了一个镜像克隆(它创建了一个裸存储库,这很好,因为我将使用索引过滤器)。然后,由于这里是 GitHub,所以我先把所有的refs/pull/ refs 都扔了。

事实证明,这些文件也被命名为 media/Juno-Trumpet/media/Juno/,因此我们需要删除所有三个路径名。

git clone --mirror https://github.com/alexmacrae/SamplerBox.git
cd SamplerBox.git
git for-each-ref --format="git update-ref -d %(refname)" refs/pull | sh
git for-each-ref         # to check that we have only wanted refs left
git count-objects -vH    # size-pack: 54.40 MiB
git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch "media/1 Juno-Trumpet" media/Juno-Trumpet media/Juno' --prune-empty --tag-name-filter cat -- --all

filter-branch 步骤需要一点时间,并以:

Ref 'refs/heads/master' was rewritten
Ref 'refs/heads/sfz' was rewritten
Ref 'refs/heads/wifi' was rewritten
WARNING: Ref 'refs/tags/v1.0' is unchanged
v1.0 -> v1.0 (7ec3254d08b65fd3ca8a048cef60b5b2c75f7e11 -> 7ec3254d08b65fd3ca8a048cef60b5b2c75f7e11)

(最后一行表示存储库中的一个标签位于任何重写的提交之前,即我们毕竟不需要--tag-name-filter cat。)

现在我们必须删除refs/original/ 名称。由于这是一个全新的克隆,因此没有过期的 reflog,但无论如何我们都会这样做,然后使用 git gc 重新打包:

git for-each-ref --format="git update-ref -d %(refname)" refs/original | sh
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git count-objects -vH     # size-pack: 1.41 MiB

我还没有完成这最后一步:

git push origin '+refs/*:refs/*'

(如果您确实确定要完全清除所有媒体文件,则可能还需要清除所有拉取请求,因为否则它们会保留它们一段时间)。


顺便说一句,我找到了三个名称下的文件:

git cat-file --batch-all-objects --batch-check | sort +2 -rn | head

要查找比较大的文件,接着:

git rev-list --all | while read ref; do
   git ls-tree -r $ref | grep 477145c7d0190f4e0aeea0f7bfb9accbf2c1ba48;
done | sort -u

477145c7d0190f4e0aeea0f7bfb9accbf2c1ba48.wav 大文件之一。我没有检查删除的所有文件是否都是.wav 文件以及是否还有其他.wav 文件。)

【讨论】:

    猜你喜欢
    • 2022-08-20
    • 2023-01-22
    • 1970-01-01
    • 2022-06-14
    • 2015-06-04
    • 1970-01-01
    • 2013-02-05
    • 1970-01-01
    • 2014-06-19
    相关资源
    最近更新 更多