减少Bitbucket上git存储库的大小

Y.A*_*.AL 5 git bitbucket

经过几个月(提交和推送)我的项目,存储库的大小在Bitbucket上逐渐增加!它大约是1 GB,我试图删除一些不重要的数据库文件夹.搜索后我发现大多数建议都是建议:

git filter-branch -f --tree-filter 'rm -rf folder/subfolder' HEAD
Run Code Online (Sandbox Code Playgroud)

删除几个文件夹后,我将更改推送到存储库 - 强制,如

git push origin master --force
Run Code Online (Sandbox Code Playgroud)

我终于发现每次使用这些命令时存储库都会变大!! 可见,存储库变大了2.5 GB!

有什么建议吗?

编辑 根据下面的建议,我尝试了以下命令
(适用于所有大文件)

git filter-branch --index-filter"git rm -rf --cached --ignore-unmatch $ files"--tag-name-filter cat - --all

(删除临时历史git-filter-branch,否则长时间留下)

rm -rf .git/refs/original /

git reflog expire --all
git gc --aggressive --prune
Run Code Online (Sandbox Code Playgroud)

但文件夹.git/objects仍然很大!!!!

tor*_*rek 7

好的,鉴于您对评论的回答,我们现在可以说出发生了什么.

什么git filter-branch确实是复制(部分或全部)您提交到新的,然后更新引用.这意味着您的存储库变得更大(不小),至少在最初阶段.

复制的提交是通过给定的引用可以访问的提交.在这种情况下,你给出的引用是HEAD(git变成"你当前的分支",可能是master,但无论你当前的分支是在filter-branch命令时).如果(并且仅当)新副本精确地,与原始副本一点一点地相同,那么它实际上原始副本并且没有实际副本(原件被重新使用).但是,只要您进行任何更改 - 例如删除folder/subfolder,从这一点开始就真的是副本.

在这种情况下,复制的东西更小,因为你删除了一些项目.(这通常不是非常小,因为git的压缩项目非常好.)但是你还是增加更多的东西,到仓库:新的提交,其中提到新的树木,其中,幸好,指的是相同的旧的斑点(文件对象)和以前一样,这次只是少了一些(folder/subfolder文件的对象仍在存储库中,但复制的提交和树对象不再引用它们).

从图像上看,在此filter-branch过程中,我们现在都有旧的提交:

R--o--o---o--o   <-- master
    \    /
     o--o        <-- feature
Run Code Online (Sandbox Code Playgroud)

和新的(我假设folder/subfolder出现在原始的根提交中,R所以我们R'在这里有一个副本):

R'-o'-o'--o'-o'
    \    /
     o'-o'
Run Code Online (Sandbox Code Playgroud)

是什么filter-branch呢,现在,在复制过程的结束,是重新指向一些引用(分支和标签名称,主要是).它重新指出的那些是你告诉它的那些,通过提及它们作为文档所谓的"积极参考".在这种情况下,如果你在master(也就是HEAD另一个名字master),你给出的单个正面参考是master......所以这是所有的 filter-branch重新点.它还会生成名称以其开头的备份引用refs/original/.这意味着您现在拥有以下提交:

R--o--o---o--o   <-- refs/original/refs/heads/master
    \    /
     o--o        <-- feature

R'-o'-o'--o'-o'  <-- master
    \    /
     o'-o'
Run Code Online (Sandbox Code Playgroud)

请注意,feature仍然指向所有旧的(未复制的)提交,因此即使在您删除任何refs/original/引用之后/之后,git将保留所有仍然引用的任何垃圾收集活动的提交,给出:

R--o
    \
     o--o        <-- feature

R'-o'-o'--o'-o'  <-- master
    \    /
     o'-o'
Run Code Online (Sandbox Code Playgroud)

filter-branch更新所有引用,您需要将它们全部命名.一个简单的方法是使用--all,它完全命名所有引用.在这种情况下,最初的"之后"图片看起来像这样:

R--o--o---o--o   <-- refs/original/refs/heads/master
    \    /
     o--o        <-- refs/original/refs/heads/feature

R'-o'-o'--o'-o'  <-- master
    \    /
     o'-o'       <-- feature
Run Code Online (Sandbox Code Playgroud)

现在,如果删除所有refs/original/引用,则所有旧提交都将被取消引用,并且可以进行垃圾回收.嗯,就是说,除非有标签指向它们,否则它们会这样做.

对于标记引用,filter-branch只有在提供标记时才以任何方式更新它们--tag-name-filter.通常你想要的--tag-name-filter cat,它保持标签名称不变,但是filter-branch指向新复制的提交.这样你就不会挂起旧的提交了:练习的重点是让一切都使用新的副本,然后丢弃旧的副本,这样大文件对象就可以被垃圾收集了.


把这一切放在一起,而不是:

git filter-branch -f --tree-filter 'rm -rf folder/subfolder'
Run Code Online (Sandbox Code Playgroud)

您可以使用:

git filter-branch -f --tree-filter 'rm -rf folder/subfolder' \
    --tag-name-filter cat -- --all
Run Code Online (Sandbox Code Playgroud)

(你不需要反斜杠换行序列;我把在只是为了让行更好地适应于计算器.需要注意的--tree-filter是很慢的:对于这个特定的情况下,它的速度要快得多使用--index-filter索引过滤命令在这里会.git rm --cached --ignore-unmatch -r folder/subfolder.)

另请注意,您需要在原始存储库(副本)上执行所有这些操作(您确实保留了备份,对吧?).(如果你没有备份,refs/originals/可能是你的救赎.)


编辑:好的,所以你做了一些filter-branch,你做了一些删除任何东西refs/originals/.(在我对temp repo的实验中,运行git filter-branchHEAD我使用的任何分支上作为重新指向的分支,并创建了前一个值的"原始"副本.)没有存储库的备份.怎么办?

那么,作为第一步,立即进行备份.这样,如果事情变得更糟,你至少可以回到"只是稍微糟糕".要备份repo,您​​可以简单地克隆它(或者:克隆它,然后将原始文件称为"backup",然后开始处理克隆).为了将来参考,由于git filter-branch可能具有相当大的破坏性,因此通常从明智的做法开始进行备份过程.(另外,我会注意,到位桶的克隆,而不是当尚未push编到,将有助于.不幸的是你做的push,也许到位桶可以检索一些备份或自己的快照存储库的早期版本.)

接下来,让我们注意一下提交的特性及其SHA-1"真实姓名",我之前提到过.提交的SHA-1名称是其内容的加密校验和.让我们看看git自己的源代码树中的一个示例提交(只是为了长度而修剪了一下,并且电子邮件地址被打到了收割机):

$ git cat-file -p 5de7f500c13c8158696a68d86da1030313ddaf69
tree 73eee5d136d2b00c623c3fceceffab85c9e9b47e
parent c4ad00f8ccb59a0ae0735e8e32b203d4bd835616
author Jeff King <peff peff.net> 1405233728 -0400
committer Junio C Hamano <gitster pobox.com> 1406567673 -0700

alloc: factor out commit index

We keep a static counter to set the commit index on newly
allocated objects. However, since we also need to set the
[snip]
Run Code Online (Sandbox Code Playgroud)

在这里,我们可以看到了这样的内容提交(其"真实名称" 5de7f50...)开始与tree和另一SHA-1,parent和另一SHA-1,authorcommitter,然后一个空行,然后提交信息文本.

如果你看一下tree,你会看到它包含了"真实姓名"(SHA-1的值)的子树(子目录)和文件对象("斑点",在git的术语)与他们一起的模式,真的,只是blob是否应该具有执行权限集,以及它们在目录中的名称.例如,上面的第一行tree是:

100644 blob 5e98806c6cc246acef5f539ae191710a0c06ad3f    .gitattributes
Run Code Online (Sandbox Code Playgroud)

这意味着5e98806...应该提取存储库对象,将其放入名为的文件中.gitattributes,并设置为不可执行文件.

如果我要求git进行新的提交,并设置,作为其内容:

  • 同一棵树(73eee5d...)
  • 同一个父母(c4ad00f...)
  • 同一作者和提交人
  • 和相同的空白行和消息

然后,当我得到git将该提交写入存储库时,它将生成相同的"真实名称" 5de7f50....换句话说,它实际上是相同的提交:它已经存储在存储库中,并且git commit-tree只会将现有ID提供给我.虽然将所有这些设置起来有点棘手,但这正是git filter-branch最终要做的事情:它提取原始提交,应用过滤器,设置所有内容,然后执行git commit-tree.

这对你意味着什么

在您的原始仓库中,您运行了一个git filter-branch命令,将提交复制到新的,已修改的提交(具有不同的trees,因此,在某些时候,不同的真实名称会导致后续提交中的不同父ID,依此类推).但是,如果通过应用此次不执行任何操作的过滤器来复制这些复制的提交,则新tree对象将与旧对象相同.如果新父级是相同的,并且作者,提交者和消息也保持不变,则副本的新提交ID将与旧ID 相同.

也就是说,这些副本毕竟不是副本,它们只是原件!

任何其他提交 - 在第一次传递中复制的提交都会被复制,因此具有不同的ID.

事情变得棘手.

如果您当前的存储库看起来像这样(从图形上讲):

R--o--o---o--o   <-- xxx [needs a name so that filter-branch will process it]
    \    /
     o--o        <-- feature

R'-o'-o'--o'-o'  <-- master
    \    /
     o'-o'
Run Code Online (Sandbox Code Playgroud)

并且我们filter-branch所有引用(或者甚至"除了master"之外)应用一个新的,这次它生成相同的树,它将R再次复制,新树将匹配R',因此副本实际上 R'.然后将复制后的第一个R节点,进行相同的更改,并且复制实际上第一个后R',o'节点.这将重复所有节点,甚至可能包括R'和所有的o'秒.如果是filter-branch副本R',那么生成的副本将会R'再次出现,因为"删除不存在的目录"没有任何变化:我们的过滤器对这些特定的提交没有任何作用.

最后,filter-branch将移动标签,留下refs/originals/版本:

R--o--o---o--o   <-- refs/originals/refs/xxx
    \    /
     o--o        <-- refs/originals/refs/feature

R'-o'-o'--o'-o'  <-- master, xxx
    \    /
     o'-o'       <-- feature
Run Code Online (Sandbox Code Playgroud)

事实上,这是理想的结果.

如果存储库看起来更像这样怎么办? 也就是说,如果没有xxx或类似的标签指向原始(预过滤)master,那么你有这个:

R--o
    \
     o--o        <-- feature

R'-o'-o'--o'-o'  <-- master
    \    /
     o'-o'
Run Code Online (Sandbox Code Playgroud)

filter-branch脚本仍将复制R,结果仍然是R'.然后它将复制第一个o节点,结果仍然是第一个o'节点,依此类推.它不会复制现在删除的节点,但它不必:我们已经拥有了那些,可通过branch-name访问master.和以前一样,filter-branch可以复制R'和各种o'节点,但这没关系,因为过滤器什么也不做,所以副本实际上只是原件.

最后,filter-branch将像往常一样更新引用:

R--o
    \
     o--o        <-- refs/originals/refs/feature

R'-o'-o'--o'-o'  <-- master
    \    /
     o'-o'       <-- feature
Run Code Online (Sandbox Code Playgroud)

使这一切工作的关键是过滤器保留已修改的提交不变,因此它们的第二个"副本"只是第一个副本.1

一旦一切都完成后,你可以做中描述的相同收缩git filter-branch文档refs/originals/名字和垃圾收集现已未引用的对象.


1如果你在使用时不容易重复过滤过(例如,一个让新提交了"当前时间"作为自己的时间标记),你真的需要一个不变原来的仓库,或者那些refs/originals/引用(或者一个只需保留一份"原件"即可.