实现这一目标有2个步骤:
创建一个新的空提交
重写历史,从这个空提交开始
为了方便起见,我们将把新的空提交放在一个临时分支newroot上。
1. 创建一个新的空提交
有很多方法可以做到这一点。
只使用管道
最简洁的方法是使用Git的管道直接创建一个提交,这避免了触及工作副本、索引或检出哪个分支等。
为空目录创建一个树对象:
Tree = ' git hash-object -wt Tree——stdin < /dev/null '
在它周围包装一个提交:
提交= ' git Commit -tree -m '根提交' $tree '
创建对它的引用:
Git分支newroot $commit
当然,如果您足够了解shell,您可以将整个过程重新排列为一行程序。
没有管道
使用常规的瓷器命令,如果不签出新根分支并重复更新索引和工作副本,就不能创建空提交。但有些人可能会觉得这更容易理解:
git checkout --orphan newroot
git rm -rf .
git clean -fd
git commit --allow-empty -m 'root commit'
注意,在非常旧的Git版本中,没有——孤儿开关来签出,你必须用这个替换第一行:
git symbolic-ref HEAD refs/heads/newroot
2. 重写历史,从这个空提交开始
您有两个选择:重设基础,或重写干净的历史记录。
变基
git rebase --onto newroot --root master
这具有简单的优点。但是,它还将在分支上的每次提交时更新提交者名称和日期。
此外,对于一些边缘历史案例,它甚至可能由于合并冲突而失败——尽管事实上您正在基于一个不包含任何内容的提交。
历史改写
更清晰的方法是重写分支。与git rebase不同,你需要查找你的分支是从哪个提交开始的:
git replace <currentroot> --graft newroot
git filter-branch master
显然,重写发生在第二步;这是需要解释的第一步。git replace所做的是告诉git,无论何时它看到一个你想要替换的对象的引用,git都应该查看该对象的替换。
通过这个-嫁接开关,你会告诉它一些与正常情况略有不同的东西。你说的是还没有替换对象,但你想要替换<currentroot>提交对象与自己的精确副本,除了替换的父提交应该是你列出的(即newroot提交)。然后git replace继续为你创建这个commit,然后声明这个commit替换了你原来的commit。
现在如果你做一个git日志,你会看到事情已经像你想要的那样:分支从newroot开始。
但是,请注意,git replace实际上并不修改历史记录——它也不会传播到存储库之外。它只是在存储库中添加一个从一个对象到另一个对象的本地重定向。这意味着没有人能看到这种替代的影响——只有你自己。
这就是为什么过滤器分支步骤是必要的。使用git replace,你可以创建一个精确的副本,调整了根提交的父提交;然后,Git过滤器分支对接下来的所有提交也重复这个过程。这就是历史被改写的地方,这样你就可以分享它。