Git 中如何解决合并冲突教程

什么是 Git 合并冲突?

Git 版本控制系统旨在团队协作和项目贡献。开发人员通常在独立的分支上工作,完成后将更改合并到主分支。这种团队合作方式非常高效,有助于发现错误。有时多个开发人员在同一行代码上工作,当他们尝试合并更改时,就会产生冲突。


Git 冲突的简单示例

上述图表完美展示了典型的Git合并冲突是如何发生的。主分支中有一个文件,内容为“HELLO, WORLD!”。用户abid复制了主分支,并将文本改为“HELLO, CAT!”。在abid进行更改的同时,原始主分支也被修改为“HELLO, DOG!”。合并这些分支将引发合并冲突问题并停止进程。

`git merge`命令的主要工作是合并两个分支并自动解决冲突。然而,有时会出现冲突,两个人修改了同一行代码,或者删除了另一个开发者正在工作的关键文件。Git会标记这些更改并停止合并过程。在这种情况下,冲突并没有被自动解决;而是需要开发者手动更改或使用工具来解决冲突。

合并类型

Git合并和变基是将目标分支的提交集成到源分支的两种方法。此外,Git合并执行快速前进或非快速前进合并。如果目标分支的头部存在于源分支中,则默认情况下,合并类型将是快速前进合并;如果不存在,则为非快速前进合并。Git变基是另一种合并类型,它会重新排列目标分支的提交历史。

快速前进合并

Git默认使用快进(fast-forward)将缺失的提交整合到目标分支中。例如,在使用拉取(pull)命令从远程服务器更新本地分支时就会用到。快进不会引发合并冲突问题,因为如果目标分支的头部在源分支中找不到,Git将不会应用快进。

非快进合并

非快进合并也称为三方合并或真正合并。它通过整合源分支和目标分支中的更改,在目标分支上创建一个新的提交。在两个分支的最后一个公共提交之后融合这些更改。在我们的案例中,是在C之后。这种合并如果在源分支与目标分支之间存在争议,将提示Git合并冲突。在上面的图表中,合并提交(X)是通过整合源分支和目标分支创建的,其中K和E是合并提交的父节点。

变基(Rebase)

Git变基与其他类型有些不同。它改变了目标分支提交历史序列。变基以这种方式整合源分支,使得目标分支包含来自源分支的所有更改,然后在最后一个公共提交C之后跟随所有目标分支的提交。在我们的案例中,D和E来自源分支,而K*提交与K相同但具有不同的提交id。它不是链接到C,而是链接到E。与非快进合并类似,如果源分支和目标分支之间存在兼容性问题,Git会在最终确定变基之前提出问题以解决冲突。

Git合并冲突类型

Git合并冲突有两种类型:在合并开始时和合并过程中 – Atlassian。在本节中,我们将学习这两种类型以及解决每种场景的方法。

合并开始时的冲突

如果工作目录或暂存区中有更改,Git合并将失败。它在开始时失败,以防止更改被即将到来的合并提交覆盖。这是由于与本地更改冲突,而不是与其他分支或开发者冲突。为了稳定本地状态,您可以使用诸如git stashgit commitgit checkoutgit reset等命令。

合并过程中的冲突

合并过程中的失败意味着源分支和目标分支之间存在冲突,多个开发者在同一文件上进行了修改。如果自动合并失败,Git将要求您手动解决冲突。您还可以使用第三方工具来帮助您可视化和整合更改。

解决Git合并冲突的命令

在本节中,我们将学习各种原生命令来可视化和解决Git合并冲突。

常用命令

Git status是最常用于显示修改文件、暂存区和提交状态的命令。在合并过程中,它用于识别冲突的文件。

git status

使用带有–merge参数的Git日志会产生与源分支冲突的提交列表。

git log --merge

默认情况下,git diff选项将显示未提交更改与之前提交之间的差异。Git diff用于比较分支、提交和文件,对于防止未来的合并冲突很有用。

git diff

起始时合并失败的命令

检出用于撤销更改或切换到新的或旧的分支。

git checkout

Git重置用于将工作目录和暂存区的更改回复到原来状态。

git reset --mixed

合并过程中的冲突命令

–abort参数将停止合并过程,并将更改恢复到合并开始前的原始状态。

git merge --abort

在合并过程中通常使用Git重置来将冲突文件恢复到原始状态。

git reset

解决已删除更改文件冲突

如果在当前分支中删除了文件,而在其他分支中有人修改了它,将会发生Git冲突。在这种情况下,您可以添加一个文件并提交,

git add <filename>

或者您可以删除文件并提交。

git rm <filename>

可视化合并工具

合并工具是用于识别和解决各种合并冲突的用户友好型可视化工具。一些工具还支持额外功能,如比较更改、Git 操作以及项目和仓库管理。Git 合并工具有两种类型:仅终端和基于 GUI。基于终端的工具在 PowerShell 或 Bash 中打开,而基于 GUI 的工具在窗口环境中打开。

要检查已安装和有效的工具列表,请使用:

git mergetool --tool-help

该列表包含了所有可以安装并与 git 命令集成的有效工具。

例如,我们默认安装了 vim 和 nvim,如果你想查看未提交的文件与先前提交之间的差异,请输入:

git difftool --tool=vimdiff3

vimdiff3 工具会突出显示更改,并允许你在终端内比较提交。

在 Vimdiff3 中比较同一文件的两个版本

Meld

Meld 是一个免费且开源的工具,它将解决合并冲突提升到了另一个层次。要将其与 Git 集成,你首先需要从官方网站下载并安装设置。接下来,将其添加到全局配置中,这样默认情况下,Git 将在解决冲突时启动 Meld。

下面的配置命令仅适用于 Windows 用户。你需要做的唯一更改是针对 Mac 或 Linux 更改 Meld 安装文件的路径。

git config --global merge.tool meld git config --global mergetool.meld.path "C:/Program Files (x86)/Meld/Meld.exe" git config --global diff.tool meld git config --global difftool.meld.path "C:/Program Files (x86)/Meld/Meld.exe"

设置默认选项后,您可以在Git本地目录中输入git difftool以启动Windows版本的Meld,或者您可以使用git mergetool来解决以下所示的合并冲突。

使用Meld解决合并冲突

VSCode

VSCode提供了解决合并冲突的最佳和最流行的方式。当Git无法自动合并文件时,VSCode将突出显示冲突的代码并为您提供四个选项:接受当前更改、接受传入更改、接受双方更改和比较更改。您可以使用这些选项来清理您的文件并解决所有待处理问题。

使用VSCode解决合并冲突

如果您正在寻找一个完整的Git操作解决方案,尝试GitKraken。它带有免费客户端、VSCode扩展,并提供解决合并冲突的内置工具。

如何解决Git合并冲突

在本节中,我们将学习如何创建一个 Git 合并冲突并解决它。本教程分为两部分。第一部分,我们将学习如何在本地解决 Git 冲突;第二部分则是关于如何与远程服务器(GitHub)解决冲突。

本地合并冲突

创建合并冲突将帮助我们了解这些问题是如何产生的。然后我们可以使用创造性的方法来解决这些问题,甚至防止它们在未来发生。

现在我们将创建一个包含单个文件的 Git 仓库,并创建我们的第一个提交来开始。

  1. 创建一个名为 datacamp 的文件夹。
  2. 切换到 datacamp 目录。
  3. 初始化 Git。
  4. 创建一个具有给定标题的 README.md 文件。
  5. 暂存并提交文件中的更改。
mkdir datacamp cd datacamp git init echo "# How to Resolve Git Merge Conflict" > README.md git add README.md git commit -m "first commit" >>> [main (root-commit) 8199ea2] first commit >>> 1 file changed, 1 insertion(+) >>> create mode 100644 README.md

接下来,我们将创建一个新的分支 readme 并将标题从“..Git Merge..”更改为“..Git..”。添加文件并使用 -am 参数创建提交。

git checkout -b readme echo "# How to Resolve Git Conflict" > README.md git commit -am "new branch conflict added" >>> [readme 155f694] new branch conflict added >>> 1 file changed, 1 insertion(+), 1 deletion(-)

返回主分支并使用>>在 README.md 文件中添加新行。通过保存更改并创建提交,我们成功地在同一文件的两个版本之间形成了冲突。

git checkout main echo "New change in base branch" >> README.md git commit -am " a line added to base branch Readme file" >>> [main f1f1874] a line added to base branch Readme file >>> 1 file changed, 1 insertion(+)

正如我们所见,在合并 readme 分支时,Git 提示消息说自动合并失败,我们必须手动进行更改然后提交结果。

git merge readme >>> Auto-merging README.md >>> CONFLICT (content): Merge conflict in README.md >>> Automatic merge failed; fix conflicts and then commit the result.

我们将通过打开并使用记事本编辑文件来手动解决冲突。下图显示了一个带有HEAD的箭头、一个分隔符以及一个方向不同的带有readme的箭头。HEAD部分显示了主分支中的现有更改,readme部分是我们想要合并的分支,它由不同的标题组成。

手动解决合并冲突

为了解决问题,我们将删除readme分支部分、箭头和分隔符。最终文件的版本应该看起来干净,如下所示。

冲突已解决

在我们将文件添加并创建提交后,合并冲突将被解决。这是解决问题的最常见和最简单的方法。您还可以使用集成开发环境(IDE)来更快地解决冲突。

git commit -am "conflict resolved in file README.md" >>> [main 9994a29] conflict resolved in file README.md

远程合并冲突

为了创建和解决远程合并冲突,我们需要在GitHub上创建一个新的仓库。

在GitHub上创建新仓库

接下来,我们需要将远程名称(原点)和地址添加到仓库中,并使用上游将本地仓库中的所有更改推送到远程主分支。

git remote add origin https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git git push --set-upstream origin main >>> Enumerating objects: 12, done. >>> Counting objects: 100% (12/12), done. >>> Delta compression using up to 4 threads >>> Compressing objects: 100% (6/6), done. >>> Writing objects: 100% (12/12), 998 bytes | 499.00 KiB/s, done. >>> Total 12 (delta 2), reused 0 (delta 0), pack-reused 0 >>> remote: Resolving deltas: 100% (2/2), done. >>> To https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git >>> * [new branch] main -> main >>> branch 'main' set up to track 'origin/main'.

为了创建一个冲突,我们需要在远程和本地的README.md文件中进行更改。您可以通过GitHub文件编辑器将“..Git merge..”更改为“..Sit-Merge..”,然后提交更改。

在GitHub编辑器中进行更改

然后,在本地仓库中,将README.md文件更改为只添加一个简单的标题,并提交更改。

echo "# How to Resolve Merge Conflicts in Git Tutorial" > README.md git commit -am "local branch changes in README.md" >>> [main c677a13] local branch changes in README.md >>> 1 file changed, 1 insertion(+), 4 deletions(-)

最后,将更改推送到远程服务器。注意,Git提出了错误,并提供了如何解决问题的提示。

git push >>> To https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git >>> ! [rejected] main -> main (fetch first) >>> error: failed to push some refs to 'https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git' >>> hint: Updates were rejected because the remote contains work that you do >>> hint: not have locally. This is usually caused by another repository pushing >>> hint: to the same ref. You may want to first integrate the remote changes >>> hint: (e.g., 'git pull ...') before pushing again. >>> hint: See the 'Note about fast-forwards' in 'git push --help' for details.

我们将遵循最简单的提示,即在推送之前从远程服务器拉取文件。

拉取文件失败,因为README.md文件中存在合并冲突。我们本可以使用记事本手动修复,但这次我们将使用一个可视化工具来协助我们完成这个过程。

git pull >>> remote: Enumerating objects: 5, done. >>> remote: Counting objects: 100% (5/5), done. >>> remote: Compressing objects: 100% (2/2), done. >>> remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 >>> Unpacking objects: 100% (3/3), 681 bytes | 75.00 KiB/s, done. >>> From https://github.com/kingabzpro/DataCamp-Git-Merge-Guide >>> aaf149d..49b7d14 main -> origin/main >>> Auto-merging README.md >>> CONFLICT (content): Merge conflict in README.md >>> Automatic merge failed; fix conflicts and then commit the result.

合并工具Meld将识别出冲突的文件,并在Meld GUI应用程序中显示它们。

git mergetool >>> Merging: >>> README.md >>> Normal merge conflict for 'README.md': >>> {local}: modified file >>> {remote}: modified file

这里有三个列:README_LOCAL_473.md、README.md和README_LOCAL_473.md。如果您认为远程更改是有效的,请点击远程列中的黑色箭头;如果您希望本地更改保留,请点击本地列中的黑色箭头。就是这么简单。

使用mergetool Meld解决冲突

进行更改后,保存文件并提交。如您所见,将文件推送到远程服务器不会引发合并冲突错误。

git commit -am "remote main branch conflict resolved" git push >>> Enumerating objects: 16, done. >>> Counting objects: 100% (16/16), done. >>> Delta compression using up to 4 threads >>> Compressing objects: 100% (6/6), done. >>> Writing objects: 100% (10/10), 1.08 KiB | 550.00 KiB/s, done. >>> Total 10 (delta 2), reused 0 (delta 0), pack-reused 0 >>> remote: Resolving deltas: 100% (2/2), completed with 1 local object. >>> To https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git >>> 49b7d14..8f5c3aa main -> main

我们已经成功解决了本地和远程的合并冲突。这些冲突是数据科学家和机器学习工程师每天都会遇到的。为了提高您的Git操作技能,请参加一门Git入门课程。 

结论

解决Git合并冲突是一项复杂且高度风险的任务,因为您可能会因为合并有缺陷的代码而破坏软件。合并工具提供了一个用户友好的环境,以更安全的方式检测和解决合并冲突。在本教程中,我们学习了为什么会发生Git冲突以及如何解决它们。我们还涵盖了各种合并和冲突类型、有用的Git命令以及可视化工具。在最后一节中,我们创建了一个合并冲突并在本地和远程库中解决了它。 

如果您是Git新手并想了解它是如何工作的,请阅读:Git和GitHub入门教程

Source:
https://www.datacamp.com/tutorial/how-to-resolve-merge-conflicts-in-git-tutorial