Git合并冲突(假设情况)

时间:2018-12-19 02:29:04

标签: git github

嗨,我刚开始使用git,但是我想知道如何解决以下假设示例:

有两个人(A和B)为一个分支(主)上的远程回购做出了贡献。我们将假设B是最新的仓库,而A不是。 A刚刚修改了新的代码行,并尝试将其推送到存储库,并被告知他需要及时更新。拉动他时遇到合并冲突,并且能够解决它。这样做后,A成功推送到存储库。现在,B意识到A的更新很重要,并决定他需要从存储库中提取信息,但是他已经在本地进行了更改,因此暂时只是藏匿。在拉动并应用存储之后,B意识到他遇到了与A先前遇到的合并冲突。现在我的问题是在这种情况下,即使另一个人之前已经这样做,从回购中撤出的人员是否总是必须再次解决合并冲突,还是有其他解决方法?

非常感谢您的协助。

1 个答案:

答案 0 :(得分:1)

这种情况永远不会发生。原因是合并操作的输入之一已自动更改。但是,B人(我在下面称他为Bob)可以看到合并冲突。

请记住,在Git中,合并是涉及提交的操作。也就是说,当我们进行合并(动词形式要合并)时,我们会找到一些提交并进行工作。还要注意,每个提交都代表一个完整快照:所有文件,这些文件自该特定提交起就及时冻结。如果您想查看文件的昨天,去年,或其他日期,它们在当天的提交中都存在。

三个输入可用于合并操作!作为运行Git的人,我们直接指定其中的一个

$ git merge origin/develop

例如。名称origin/develop解析为某个特定的提交ID,这是一个很大的丑陋的哈希ID字符串,例如5d826e972970a784bd7a7bdf587512510097b8c7,这是我们在存储库中拥有的一个提交,我们早些时候可能通过运行{{1} }。 (请记住,git fetch本质上是一个便捷命令,其含义是:为我运行git pull,然后在完成后立即为我运行第二个Git命令,通常为git fetch < / em>。)

合并的第二个输入是无论当前提交是什么。也就是说,在我们运行git merge(也许通过git merge)之前,我们运行了git pull命令:

git checkout

这将选择分支上的最新提交,因为分支名称​​的定义是“分支上的最新提交”。这意味着分支名称会随着时间不断更改它们的含义。要查看当前是哪个提交$ git checkout develop 或现在是哪个提交master,可以运行developgit rev-parse master,这将吐出当前的哈希ID。以后,如果有更多提交,请再次运行此命令,您将获得一个不同的哈希ID。

绘制历史记录

因此,考虑到这一点,始终值得绘制 commit图。提交图在存储库中的历史记录,因为Git中的历史记录不过是提交。

如果您之前未绘制过提交图,则需要进行一些练习。有很多方法可以在浏览器或GUI中绘制或查看它们,尽管我对GUI非常满意,因为许多GUI都是谎言(通常是出于与性能良好相关的充分原因,但是仍然烦人)。有很多方法可以做到这一点,但是我喜欢水平地在StackOverflow帖子中做到这一点:

git rev-parse develop

在这些图中,最近的提交在右侧,时间向左移动。 分支名称指向最右边(最新)的提交,因为根据定义,名称​​是 last 在其分支中的提交。我们(和Git)必须从结尾开始,然后向后移到较早的提交,以查看进展情况。

从该图上,我们可以看到 o--o--o <-- develop (HEAD) / ...--o--* \ o--o <-- origin/develop 上有三个提交(圆形develop节点)不在共享历史记录上,而我们的o有两个提交提交不在共享历史记录上的内容。

共享历史记录从提交origin/develop开始,并在时间上向后延伸。在此图中,提交*是提交*develop合并基础当Git进行真正的合并时-在合并过程中执行合并部分-三个输入是合并基础,origin/develop提交(HEAD)和其他提交(--ours)。

Git进行合并的方式现在实际上非常简单。因为每个提交都是完整快照,所以Git首先将合并基础快照提取到一个临时区域。然后提取我们的提交-嗯,它确实已经存在了-和他们的提交,现在运行两个单独的比较,我们可以我们自己使用--theirs一次复制一个。我们首先找到提交git diff的哈希ID(也许通过查看图表),然后运行:

*

git diff --find-renames <hash-of-*> HEAD # what we changed git diff --find-renames <hash-of-*> origin/develop # what they changed 现在要做的就是结合这些更改。从基本文件开始。然后,无论我们在哪里更改了文件,如果他们没有更改该文件,请使用我们的文件。无论他们在哪里更改了文件,而我们没有更改,请使用他们的文件。无论我们都是在哪里更改了同一文件,都应合并更改。

如果/当两个更改集的组合试图更改 same 文件的 same 行时,发生

合并冲突。在这种情况下,Git会保留文件的所有三个原始副本(隐藏在Git的 index 中),并尽最大努力将更改以及一些冲突标记组合到工作树中。

如果Git完全没有冲突,则Git进行新的提交。或者,在您手动修复了发生冲突时留下的混乱Git之后,进行结果的最后提交。在我们的图表中,此提交看起来有些奇怪,因为它以前有两个个提交,而不是通常的提交。我给这个新提交一个字母ID(不是真实的哈希ID,只是一个替代),git merge

M

这种提交是 merge merge作为名词:是双亲提交,两个父母都是旧的 o--o--o--M <-- develop (HEAD) / / ...--o--* / \ / o----o <-- origin/develop (父#1)和另一个提交(#2)。

现在假设您的人A(爱丽丝)和B(鲍勃)继续工作

您建议Alice进行合并,然后运行HEAD。当爱丽丝这样做时,假设她的git push成功了,那将使另一个Git存储库调用提交git push M的技巧。例如,也许第三个存储库位于GitHub上:

develop

我对此进行了一些不同的绘制,但这是同一组提交。这里没有 o--o--o / \ ...--o--o M <-- develop (HEAD) \ / o-----L ,只有合并提交origin/develop会导致前两次提交。我也未标记旧的共享点,并给M表示Bob的 current 提交,因为我们很快就要讨论它了。

现在,Bob在他的存储库中运行L,以接管Alice的工作。尽管Bob自己还没有新的提交,但他获得了新的提交。请注意,他的git fetch已经存在,当前指向提交develop

L

请注意,鲍勃的 o--o--o / \ ...--o--o M <-- origin/develop \ / o-----L <-- develop (HEAD) 是他分支的结尾!如果他有未提交的更改,则可以运行develop将其保存为一次提交(实际上是两次提交,但现在仅假装一次),即位于 no 分支上:

git stash

现在,鲍勃可以运行 o--o--o / \ ...--o--o M <-- origin/develop \ / o-----L <-- develop (HEAD) \ S [stash] (或git merge --ff-only,这将执行另一次不执行任何操作的提取,因为没有比git pull更新的内容,然后进行了快速转发而不是真的-a -merge“ merge”)以使 Bob M指向提交develop

M

Bob现在可以应用并拖放(或“弹出”)存储库,以将更改添加到他的工作树中。如果可行,他们将准备添加和提交。然后Bob进行了新提交,我们可以将其绘制为 o--o--o / \ ...--o--o M <-- develop (HEAD), origin/develop \ / o-----L \ S [stash]

N

o--o--o / \ ...--o--o M--N <-- develop (HEAD), origin/develop \ / o-----L 的内容已经包含N的内容,因为M接受了git stash apply,并将其与它的父级进行比较,然后进行了相同的更改S中的任何内容。

当鲍勃看到冲突时

Bob看到冲突的时间点不在获取和快速转发期间,而是在Bob运行M(或git stash apply时),该运行以运行git stash pop开始)。实际上,这将运行另一个合并!但是它运行的合并操作不会 merge commit 结尾。这只会执行动作的 commerging 动词部分。

此合并将提交git stash apply作为其最后的“其他提交”输入。中间输入是S-提交HEAD-像往常一样是当前提交,第一个输入是两者的合并基础,即提交M

L

Git比较(比较) o--o--o / \ ...--o--o M <-- develop (HEAD), origin/develop \ / o-----L \ S [stash] L来找出“我们”做了什么。 (当然,这实际上是 Alice 所做的,或多或少,但是Git不在乎。)它将ML进行比较以查看“他们”的意思(真的,鲍勃)做到了。然后,它完全按照常规合并的方式合并或尝试合并这两个变更集。

如果一切顺利,S会停在那里。尚无新提交。如果问题很严重,git stash会因合并冲突而停止,由Bob来解决合并冲突。 Bob完成后,Bob进行的下一次提交将是普通提交,而不是合并提交。

如果Bob使用git stash并且存在合并冲突,则Git在git stash pop步骤之后停止,并且Bob仍然具有隐藏位置:他必须运行apply才能将其修复后丢弃乱七八糟。但是如果Git认为合并进行得很好,并且Bob运行git stash drop,那么 Git 将为Bob运行git stash pop。因此,在使用git stash drop时一定要格外小心:有时会同时应用和删除,但有时在应用之后会失败,并且不会执行删除部分。

(我主要建议您避免使用git stash,但如果要使用它,请分别进行应用和删除,以免在以 Git 的方式应用时意外地删除存储空间认为很干净,但是实际上这是您的错误。即使对于Git专家,这的确确实发生了,要从中恢复是很痛苦的。)