【问题标题】:git merge somehow merged "both ways" (A to B and B to A) in one commitgit merge 在一次提交中以某种方式合并了“双向”(A 到 B 和 B 到 A)
【发布时间】:2015-02-01 16:07:06
【问题描述】:

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

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

编辑:我以为我理解合并的含义,但从答案中我现在更加困惑。如果我创建了一个包含很多新内容的功能分支,并且我想“导入”一些在 master 中进行的更改,但保持 master 不变,这不是这样做的吗?

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 个合并操作,其中一个是快进操作,是否有任何方法可以恢复有关该快进合并的信息,比如是谁在什么时候做的?如果提交信息说“merge master into feature”,我猜这意味着它是先朝那个方向完成的,然后有人快进将 feature 合并到 master 中,对吧?

谢谢!

【问题讨论】:

  • 我的理解是,您只需要将 master 重置为合并提交之前的提交(通常将 masterfeature 之前的提交作为父提交)。 git reset --hard 提交到那个提交(可能是HEAD~1,现在不太确定)同时在主分支上应该这样做。
  • 这并不是特别不寻常,也没有什么可担心的。您可以通过明确告诉合并您不想进行快进合并来避免它。
  • 那么要强制推送,您需要拥有 repo 的权限。
  • @njzk2 谢谢,这就是我想“解决”这个问题的原因。

标签: git git-gui


【解决方案1】:

我可以通过一种方式看到它的发生。假设您有一个名为“feature”的功能分支。您将“master”合并为“feature”。所以图表看起来像:

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

如果您签出“功能”并合并主节点,您可能会获得快进合并,因为“主节点”上的新提交直接在“特征”中的最后一次提交之前。所以图表最终看起来像这样:

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

这不是额外的提交,只是“master”和“feature”现在指向 相同的提交。 FWIW,我和我的团队发现这种行为令人困惑,所以我 下面概述了我们为帮助防止该问题所做的工作。

另外,我不确定您使用“gitk”进行提交是什么意思。据我所知, gitk 仅供查看。你的意思是“git gui”吗?不过,我确实建议您使用“gitk”查看图表。我倾向于使用以下内容:

gitk --date-order master feature

这将显示一张带有 master 和 feature 分支的图片,并帮助我辨别关系。如果我在同一个提交上看到 master 和 feature 分支标签,这是一个好兆头,表明我不小心将合并的功能快进到我的 master 分支上。

如果您看到不同的东西,那么张贴图片会很有帮助。但我怀疑这是功能分支上发生的这种快进合并。您可以使用以下步骤备份功能分支:

git checkout master
git reset --hard HEAD^

您可能需要git push --force origin master。我推荐使用这种完整的形式,因为 Git 的默认推送行为是推送所有本地跟踪分支,这可能导致分支倒回。此外,在与他人合作时,强行推动“大师”并不是一个好的答案,所以如果你愿意的话,你需要与你的团队协调。简单的答案可能是保持原样。

视情况而定,可能涉及的内容不止于此。

另外,如果你想要一个合并提交,那么你可以这样做:

git checkout master
git merge --no-ff feature

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

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

顺便说一句,自从我使用 Bazaar 和 Subversion 一段时间以来,我变得非常 敏感的谁是历史上的主线(最左边)父母。 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 将从服务器获取更新并 快进当前分支。这通常在“master”上完成。 git ff 可以用来做类似的事情,但没有 fetch 步骤。它也是 如果需要,可以更轻松地在您的分支之上快速转发另一个分支 这样的事情(有时会出现)。

其中一些仍然比我关心的要复杂,所以我写了 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 git push -f 的操作,但最终会倒带您在本地拥有的其他分支 跟踪远程分支。这是因为在 Git matching 会推送你所有的远程跟踪分支,它们是 很少是最新的。

这有点啰嗦,但我想告诉你还有另一种方法 工作让你有更好的身材,消除一些混乱,感觉 从长远来看更容易——至少对我和我的团队来说是这样。

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

【讨论】:

  • 是的,对 gitk 感到抱歉,发布后一分钟进行了编辑。
  • 这似乎是合理的。但我从不想将功能合并到主控中。而且我找不到任何此类承诺的证据。有问题的提交被称为“将主人合并到功能中”,而不是相反......我会看看我能不能在这里得到一张照片。
  • @zorgkang - 尝试使用git reflog master 查看历史记录。正如他所说,他不会创建新的提交,它会将 master 移动到现有的提交。
  • @zorgkang 你发帖的时候我很早就发现了一些东西,所以我不太确定方向。我现在已经颠覆了理智。我还用我们在我的商店所做的事情更新了答案。这很罗嗦,但它可以帮助我们保持我们想要的形状,并避免在 master 上的主线提交中看到诸如“merged master into feature”之类的东西——这很令人不安。
  • 帮助很大,谢谢!实际上我昨天晚上做了一些阅读,我也决定在合并分支时不 ff,但仅在拉动时才 ff。 :)
【解决方案2】:

当我读到这样的陈述并看到像这样的 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.

它暗示了一种颠覆性的思维方式:即分支是存储桶并且是稳定的,提交存在于存储桶中,提交和合并是关于将提交移入和移出存储桶。


我认为对于 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 中,即创建一个合并提交并将主标签移动到合并提交。然后特征被快速转发到主节点,即特征标签被移动到主节点指向的同一个提交。最终结果/图表将是相同的,哪个分支是哪个分支或标签如何到达它们最终的位置并不重要。

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

如果您对一个或两个合并不满意,请使用git log --graph --decorategitk 可视化当前提交树和分支标签。然后在脑海中想象出你想要的树应该是什么样子,然后移动主标签和特征标签以反映这一点。

要将 master 合并到 feature 中,但尚未将 feature 合并到 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

【讨论】:

  • 谢谢你的努力。我在绘图中复制 F 的主要原因是绘制这些需要付出很多努力……但我理解你的解释。我的困惑是由于没有意识到我已经完成了 ff 合并并且这些不作为单独的提交存在,所以我认为单个合并做了一些奇怪的事情。而且因为它是一个单一的提交,我不确定如果我恢复它会发生什么。因为我没有“强制推送”的权限,所以我无法重置,所以我做了一个还原(并使用 -s ours 将 master 合并到 feature 以允许最终“nice”合并回来)。
猜你喜欢
  • 2017-12-12
  • 2016-11-20
  • 2016-03-07
  • 1970-01-01
  • 2011-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多