这是我的本地回购的状态。我在AAAAA,并承诺CCCC。我做了一个git pull
,它提取了提交,并将BBBBB自动(ish)合并到CCCC中,并制作了DDDDD。我不想那样,所以我用一个数字重置杀死了DDDDD。
$git树--所有
CCCCC (HEAD, main) foobar issue 666
| * BBBBB (tag: fubar, origin/main, origin/HEAD) fubar issue #69
|/
* AAAAA foo
与其合并,我想把CCCC转移到BBBBB。我该如何重新设定基准?我需要先切换或结账到BBBBB吗?
* CCCCC (HEAD, main) foobar issue 666
* BBBBB (tag: fubar, origin/main, origin/HEAD) fubar issue #69
* AAAAA foo
回扣是正确的:
git fetch
git switch main
git rebase origin/main
你可能只是说git pull --rebase
,但我不是一个冒险的人。
从技术上讲,您实际上并没有移动提交。相反,您可以将其复制到一个新的改进提交中(使用不同的哈希ID)。Mercurial也是如此。然而,与Git的rebase相比,Mercurial的rebase("移植")和历史编辑(hg histedit
)界面对Mercurial新手来说要清晰得多。(这是Mercurial与Git的总主题。)
在Git中,提交从来没有实际绑定到任何特定的分支。相反,Git通过从某个分支名称开始查找提交——这实际上更像是一个Mercurial"书签"--然后反向工作。这个向后工作部分所达到的提交集合被称为"提交";在分支上";。
因此,当两个或多个分支名称识别出相同的最终提交时——就好像你在Mercurial中有两个或更多指向同一提交的书签一样——这些提交在同一个分支上,但一旦你在Git中移动了其中一个或两个分支名称,那两个完全不变的提交可能就不在同一分支上了。
git rebase
命令通过以下方式工作:
- 将当前分支名称保存到某个位置
- 枚举要复制的提交的原始哈希ID
- 使用Git的分离HEAD模式来选择一些特定的目标(
--onto
)提交。这就是新副本的去向 - 复制提交,一次一个,就像通过
git cherry-pick
一样(相当于Mercurialhg graft
,只是分支名称无关紧要:提交永远不会绑定到任何分支) - 最后,猛拉在步骤1中保存的分支名称以指向最后复制的提交,并退出"提交";"分离的头";模式返回到您在步骤1中使用的分支上
为了处理步骤2和3;其承诺复制";列表和";目标";commit——Git通常使用单个参数。这个参数通常是一个目标分支名称。因此,要复制的提交的来源是当前分支(根据步骤1)。这就是为什么我们从开始
git switch main
或等效物。
为了列出要复制的提交,Git使用大致等效的:
git log target..HEAD
这意味着发现可从CCD_ 8访问但不能从target
访问的提交。(Mercurial也有这一点,使用target::.
,除了Mercurial的::
图操作符包括区间的两端,而Git的是半开放区间:它总是排除最左边的提交。)
分离的HEAD签出目标实际上是这种形式的target
参数。
在某些情况下,不可能以这种方式生成正确的提交列表,因此git rebase
具有--onto
语法,这有点奇怪:
git rebase --onto target upstream
步骤2中的提交列表现在是upstream..HEAD
,而不是target..HEAD
。步骤3中的签出仍然是target
。
给定:
CCCCC (HEAD -> main) foobar issue 666
| * BBBBB (tag: fubar, origin/main, origin/HEAD) fubar issue #69
|/
* AAAAA foo
您想要复制的提交哈希ID的列表只有一个元素长,列出了CCCCC
。您希望副本所在的位置是提交BBBBB
。因此,如果需要的话——HEAD -> main
说不是——我们git checkout main
或git switch main
使提交CCCCC
成为当前的(即使不需要也可以这样做,那只是不操作)。然后我们运行git rebase origin/main
。
Git现在向后列出了这里的所有哈希ID(HEAD
或CCCCC
):这是CCCCC
,然后是AAAAA
,然后是下面的任何东西。从这个列表中,Git去掉了BBBBB
(它不在那里,但这只会让剥离速度很快!),然后是AAAAA
,然后是下面的任何东西——只剩下CCCCC
。
现在Git对BBBBB
执行分离的HEAD检查。然后,它按照正确的顺序为保存的列表中的所有提交发出cherry pick。列表中只有一个提交:CCCCC
。所以Git做了一个新的提交,也许是CCCCD
,就像CCCCC
一样,做同样的事情,但这是在BBBBB
:之后
CCCCC (main) foobar issue 666
| * CCCCD (HEAD) foobar issue 666
| * BBBBB (tag: fubar, origin/main, origin/HEAD) fubar issue #69
|/
* AAAAA foo
现在所有的复制都完成了,Git将名称main
拉到这里——拉到CCCCD
——并重新附加HEAD,这样我们就有了:
CCCCC foobar issue 666
| * CCCCD (HEAD -> main) foobar issue 666
| * BBBBB (tag: fubar, origin/main, origin/HEAD) fubar issue #69
|/
* AAAAA foo
提交CCCCC
无法找到,因此git log
不会显示it:
* CCCCD (HEAD -> main) foobar issue 666
* BBBBB (tag: fubar, origin/main, origin/HEAD) fubar issue #69
* AAAAA foo
空白行也不再有任何理由了,所以它就消失了。