How to resolve merge conflicts in Git
有没有一个很好的方法来解释如何解决Git中的合并冲突?
试一试:
它打开一个GUI,引导您解决每个冲突,您可以选择如何合并。有时事后需要一些手工编辑,但通常这本身就足够了。当然,这比手工做整个事情要好得多。
根据@joshglover评论:
该命令不一定要打开一个GUI,除非安装了一个。为我运行
下面是使用
步骤1:在终端中运行以下命令
1 2 3 | git config merge.tool vimdiff git config merge.conflictstyle diff3 git config mergetool.prompt false |
这将把VimDiff设置为默认的合并工具。
步骤2:在终端中运行以下命令
1 | git mergetool |
步骤3:您将看到以下格式的VimDiff显示
1 2 3 4 5 6 7 8 | +----------------------+ | | | | |LOCAL |BASE |REMOTE | | | | | +----------------------+ | MERGED | | | +----------------------+ |
这4个视图是
LOCAL – this is file from the current branch
BASE – common ancestor, how file looked before both changes
REMOTE – file you are merging into your branch
MERGED – merge result, this is what gets saved in the repo
您可以使用
有关VimDiff导航的更多信息
步骤4。可以按以下方式编辑合并视图
如果要从远程获取更改
1 | :diffg RE |
如果您想从基础获取更改
1 | :diffg BA |
如果要从本地获取更改
1 | :diffg LO |
步骤5。保存、退出、提交和清理
下面是一个可能的用例,从顶部看:
你会做出一些改变,但糟糕的是,你不是最新的:
1 2 3 4 5 6 7 | git fetch origin git pull origin master From ssh://[email protected]:22/projectname * branch master -> FETCH_HEAD Updating a030c3a..ee25213 error: Entry 'filename.c' not uptodate. Cannot merge. |
因此,您可以更新并重试,但有一个冲突:
1 2 3 4 5 6 7 8 9 | git add filename.c git commit -m"made some wild and crazy changes" git pull origin master From ssh://[email protected]: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. |
所以你决定看看这些变化:
1 | git mergetool |
哦,我,哦,我的,上游改变了一些事情,但只是为了使用我的改变…不…他们的改变…
1 2 3 4 | git checkout --ours filename.c git checkout --theirs filename.c git add filename.c git commit -m"using theirs" |
然后我们试着最后一次
1 2 3 4 5 | git pull origin master From ssh://[email protected]:22/projectname * branch master -> FETCH_HEAD Already up-to-date. |
塔达!
我发现合并工具很少能帮助我理解冲突或解决方案。我通常更成功地在文本编辑器中查看冲突标记,并使用git日志作为补充。好的。
以下是一些提示:好的。提示一
我发现最好的方法是使用"diff3"合并冲突样式:好的。
这会产生如下冲突标记:好的。
1 2 3 4 5 6 7 8 9 | <<<<<<< 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. >>>>>>> |
中间部分是共同祖先的样子。这很有用,因为您可以将其与顶部和底部版本进行比较,以更好地了解每个分支上的更改,从而更好地了解每个更改的目的。好的。
如果冲突只是几行,这通常会使冲突非常明显。(知道如何解决冲突是非常不同的;你需要知道其他人在做什么。如果你很困惑,最好是把那个人叫到你房间里,让他们看到你在看什么。)好的。
如果冲突持续时间较长,我会将这三个部分分别剪切并粘贴到三个单独的文件中,如"我的"、"共同的"和"他们的"。好的。
然后我可以运行以下命令来查看导致冲突的两个不同块:好的。
1 2 | diff common mine diff common theirs |
这与使用合并工具不同,因为合并工具也将包含所有不冲突的diff块。我觉得这让人分心。好的。小窍门二
有人已经提到过这一点,但是了解每个不同的人背后的意图通常对理解冲突的来源和如何处理冲突非常有帮助。好的。
1 | git log --merge -p <name of file> |
这将显示在共同祖先和您要合并的两个头之间接触到该文件的所有提交。(所以它不包括合并前已经存在于两个分支中的提交。)这有助于您忽略明显不是当前冲突因素的差异块。好的。小贴士三
使用自动化工具验证您的更改。好的。
如果您有自动测试,运行这些测试。如果你有线头,就跑吧。如果它是一个可构建的项目,那么在提交之前构建它,等等。在所有情况下,您都需要做一些测试,以确保您的更改不会破坏任何东西。(如果没有冲突,即使合并也会破坏工作代码。)好的。小贴士四
提前计划;与同事沟通。好的。
提前计划和了解其他人正在进行的工作有助于防止合并冲突和/或提前解决冲突,而细节仍在头脑中。好的。
例如,如果您知道您和另一个人正在进行不同的重构,这两个重构都会影响同一组文件,那么您应该提前与对方交谈,并更好地了解每个人正在进行的更改类型。如果您连续而不是并行地执行计划更改,可能会节省大量时间和精力。好的。
对于跨越大量代码的主要重构,您应该强烈考虑连续工作:当一个人执行完整的重构时,每个人都停止在代码的该区域工作。好的。
如果你不能连续工作(可能是由于时间压力),那么沟通预期的合并冲突至少可以帮助你在细节还没有被记住的时候更快地解决问题。例如,如果一个同事在一周内进行一系列破坏性的提交,您可以选择在该周内每天合并/重新平衡该同事分支一次或两次。这样,如果您确实发现合并/重新平衡冲突,那么您可以比等待几周将所有内容合并到一个大的集合中更快地解决冲突。好的。小贴士五
如果你不确定是否合并,不要强行合并。好的。
合并可能会让人感到难以抗拒,尤其是当有很多冲突文件,并且冲突标记覆盖了数百行时。通常在评估软件项目时,我们没有足够的时间来处理像处理粗糙合并这样的开销项目,所以花几个小时来分析每个冲突感觉就像是一种真正的拖累。好的。
从长远来看,提前计划和了解其他人正在进行的工作是预测合并冲突的最佳工具,并准备在更短的时间内正确解决这些冲突。好的。好啊。
确定哪些文件有冲突(Git应该告诉您这一点)。
打开每个文件并检查差异;Git对其进行划分。希望每个块的哪一个版本都是显而易见的。您可能需要与提交代码的其他开发人员讨论这个问题。
一旦解决了文件
一旦你解决了所有的冲突,执行
查看在Git中中止合并的堆栈溢出问题中的答案,尤其是Charles Bailey的答案,它显示了如何查看有问题的文件的不同版本,例如,
1 2 3 4 5 6 7 8 | # 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 |
同时对文件进行更改时会发生合并冲突。下面是如何解决这个问题。
以下是当您进入冲突状态时要做的简单步骤:
通过以下方法之一分别解决每个文件的冲突:
使用GUI解决冲突:
git mergetool (最简单的方法)。要接受远程/其他版本,请使用:
git checkout --theirs path/file 。这将拒绝您对该文件所做的任何本地更改。要接受本地/我们的版本,请使用:
git checkout --ours path/file 。但是,您必须小心,因为远程更改导致冲突是由于某种原因造成的。
相关:Git中"我们的"和"他们的"的确切含义是什么?
手动编辑冲突文件,查找
<<<<< />>>>> 之间的代码块,然后从上面或下面选择版本===== 。请参见:冲突是如何呈现的。路径和文件名冲突可以通过
git add /git rm 来解决。
最后,使用:
如果您在
如果所有冲突都成功解决了,则通过:
另请参见:从github的命令行解决合并冲突
多文件比较我已经成功地使用了diffmerge,它可以直观地比较和合并Windows、MacOS和Linux/Unix上的文件。
它可以以图形方式显示3个文件之间的更改,并允许自动合并(安全时)和对编辑结果文件的完全控制。
图片来源:diffmerge(Linux屏幕截图)
只需下载它并在repo中运行,如下所示:
1 | git mergetool -t diffmerge . |
马科斯
在MacOS上,您可以通过以下方式安装:
1 2 | brew install caskroom/cask/brew-cask brew cask install diffmerge |
而且(如果没有提供)您可能需要在路径中放置以下额外的简单包装器(例如,
1 2 3 4 | #!/bin/sh DIFFMERGE_PATH=/Applications/DiffMerge.app DIFFMERGE_EXE=${DIFFMERGE_PATH}/Contents/MacOS/DiffMerge exec ${DIFFMERGE_EXE} --nosplash"$@" |
然后可以使用以下键盘快捷键:
- ? alt upbkbd/down跳到上一个/下一个更改。
- ? alt left/right接受从左或右更改
或者,您可以使用OpenDiff(Xcode工具的一部分),它允许您将两个文件或目录合并在一起以创建第三个文件或目录。
如果您经常进行小规模的提交,那么首先查看
对于涉及多行的冲突,更容易在外部GUI工具中看到发生了什么。我喜欢opendiff——git也支持vimdiff、gvimdiff、kdiff、tkdiff、meld、xxdiff,从盒子里出来,你可以安装其他的:
每次编辑文件以解决冲突时,
查看冲突是如何呈现的,或者在Git中,查看
此外,"如何解决冲突"部分解释了如何解决冲突:
After seeing a conflict, you can do two things:
Decide not to merge. The only clean-ups you need are to reset the index file to the
HEAD commit to reverse 2. and to clean up working tree changes made by 2. and 3.;git merge --abort can be used for this.Resolve the conflicts. Git will mark the conflicts in the working tree. Edit the files into shape and
git add them to the index. Usegit commit to seal the deal.You can work through the conflict with a number of tools:
Use a mergetool.
git mergetool to launch a graphical mergetool which will work you through the merge.Look at the diffs.
git diff will show a three-way diff, highlighting changes from both theHEAD andMERGE_HEAD versions.Look at the diffs from each branch.
git log --merge -p will show diffs first for theHEAD version and then theMERGE_HEAD version.Look at the originals.
git show :1:filename shows the common ancestor,git show :2:filename shows theHEAD version, andgit show :3:filename shows theMERGE_HEAD version.
您还可以在Pro Git图书的"基本合并冲突"部分中阅读有关合并冲突标记以及如何解决这些标记。
对于要半手动解决合并冲突的Emacs用户:
1 | git diff --name-status --diff-filter=U |
显示所有需要冲突解决的文件。
通过以下方式逐个或同时打开这些文件:
1 | emacs $(git diff --name-only --diff-filter=U) |
访问需要在Emacs中编辑的缓冲区时,键入
1 | ALT+x vc-resolve-conflicts |
这将打开三个缓冲区(我的、他们的和输出缓冲区)。按"N"(下一个区域),"P"(预览区域)导航。按"a"和"b"分别将我的或他们的区域复制到输出缓冲区。和/或直接编辑输出缓冲区。
完成后:按"Q"。Emacs会询问您是否要保存此缓冲区:是。完成缓冲区后,通过从外部运行将其标记为已解决:
1 | git add FILENAME |
完成所有缓冲区类型时
1 | git commit |
完成合并。
我要么想要完整的我或他们的版本,要么想要检查单个的更改并为每个更改作出决定。
完全接受我或他们的版本:
接受我的版本(本地,我们的):
1 2 3 | git checkout --ours -- <filename> git add <filename> # Marks conflict as resolved git commit -m"merged bla bla" # An"empty" commit |
接受他们的版本(远程,他们的):
1 2 3 | git checkout --theirs -- <filename> git add <filename> git commit -m"merged bla bla" |
如果要对所有冲突文件执行此操作,请运行:
1 | git merge --strategy-option ours |
或
1 | git merge --strategy-option theirs |
审阅所有更改并分别接受它们
默认的
您还可以为此安装可视化工具,例如
1 | git mergetool -t meld |
它将打开本地版本(我们的)、"基本"或"合并"版本(合并的当前结果)和远程版本(它们的)。完成后保存合并的版本,再次运行
请按照以下步骤修复Git中的合并冲突:
检查Git状态:GIT状态
获取补丁集:git fetch(从git提交中签出正确的补丁)
签出本地分支(这里是我的示例中的temp1):Git结帐-B温度1
从主控形状中提取最近的内容:git pull—重新设置原点主控形状
启动mergetool并检查冲突并修复它们……然后使用当前分支检查远程分支中的更改:吉特混血儿
再次检查状态:GIT状态
删除由mergetool本地创建的不需要的文件,通常mergetool会创建扩展名为*.orig的额外文件。请删除该文件,因为它只是副本,并在本地修复更改,然后添加正确版本的文件。git添加您的更改的正确的文件
再次检查状态:GIT状态
将更改提交到相同的提交ID(这样可以避免新的单独补丁集):修改最后一次提交
推到主分支:Git Push(到Git存储库)
简单地说,如果您很清楚其中一个存储库中的更改并不重要,并且希望以另一个存储库为例解决所有更改,请使用:
1 | git checkout . --ours |
以有利于存储库的方式解决更改,或者
1 | git checkout . --theirs |
以有利于其他存储库或主存储库的方式解决更改。
否则,您将不得不使用一个GUI合并工具逐个地遍历文件,比如合并工具是
1 | git mergetool -t p4merge |
在完成一个文件之后,你必须保存并关闭,这样下一个文件就会打开。
奖金:
在谈到拉/取/合并上述答案时,我想分享一个有趣且富有成效的技巧,
上面的命令是我Git生活中最有用的命令,它节省了很多时间。
在将新提交的更改推送到远程服务器之前,请尝试使用
如果发生冲突,只需使用
1 2 3 | git mergetool git add conflict_file git rebase --continue |
详情请访问:http://gitolite.com/git-pull--rebase
您可以像其他人详细描述的那样,以多种方式修复合并冲突。
我认为真正的关键是知道更改如何与本地和远程存储库一起流动。关键是理解跟踪分支。我发现跟踪分支是我的本地实际文件目录和远程文件(定义为源文件)之间的"中间缺失部分"。
为了避免这种情况,我个人养成了两件事的习惯。
而不是:
1 2 | git add . git commit -m"some msg" |
有两个缺点-
a)所有新的/更改的文件都会被添加,这可能包括一些不需要的更改。b)您不必先查看文件列表。
所以我改为:
1 2 | git add file,file2,file3... git commit # Then type the files in the editor and save-quit. |
这样,您就可以更仔细地考虑添加哪些文件,并且在使用消息编辑器时,您还可以查看列表并进行更多的思考。我发现当我使用全屏编辑器而不是
[更新-随着时间的推移,我将更多的内容切换到:
1 2 3 | git status # Make sure I know whats going on git add . git commit # Then use the editor |
]
另外(和你的情况更相关),我尽量避免:
1 | git pull |
或
1 | git pull origin master. |
因为pull意味着合并,如果您在本地有不希望合并的更改,您可以很容易地以合并代码和/或合并不应该合并的代码的冲突结束。
相反,我试着去做
1 2 3 | git checkout master git fetch git rebase --hard origin/master # or whatever branch I want. |
您也会发现这一点很有帮助:
git分支、fork、fetch、merge、rebase和clone,有什么区别?
Coolaj86的答案总结了几乎所有的事情。如果在同一段代码中对两个分支都进行了更改,则必须手动合并。在任何文本编辑器中打开冲突的文件,您应该看到以下结构。
1 2 3 4 5 6 7 8 9 | (Code not in Conflict) >>>>>>>>>>> (first alternative for conflict starts here) Multiple code lines here =========== (second alternative for conflict starts here) Multiple code lines here too <<<<<<<<<<< (Code not in conflict here) |
在删除等号和尖括号的同时,以希望新代码成为的方式选择一个备选方案或两者的组合。
1 2 | git commit -a -m"commit message" git push origin master |
有三个步骤:
通过命令查找导致冲突的文件
1 | git status |
检查文件,在其中可以找到标记为
1 2 | <<<<<<<<head blablabla |
将其更改为所需的方式,然后使用命令提交
1 2 | git add solved_conflicts_files git commit -m 'merge msg' |
使用
我很惊讶没有其他人提到使用
例如,如果更改了程序的缩进,默认的Git合并策略有时会与属于不同函数的单括号
1 | git merge -s recursive -X patience other-branch |
从文档中:
1 2 3 4 | With this option, merge-recursive spends a little extra time to avoid mismerges that sometimes occur due to unimportant matching lines (e.g., braces from distinct functions). Use this when the branches to be merged have diverged wildly. |
与共同祖先的比较
如果您有合并冲突,并且希望在修改分支时了解其他人的想法,那么有时将分支直接与共同的祖先(而不是我们的分支)进行比较更容易。为此,您可以使用
1 | git diff $(git merge-base <our-branch> <their-branch>) <their-branch> |
通常,您只想查看特定文件的更改:
1 | git diff $(git merge-base <our-branch> <their-branch>) <their-branch> <file> |
截至2016年12月12日,您可以在github.com上合并分支机构并解决冲突。
因此,如果您不想使用命令行或旧答案中提供的任何第三方工具,请使用Github的本地工具。
这篇博文详细解释了这个问题,但最基本的是,通过用户界面"合并"两个分支后,您将看到一个"解决冲突"选项,它将带您到一个编辑器,允许您处理这些合并冲突。
1 | git log --merge -p [[--] path] |
似乎并不总是对我有效,并且通常会显示两个分支之间不同的每个提交,即使使用
我要解决这个问题的方法是打开两个命令行,一次运行
1 | git log ..$MERGED_IN_BRANCH --pretty=full -p [path] |
而在另一个
1 | git log $MERGED_IN_BRANCH.. --pretty=full -p [path] |
将
这将允许您查看在两个分支出现分歧后,提交进入文件的内容。它通常使解决冲突变得容易得多。
为了避免冲突,我总是遵循以下步骤。
- Git结帐主(到主分行)
- Git Pull(更新主控形状以获取最新代码)
- git checkout-b mybranch(签出一个新的a分支并开始对该分支进行操作,这样您的主机就始终保持在主干的顶部。)
- 加特。以及git commit和git push(在更改后在本地分支上)
- Git结账大师(回到你的大师那里。)
现在,您可以做同样的事情,维护您想要的本地分支机构,并在必要时同时工作,我只是在您的分支机构执行Git结帐。
如果要从分支(测试)合并到主控,可以执行以下步骤:
第一步:到分店
1 | git checkout test |
第二步:
步骤3:如果有冲突,请转到这些文件进行修改。
步骤4:添加这些更改
1 | git add #your_changes_files |
步骤5:
步骤6:如果仍有冲突,请再次返回步骤3。如果没有冲突,请执行以下操作:
第7步:然后测试和主控之间没有冲突。可以直接使用合并。
合并冲突可能在不同的情况下发生:
- 当运行"git fetch"然后运行"git merge"时
- 运行"git fetch"然后运行"git rebase"时
- 运行"git pull"时(实际上等于上述条件之一)
- 运行"git stash pop"时
- 应用Git修补程序时(提交,导出到要传输的文件,例如通过电子邮件)
您需要安装一个与Git兼容的合并工具来解决冲突。我个人使用kdiff3,我发现它既好又方便。您可以在此处下载其Windows版本:
https://sourceforge.net/projects/kdiff3/files/文件/
btw如果安装git扩展,在其安装向导中有一个安装kdiff3的选项。
然后设置git configs以使用kdiff作为其合并工具:
1 2 3 4 5 6 7 | $ git config --global --add merge.tool kdiff3 $ git config --global --add mergetool.kdiff3.path"C:/Program Files/KDiff3/kdiff3.exe" $ git config --global --add mergetool.kdiff3.trustExitCode false $ git config --global --add diff.guitool kdiff3 $ git config --global --add difftool.kdiff3.path"C:/Program Files/KDiff3/kdiff3.exe" $ git config --global --add difftool.kdiff3.trustExitCode false |
(请记住将路径替换为kdiff exe文件的实际路径。)
每次遇到合并冲突时,只需运行以下命令:
1 | $git mergetool |
然后它打开kdiff3,首先尝试自动解决合并冲突。大多数冲突都是自发解决的,您需要手动修复其余的冲突。
以下是kdiff3的外观:
完成后,保存该文件并将其转到下一个有冲突的文件,然后再次执行相同的操作,直到解决所有冲突。
要检查是否成功合并了所有内容,只需再次运行mergetool命令,您应该得到以下结果:
1 2 | $git mergetool No files need merging |
这个答案是为像我这样喜欢在编辑器中做任何事情的VIM用户添加一个替代方案。
DRtpope为vim开发了一个名为"逃犯"的伟大插件。安装后,您可以运行
一旦通过三种方式合并,逃犯将允许您以以下方式获得正在合并的任何分支的更改:
:diffget //2 ,从原(头)分公司取得变更::diffget //3 ,合并分公司变更:
合并完文件后,在合并的缓冲区中键入
git fetch
git checkout your branch
git rebase master
在此步骤中,您将尝试使用首选的IDE解决冲突。
您可以通过此链接检查ho以修复文件中的冲突。https://help.github.com/articles/resoluting-a-merge-conflict-using-the-command-line/
git add
git rebase --continue
git commit --amend
git push origin HEAD:refs/drafts/master (push like a drafts)
现在一切都很好,你会发现你在格瑞特的承诺
我希望这将有助于有关这个问题的每一个人。
用于VS代码的Gitlense
您可以尝试使用Gitlense for VS代码,它们的主要功能是:
三。轻松解决冲突。我已经喜欢这个功能了:
2。当前的责任线。三。沟怪4。状态栏责备这里有很多功能可以检查。
如果还没有,请尝试使用Visual Studio代码进行编辑。它所做的是在您尝试合并(并在合并冲突中着陆)之后,VS代码会自动检测合并冲突。
它可以很好地帮助您显示对原始版本所做的更改,如果您接受
对我有帮助,对你也有帮助!
PS:只有用代码和Visual Studio代码配置了Git,它才能工作。
对于使用Visual Studio的用户(在我的例子中是2015年)
在vs.中关闭项目,尤其是在大型项目中,vs.在使用UI合并时往往会出现异常。
在命令提示下执行合并。
git签出目标分公司
Git合并源代码分支
然后在vs中打开项目,转到团队资源管理器->分支。现在有一条消息说合并正在挂起,冲突文件列在消息的正下方。
单击冲突文件,您将可以选择合并、比较、获取源文件、获取目标文件。VS中的合并工具非常容易使用。
解决冲突的一个更安全的方法是使用git-mediate(这里建议的常见解决方案非常容易出错)。
有关如何使用它的快速介绍,请参阅本文。
如果您使用Intellij作为IDE尝试将父级合并到分支
1 2 | git checkout <localbranch> git merge origin/<remotebranch> |
它将显示像这样的所有冲突
A_MBPro:test anu$ git merge origin/ Auto-merging
src/test/java/com/.../TestClass.java CONFLICT
(content): Merge conflict in
src/test/java/com/.../TestClass.java
现在请注意,文件testclass.java在intellij中以红色显示。还将显示Git状态
1 2 3 | Unmerged paths: (use"git add <file>..." to mark resolution) both modified: src/test/java/com/.../TestClass.java |
在intellij中打开该文件,它将包含
1 2 3 4 5 6 7 | <<<<<<< HEAD public void testMethod() { } ======= public void testMethod() { ... } >>>>>>> origin/<remotebranch> |
其中head是本地分支的更改,origin/is是远程分支的更改。把你需要的东西放在这里,把你不需要的东西拿出来,然后正常的步骤就可以了。那就是
1 2 3 | git add TestClass.java git commit -m"commit message" git push |
我遵循以下流程。
修复合并冲突的过程:
首先,从要合并到的目标分支中提取最新的
当您从目的地获取最新信息时,现在通过删除这些额外的字符在IDE中手动解决冲突。
执行
当
现在,通过
这就是它,如果您使用bitback或github,您将在拉请求中看到它被解决。
这对我来说总是比使用Git更快更容易。如果更改导致拉请求混乱,并且您的IDE不能很好地处理Git合并,那么这将特别有益。
1 2 3 4 5 | git checkout branch1 git fetch origin git rebase -p origin/mainbranch |
如果存在合并冲突,请修复它们。然后,通过运行:
修复之后,您可以提交本地分支并将其推送到远程分支。
1 | git push origin branch1 |
我已经安装了beyond compare,所以当使用
如果不使用工具进行合并,请先将代码复制到外部:
1 2 3 4 | - `checkout master` - `git pull` / get new commit - `git checkout` to your branch - `git rebase master` |
它解决了冲突,您可以复制代码。