将历史移植到*current*分支上

Ram*_*man 1 git

我知道如何使用git rebase --onto移植历史.但是,我经常发现自己想要将历史从不同的分支移植到我当前的分支上,而没有中间分离的HEAD状态.

换一种说法:

git checkout foo
git rebase --onto foo A^ B
# results in a detached HEAD
# how to avoid this last step?
git checkout -B foo
Run Code Online (Sandbox Code Playgroud)

tor*_*rek 5

TL; DR

使用git cherry-pick,以及(稍后)git resetgit branch -f视情况而定.

当我回顾你已经知道的事情时,请耐心等待一段时间,因为他们会让答案更加清晰.

Rebase通过复制提交来工作.也就是说,给定一个表单的提交图:

...--o--*--I   <-- mainline
         \
          F--G--H   <-- branch (HEAD)
Run Code Online (Sandbox Code Playgroud)

我们常常发现自己希望有提交序列F-G-H提交I的主线.为此,我们运行git checkout branch && git rebase mainline,将提交复制F-G-H到新的和改进的.然后,在制作副本后,它将当前分支名称移动到指向最后一个这样的副本:

             F'-G'-H'  <-- branch (HEAD)
            /
...--o--*--I   <-- mainline
         \
          F--G--H   [abandoned, sort of]
Run Code Online (Sandbox Code Playgroud)

正如您已经知道的那样,我们可以将要复制的提交集合从他们应该使用的地点分开git rebase --onto.例如F-G-H,我们可能不想复制所有内容,而是留下F来,仅复制GH:

             G'-H'  <-- branch (HEAD)
            /
...--o--*--I   <-- mainline
         \
          F   <-- ???
           \
            G--H   [abandoned, sort of]
Run Code Online (Sandbox Code Playgroud)

当我们这样做时,我们让Git移动名称之前,将一些名称附加到我们留下的提交中是个好主意branch.我们可以使用git branch,例如git checkout -B,或等等.然而,关键是我们使用--onto选择提交I- 目标提交之后副本将去 - 和另一个参数来限制哪些提交被复制.复制的提交我们给出的参数之后的那些提交,包括HEAD分支上的提交.

这个--onto案例的论点如下:

git rebase --onto $target $stop
Run Code Online (Sandbox Code Playgroud)

在哪里$stop,例如,提交F.

rebase如何在内部工作

rebase实际实现这一点的方式非常简单:

  1. 它会记住当前分支(或者对于分离的HEAD,哈希ID):saved_branch=$(git symbolic-ref -q --short HEAD)或多或少.

  2. 它列出了要复制的提交的哈希ID.它使用git rev-list了很多花哨的选项来处理所有的难题,但对于我们的简单情况,我们可以使用$stop..HEAD.

  3. 它运行git checkout --detach $onto(或或多或少等价的东西).

  4. 它运行git cherry-pick <saved list>,即git cherry-pick $stop..$saved_branch.

  5. 一旦完成所有樱桃选择 - 包括解决冲突的任何停止,由人工处理它git branch -f $saved_branch HEAD(或者或多或少等价的东西)来使保存的分支名称指向最后复制的提交.(还有一些特殊的名字ORIG_HEAD.)

你想要什么

在你的情况下,你已经在这个--onto部分.也就是说,步骤1是无关紧要的,步骤2不可能以完全相同的方式发生.第3步不应该发生 - 我们希望新的提交增长当前的分支!

这让我们只需要步骤4和5来实现.

在任何git cherry-pick可以处理序列的现代Git中,步骤4都特别容易:我们只运行:

git cherry-pick $stop..$branch
Run Code Online (Sandbox Code Playgroud)

这里$stop是停止在提交(提交我们希望复制),并且$branch是指向最后一次提交,我们的分支想复制.如果我们mainline现在,那git cherry-pick <hash-of-F>..branch或是git cherry-pick branch~2..branch,复制提交GH.

为了实现第5步 - $branch重新提交 - F我们将在完成复制后将其保留.当那些副本完成后,我们可以运行:

git checkout $branch && git reset --hard $stop
Run Code Online (Sandbox Code Playgroud)

要么:

git branch -f $branch $stop
Run Code Online (Sandbox Code Playgroud)

我们在的时候mainline.