它们不一样,--fork-point 选项也会使这变得复杂。我认为这可能会让您感到困扰,尽管仅根据您所描述的内容无法确定,因为您概述的步骤之一只会产生错误。
我从一个合理的猜测开始,但它是一个猜测
要查看实际发生的情况,绘制(部分)提交图非常有帮助,特别注意标签,因为您使用的多个名称都指向一个提交。
假设当前分支是FeatureABC,它与远程分支完全同步。
因此我们有这样的东西——但是这样的东西还不够好; 你有存储库,所以你应该画图;我不得不猜测:
...--o--A--B--C--D--E <-- FeatureABC (HEAD), origin/FeatureABC
现在你运行:
#---create two identical branches, behind current branch by 5 commits
(FeatureABC) git branch Demo1-Rebase-ABC HEAD~4
(FeatureABC) git branch Demo2-Rebase-onto-ABC HEAD~4
由于HEAD~4名称提交A(HEAD~1是D,HEAD~2是C,等等),我们需要做一些事情来标记这两个新名称指向的事实提交A。不过,我会将名称缩短为 Demo1 和 Demo2。 (此时我已经创建了一个仅包含 o 到 E 提交的存储库,并在此处实际运行 git branch Demo1 HEAD~4; git branch Demo2 HEAD~4。)
...--o--A <-- Demo1, Demo2
\
B--C--D--E <-- FeatureABC (HEAD), origin/FeatureABC
顺便说一句,git log --all --decorate --oneline --graph(有人说“从狗那里得到帮助”)以这种方式显示了这个测试存储库(在我的例子中没有origin/ 分支):
* c4a0671 (HEAD -> master) E
* a7b8ae4 D
* 3deea72 C
* b11828d B
* ffc29b5 (Demo2, Demo1) A
* 3309a8d initial
接下来,你看看Demo1,移动HEAD:
git checkout Demo1-Rebase-ABC
...--o--A <-- Demo1 (HEAD), Demo2
\
B--C--D--E <-- FeatureABC, origin/FeatureABC
并修改工作树,将修改后的文件添加到索引中,然后提交,以进行新的提交,我将其称为F,它更新了HEAD 分支,因此将Demo1 和@987654355 分开@。我现在将在这里使用我自己的命令及其输出:
$ git checkout Demo1
Switched to branch 'Demo1'
$ echo demo1 > demo1.txt && git add demo1.txt && git commit -m F
[Demo1 89773b6] F
1 file changed, 1 insertion(+)
create mode 100644 demo1.txt
绘制图表变得有点困难;我会用一行:
F <-- Demo1 (HEAD)
/
...--o--A <-- Demo2
\
B--C--D--E <-- FeatureABC, origin/FeatureABC
现在我们开始执行您的第一个git rebase 命令。我必须使用master,当然:
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: F
这适用于当前分支(HEAD 或 Demo1)。它会找到在HEAD 上但不在FeatureABC 上的提交(FeatureABC.. 在gitrevisions 语法中)。那是提交F。这些提交被放入到提交列表中也许 复制—git rebase 将检查具有相同git patch-id 的提交并跳过它们,尽管很明显这里没有发生这种情况。所以现在将提交F 复制到新的提交F',具有不同的哈希ID 和不同的基数:
F [abandoned]
/
...--o--A <-- Demo2
\
B--C--D--E <-- FeatureABC, origin/FeatureABC
\
F' <-- Demo1 (HEAD)
(这是实际的git log 输出,显示了副本的新提交哈希。除非我在命令中添加Demo1@{1},否则不会显示原始的,现已废弃的F,我在此处执行此操作。原始是 second F 显示的,即较早的提交:
$ git log --all --decorate --oneline --graph Demo1@{1}
* c1d0896 (HEAD -> Demo1) F
* c4a0671 (master) E
* a7b8ae4 D
* 3deea72 C
* b11828d B
| * 89773b6 F
|/
* ffc29b5 (Demo2) A
* 3309a8d initial
我更喜欢水平图,但是这个有更多的信息,特别是缩写的哈希 ID。)
Reproducer 失败,我得再猜一次
现在我们尝试使用Demo2 重复此操作,但失败了。这是我的实际命令,剪切和粘贴。第一步工作正常:
$ git checkout Demo2
Switched to branch 'Demo2'
$ echo demo2 > demo2.txt && git add demo2.txt && git commit -m G
[Demo2 ae30665] G
1 file changed, 1 insertion(+)
create mode 100644 demo2.txt
不再画原来的F,这是新图。我把G 放在了F 以前的位置,虽然我可以把它画成...--o--A--G:
G <-- Demo2 (HEAD)
/
...--o--A
\
B--C--D--E <-- FeatureABC, origin/FeatureABC
\
F <-- Demo1
但是,rebase 不起作用。我必须再次使用master 而不是FeatureABC,但这在您的示例中的行为方式相同,因为git branch 命令没有设置上游(“跟踪”)名称:
$ git rebase --onto master
There is no tracking information for the current branch.
Please specify which branch you want to rebase against.
See git-rebase(1) for details.
git rebase <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=<remote>/<branch> Demo2
git rebase 失败并显示此错误消息的原因是 --onto has absorbed the argument as <newtarget>, leaving us with no <upstream>:
如果未指定<upstream>,则将使用branch.<name>.remote 和branch.<name>.merge 选项中配置的上游(详见git-config(1))并假定--fork-point 选项。如果您当前不在任何分支上,或者当前分支没有配置上游,rebase 将中止。
这里的粗体字是我的,但我认为它也是关键。我假设你运行了一个git rebase --onto <somename>,没有失败。为了让它没有失败,你的分支必须有一个上游集。上游可能是 origin/FeatureABC 或类似的,这意味着就 Git 而言,你正在运行:
git rebase --onto FeatureABC --fork-point origin/FeatureABC
而不是:
git rebase --onto FeatureABC --no-fork-point origin/FeatureABC
有些further reading in the (overly cryptic, in my opinion) git rebase documentation会翻出这句话:
如果在命令行中给出<upstream> 或--root,则
默认为--no-fork-point,否则默认为
--fork-point.
换句话说:
git rebase FeatureABC
关闭--fork-point 选项,如下所示:
git rebase --onto FeatureABC FeatureABC
但是:
git rebase
或:
git rebase --onto FeatureABC
保留--fork-point 选项on。
--fork-point 是关于什么
--fork-point 的目标 是专门drop 提交,这些提交曾经一度在您的上游,但不再在您的上游。有关示例,请参阅Git rebase - commit select in fork-point mode。 The specific mechanism is complicated and relies on the upstream branch's reflog. 由于我没有您的存储库或您的 reflog,因此我无法测试您的具体案例 - 但这是一个原因,并且可能是最可能的原因,因为您的问题中的提示是,提交 会 影响 rebase tree 结果 会被丢弃。由于与上游提交具有相同的patch ID 而被丢弃的提交是 [edit:] 通常1 不会 影响的提交最后一次复制的提交的最终树:它们只会导致合并冲突和/或强制您使用git rebase --skip 跳过它们(如果包含它们)。
1写完这篇文章后我突然想到有一个重要的例外(这可能与最初的问题无关,但我应该提一下)。将一个特性或主题分支重新定位到一个更主线的分支上,当一个提交首先从特性中挑选出 out 到主线中,然后在主线中 reverted,将造成问题。考虑一下,例如:
...--o--*--P--Q--C'-R--S--X--T <-- mainline
\
A--B--C--D--E <-- topic
其中C' 是提交C 的副本,X 是提交C 的还原,它不应该被放入mainline。正在做:
git checkout topic
git rebase mainline
将指示 Git 将提交 A 到 E 放入“候选复制”列表中,但还要查看 P 到 T 以查看是否已采用任何提交。提交C 被采纳,为C'。如果C 和C' 具有相同的补丁ID——通常,它们会——Git 会将C 从列表中删除为“已复制”。但是,C 在提交 X 中被明确还原。
做rebase的人需要注意,如果需要和适当的话,仔细恢复C。
这种特殊行为不是git merge 的问题(因为合并忽略中间提交),只有git rebase 有问题。