什么是 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 stash
、git commit
、git checkout
或git 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 仓库,并创建我们的第一个提交来开始。
- 创建一个名为 datacamp 的文件夹。
- 切换到 datacamp 目录。
- 初始化 Git。
- 创建一个具有给定标题的 README.md 文件。
- 暂存并提交文件中的更改。
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