如何通过非交互式压缩除最近的提交之外的所有提交来减少膨胀的Git仓库的大小?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何通过非交互式压缩除最近的提交之外的所有提交来减少膨胀的Git仓库的大小?相关的知识,希望对你有一定的参考价值。

我的Git repo有数百GB的数据,比如数据库备份,所以我试图删除旧的,过时的备份,因为它们会使一切变得越来越大。所以我自然需要一些快速的东西;越快越好。

除了最新的提交之外,我如何压缩(或者只是简单地删除)所有提交,而不必手动压缩interactive rebase中的每个提交?具体来说,我不想使用

git rebase -i --root

例如,我有这些提交:

A .. B .. C ... ... H .. I .. J .. K .. L

我想要的是(将AH之间的所有东西压缩到A):

A .. H .. I .. J .. K .. L

或者甚至这样可以正常工作:

H .. I .. J .. K .. L

有一个关于如何squash all commits的答案,但我想保留一些最近的提交。我也不想要squash the most recent commits。 (特别是我需要保持前两个提交从顶部开始计数。)

(编辑,几年后。这个问题的正确答案是使用正确的工具.Git不是一个非常好的工具来存储备份,无论它多么方便.There are better tools.

答案

原始海报comments

如果我们拍摄提交10004的快照,删除它之前的所有提交,并使提交10004成为root提交,我会没事的

一种方法是在这里,假设你当前的工作被称为branchname。我喜欢使用temp标签,每当我做一个大的rebase来仔细检查没有变化并标记一个点我可以reset回到出现问题(不确定这是否是标准程序但是它适用于我):

git tag temp

git checkout 10004
git checkout --orphan new_root
git commit -m "set new root 10004"

git rebase --onto new_root 10004 branchname

git diff temp   # verification that it worked with no changes
git tag -d temp
git branch -D new_root

要删除旧分支,您需要删除其上的所有标记和分支标记;然后

git prune
git gc

将从您的回购清洗它。

请注意,你将暂时拥有两份副本,直到你有gc'd,但这是不可避免的;即使你做标准的壁球和篮板,你仍然有两份副本,直到篮板完成。

另一答案

最快的计数实现时间几乎可以肯定是移植和filter-branch,尽管你可以通过commit-tree输出的手动rev-list序列获得更快的执行速度。

构建Rebase是为了对不同内容应用更改。你在这里做的是保留内容并故意丢失产生它们的变化历史,因此几乎所有rebase最乏味和最慢的工作都被浪费了。

这里的有效载荷是从你的图片开始,

echo `git rev-parse H; git rev-parse A` > .git/info/grafts  
git filter-branch -- --all

git rev-parsegit filter-branch的文档。

过滤分支是非常小心的,可以在任何一点失败后恢复,这当然是最安全的....但它只是真正有用的简单重做它的恢复不会更快更容易,如果事情向南走。失败是罕见的,重新启动通常很便宜,要做的事情是做一个非“安全”但非常快速的操作,但几乎可以确定。为此,这里最好的选择是在tmpfs上进行(我在Windows上知道的最接近的等价物就像ImDisk这样的ramdisk),这将是非常快速的,并且在你确定你之前不会触及你的主要回购得到了你想要的结果。

所以在Windows上,说T:wip是在ramdisk上,请注意这里的克隆没有任何副本。除了阅读有关git clone--shared选项的文档外,还要检查克隆的内部以查看真实效果,这非常简单。

# switch to a lightweight wip clone on a tmpfs
git clone --shared --no-checkout . /t/wip/filterwork
cd !$

# graft out the unwanted commits
echo `git rev-parse $L; git rev-parse $A` >.git/info/grafts
git filter-branch -- --all

# check that the repo history looks right
git log --graph --decorate --oneline --all

# all done with the splicing, filter-branch has integrated it
rm .git/info/grafts

# push the rewritten histories back
git push origin --all --force

对于您可能想要做的事情以及您的回购中可能存在的内容,有足够的可能变化,这些命令中的几乎任何选项都可能有用。以上内容经过测试,可以按照它的说法进行测试,但这可能不是您想要的。

另一答案

XY问题

请注意,原始海报有一个XY problem,他试图找出如何压缩他的旧提交(Y问题),当他真正的问题实际上是试图减少他的Git存储库(X问题)的大小时,as I've mentioned in the comments

有很多提交不一定会膨胀您的Git仓库的大小。 Git非常有效地压缩基于文本的文件。您确定提交的数量是导致您的大型回购规模的实际问题吗?更可能的候选者是你有太多版本化的二进制资产,与纯文本文件相比,Git也不会压缩(或根本不压缩)。

尽管如此,为了完整起见,我还将为Matt McNabb's answer添加一个替代解决方案来解决Y问题。

压制(数百或数千)旧提交者

正如原始海报已经注意到的那样,当有许多提交(编号为数百或数千)时,使用带有--root标志的交互式rebase可能是不切实际的,特别是因为交互式rebase不能在如此大量的提交上有效运行。

正如Matt McNabb在他的回答中所指出的,一种解决方案是使用孤儿分支作为新的(压扁的)根,然后在其上进行变基。另一个解决方案是使用分支的几个不同的重置来实现相同的效果:

# Save the current state of the branch in a couple of other branches
git branch beforeReset
git branch verification

# Also mark where we want to start squashing commits
git branch oldBase <most_recent_commit_to_squash>

# Temporarily remove the most recent commits from the current branch,
# because we don't want to squash those:
git reset --hard oldBase

# Using a soft reset to the root commit will keep all of the changes
# staged in the index, so you just need to amend those changes to the
# root commit:
git reset --soft <root_commit>
git commit --amend

# Rebase onto the new amended root,
# starting from oldBase and going up to beforeReset
git rebase --onto master oldBase beforeReset

# Switch back to master and (fast-forward) merge it with beforeReset
git checkout master
git merge beforeReset

# Verify that master still contains the same state as before all of the resets
git diff verification

# Cleanup
git branch -D beforeReset oldBase verification

# As part of cleanup, since the original poster mentioned that
# he has a lot of commits that he wants to remove to reduce
# the size of his repo, garbage collect the old, dangling commits too
git gc --prune=all

--prune=allgit gc选项将确保所有悬空提交都是垃圾收集,而不仅仅是超过2周的那些,这是git gc的默认设置。

以上是关于如何通过非交互式压缩除最近的提交之外的所有提交来减少膨胀的Git仓库的大小?的主要内容,如果未能解决你的问题,请参考以下文章

如何压缩除 n 最近的所有提交?

如何使用 SVN 提交除一个文件之外的所有文件

如何在下拉列表中获取除隐藏和提交按钮之外的所有表单字段

非交互式方式来压缩一系列git提交,而不是从HEAD开始

有没有办法以非交互方式压缩大量提交?

提交后如何使表单域为空