好吧,您的提交图并不完全准确(可以稍微澄清一下)。这样做的原因是,当我们达到这一点时,它将有助于理解解决方案。所以:
我就是这样开始的:
commits: A -> B -> C -> D (HEAD)
现在,如果您要在提交之间绘制箭头,它们应该及时“向后”指向,因为 git 存储父指针而不是子指针。 (我只是画线而不是箭头。)
我假设master 分支(和origin/master 参考)位于D。此外,由于您没有显示分离的HEAD 状态,我假设您已签出master。我通常会画这样的东西
commits: A -- B -- C -- D <--(master)(origin/master)
^(HEAD)
这显示了 ref 指向的位置,并反映出 HEAD 是指向 master 的符号 ref。
我 git checkout 到 C:
所以现在我们有
commits: A -- B -- C -- D <--(master)(origin/master)
^(HEAD)
(这显示了以这种方式绘制HEAD 的优势;只需移动它使其指向提交,我们就知道我们处于分离状态。)
修改C处的代码,导致C':
这就是上述图表变得不准确的地方。这个新的提交不会替换 C,而是C 的新子代。所以这更像是
commits: A -- B -- C -- D <--(master)(origin/master)
\
C'
^(HEAD)
(这显示了绘制线条而不是箭头的优点;更容易描述不同的提交线)。但即使这样也不完全正确,因为按照惯例,C' 将意味着“一个新的/重写的提交,它将与C 应用于其父级相同的更改应用于Cs 父级”;所以我们应该叫它别的名字。
commits: A -- B -- C -- D <--(master)(origin/master)
\
E
^(HEAD)
现在我们可以解决您正在尝试做的事情。
现在,虽然您在更新中说您不关心保留 D,但您可能没有考虑保留它的所有理由。如果你说“我真的想把 D 从历史中剔除,这就是为什么......”那是一回事,但如果只是“我不在乎一种或另一种”,那么你应该考虑保留它。原因如下:
删除D 是对历史的重写。推送分支后,对该分支进行历史重写可能会导致问题,尤其是在与其他用户共享存储库的情况下。请参阅“从上游变基中恢复”下的 git rebase 文档。
现在,如果您了解其中的问题 - 即,如果您知道您需要与拥有 ref 副本的任何其他人协调,如果不这样做可能会导致您的重写被意外撤消 - 而且仍然想丢弃D,那么你可以这样做:
重写方法
从您原来的问题停止的地方开始,您将master 分支移动到您新创建的提交。
git branch -f master
git checkout master
这会给你
commits: A -- B -- C -- D <--(origin/master)
\
E <--(master)
^(HEAD)
(事实上,更容易的事情是从一开始就将master 重置为HEAD^,而不是检查到分离的HEAD 状态;假设,也就是说,你知道你要去在那一点上进行重写。)
然后您可以推送master 的重写,但您必须“强制”推送。这是您将导致上游变基的“危险信号”
git push --force-with-lease
如果其他人向origin/master 添加了更多提交,这将失败。这是因为完成重写可能会丢失他们的工作,并且至少应该采取额外的步骤来解决这个问题。如果您仍想覆盖该安全检查,您可以说
git push -f
请注意,无论是这个方法还是任何其他方法都不会真正删除提交D。它会从master 历史记录中删除D,这意味着它最终可能会被gc 删除。
commits: A -- B -- C -- D
\
E <--(master)(origin/master)
^(HEAD)
无重写方法
另一方面,如果您认为重写带来的麻烦多于其价值,您会这样做:
再次从原始问题中断的地方开始,您可能希望保留所做的更改,这样您就不必重新开始。
git branch temp
git checkout master
现在恢复在D中所做的更改
git revert HEAD
屈服
~D <--(master)
/ ^(HEAD)
commits: A -- B -- C -- D <--(origin/master)
\
E <--(temp)
~D 的内容 (TREE) 将匹配 C 的内容,所以现在你可以说
git rebase master temp
git checkout master
git merge --ff-only temp
git branch -d temp
所以我们终于有了
~D -- E' <--(master)
/ ^(HEAD)
commits: A -- B -- C -- D <--(origin/master)
\
E
原来的E 提交不再感兴趣;对D(~D)的反转和对E(E')的改动添加在master上,可以正常推送。