题 如何在Git中解决合并冲突


有没有一种很好的方法来解释如何解决Git中的合并冲突?


4134
2017-10-02 11:31


起源


以下博客文章似乎给出了一个很好的例子,说明如何处理与Git的合并冲突,这应该会让你朝着正确的方向前进。 处理和避免Git中的冲突 - mwilliams
您可以配置合并工具(kdiff3 jebaird.com/2013/07/08/...)然后使用git mergetool。当您在大型开发人员团队中工作时,您将始终遇到合并冲突。 - Grady G Cooper
不要忘记您可以通过定期合并下游来缓解大多数合并冲突! - Ant P
另见 git-tower.com/learn/git/ebook/command-line/tools-services/... - Pacerier
这似乎是一个详细的教程 - githubtraining.com/fix-merge-conflict-git-using-sourcetree - Rajavanya Subramaniyan


答案:


尝试: git mergetool

它会打开一个GUI,引导您完成每个冲突,然后您可以选择合并方式。有时它需要稍后进行一些手工编辑,但通常它本身就足够了。这肯定比手工做整件事要好得多。

根据@JoshGlover评论:

除非您安装GUI,否则该命令不一定会打开GUI。运行 git mergetool 对我来说导致了 vimdiff 正在使用。您可以安装以下工具之一来代替使用它: meldopendiffkdiff3tkdiffxxdifftortoisemergegvimdiffdiffuseecmergep4mergearaxisvimdiffemerge

以下是要使用的示例程序 vimdiff 用于解决合并冲突。基于 这个链接

步骤1:在终端中运行以下命令

git config merge.tool vimdiff
git config merge.conflictstyle diff3
git config mergetool.prompt false

这会将vimdiff设置为默认合并工具。

第2步:在终端中运行以下命令

git mergetool

第3步:您将看到以下格式的vimdiff显示

  +----------------------+
  |       |      |       |
  |LOCAL  |BASE  |REMOTE |
  |       |      |       |
  +----------------------+
  |      MERGED          |
  |                      |
  +----------------------+

这4个观点是

LOCAL - 这是当前分支的文件

BASE - 共同的祖先,文件在两个变化之前看起来如何

REMOTE - 您正在合并到您的分支机构的文件

合并 - 合并结果,这是在回购中保存的内容

您可以使用这些视图导航 ctrl+w。您可以直接使用MERGED视图 ctrl+w 其次是 j

有关更多信息vimdiff navigation 这里 和 这里

步骤4。您可以通过以下方式编辑MERGED视图

如果您想从REMOTE获得更改

:diffg RE  

如果您想从BASE获得更改

:diffg BA  

如果您想从LOCAL获得更改

:diffg LO 

第5步。保存,退出,提交和清理

:wqa 保存并退出vi

git commit -m "message"

git clean 删除diff工具创建的额外文件(例如* .orig)。


2425
2017-10-02 17:50



仅供参考,你可以使用 git mergetool -y 如果你要同时合并很多文件,请保存一些按键。 - davr
好吧,除非你安装一个GUI,否则它不一定会打开GUI。运行 git mergetool 对我来说导致了 vimdiff 正在使用。您可以安装以下工具之一来代替使用它: meld opendiff kdiff3 tkdiff xxdiff tortoisemerge gvimdiff diffuse ecmerge p4merge araxis vimdiff emerge。 - Josh Glover
好点Josh。在ubuntu上我和meld一起运气最好,它的三路合并显示也不错。在OSX上,git选择了一个不错的默认值。 - Peter Burns
这打开了KDiff3。哪个我绝对不知道如何使用。 - David Murdoch
我不明白为什么这个答案得到了这么多的赞成,它不是真的很有用,因为它只包含这一个命令,绝对没有解释如何使用它。正如其他人所说,它打开了一个vimdiff,即使我知道如何使用vim(至少切换窗口或关闭它们),我甚至不知道每个窗口代表如何比较或接受更改。很高兴知道有这样的命令,但没有解释如何使用它或安装其他第3工具,这是无用的答案 - Petr


这是一个可能的用例,从顶部:

你要做一些改变,但是哎呀,你不是最新的:

git fetch origin
git pull origin master

From ssh://gitosis@example.com:22/projectname
 * branch            master     -> FETCH_HEAD
Updating a030c3a..ee25213
error: Entry 'filename.c' not uptodate. Cannot merge.

所以你得到最新的并再试一次,但是有冲突:

git add filename.c
git commit -m "made some wild and crazy changes"
git pull origin master

From ssh://gitosis@example.com:22/projectname
 * branch            master     -> FETCH_HEAD
Auto-merging filename.c
CONFLICT (content): Merge conflict in filename.c
Automatic merge failed; fix conflicts and then commit the result.

所以你决定看一下这些变化:

git mergetool

哦,我,我的,上游改变了一些事情,但只是为了使用我的改变......不......他们的改变......

git checkout --ours filename.c
git checkout --theirs filename.c
git add filename.c
git commit -m "using theirs"

然后我们尝试最后一次

git pull origin master

From ssh://gitosis@example.com:22/projectname
 * branch            master     -> FETCH_HEAD
Already up-to-date.

当当!


1610
2017-08-04 17:04



这非常有用,因为我与二进制文件(艺术资产)有很多合并错误,合并这些似乎总是失败,所以我需要用新文件覆盖它而不是“合并” - petrocket
小心! - 和 - 他们的意思是相反的。 --ours ==遥控器。 - 他们==本地。看到 git merge --help - mmell
在我的例子中,我确认--theirs = remote repository,--ours =我自己的本地存储库。它与@mmell评论相反。 - Aryo
@mmell显然只是在一个rebase上。看到 this question - Navin
伙计们,“我们的”和“他们的”与你是否合并或变基有关。 如果你是 合并,然后“我们的”意味着你要合并的分支,“他们的”是你合并的分支。当你是 垫底,然后“我们的”意味着你要改变的提交,而“他们的”是指你想要改变的提交。


我发现合并工具很少帮助我理解冲突或解决方案。我通常更成功地在文本编辑器中查看冲突标记并使用git log作为补充。

以下是一些提示:

提示一

我发现的最好的事情是使用“diff3”合并冲突风格:

git config merge.conflictstyle diff3

这会产生如下冲突标记:

<<<<<<<
Changes made on the branch that is being merged into. In most cases,
this is the branch that I have currently checked out (i.e. HEAD).
|||||||
The common ancestor version.
=======
Changes made on the branch that is being merged in. This is often a 
feature/topic branch.
>>>>>>>

中间部分是共同祖先的样子。这很有用,因为您可以将它与顶部和底部版本进行比较,以更好地了解每个分支上的更改内容,从而更好地了解每个更改的目的。

如果冲突只有几行,这通常会使冲突非常明显。 (知道如何解决冲突是非常不同的;你需要知道其他人正在做什么。如果你感到困惑,最好是把那个人叫到你的房间,这样他们就能看到你在看什么在。)

如果冲突时间较长,那么我会将这三个部分中的每一部分剪切并粘贴到三个单独的文件中,例如“我的”,“普通”和“他们的”。

然后我可以运行以下命令来查看导致冲突的两个差异:

diff common mine
diff common theirs

这与使用合并工具不同,因为合并工具也将包括所有非冲突的差异。我觉得这会分散注意力。

提示二

有人已经提到了这一点,但理解每个差异背后的意图通常对于理解冲突的来源和如何处理它非常有帮助。

git log --merge -p <name of file>

这显示了在共同祖先和要合并的两个头之间触及该文件的所有提交。 (因此它不包括在合并之前已存在于两个分支中的提交。)这有助于您忽略明显不是当前冲突因素的差异。

提示三

使用自动化工具验证您的更改。

如果您有自动化测试,请运行它们。如果你有 皮棉,运行。如果它是一个可构建的项目,那么在你提交之前构建它等等。在所有情况下,你需要做一些测试,以确保你的更改没有破坏任何东西。 (哎呀,即使没有冲突的合并也会破坏工作代码。)

提示四

未雨绸缪;与同事沟通。

提前规划并了解其他人正在进行的工作可以帮助防止合并冲突和/或帮助他们提前解决 - 而细节仍然是新鲜的。

例如,如果您知道您和另一个人都在进行不同的重构,这两个重构都会影响同一组文件,那么您应该提前相互交谈,并更好地了解每个人的变化类型。制造。如果您按顺序而不是并行地执行计划的更改,则可能会节省大量时间和精力。

对于跨越大量代码的主要重构,您应该强烈考虑连续工作:当一个人执行完整的重构时,每个人都停止在代码的该区域工作。

如果你不能连续工作(由于时间压力,也许),那么沟通预期的合并冲突至少可以帮助你更快地解决问题,同时细节仍然是新鲜的。例如,如果同事在一周的时间内进行了一系列破坏性的提交,您可以选择在该周期间每天一次或两次合并/重组该同事分支。这样,如果你确实发现了合并/ rebase冲突,你可以比等待几周将所有内容合并成一个大块的更快地解决它们。

提示五

如果您不确定合并,请不要强迫它。

合并可能会让人感到压力,特别是当存在大量冲突文件且冲突标记覆盖数百行时。通常在评估软件项目时,我们没有足够的时间来处理开销项目,例如处理粗糙的合并,因此花费几个小时来解决每个冲突感觉真的很麻烦。

从长远来看,提前规划并了解其他人正在开展的工作是预测合并冲突并准备好在更短的时间内正确解决冲突的最佳工具。


685
2017-09-28 21:08



diff3选项是合并的一个很好的功能。我遇到的唯一一个GUI显示它是Perforce的 p4merge,可以单独安装和使用Perforce的其他工具(我没有使用过,但听过有关的投诉)。 - alxndr
这是我在互联网上找到的关于“如何修复合并冲突”的最佳教程! - Venkat Sudheer Reddy Aedama
这个答案值得投票;不是上面两个充满了选票的人; - DJphy
在导致合并冲突的rebase尝试之后:$ git log --merge -p build.xml输出:致命: - 没有MERGE_HEAD? - Ed Randall
如果我在branch1中对一个文件进行了更改并在branch2中删除了该文件该怎么办?我该如何解决合并冲突?有没有办法使用git我可以通过保持一个分支的更改来合并它们? - Honey


  1. 确定哪些文件存在冲突(Git应该告诉您)。

  2. 打开每个文件并检查差异; Git划定了他们。希望很明显每个块的哪个版本要保留。您可能需要与提交代码的开发人员讨论它。

  3. 一旦解决了文件中的冲突 git add the_file

  4. 一旦你解决了 所有 冲突,做 git rebase --continue 或任何命令 当你完成时Git说要做。


321
2017-10-02 12:41



@Justin认为Git是跟踪 内容 而不是跟踪文件。然后很容易看到您更新的内容 不 在存储库中,需要添加。这种思维方式也解释了为什么Git不跟踪空文件夹:尽管它们是技术上的文件,但没有任何内容可供跟踪。 - Gareth
内容存在,因为有2个版本的内容发生冲突。因此“git add”听起来不正确。如果你想在冲突解决后只提交一个文件,它就不起作用(git add,git commit)(“致命:在合并期间不能进行部分提交。”) - Dainius
是的,从技术上讲,这回答了问题,但在我看来,这不是一个有用的答案,对不起。什么是 点 使一个分支与另一个分支相同?当然合并会有冲突.. - Thufir
Thulfir:谁说过让一个分支与另一个分支相同?有不同的场景需要合并,而不是“使一个分支与另一个分支相同”。一个是你完成了一个开发分支,并希望将其更改合并到主分支中;在此之后,可以删除开发分支。另一个是你想要改变你的开发分支,以便最终最终合并到主人。 - Teemu Leisti
@JustinGrant git add 在索引中分段文件;它确实 不 向存储库添加任何内容。 git commit 将内容添加到存储库。这种用法对合并有意义 - 合并自动分阶段可以自动合并的所有更改;您有责任合并其余的更改,并在完成后将这些更改添加到索引中。 - Mark E. Haase


查看Stack Overflow问题中的答案 在Git中中止合并特别是 Charles Bailey的回答 其中显示了如何查看具有问题的文件的不同版本,例如,

# Common base version of the file.
git show :1:some_file.cpp

# 'Ours' version of the file.
git show :2:some_file.cpp

# 'Theirs' version of the file.
git show :3:some_file.cpp

97
2017-10-03 15:15



另请查看“git checkout -m”的“-m”选项 - 它允许您将不同的苍蝇提取回工作区 - qneill
这救了我。分别查看每个文件让我记住了每个分支的用途。然后我可以决定选择。 - Rohmer


当同时对文件进行更改时,会发生合并冲突。这是如何解决它。

git CLI

以下是进入冲突状态时要执行的操作的简单步骤:

  1. 请注意冲突文件列表: git status (下 Unmerged paths 部分)。
  2. 通过以下方法之一单独解决每个文件的冲突:

    • 使用GUI解决冲突: git mergetool (最简单的方法)。

    • 要接受远程/其他版本,请使用: git checkout --theirs path/file。这将拒绝您对该文件所做的任何本地更改。

    • 要接受本地/我们的版本,请使用: git checkout --ours path/file

      但是你要小心,因为远程改变冲突是出于某种原因。

      有关: git中“我们的”和“他们的”的确切含义是什么?

    • 手动编辑冲突的文件并查找之间的代码块 <<<<</>>>>> 然后从上方或下方选择版本 =====。看到: 如何呈现冲突

    • 路径和文件名冲突可以通过解决 git add/git rm

  3. 最后,使用以下命令查看准备提交的文件: git status

    如果你还有任何文件 Unmerged paths,你确实手动解决了冲突,然后让Git知道你解决了它: git add path/file

  4. 如果成功解决了所有冲突,请通过以下方式提交更改: git commit -a 像往常一样推到遥控器。

也可以看看: 从命令行解决合并冲突 在GitHub

DiffMerge

我成功使用了 DiffMerge 它可以在Windows,macOS和Linux / Unix上直观地比较和合并文件。

它以图形方式显示3个文件之间的变化,它允许自动合并(在安全的情况下),并完全控制编辑生成的文件。

DiffMerge

图片来源: DiffMerge (Linux截图)

只需下载并在repo中运行:

git mergetool -t diffmerge .

苹果系统

在macOS上,您可以通过以下方式安装

brew install caskroom/cask/brew-cask
brew cask install diffmerge

并且可能(如果未提供)您需要在PATH中放置以下额外的简单包装(例如, /usr/bin):

#!/bin/sh
DIFFMERGE_PATH=/Applications/DiffMerge.app
DIFFMERGE_EXE=${DIFFMERGE_PATH}/Contents/MacOS/DiffMerge
exec ${DIFFMERGE_EXE} --nosplash "$@"

然后您可以使用以下键盘快捷键:

  • - Alt键 - 向上/ 跳转到上一个/下一个更改。
  • - Alt键 - 剩下/ 从左或右接受改变

或者你可以使用 了opendiff (Xcode Tools的一部分),它允许您将两个文件或目录合并在一起以创建第三个文件或目录。


88
2017-08-05 14:29





如果您经常进行小型提交,那么首先查看提交注释 git log --merge。然后 git diff 会告诉你冲突。

对于涉及多行的冲突,可以更容易地看到外部GUI工具中发生了什么。我喜欢opendiff - Git也支持vimdiff,gvimdiff,kdiff3,tkdiff,meld,xxdiff,emerge开箱即用,你可以安装其他人: git config merge.tool "your.tool" 将设置您选择的工具然后 git mergetool 合并失败后会在上下文中显示差异。

每次编辑文件以解决冲突时, git add filename 将更新索引,你的差异将不再显示它。当处理完所有冲突并且他们的文件已经存在时 git add-ed, git commit 将完成您的合并。


73
2017-10-02 16:11



使用“git add”是真正的诀窍。你可能甚至不想提交(也许你想隐藏),但你必须做“git add”来完成合并。我认为mergetool为你做了添加(尽管它不在手册页中),但是如果你手动进行合并,你需要使用“git add”来完成它(即使你不想提交)。 - nobar


看到 如何提出冲突 或者,在Git中, git merge 用于了解合并冲突标记的文档。

而且, 如何解决冲突 部分解释了如何解决冲突:

看到冲突后,你可以做两件事:

  • 决定不合并。您需要的唯一清理是将索引文件重置为 HEAD 提交反向2.并清理由2.和3进行的工作树更改。 git merge --abort 可以用于此。

  • 解决冲突。 Git将标记工作树中的冲突。将文件编辑为形状和 git add 他们到索引。使用 git commit 达成协议。

您可以使用许多工具解决冲突:

  • 使用mergetool。 git mergetool 启动图形合并工具,它将帮助您完成合并。

  • 看看差异。 git diff 将显示三向差异,突出显示两者的变化 HEAD 和 MERGE_HEAD 版本。

  • 查看每个分支的差异。 git log --merge -p <path> 将首先显示差异 HEAD 版本,然后 MERGE_HEAD 版。

  • 看看原件。 git show :1:filename 显示共同的祖先, git show :2:filename 显示了 HEAD 版本,和 git show :3:filename 显示了 MERGE_HEAD 版。

您还可以阅读有关合并冲突标记以及如何解决它们的问题 亲Git 书部分 基本合并冲突


43
2017-07-14 18:34





对于 Emacs的 想要半手动解决合并冲突的用户:

git diff --name-status --diff-filter=U

显示需要解决冲突的所有文件。

逐个打开每个文件,或者一次全部打开:

emacs $(git diff --name-only --diff-filter=U)

访问需要在Emacs中编辑的缓冲区时,请键入

ALT+x vc-resolve-conflicts

这将打开三个缓冲区(我的,他们的和输出缓冲区)。按'n'(下一个区域),'p'(预视区域)进行导航。按'a'和'b'分别将我或他们的区域复制到输出缓冲区。和/或直接编辑输出缓冲区。

完成后:按'q'。 Emacs会询问您是否要保存此缓冲区:是的。 完成一个缓冲区后,通过从teriminal运行解决它:

git add FILENAME

完成所有缓冲区类型后

git commit

完成合并。


36
2018-02-22 23:04