我想要重基到一个特定的提交,而不是另一个分支的HEAD:

A --- B --- C          master
 \
  \-- D                topic

to

A --- B --- C          master
       \
        \-- D          topic

而不是

A --- B --- C          master
             \
              \-- D    topic

我怎样才能做到呢?


当前回答

你可以通过在commit上创建一个临时分支来避免使用——onto参数,然后以简单的形式使用rebase:

git branch temp master^
git checkout topic
git rebase temp
git branch -d temp

其他回答

你可以通过在commit上创建一个临时分支来避免使用——onto参数,然后以简单的形式使用rebase:

git branch temp master^
git checkout topic
git rebase temp
git branch -d temp

一个更简单的解决方案是git rebase <SHA1 of B> topic。不管你的头在哪里,这都是有效的。

我们可以从git rebase文档中确认这种行为

<upstream>要比较的上游分支。可能是任何有效的 提交,而不仅仅是一个现有的分支名称。默认为配置的 当前分支的上游。

你可能会想,如果我在上面的命令中也提到topic的SHA1会发生什么?

git rebase <SHA1 of B> <SHA1 from topic>

这也可以工作,但rebase不会使Topic指向这样创建的新分支,HEAD将处于分离状态。因此,从这里开始,您必须手动删除旧的Topic,并在rebase创建的新分支上创建一个新的分支引用。

git rebase --onto <commitB> <commitA> <commitD>

实际上,使用commit (<commitD>)作为最后一个参数,而不是分支名称(topic),应该创建一个分离的分支:

    A --- B --- C          master
     \     \
      \     -- D'          <detached HEAD>
       \
        \-- D             topic

除了……在Git 2.36之前,在某些情况下不会:

"git rebase $base $non_branch_commit"(man),当$base是一个祖先或$non_branch_commit时,修改了当前的分支,这已在git 2.36 (Q2 2022)中更正。

参见John Cai (John - Cai)的commit bdff97a, commit 77ab58c (18 Mar 2022)。 (由Junio C Hamano - gitster -在提交f818536, 2022年3月29日合并)

rebase:在checkout_up_to_date()中设置REF_HEAD_DETACH 报道:Michael McClimon 签字人:John Cai

"git rebase A B"(man) where B is not a commit should behave as if the HEAD got detached at B and then the detached HEAD got rebased on top of A. A bug however overwrites the current branch to point at B, when B is a descendant of A (i.e. the rebase ends up being a fast-forward). See this thread for the original bug report. The callstack from checkout_up_to_date() is the following: cmd_rebase() -> checkout_up_to_date() -> reset_head() -> update_refs() -> update_ref() When B is not a valid branch but an oid, rebase sets the head_name of rebase_options to NULL. This value gets passed down this call chain through the branch member of reset_head_opts also getting set to NULL all the way to update_refs(). Then update_refs() checks ropts.branch to decide whether or not to switch branches. If ropts.branch is NULL, it calls update_ref() to update HEAD. At this point however, from rebase's point of view, we want a detached HEAD. But, since checkout_up_to_date() does not set the RESET_HEAD_DETACH flag, the update_ref() call will deference HEAD and update the branch its pointing to. We want the HEAD detached at B instead. Fix this bug by adding the RESET_HEAD_DETACH flag in checkout_up_to_date if B is not a valid branch, so that once reset_head() calls update_refs(), it calls update_ref() with REF_NO_DEREF which updates HEAD directly intead of deferencing it and updating the branch that HEAD points to.

既然调整基地是如此重要,下面是内斯特·米利耶夫(Nestor Milyaev)答案的扩展。结合jsz和Adam Dymitruk回答中的Simon South的评论,得到了这个命令,无论它是从主分支的提交A还是C中分支,它都可以在topic分支上工作:

git checkout topic
git rebase --onto <commit-B> <pre-rebase-A-or-post-rebase-C-or-base-branch-name>

注意,最后一个参数是必需的(否则它会倒带分支以提交B)。

例子:

# if topic branches from master commit A:
git checkout topic
git rebase --onto <commit-B> <commit-A>
# if topic branches from master commit C:
git checkout topic
git rebase --onto <commit-B> <commit-C>
# regardless of whether topic branches from master commit A or C:
git checkout topic
git rebase --onto <commit-B> master

最后一个命令是我通常使用的命令。

使用"onto"选项:

git rebase --onto master^ D^ D

OR

git rebase --onto <commitB> <commitA> <commitD>

最后3个参数的意思是: destination (new-parent,这里是commitB) Start-after (current-parent,第一个提交被移动的父节点), 和end-inclusive(要移动的最后一个提交)。