【问题标题】:Master contains changes from branch after merging master into branch将 master 合并到分支后,master 包含来自分支的更改
【发布时间】:2018-03-08 05:44:10
【问题描述】:

我最近创建了一个分支,branchA 关闭了 master 来开发一个功能。在开发此功能时,其他提交被推送到master。我想将这些更改带入分支,所以在branchA 中,我运行了

git merge master 

然后我在此基础上进行了更改 (B)。但是,后来我意识到我将master合并到分支中很糟糕,所以我恢复了提交使用

git revert [hash of merge of master in branchA] -m 1

然后我重新应用了 B 的更改,并提交了它。最后,我将master 重新合并为branchA

我对这次合并很满意,所以我想将 branchA 的更改带入 master。令我惊讶的是,更改已经在master 中。当我检查出主人并运行时

git merge branchA

我看到了

已经是最新的了。

这里发生了什么?

我来自 SVN 背景,所以我预计需要将 branchA 合并回 master,但似乎这似乎是自动发生的?这种行为是否与快进有关?如果我不希望这种行为发生(例如,将更改从 master 拉入分支,而不会让分支中的更改溢出回 master)。

提前致谢!

【问题讨论】:

  • 你已经将你的分支推送到远程了吗?还是仅限本地?
  • @Anonymous:我根本看不到那个。如果你在看“Merging master into the feature branch”的图片,认为这说明你理解错了:master的tip在你merge到你的feature分支时根本没有变化,你只是带过来变化。当另一个更改提交到主分支时,它仍然不会有来自功能分支的更改。
  • 好吧,我开始明白我做错了什么。在我推送合并的分支之后,我显然不小心通过 GUI 将分支合并到了主干中(我第一次使用 GUI,所以我一定是点击错误)并提交了它。因此,当我去命令行将分支合并到主干时,无事可做。我很困惑,因为当我转到 GUI 的历史可视化时,它显示 master 的最后一次提交与分支的最后一次提交相同(将 master 合并到 branchA),让我认为它以某种方式发生了自动地。 PEBCAK!
  • @Anonymous:是的,快进“合并”实际上根本不是合并。当合并的目标(您请求合并的提交)严格高于当前(HEAD)提交时,就会发生这种情况。如果没有--no-ffgit merge 会自言自语:啊哈,我可以在不实际合并的情况下做到这一点,我所要做的就是移动这个分支,然后将 HEAD 向前移动并检查另一个提交!

标签: git version-control merge revert


【解决方案1】:

masterbranchA 开头,每个都有几个提交,branchA 已签出(显示为 *):

m1 - m2 - m3     <--- master
 \ 
  a1 - a2       <--- branchA*

[编辑:更新了每个 cmets 的合并结果,以显示 master 不会自动前进到合并提交]

然后merge master到branchA,显示为merged commit a2m3:

m1 - m2 - m3        <-- master
 \          \ 
   a1 - a2 - a2m3   <-- branchA*

合并的恢复只是应用“撤消”更改,它不会撤消实际的合并 - 显示为 a2m3':

m1 - m2 - m3      <-- master
 \         \      
  a1 - a2 - a2m3 - a2m3' <-- branchA*

由于您在 branchA 仍然签出的情况下进行了此还原,因此 branchA 引用将指向新提交,master 引用仍指向 m3 提交。

然后您添加了另一个提交:

m1 - m2 - m3      <-- master
 \         \      
  a1 - a2 - a2m3 - a2m3' - a3 <-- branchA*

最后,当您签出master 并合并到branchA 时,正如您所猜测的,这只是一个快进,它转发了master 以指向与branchA 相同的提交。这是可能的,因为从 m3 到 a2m3 的合并链接仍然存在(revert 没有删除它),所以master 被认为是 a3 的父提交(a3 有一个完整的链)并且可以快速- 转发。

m1 - m2 - m3  
 \         \      
  a1 - a2 - a2m3 - a2m3' - a3 <-- branchA / master*

此时您尝试将 branchA 合并到 master,但得到的响应是“已经是最新的”。

现在,在第二次合并尝试之前,如果您在合并 branchA 之前已签出 master 并至少提交了一次,或者其他人已在遥控器上提交了 master 并且您将其拉下,那么你的分支会再次分叉(想想 m3 右侧的新 m4 提交)。如果发生这种情况,从 branchAmaster 的合并将是一次完全合并,而不仅仅是快进。


如何看待分支

对我来说如何考虑分支的突破是,分支实际上只是对提交的引用——从技术上讲,所有的父提交都与它相关联。当您合并两个分支时,您只是创建了一个具有两个父级而不是一个的提交,此时没有两个分支,只有一个 - 甚至曾经是分开的 masterbranchA,不再是分开的——它们已经真正合并了,现在两个分支都引用了它们。


你还能做什么?

根据我认为你想做的事情,你可以这样做:

警告:如果您在推送到其他人 (tm) 有权访问的遥控器后更改历史记录,则会出现“可能发生的坏事 (tm)”……但至少在这种情况下,它会只重写 branchA 历史,而不是 master

  1. 与其将master 的合并还原为branchA,不如将​​branchA 重置为先前的提交a2

    git reset --hard <hash of a2>
    

    像这样重置会删除对 a2m3 的所有引用,因此它会有效地从您的存储库中删除。它仍然会在你的仓库中一段时间​​,但除非你将哈希保存在某个地方,否则将无法访问它。 Git 会跟踪这样的提交一段时间,然后如果它们没有被重用足够长的时间,就会进行垃圾收集并删除它们。

  2. 此时你会:

    m1 - m2 - m3   <-- master
     \
      a1 - a2      <-- branchA
    

    就像合并之前一样。

【讨论】:

  • @Anonymous 谢谢!要设置为先前的提交(或整个 repo 中的任何提交),请使用 reset 命令,是的。谷歌关于软重置、混合重置和硬重置之间的区别。当您准备好在遥控器上永久更改更改时,最后需要强制推送 - 您不需要每次在本地更改任何内容时都推送(事实上,当您正在进行更改并且还没有进行更改时)尚未测试所有内容,您通常不会推送,只需将所有内容保存在本地,除非有人需要访问您的工作)。
  • @Anonymous 对于合并,否 - 如果一个在提交行中领先于另一个,则只有后面的那个可以快进(即与前面的合并);实际上,我不确定它会在另一个方向上说什么,但是某种错误或者它需要一个强制标志。在这种情况下,您最好直接重置为所需的提交以向后退(请记住推送后对远程用户的影响)。此外,如果没有先标记主要提交或在那里创建临时分支,请不要向后退,否则您可能无法访问它。
  • 好吧,我现在有点糊涂了,抱歉!所以在你的第二张图中,在运行git merge master 之后,假设我已经签出了 branchA,master 的尖端和分支的尖端是否都指向同一个提交?换句话说,如果有人在合并后向 master 提交,那么新的提交是否会包含来自 branchA 的更改?
  • @Anonymous 你的权利!刚刚对此进行了测试,合并的分支不会自动移动到合并提交。我回去并以此为基础清理了所有内容。这实际上使您的修复更加干净。如果您检查了master 并在将branchA 合并到其中之前至少再提交一次,那么这将是一个正常的合并,但由于master 仍在m3,它最终只是一个快速-向前。应该都修好了。 [附:这就是我使用 Git 扩展的原因,所以它可以直观地向我展示我所有的分支和引用在哪里......在任何时候...... :) ]
  • 已更新 - 尽管它发生在“最后”段落之后(而不是之前)。好点 - 我添加了这一点并清理了该部分的最后一段,关于什么会使其完全合并而不是快进。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-30
  • 1970-01-01
  • 2017-04-24
  • 2013-01-14
  • 1970-01-01
  • 2016-09-20
  • 2018-04-14
相关资源
最近更新 更多