修剪旧的Git提交记录而不需要变基

3
假设我有一个Git仓库,其中包含大量的树(~60 GiB)和一些历史记录,旧版本包含许多已删除的文件。
现在我想要修剪旧的历史记录,但是不想对修剪点后的所有提交进行rebase,因为每个提交都需要几个小时的时间。
1. 我可以删除第一个要删除的提交对象,希望git gc会删除所有(现在未引用的)较旧的提交吗?还是由于缺少对象而引起恐慌?
2. 我可以使用git replace将要删除的第一个提交替换为虚拟提交,然后调用git gc吗?
3. 是否有其他方法可以就地删除我的旧提交?

我认为任何形式的历史重写都必须涉及重新编写剪枝点之后的所有内容,除非每个提交的SHA ID实际上并非基于其父数据。您可能可以使用git filter-branch或BFG Repo Cleaner更有效地完成此操作。 - user456814
每个提交的SHA-1 ID都是根据其父提交的数据设计而来。您必须进行变基:从您的修剪点开始的每个提交都需要具有全新的ID,因为它是基于不同的早期提交ID。 - Matthew Strawbridge
1个回答

3
在修剪点之后,没有重新设置所有提交记录,因为这会使每个提交记录花费几个小时。自Git 2.18(2018年第二季度)以来,graft已被git ref/replace/取代。可以使用git replace --convert-graft-file命令来创建所有$GIT_DIR/info/grafts条目的嫁接提交,并在成功后删除该文件,以帮助用户过渡到现在已弃用的嫁接文件。所以可以运行git rev-parse HEAD~100 > .git/info/grafts

git filter-branch 或 BFG 在 Git 2.22 后已过时

安装 git filter-repo使用 git filter-repo --force


Git 2.18之前(2018年第二季度):

嫁接点就是为了解决这个问题的(在这种情况下比git replace更好,因为我在此处详细说明)。

文件.git/info/grafts仅有一行提交ID,表示该提交没有父提交。
使用git rev-parse来保留最近的100个提交:

 git rev-parse HEAD~100 > .git/info/grafts

然后:

 git filter-branch -- --all

最后:
rm -Rf .git/refs/original

然后你可以修剪剩下的部分:
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
git repack -Ad      # kills in-pack garbage
git prune           # kills loose garbage

@cfstras 不,嫁接是纯粹的本地操作。因此需要使用 git filter-branch 将其修改集成到仓库历史中。完成后,您可以推送更改的历史记录。(使用 push --force,因为历史记录已被重写,请确保您是唯一在该仓库上工作的人,或确保您的同事知道这种变化) - VonC
啊,这很有道理。我猜在这种情况下使用 git filter-branch 只会重新处理提交对象(更改父 SHA),因此运行几乎是瞬间完成的? - cfstras
1
@cfstras 是的,非常快(不过,除非你的历史记录很小,否则不是“即时”的)。 - VonC
git 2.26 版本表示嫁接(grafts)已被弃用并将被移除。建议使用 git replace --convert-graft-file 将嫁接转换为替换引用(replace ref)。 - Kevin Buchs
@KevinBuchs 感谢您的反馈。实际上是2.18版本。我已经编辑了答案以更新命令。 - VonC
显示剩余5条评论

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