为什么 "rebase --onto ABC" 与 "rebase ABC" 不同?

Why is "rebase --onto ABC" different than "rebase ABC"?

使用 git 2.11,git rebase 文档说:

The current branch is reset to <upstream>, or <newbase> if the --onto option was supplied. This has the exact same effect as git reset --hard (or ). ORIG_HEAD is set to point at the tip of the branch before the reset.

我理解好像upstreamnewbase指向相同的"base reference",那么这意味着下面的两个rebase语法是等价的:

git rebase ABC
git rebase --onto ABC

这是我设置的演示。假设当前分支是 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

#---Make a new commit in branch Demo1
git checkout Demo1-Rebase-ABC
echo "Demo of: git rebase FeatureABC Demo1-Rebase-ABC" > ./Demo1_BogusFile.txt
git add ./Demo1_BogusFile.txt
git commit -m "Create file Demo1_BogusFile.txt"

git rebase FeatureABC

First, rewinding head to replay your work on top of it
... Applying: Create file Demo1_BogusFile.txt

git log --oneline -3 显示分支 Demo1-Rebase-ABC 与 FeatureABC 的 HEAD 同步。并且提交 "Create file Demo1_BogusFile.txt" 被正确应用在它之上。

#---Make a new commit in branch Demo2
git checkout Demo2-Rebase-onto-ABC
echo "Demo of: git rebase --onto FeatureABC Demo2-Rebase-onto-ABC" > ./Demo2_Onto_BogusFile.txt
git add ./Demo2_Onto_BogusFile.txt
git commit -m "Create file Demo2_Onto_BogusFile.txt"

git rebase --onto FeatureABC

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=origin/<branch> Demo2-Rebase-onto-ABC

我误解了警告信息。当使用 --onto 时,认为 git 在默认值中被混淆了。所以我只想 "help" 通过告诉 git 我想变基的当前分支

git rebase --onto FeatureABC Demo2-Rebase-onto-ABC

First, rewinding head to replay your work on top of it...

git log --oneline -3 表明分支 Demo2-Rebase-onto-ABCFeatureABC 相同。最后一次提交 "Create file Demo2_Onto_BogusFile.txt" 已消失,文件 ./Demo2_Onto_BogusFile.txt 已删除。

问题git rebase --onto FeatureABC Demo2-Rebase-onto-ABC 没有应用在 Demo2-Rebase-onto-ABC 分支上所做的新提交的原因是什么?

它们不一样,--fork-point 选项也会使这变得复杂。我认为这可能是您的原因,尽管无法确定,仅从您所描述的内容来看,因为您概述的步骤之一只会产生错误。

我从一个合理的猜测开始,但它一个猜测

要查看真正发生的事情,绘制(部分)提交图非常有帮助,特别注意标记,因为您使用的是多个名称,所有名称都指向单个提交。

Let's assume the current branch is FeatureABC it is perfectly in sync with the remote branch.

因此我们有这样的东西——但是这样的东西还不够好; 有存储库,所以应该画图;我得猜一下:

...--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 名称提交 AHEAD~1DHEAD~2C,等等),我们需要做一些事情来标记这两个新名称指向提交 A 的事实。不过,我打算将名称缩短为 Demo1Demo2。 (此时我创建了一个只有 oE 提交的存储库,实际上 运行 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("get help from A DOG" 正如有人所说)以这种方式显示了这个测试存储库(在我的例子中没有 origin/ b运行ch):

* 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 b运行ch 因此将 Demo1Demo2 分开。我现在将在这里使用我自己的命令及其输出:

$ 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

这适用于当前的 b运行ch(HEADDemo1)。它会找到 HEAD 上不在 FeatureABC 上的提交(gitrevisions 语法中的 FeatureABC..)。那是提交 F。这些提交被放入到 maybe 副本的提交列表中——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 , which I did here. 原来是显示的 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。)

复制失败,我得再猜一次

现在我们尝试用 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 命令没有设置上游("tracking")姓名:

$ 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>:

If <upstream> is not specified, the upstream configured in branch.<name>.remote and branch.<name>.merge options will be used (see git-config(1) for details) and the --fork-point option is assumed. If you are currently not on any branch or if the current branch does not have a configured upstream, the rebase will abort.

这里的粗体是我的,但我认为它也是关键。我假设你 运行 git rebase --onto <somename> 没有 失败。为了让它没有失败,你的 b运行ch 必须有一个上游集。上游可能 origin/FeatureABC 或类似的,这意味着就 Git 而言,你是 运行ning:

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会出现这句话:

If either <upstream> or --root is given on the command line, then the default is --no-fork-point, otherwise the default is --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 曾经在你的上游提交的,但不再在您的上游。请参阅 for an example. Since I don't have either your repository or your reflog, I cannot test out your specific case—but that's one reason, and probably the most likely reason given the hints in your question, that a commit that would affect the rebase tree result would get dropped. The commits that are dropped due to having the same patch ID 作为上游提交 [edit:] 通常 1 而不是 影响最后复制的提交的最终树:它们只会导致合并冲突 and/or 强制你使用 git rebase --skip 跳过它们,如果它们被包含。


1写完之后我突然想到有一个重要的例外(这可能与原始问题无关,但我应该提一下)。将功能或主题 b运行ch 重新定位到更主线的 b运行ch 上,当提交首先被挑选 out 功能进入主线时,然后 reverted 在主线,会导致问题。考虑,例如:

...--o--*--P--Q--C'-R--S--X--T   <-- mainline
         \
          A--B--C--D--E          <-- topic

其中 C' 是提交 C 的副本,X 是不应该放入 mainline 的提交 C 的还原然而。正在做:

git checkout topic
git rebase mainline

将指示 Git 将提交 AE 放入 "candidates to copy" 列表中,但也会查看 PT看看是否有任何已经被采用。提交 C 采用,作为 C'。如果 CC' 具有相同的补丁程序 ID——通常,它们 ——Git 将从列表中删除 C 作为 "already copied"。但是,C 在提交 X.

中被显式 还原

谁做 rebase 需要注意,如果需要和适当,请小心恢复 C

这种特殊行为 不是 git merge 的问题(因为合并忽略中间提交),只有 git rebase.