Git
是一个开源的分布式版本控制系统。它帮助你使用本地分支、暂存和工作流程轻松管理你的项目文件。
现在很多开发者都在使用 Git。而且他们通常都熟悉 Git 的基本概念,比如:
- 如何初始化一个仓库。
- 如何创建分支。
- 如何进行暂存/撤销暂存性修改。
- 如何提交更改。
- 如何将提交内容推送到远程。
然而,许多开发人员对 合并(merging)
和 解决合并冲突(resolving merge conflicts)
等概念感到困惑。在这篇文章中,我们将学习如何以一种实用的方式解决合并冲突。
开发人员对合并冲突有何看法?
最近我在 Twitter、LinkedIn 和 YouTube 上进行了一次投票,询问开发者对解决 Git 中的合并冲突是否感到满意。你猜我发现了什么?
70%-80% 的开发者分享说,他们认为在 Git 中解决合并冲突很有挑战性。所以这意味着 “解决合并冲突” 是一个重要的讨论话题。
投票结果 – 你对解决 Git 中的合并冲突感到满意吗?
什么是 Git 合并,什么是合并冲突?
Git
是一个版本控制系统,它保留了你所有文件版本的历史。你可以在任何时候回到任何一个版本,并检索到更早的版本。
假设你创建了一个叫 abc.txt
的文件,并将其推送到 Git 仓库。此时,该文件有其当前的版本与之相关。现在,如果你的同事修改了同一个文件,并把它推回仓库,该文件就有了新的版本。
Git Merge
是一个允许你将文件的当前内容与其他以前的版本保持同步的功能。这一点非常重要,因为任何人在任何时间点都应该在文件的最新内容上工作,而不会覆盖以前版本的任何变化。
Git merge
可以帮助你在合并来自其他开发者的修改,向同一文件推送新的修改之前。
在 Git 合并的情况下,我们需要注意两件事:
- changes(变化):一个文件的两个版本之间发生了什么类型的操作?新的内容被添加或删除,或者现有内容被更新。
- Possibilities(可能性): 有两种可能性。变化发生在文件的 “不同区域(different regions)”,或者变化发生在文件的 “相同区域(Same region)”。同一区域意味着开发者在文件的同一位置(例如,段落、行等)进行了修改。
幸运的是,Git 使用 “自动合并(auto-merge)” 策略自动处理了大多数这种情况。但是当这些变化发生在文件的 “同一区域” 时,Git 不会执行自动合并。相反,它让你去解决合并的冲突。
Git 合并冲突。一个恐怖的故事
让我们通过两个开发商 Alex 和 Tina 的故事来理解上述情况。
一个晴朗的日子,
- Alex 把远程版本库的修改拉到他的本地版本库。
- 他修改了名为
abc.txt
的文件,将其暂存(staged),提交(committed),最后推送(pushed)回远程版本库。 - 同时,Tina 不知道 Alex 对
abc.txt
文件的修改,在该文件的相同区域
做了一些修改,并尝试将其推送到远程仓库。 Git
是一个版本控制系统,所以它警告 Tina,她修改的版本比远程中的版本要早(因为 Alex 的修改已经在远程中)。- 现在,Tina 需要先从远程拉出修改,更新文件,然后再尝试推送。
- Tina 这样做了。然而,在她最疯狂的噩梦中,她得到了 “自动合并(auto-merge)” 失败的警告,所以她现在需要解决合并冲突。
这个故事有什么印象吗?上述故事与你有关吗?有可能你过去曾站在 Tina 的位置上。如果没有,你最终会遇到这种情况! 那么,让我们来了解 Tina 如何有效地处理这种情况。
如何解决 Git 中的合并冲突
解决合并冲突并不像它听起来那么棘手。在 90%的情况下,一旦你对变化有了清晰的认识,并有一个平和的心态,就会比较容易。
思考过程
当 Tina 拉取了这些修改,Tina 的本地文件就有 Tina 的修改和 Alex 的修改。现在 Tina 可以做这四件事中选择一件:
- 她可以保留 Alex 的修改并删除她的修改。
- 她可以删除 Alex 的修改并保留她的修改。
- 她可以同时保留 Alex 的和她的修改。
- 她可以同时删除 Alex 的和她的改动。
好吧,但她应该做哪一个?这完全取决于项目的需求和用例。Tina 会理解要做的变化,并做任何与情况相关的事情。
那么,什么是要做的变化?Tina 如何识别?Tina 如何做出改变?我知道你有很多这样的问题。让我们通过下面一节中的几个现实生活中的例子来获得所有的答案。
解决 Git 中合并冲突的步骤
让我们举几个现实生活中的合并冲突的例子,并学习如何解决它们。
例子 1:改变是在文件的同一地区
当 Git 无法执行自动合并时,因为更改在同一区域,它会用特殊字符来表示冲突的区域。这些字符序列是这样的:
<<<<<<<
和=======
之间的所有内容都是你的本地修改。 这些修改还没有在远程版本库中。=======
和>>>>>>>
之间的所有行都是来自远程版本库或另一个分支的修改。现在你需要研究这两个部分并做出决定。
下面的图片显示了一个文件的内容,表明自动合并没有发生,有一个冲突。冲突发生在我们在本地修改了文件,增加了一行- Sleep
。但与此同时,其他人在同一区域添加了 - Gym
行,从而推送了一个修改。
因此,- Sleep
行被标记为本地修改,- Gym
行被标记为从远程仓库或其他分支传入的修改。
由于同一区域的变化而产生的合并冲突
根据你的用例和项目的需要,你将做出解决冲突的决定。如果你只需要保留带有 - Sleep
的那一行,你将保留这一行,并删除其余冲突的文本。在这种情况下,文件内容变成:
- Eat - Read - Sleep
相反,你可以保留 - Gym
这一行,并删除 - Sleep
的修改:
- Eat - Read - Gym
如果你需要保留这两行,请删除与冲突指标有关的线:
- Eat - Read - Sleep - Gym
如果你认为不需要任何修改,就把它们全部删除吧。
- Eat - Read
完全由你来决定哪些修改与情况有关。在你的修改之后,你需要确保文件中不存在任何提示冲突的字符(<<<<<<<, =======, >>>>>>>)。当你解决了这些修改,请做以下工作:
暂存变化:
git add <files>
提交修改,并添加信息:
git commit -m "Message"
最后,将变化推送到远程:
git push
这就是解决这种情况下的合并冲突的全部内容。
例 2:文件在 Remote/Other 分支被删除
在删除文件的合并冲突中,一个开发者在一个分支中删除了一个文件,而另一个开发者在另一个分支中编辑了同一个文件。在这种情况下,你需要决定是否要保留这个文件,或者删除它是否正确。
要把被删除的文件加回你的分支,请这样做:
git add <file-name>
要继续删除文件,请这样做:
git rm <file-name>
然后提交你的修改,并添加消息:
git commit -m "Message"
Finally, push it:
git push