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" 部分——创建合并:
git checkout R1609; git pull origin R1609
:这可能会在您当前的(即R1609
)分支上创建一个新的合并提交,使用它从您的上游找到和获取的任何内容,即其他(来源的)Git的R1609
.
git checkout R1609_dpl; git pull origin R1609_dpl
。与步骤 1 一样,这可能会创建一个新的合并提交。
git merge --no-ff R1609
:这个绝对创建一个合并提交,除非它什么都不做。 (这里最好不要因为第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 fetch
和 git pull
都有内置代码来检测快进移动和非快进移动。这就是 --force
标志的全部意义所在,在推送时——就此而言,在获取时,尽管获取总是通过 refspec 中的 +
语法来完成。如果没有 --force
或 +
标志,Git 拒绝将分支标签从提交 X 更改为提交 Y,除非提交 X 是 Y 的祖先。
当然,merge
命令也有相同的代码,它将快进标签而不是合并标签,前提是 (a) 这是可能的并且 (b) 您没有禁止它。由于此操作是在 当前分支 上完成的,因此使用当前索引和工作树,git merge
也会更新索引和工作树以匹配。获取和推送代码既不更新索引也不更新工作树。
我的 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" 部分——创建合并:
git checkout R1609; git pull origin R1609
:这可能会在您当前的(即R1609
)分支上创建一个新的合并提交,使用它从您的上游找到和获取的任何内容,即其他(来源的)Git的R1609
.git checkout R1609_dpl; git pull origin R1609_dpl
。与步骤 1 一样,这可能会创建一个新的合并提交。git merge --no-ff R1609
:这个绝对创建一个合并提交,除非它什么都不做。 (这里最好不要因为第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 fetch
和 git pull
都有内置代码来检测快进移动和非快进移动。这就是 --force
标志的全部意义所在,在推送时——就此而言,在获取时,尽管获取总是通过 refspec 中的 +
语法来完成。如果没有 --force
或 +
标志,Git 拒绝将分支标签从提交 X 更改为提交 Y,除非提交 X 是 Y 的祖先。
当然,merge
命令也有相同的代码,它将快进标签而不是合并标签,前提是 (a) 这是可能的并且 (b) 您没有禁止它。由于此操作是在 当前分支 上完成的,因此使用当前索引和工作树,git merge
也会更新索引和工作树以匹配。获取和推送代码既不更新索引也不更新工作树。