删除远程仓库的历史记录,但又不影响最新的仓库内容一直是我想实现的一个功能。它有两个非常好的用处:
然而,需要说明的是,Git删除历史记录并不是我们想象中的那么简单,需要使用rebase(变基)功能。由于这个功能对仓库的改变相当大,为了安全起见,我们首先进行备份,并创建一个新的分支:
git checkout -b cleanup-history
接下来,使用变基指令重写提交历史,如下所示:
git rebase -i HEAD~n
这里的
-i
表示交互式重写,它会弹出一个包含所有历史提交记录的页面,让你进行编辑。在这里,n表示往前回溯n个版本。例如,可以先检查所有历史提交:
git rev-list --count HEAD
如果得到的数值是500,那么将n设置成499就可以看到所有的历史记录。有时候这个数值不对,可能是因为包含了合并的提交,那么可以尝试以下指令:
git rev-list --first-parent --count HEAD
或者:
git rev-list --count --no-merges HEAD
通过以上指令可以大概估算n的数值。当然,如果你回溯的历史提交不太远,给一个大概的数值即可查看你要删除的历史提交。
在使用
git rebase -i HEAD~n
之后,在交互式页面中,将需要删除的历史提交记录的操作从pick改为drop。保存并退出编辑器,Git会开始重写历史,删除指定的提交。有时你想删除的历史提交太多,一个一个改成drop会很麻烦,可以使用像NotePad3这样的文本工具,通过列选取功能来批量修改。
如果你删除的历史记录足够远并且足够多,接下来你会看到一个比较揪心的过程:你的Git代码仓库会回溯到最远的历史状态,然后逐步开始自动提交。这个过程很可能会出现一些问题。例如,检测到空提交,会提示并中止变基过程,可以进行跳过:
git rebase --skip
还可能会遇到冲突的问题,需要你解决这些冲突。如果是文本文件,编辑后再使用
git add xxx
;如果是二进制文件,要么删除
git rm xxx
,要么直接
git add xxx
冲突的文件,然后继续变基:
git rebase --continue
接下来,如果一切顺利,就可以将改动强制推送到远程分支:
git push origin cleanup-history --force
最后,检查一下分支的历史提交内容,没有问题的话就将这个分支替换为主分支:
git checkout main
git reset --hard cleanup-history
git push origin main --force
如果要彻底清除这些提交记录,并压缩Git仓库的体积,可以使用以下指令:
git reflog expire --expire=now --all
git gc --prune=now --aggressive
对于其他用户,可以使用以下指令进行更新:
git pull -rebase origin main
在我的实际使用过程中,我遇到了很多冲突问题,经常需要停下来解决这些冲突。我也不太理解为什么删除历史记录后还需要解决与当前仓库快照冲突的问题。我猜想可能是因为我历史提交记录包含了很多合并的提交。因此,这个方法对有些读者可能不太适用,也许合并历史提交或者只保留最新提交的版本更合理一些。