我之前问过如何squash the first two commits在 git 存储库中。
虽然这些解决方案相当有趣并且不像 git 中的其他一些东西那样真正令人费解,但如果您需要在项目开发过程中多次重复该过程,它们仍然有点像众所周知的伤害包.
所以,我宁愿只经历一次痛苦,然后就可以永远使用标准的交互式 rebase 。
然后,我想做的是有一个空的初始提交,它的存在仅仅是为了成为第一个。没有代码,什么都没有。只是占用空间,所以它可以作为 rebase 的基础。
那么我的问题是,有了一个现有的存储库,我该如何在第一个提交之前插入一个新的空提交,并将其他所有人向前移动?
最佳答案
实现这一目标有 2 个步骤:
- 创建一个新的空提交
- 重写历史以从这个空提交开始
我们将把新的空提交放在临时分支上 newroot
为了方便。
1。创建一个新的空提交
有多种方法可以做到这一点。
仅使用管道
最干净的方法是使用 Git 的管道直接创建一个提交,这样可以避免触及工作副本或索引或哪个分支被 checkout 等。
为一个空目录创建一个树对象:
tree=`git hash-object -wt tree --stdin < /dev/null`
围绕它包装一个提交:
commit=`git commit-tree -m 'root commit' $tree`
创建对它的引用:
git branch newroot $commit
如果您足够了解您的 shell,您当然可以将整个过程重新安排到一行中。
没有管道
使用常规瓷器命令,您不能在不检查 newroot
的情况下创建空提交无缘无故地重复分支和更新索引和工作副本。但有些人可能会觉得这更容易理解:
git checkout --orphan newroot
git rm -rf .
git clean -fd
git commit --allow-empty -m 'root commit'
请注意,在缺少 --orphan
的非常旧版本的 Git 上切换到 checkout
,您必须将第一行替换为:
git symbolic-ref HEAD refs/heads/newroot
2。重写历史以从这个空提交开始
您在这里有两个选择: rebase 或重写干净的历史记录。
rebase
git rebase --onto newroot --root master
这具有简单的优点。但是,它还会更新分支上每次最后一次提交的提交者姓名和日期。
此外,对于一些边缘案例历史,它甚至可能由于 merge 冲突而失败——尽管事实上您正在 rebase 到一个不包含任何内容的提交上。
重写历史
更简洁的方法是重写分支。与 git rebase
不同,您将需要查找您的分支从哪个提交开始:
git replace <currentroot> --graft newroot
git filter-branch master
显然,重写发生在第二步;这是需要解释的第一步。什么git replace
它的作用是告诉 Git,每当它看到对要替换的对象的引用时,Git 应该查看该对象的替换。
随着 --graft
开关,你告诉它一些与平常略有不同的东西。你说还没有替换对象,但你想替换 <currentroot>
提交具有自身精确副本的对象 except 替换的父提交应该是您列出的那个(即 newroot
提交)。那么git replace
继续为您创建此提交,然后声明该提交将替换您的原始提交。
现在如果你做 git log
, 你会看到事情已经如你所愿:分支从 newroot
开始.
但是,请注意 git replace
实际上不会修改历史记录 – 它也不会传播到您的存储库之外。它只是将本地重定向添加到您的存储库,从一个对象到另一个对象。这意味着没有其他人可以看到此替换的效果 - 只有您。
这就是为什么 filter-branch
步骤是必要的。与 git replace
您为根提交创建了一个具有调整后父提交的精确副本; git filter-branch
然后对所有以下提交也重复此过程。这就是历史实际被重写的地方,以便您可以分享它。
关于git - 在 Git 中的根提交之前插入一个提交?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/645450/