当git-merge和git-rebase说“已经是最新的”但git-diff清楚地显示了差异时,如何合并两个本地分支

时间:2019-02-08 02:43:58

标签: git version-control

我有2个分支:fetchFeedForURLString(urlString:<#YourUrl#>) { arr in if let arr = arr { print(arr) } } feature-branch-a 如果您愿意,我可以详细介绍此本地存储库的git历史记录细节,但摘要是feature-branch-bfeature-branch-a的祖先分支。

当我feature-branch-bgit checkout feature-branch-bgit merge feature-branch-a时,什么也没有发生,因为它是git rebase feature-branch-a

但是,从Already up to date.运行git diff feature-branch-a可以清楚地显示我编辑过的更改。

请帮助我将其翻译为git命令:

  

亲爱的git,我在feature-branch-b中做了一些更改,想要添加到feature-branch-a中。我知道此添加将导致合并冲突。请将这些更改与不错的<<< === ...冲突解决标记

合并在一起

我只是想知道如何合并任何两个明显具有不同内容的分支(根据feature-branch-b)而没有虚假的,有趣的git diff消息。谢谢:)

2 个答案:

答案 0 :(得分:2)

Git不存储更改。 Git存储快照。

像白天的高温一样思考。假设我仅告诉您,今天比昨天温暖了几度。你能告诉我昨天和今天的温度吗?如果没有,为什么不呢?如果我只告诉您今天今天为50˚,今天今天比上周温暖吗?如果您不能告诉我,为什么不呢?如果只给出差异,哪些信息可以使您每天提供绝对温度?或者,如果每天仅给您绝对温度,您将如何提供给定两天之差?

考虑这个问题后,您应该有一个清晰的“变化”(变化或差异:今天比昨天温暖,我们通过减去两个快照发现)与“快照”(昨天是X度,现在是是的,今天,只有在我们有一个开始快照并将其添加时,才能让我们获得快照,以及如何从一个快照移到另一个快照。因此,现在下一部分将很有意义:要从快照过渡到增量,必须为Git提供两个特定的快照。

现在让我们回到您的公式:

  

亲爱的git,我在feature-branch-a中做了一些更改...

要进行更改,必须选择两个快照。您要选择哪两个快照?

  

...我想添加到feature-branch-b

一旦在功能分支A中选择了两个快照,您可以让Git将它们变成更改(差异)并将其应用于分支B尖端的快照。这并不难。但是您想要自动 进行此操作,这就是您遇到问题的地方:

  

...摘要是feature-branch-afeature-branch-b的祖先分支。

分支不是分支的祖先,也不是孩子,也没有任何这种关系。或者,也许,他们做到了:这里的问题是我们还没有定义我们(或您)称呼“分支”的含义。参见What exactly do we mean by "branch"?

在Git中,分支{em> name (如feature-branch-a)仅通过一个提交的唯一哈希ID指定一个特定的提交。 提交具有祖先/后代的关系,根据您的描述和运行git merge时的失败,我们可以得出这种关系:

...--o--o--A   <-- feature-branch-a
            \
             o--o--...--B   <-- feature-branch-B

我在这里选择了两个特别有趣的提交:提交A(其祖先是左边的提交)(未命名的o节点)和提交B(其祖先)是B左侧的提交,其中包括提交A

也就是说,feature-branch-A中包含的快照是A本身和A左侧的提交。 feature-branch-B中包含的快照是B本身以及其中的快照,包括 AA的快照。 分支A中的每个“提交”或“提交”都已经在分支B中。

如果您运行:

git checkout feature-branch-b
git merge feature-branch-a

git merge找出要合并的更改的方法是在当前分支的尖端之间找到最佳共同祖先,即提交B,然后您已命名分支的尖端,即,提交A最好的祖先是两个提交都可以实现的,但是在某种模糊但明显的意义上,最近对他们也是如此。 / p>

如果您看起来像这样:

          o--...--A   <-- branch-A
         /
...--o--*
         \
          o--...--B   <-- branch-B

然后合并基础将成为加星标的提交:不仅在两个分支上,而且还可以同时到达AB而两个分支上的。 (*左侧的提交也在两个分支上,但都在“后面”。)

因此,Git 可以通过将git diff*A进行比较,看看它们它们< / em>,然后*-vs-B来查看的行为。然后,Git可以将他们的改进*-vs-A与您的改进*-vs-B结合起来。将{em> combined 组合的更改应用于*可以为您提供两套改进的快照,将这两项工作合并到可以作为 merge commit 提交的东西中(一种稍微特殊的提交方式:一个有两个父代而不是一个父代,但是像其他任何提交一样,都是普通快照)。

但是根据您的设置

...--o--o--A   <-- feature-branch-a
            \
             o--o--...--B   <-- feature-branch-B (HEAD)

Git将发现合并基础本身就是提交A。它在两个分支上,并且尽可能靠近A,并且尽可能靠近B。因此,Git将AA进行对比,发现没有任何可合并的内容!

这显然不是您想要的。但是仍然没有什么可以合并的:谁造了B 始于 A,并从那时起加入了改进。这就是全部。如果您希望Git 删除这些改进,则可以执行此操作,但不能使用git merge

如果在AB之间的某个提交毕竟不是 改进,则可以告诉Git撤消它:

...--o--o--A   <-- feature-branch-a
            \
             o--X--...--B   <-- feature-branch-B (HEAD)

如果提交X使情况变得更糟,则可以运行git revert <hash-of-X>。 Git会将X变成一个变更集,方法是将其与绘图中左侧的父提交进行比较,然后尝试“取消变更” 当前承诺完全撤消X中所做的操作。这可能会成功完成,也可能会因合并​​冲突而失败。 (在内部,Git实际上是在使用git merge作为公共起点,在其左侧的X作为另一个分支提示,而将o作为您的起点来进行B自己的分支提示。这有点令人困惑:因为Git只是“撤消更改”,所以可能更容易想到它。合并在技术上是优越的,因为它可以处理一些更困难的情况,但通常是同一回事。)

但是,最终理解这一点的关键是要认识到合并并不意味着“制造相同”。这意味着从一个共同的起点开始组合就发生变化。如果您没有共同的起点,那就很难了–除非您添加--allow-unrelated-histories,否则现代Git不会使用其“猜测模式” –并且如果起点是两个提交之一,则没有任何可合并的地方。

答案 1 :(得分:0)

虽然@torek给出了一个非常出色和彻底的答案,但我想稍微介绍一下。如果您设法通过各种方式破坏了文件的版本,并且即使diff清楚地表明了差异,您对令人恐惧的“ 已经是最新的”消息似乎也感到非常不舒服。烦人。不要撒谎!!),您只需利用Microsoft's Visual Studio Code editor.

awesome extension called GitLens确实为您增强了git。因此,当我整理文件版本时,或者仅在单个或几个文件中手动选择更改时,我决定使用此功能。该扩展允许并排打开来自不同提交的文件,并且很好地突出显示了两个版本中的差异,因此可以根据需要轻松进行更改。

这只是一个节省时间(也可以节省头发:p))