git merge以某种方式合并"两种方式" (A到B和B到A)在一次提交中

时间:2014-12-03 22:23:51

标签: git git-gui

我不明白发生了什么。我将分支主服务器合并到我的功能分支中,这是我最近经常做的,并且不知何故合并提交现在存在于两个分支中,并且两个分支在提交之后处于相同的状态。我还没有找到关于如何发生这种情况的任何信息(或者它实际上可以完成)。我在Windows上使用git-gui(但在其他机器上使用git命令行,所以我理解基本的git命令)。 git-gui和GitLab都在历史图表中显示这个提交是将两个分支相互合并。有任何线索是怎么发生的?我不知道它会再发生什么,我应该摆脱git-gui吗?

然后,因为不幸的是这个提交被推送到远程,我仍然想要重置 - 硬,因为如果我做一个恢复,那么当我将来再次将master合并到功能中时,我会得到一切在功能分支中作为冲突,因为它在master中的revert中都被撤消了。关于如何最好地进行的任何意见或建议?

编辑:我以为我理解合并意味着什么,但从答案我现在更加困惑。如果我创建一个有很多新东西的功能分支,我想" import"在主人身上发生了一些变化,但是大师没有动过,这不是这样做的吗?

git checkout feature
git merge master

master A ------------ E <-- last master commit
        \              \  (try to merge into feature)
feature  B -- C -- D -- F <-- feature now contains "E" edits?

但我得到的是:

master A ------------ E  F <-- master now contains all of feature stuff (B, C, D).
        \              \ | 
feature  B -- C -- D --- F <-- feature now contains "E" edits.

Edit2:如果根据jszakmeister的回答,这是因为执行了2次合并操作,其中一次是快进,有没有办法恢复有关快进合并的信息,比如谁做了什么时候?如果提交消息说&#34;将master合并到feature&#34;,我想这意味着它首先在那个方向完成,然后有人快进合并功能到master,对吧?

谢谢!

2 个答案:

答案 0 :(得分:1)

我能看到它发生的一种方式。我们假设您有一个名为&#34; feature&#34;的功能分支。你合并了#34; master&#34;进入&#34;功能&#34;。所以图表看起来像:

---o---o---o        master
            \
-o---o---o---*      feature

如果您结帐&#34;功能&#34;并且合并master,你可能会得到一个快进合并,因为新的提交在&#34; master&#34;直接在&#34;功能&#34;的最后一次提交之前。所以图表最终看起来像这样:

---o---o---o
            \
-o---o---o---*      master, feature

这不是一个额外的提交,而是#34; master&#34;和&#34;功能&#34;现在指向 同样的提交。 FWIW,我的团队和我发现这种行为令人困惑,所以我已经 概述了我们在下面的工作,以帮助防止这个问题。

另外,我不确定你使用&#34; gitk&#34;是什么意思做你的提交。据我所知,gitk仅供查看。你的意思是&#34; git gui&#34;?我建议您使用&#34; gitk&#34;来查看图表。虽然。我喜欢使用以下内容:

gitk --date-order master feature

这将显示主画面和功能分支的图片,并帮助我辨别关系。如果我在同一次提交中看到master和feature分支标签,那么这是一个很好的迹象,表明我不小心将合并功能快进到我的主分支上。

如果您看到不同的内容,那么发布图片会很有帮助。但我怀疑它已经发生在功能分支上的这种快速合并。您可以使用以下步骤备份功能分支:

git checkout master
git reset --hard HEAD^

您可能需要git push --force origin master。我建议使用这个完整的表单,因为Git的默认推送行为是推送所有本地跟踪分支,这可能导致重新分支分支。此外,强迫推动&#34;掌握&#34;与他人合作时不是一个好的答案,所以如果你愿意的话,你需要与你的团队协调。简单的答案可能就是保持原样。

根据具体情况,可能会涉及更多。

此外,如果您想要合并提交,那么您可以这样做:

git checkout master
git merge --no-ff feature

这将强制创建合并提交,即使它可以快速转发。我的团队和我非常喜欢这个,我们把它作为默认。我在下面解释一下。

编辑:我改变了感觉以匹配问题并扩展了某些部分。

顺便说一句,因为我在Subversion使用Bazaar一段时间后,我变得非常 敏感谁是历史上的主线(最左边)父母。 Git和它 快进合并使这更加困难,我发现我的团队真的 发现它也令人不快。所以我们最终做了一些事情。

首先,我们更改了配置,以便合并始终进行合并 承诺。您可以从命令行执行以下操作:

git config merge.ff false

或全球,使用:

git config --global merge.ff false

接下来的一点是,我们确实希望在引入更新时进行快速合并 从上游。所以我们有几个别名可以提供帮助:

[alias]
    # Fetch the remote and update the current branch.
    up = !git remote update -p && git merge --ff --ff-only @{u}

    # Fast-forward pull locally
    ff = !sh -c 'git merge --ff --ff-only ${1:-@\\{u\\}}' -

在大多数情况下,团队将上游分支设置为他们想要推送的位置 从中拉出来。因此,git up将从服务器获取更新 快进当前分支。这通常在&#34; master&#34;上完成。 git ff 可以用来做类似的事情,但没有获取步骤。它也是 如果您需要,可以更轻松地快速转发您的另一个分支 这样的事情(有时会出现)。

其中一些比我关心的还要多,所以我写道 git ffwd 帮助我们git ffwd将进行抓取,然后快进我的所有内容 在遥控器上具有等效物的本地分支。它也照顾 当分支机构被移除到服务器端时,修剪你的遥控器 更容易修饰裁判并防止它变得无序。 git ffwd 将仅进行快进合并(git up也做同样的事情),如果失败则会失败 分支有分歧,所以它最终成为一个关于某事的好标记 有趣的事情,你需要介入并找出正确的过程 动作。

所有这一切的净效果是,现在当我们输入git merge foo时,那里 将是合并提交。 Git会要求提供一条消息,如果你要做的话 错误的是,你可以简单地删除缓冲区的内容并保存 (导致Git中止)或者在Vim中以:cq退出,这稍微容易一些 导致编辑器退出并显示错误。无论哪种方式,你都需要更多 控制,它已经走了很长的路,使跟踪事情变得更容易 并保持我们的历史看起来很尖锐。

另外请注意,如果我们都在努力,我们也会设置git config push.default upstream 同一个项目中的同一个项目,或git config push.default current 如果我们要进入单独的回购。如果你是Git&lt; 2.0,然后呢 设置此设置很重要,否则您可能会在主分支上执行类似git push -f的操作,但最终会在本地重写其他分支 跟踪远程分支。这是因为在Git&lt; 2.0,默认是 matching并推送所有远程跟踪分支,他们就是 很少是最新的。

这有点啰嗦,但我想告诉你还有另外一种方法 工作,让你有一个更好的形状,消除一些困惑和感觉 从长远来看比较容易 - 至少对我和我的团队来说都是如此。

我有更多配置here, 如果您有兴趣。

答案 1 :(得分:1)

当我阅读这样的陈述并看到像这样的WRT git的图片时,我总是畏缩。

  

某种程度上,合并提交现在存在于两个分支中... git-gui和GitLab都在历史图表中显示这个提交为彼此合并两个分支。

master A ------------ E  F <-- master now contains all of feature stuff (B, C, D).
        \              \ | 
feature  B -- C -- D --- F <-- feature now contains "E" edits.

它暗示了一种非常Subversion的思维方式:即分支是桶和稳定的,提交存在于桶中,提交和合并是关于移入和移出桶的提交。


我认为对于git来说,使用不同的心智模型确实很有帮助,其中提交/提交树是稳定的,分支标签是瞬态的并且可以移动。

我要重新绘制@jszakmeister发生的图表:

$ git checkout feature

                    master
                      |
                      |
         ,----------- E
        /
       A
        \              
         B -- C -- D 
                   |
                   |
                feature

$ git merge master

                     master    <-- last master commit
                      |
                      |
         ,----------- E 
        /              \
       A                F      (try to merge [master] into feature)
        \              /|
         B -- C -- D -/ |
                        |
                        |
                     feature   <-- feature now contains "E" edits?

$ git checkout master

$ git merge feature # fast forwarded

                      master   <-- master now contains all of feature stuff (B, C, D).
                        |
                        |
         ,----------- E |
        /              \|
       A                F
        \              /|
         B -- C -- D -/ |
                        |
                        |
                     feature   <-- feature now contains "E" edits

我会避免顶线是主分支而底线是功能分支的想法 - “分支”是移动的标签。从现在开始的一个月你不会关心主标签是否沿着A - E或B - C - D行驶并且无关紧要 - 合并后它的所有主分支。如果您查看git log --graph / gitk行并开始将某些列视为固定分支(存储桶),最终该工具会误导您:“master”分支可能是第一列中的第一列树的一部分,但树的另一部分是不同的列。

请注意,它可能很容易就是另一种方式。首先可以将功能分支合并到主服务器中,即创建合并提交并将主标签移动到合并提交。然后功能被快速转发给master,即功能标签被移动到master指向的同一个提交。最终结果/图表将是相同的,并且标签到达哪个或如何到达最终位置并不重要。

另外请注意,移动分支装饰既便宜又容易。像Both git-gui and GitLab show in the history graph this commit as merging both branches with each other这样的声明听起来很重要,但鉴于图片,这真的不是什么大问题 - 它只是意味着一个分支标签被错误地移动了,但它很容易将其移动到其他地方。

如果您对一个或两个合并不满意,请使用git log --graph --decorategitk可视化当前提交树和分支标签。然后在心理上可视化树应该看起来像你想要的样子,并移动主要和特征标签以反映它。

要将master合并到功能中,但尚未将功能合并到master中,您需要:

                     master
                      |
                      |
         ,----------- E 
        /              \
       A                F 
        \              /|
         B -- C -- D -/ |
                        |
                        |
                     feature

$ git checkout master
$ git reset --hard E
$ git checkout feature   # Move feature label if necessary
$ git reset --hard F

要将功能合并到主设备中,但尚未将主功能合并到功能中,您需要:

                       master
                        |
                        |
         /----------- E |
        /              \|
       A                F 
        \              /
         B -- C -- D -/
                   |
                   |
                feature

$ git checkout master   # Move master label if necessary
$ git reset --hard F
$ git checkout feature
$ git reset --hard D

要在两次合并之前重新开始,您需要:

                     master
                      |
                      |
         ,----------- E
        /           
       A            
        \           
         B -- C -- D
                   |
                   |
                feature

$ git checkout master
$ git reset --hard E
$ git checkout feature
$ git reset --hard D

-

更多关于此类思维的信息:https://stackoverflow.com/a/23375479/11296