git:如何将分支的根两个提交移回

git: how to move a branch's root two commits back

假设我有:

A - B - C - D - E - F  master
            \ 
             \- G - H  new feature branch

现在我意识到实际上提交了 B 和 C 属于新功能,所以我想把它们移到"new feature branch"。换句话说,我希望 "new feature branch" 从 A 开始,并包括提交 B 和 C:

A - D - E - F  master
 \ 
  \- B - C - G - H  new feature branch

我该怎么做?从我读过的内容来看,rebase 似乎是我正在寻找的功能,但我想在弄乱我的存储库之前确定一下。

(我搜索并发现了很多非常相似的问题和示例,但是 none 完全 就像我描述的场景一样,所以我要求确定(毕竟,回购是一件很珍贵的东西)。

修复大师:

git checkout A
git cherry-pick master~3..master # apply the changes we actually want to keep on master
git branch -f master # reposition master on new position
git checkout master

要从功能分支中删除提交 D:

git checkout feature-branch~3 # or git checkout C
git cherry-pick feature-branch~2..feature-branch # apply the last 2 revisions
git branch -f feature-branch
git checkout feature-branch

是正确的(并且已投票),但值得指出一些额外的项目。

首先,您的问题涉及 "moving the branch's root"——但这是 Git;分支没有 根,反正不是你想的那样。来看看你画的图:

A - B - C - D - E - F  master
            \ 
             \- G - H  new feature branch

我想你和我刚开始使用 Git 时一样,会认为提交 A-B-C-D-E-F 是在 master 分支上,而提交 G-H 作为功能分支。但这不是 Git 的工作原理。让我们重新绘制它,而不更改任何提交 links:

           E--F   <-- master
          /
A--B--C--D
          \
           G--H   <-- feature

这应该更清楚地表明,就 Git 而言,提交 A-B-C-D 两个 分支上。只有一个根。那是提交 A:它是根,因为它没有父提交,从提交到父的链中没有向后 link。

其次,无论您如何处理,最终都必须复制 一些提交。原因是每个提交的父 ID 都是该提交身份的一部分。任何提交的 "true name" 是其哈希 ID,哈希 ID 是通过读取提交的完整内容构建的:源代码树、提交消息、作者姓名和日期等,但始终包括父 ID .1 您希望最终图表类似于:

  D--E--F   <-- master
 /
A
 \ 
  B--C--G--H   <-- feature

但是现有的Dlinks(或点)回到现有的C,而不是A,以及现有的G点回到现有的 D.

这就是 cherry-pick 起作用的原因:git cherry-pick 本质上是 复制 一个提交。新副本 "does the same thing" 与原始副本一样,但有 某些地方 不同,即使它像 "my parent is ..." 一样简单。 (通常它也有一个不同的附加 tree 对象,只是它所做的 change 与它的新父对象相比,是与将原件与原件的父级进行比较时原件所做的更改相同)。这意味着 you can't actually get what you want, just what you need:

  D'-E'-F'  <-- master
 /
A
 \ 
  B--C--G'-H'  <-- feature

其中的小勾号表示结果是原件的副本

原件会怎样?答案是:它们仍然存在于存储库中,以备不时之需。完整图更像是这样:

    D'-E'-F'   <-- master
   /
  /        E--F   [abandoned]
 /        /
A--B--C--D
       \  \
        \  G--H   [abandoned]
         \
          G'-H'   <-- feature

虽然 git cherry-pick 有效,但 git rebase——尤其是 git rebase -i——所做的是以一种奇特的自动化方式进行这些复制,最后一步是移动分支名称,放弃原始提交。所以 git rebase -i 有时是 更简单 的方法。

如果你 运行 git rebase -i 你会看到所有那些 pick 命令,以及那些字面上的 运行 git cherry-pick:它真的是在做一系列的樱桃-选秀权。如果你自己做这些,可能会更清楚发生了什么,并且你可以更好地控制何时移动分支标签。


1对于合并提交,这是父s,复数。所有家长都参与哈希。

您可以使用 git rebase 来做到这一点。

首先,让我们 rebase 来自 master 的一些提交:

$ git checkout master
$ git rebase --onto A C

这会将 C 到 master 范围内的所有提交(但不包括 C 本身)移动到提交 A 上。

现在变基 feature 但抛出提交 D:

$ git checkout feature
$ git rebase --onto C D

与上一个命令类似,这会将 D 到 feature 范围内的提交(不包括 D 本身)移动到 C。