Git 合并时创建额外的分支

Git creates extra branch when merging

我的 git 实验室有两个 git 分支 R1609 和 R1609_dpl。

每次我将某些内容推送到 R1609 时,我都想使用触发器将这些更改合并到 R1609_dpl 中。 出于某种原因 git 为合并提交创建了一个额外的分支。合并后提交历史看起来像这样:

提交 "Merge branch X of URL into X" 似乎完全没有必要。 我的合并代码:

git checkout R1609
git pull origin R1609
git checkout R1609_dpl
git pull origin R1609_dpl
git merge --no-ff R1609
git commit --amend -m "Push to R1609 triggered: Merge R1609 into R1609_dpl"
git push origin R1609_dpl

我错过了什么?我想摆脱额外的分支并简单地合并到 R1609_dpl 分支而不需要两次合并提交。

提前致谢!

编辑: 摆脱了

git commit --amend -m "Push to R1609 triggered: Merge R1609 into R1609_dpl"

并添加了一个变基,但仍然没有成功

git checkout R1609
git pull origin R1609
git checkout R1609_dpl
git pull origin R1609_dpl
git merge --no-ff R1609
#Added rebase
git pull --rebase origin R1609_dpl
git push origin R1609_dpl

据我所知,没有"extra"分支。您所看到的只是对 Git 中实际发生的事情的图形解释。这是显示合并过程的前两个步骤的图表:

R1609:     _____
R1609_dpl: _____\_______   (R1609_dpl after merge with R1609)

如果您只有一个简单的合并,没有修改提交,您的图表应该看起来像我上面的那样。也就是说,您可以看到 R1609 在某个时候进入 R1609-dpl

但是,您执行了以下附加步骤:

git commit --amend -m "Push to R1609 triggered: Merge R1609 into R1609_dpl"

修改了(阅读:重写)合并提交。这意味着原始合并提交不再是 R1609_dpl 分支的一部分。我的预感是您的图形工具试图通过单独的棕色线来显示这一点。你会注意到这个 "branch" (它不是真正的分支)似乎只有一个提交。此提交是您的原始合并提交。

记住 git pull 表示 git merge.

嗯,意思是"First run git fetch, then run git merge unless I tell you to run git rebase as your second step instead."

因此,您有 四个 项可能——我们稍后会回到 "potentially" 部分——创建合并:

  1. git checkout R1609; git pull origin R1609:这可能会在您当前的(即R1609)分支上创建一个新的合并提交,使用它从您的上游找到和获取的任何内容,即其他(来源的)Git的R1609.

  2. git checkout R1609_dpl; git pull origin R1609_dpl。与步骤 1 一样,这可能会创建一个新的合并提交。

  3. git merge --no-ff R1609:这个绝对创建一个合并提交,除非它什么都不做。 (这里最好不要因为第4条什么都不做。)

  4. git commit --amend -m "Push to R1609 triggered: Merge R1609 into R1609_dpl":此 "amend" 将当前提交复制到具有新的不同消息的新提交。它将当前提交推到一边。假设我们在第 3 步中进行了合并,这会将合并推到一边并用新的合并替换它。被推到一边的合并将从 git log 输出中消失,因为当前的 分支 (现在 R1609_dpl)指向新的副本并且只有它的 reflog 指向原始的, 一旦修改完成.

那么,在某些方面,您没有获得 三个 次提交有点令人惊讶。您 得到的那个来自第 1 步,因为日志显示它的消息是 "Merge branch 'R1609' of <url> into R1609"。所以现在是描述 "potentially" 事情的时候了。

git fetch有两种可能,第二种又分为两种情况:

  • 您的 Git 调用 origin/R1609 但他们的(来源)Git 仅调用 R1609 的上游与您自己的同步R1609,或落后:您有一些他们缺少的提交(直到您推送它们)。在这种情况下,git fetch什么都不做,也没有什么要合并的,git merge什么也不做。 (是的,还有嘘,我们为什么还要打扰?:-)好吧,当然要检查。)

  • 上游与您的 R1609 不同步。 Git 从上游获取一些提交。这可能是 严格领先 你自己在 R1609 上的提交,或者可能是他们的 R1609 有一个你缺少的提交,而您的 R1609 有他们缺少的承诺。 fetch这一步不需要关心;它只会带来他们拥有的任何承诺,而您没有。然后它会更新您的 origin/R1609 以记住他们的新提交。1

当 fetch 确实 带来一些东西时,您现在可能 完全落后于——您只需要赶上他们的 R1609——或者你可能同时领先落后。在前一种情况下,git merge 默认执行 "fast forward" 操作。2 Git 更改您的分支标签,R1609,以便它指向与他们的 R1609(您的 origin/R1609)相同的提交。当然,如果您指定 --no-ff,则 Git 会进行真正的合并。但是,在后一种情况下,Git 必须 进行真正的合并,以合并您的提交和他们的提交。所以你在这里得到一个合并,它的消息是 Merge branch 'R1609' of <url> into R1609.

很明显,上游的某些东西导致新的提交出现在 他们的 R1609,你的 Git 发现它必须在上面的步骤 1 中合并.上游 R1609_dpl 没有任何反应,因此您的 Git 能够在第 2 步中根据需要快进或什么都不做。第 3 步创建一个新的合并,第 4 步将其推到一边,同时创建第三个合并,留下两个可见的合并。

(我看到当我写完所有这些时,您已经找到了在 origin 上引入提交的罪魁祸首。)


1嗯,只要你的Git是1.8.4或更新的版本。较旧的 Git 版本有时无法更新远程跟踪分支。 (具体来说,他们故意不在 pull 脚本的 fetch 运行 上更新它。结果证明这是一个错误,因此在 1.8.4 中进行了更改。)

2尽管 git merge 文档将此描述为 快进合并,但它实际上根本不是合并.快进是 标签移动 的 属性。给定一个指向提交 X 的标签,以及将其更改为指向标签 Y 的请求,我们测试: "Is X an ancestor of Y?" 如果是这样,则此更改是快进。如果不是,那就不是。

git fetchgit pull 都有内置代码来检测快进移动和非快进移动。这就是 --force 标志的全部意义所在,在推送时——就此而言,在获取时,尽管获取总是通过 refspec 中的 + 语法来完成。如果没有 --force+ 标志,Git 拒绝将分支标签从提交 X 更改为提交 Y,除非提交 X 是 Y 的祖先。

当然,merge命令也有相同的代码,它将快进标签而不是合并标签,前提是 (a) 这是可能的并且 (b) 您没有禁止它。由于此操作是在 当前分支 上完成的,因此使用当前索引和工作树,git merge 也会更新索引和工作树以匹配。获取和推送代码既不更新索引也不更新工作树。