我经常遇到以下合并冲突问题并解决它,但我想问一下我的解决方案是否正确。就个人而言,我没有看到任何问题,因为功能测试正在通过,但是对这个问题有所了解会很好。
所以,假设我们有两个分支:
develop
feature
我正在更改feature
分支中的代码,然后创建PR以合并到develop
。但是,Github
或Bitbucket
的差异工具表明我们遇到了合并冲突;编码很常见。
然后我采取以下步骤:
git checkout develop
和git pull origin develop
git checkout feature
然后git pull origin feature
git merge develop
,以便更新我当前的分支。这些步骤可以帮助我找出发生冲突的文件。`你有未合并的路径。 (修复冲突并运行“git commit”)
要提交的更改:
modified: app/BlablaFile.php
modified: app/Blabla2File.php
renamed: app/OldName.php -> app/NewName.php
然后:
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: app/UnmergedFile.php
基本上是我为解决合并冲突而编辑的文件列表。
问题是我应该只git add app/UnmergedFile.php
然后git commit -m "Updated app/UnmergedFile.php"
吗?或者我应该git add
还包含Changes to be committed:
部分中包含的文件?
我通常会做第一个但是想到另一个想法会很好。
答案 0 :(得分:2)
TL; DR:您无需重新添加&#34;要提交的更改的文件&#34;。
Git有一个名称,不同的是,索引或登台区域,有时甚至是缓存(如{{1} }})。它解释得不是很好,部分原因是因为它很复杂。幸运的是,索引的 use 非常简单,除了(唉)合并期间。 : - )
索引中的内容最好被描述为您将要进行的下一次提交。它还有助于记住Git始终具有当前提交,命名为git diff --cached
和(HEAD
存储库除外),工作树或工作树,您可以在其中查看文件,编译,编辑以及您对文件执行的任何其他操作。
最初,您的工作树,索引和--bare
提交都匹配(当您第一次克隆存储库时)。您无法直接在索引中触摸文件,因此您可以在工作树中进行任何更改 - 甚至创建新文件 - 您可以在其中完成所有工作。然后,要将修改后的文件放回索引,请在路径上运行HEAD
,以便Git将修改后的文件复制到索引中。要将新文件放入索引,您还可以在路径上运行git add
。
如果你出于某种原因让你兴奋并立即运行git add
,Git会读取你当前的索引并将其打包为新提交。作为工作树快照,新提交将在索引中现在处于索引中的每个文件。
如果该状态匹配 git commit
提交,HEAD
只是说:&#34; Huhwha?为什么你现在尝试做出新的提交?&#34;所以大概是你在这一点上改变了索引中的某些东西。 git commit
会向您显示索引中与 git status
提交不同的内容:这些是&#34;要提交的更改&#34 ;
运行HEAD
有时会显示有关索引的秘密。 索引中的每个条目实际上都有四个插槽,它们都已编号。通常情况下,您只会看到插槽零,这是为&#34;正常&#34;文件,准备提交。
合并旨在合并两个(有时更多,但让我们坚持两个)一组变更。例如,也许你和Bob开始使用相同的文件。然后,您对某些文件进行了一些更改,并git merge
- 编辑并提交了源的新快照。与此同时,Bob对某些文件进行了一些更改 - 可能是相同的文件,甚至是git add
- 并且已经提交,现在是时候将更改和Bob的更改结合起来。
要执行合并 - 合并动词某些文件--Git首先找到合并基础,即您和Bob同步的点。然后Git运行两个git add
:一个从该合并基地到你的最新提交,一个从同一个合并基地到 Bob 最新承诺。这表明&#34;你改变了什么&#34;和#34; Bob改变了什么&#34;。
Git然后尽力合并这些更改(包括,在这种情况下,计算和关注文件重命名,并确定如果只有一个该怎么做您重命名了一些文件 - 但大多数合并冲突都不包含文件重命名,除非您编写了大量Java代码...)。但有时候,Git无法独立完成这项工作。在这种特殊情况下,Git会将合并工作交还给您,此时您可以看到索引中的所有插槽。
索引槽1是Git存储文件的 base 版本的地方。这是您开始使用的常见版本。
索引插槽2是文件的git diff
或HEAD
版本。 (它有时也被称为&#34;本地&#34;版本。)
索引插槽3是该文件的另一个或--ours
(或有时是&#34;远程&#34;)版本。
这几乎就是它的全部内容:这三个版本保留了三个版本,最终你会得到&#34;未合并的路径&#34;。工作树中的文件包含Git的初始合并工作,以及冲突标记和--theirs
和--ours
版本的一些混搭。如果您将--theirs
设置为merge.conflictstyle
,Git还会包含文件的 base 版本中的部分,这些部分存储在索引槽1中。现在它已经到了你解决合并。
一旦您找到了正确的解决方案并更新了文件的工作树版本,您就必须diff3
路径。这将工作树版本存储到索引槽零中,消除了插槽1-3中的三个条目。由于插槽零保存正常的,即将提交的版本,因此文件现在可以提交了。
对于根本没有任何更改的文件,或只是其中一人更改了某些内容,或,其中Git认为它正确地合并了您的更改,而Bob&#39; s更改,那些文件已在工作树和(插槽零)索引中更新。如果结果与当前git add
提交不同,HEAD
会将文件显示为&#34;要提交的更改&#34;。
我上面说过这是几乎所有它都有。关于索引槽的最后几件事要记住:
某些广告位可能为空。假设您和 Bob都添加了一个文件。在这种情况下,您将获得&#34;添加/添加冲突&#34;。该文件的哪个版本是常见的基本版本?当然没有:你们都添加了 new 文件。所以在这种情况下,基本插槽1是空的。
如果删除文件并且Bob更改了文件,也会发生同样的情况。在这种情况下,git status
插槽,插槽2为空,而插槽1和3保留基本版本和Bob的版本。由您自行决定是保留Bob的版本,保留文件,还是使用第三个版本,但三个插槽中只有两个被填充。
如果您中的一个或两个重命名文件,则三个插槽中的文件版本可能来自先前提交中具有不同名称的文件。例如,您的输出显示:
--ours
此处没有冲突,但如果有, renamed: app/OldName.php -> app/NewName.php
的至少一个广告位将从app/NewName.php
的其他提交版本中填充。
当你去看旧版本时,这主要是重要的。如果在单独的克隆或工作树中检出其中一个尚未重命名文件的提交,或者如果使用app/OldName.php
查看文件而未将其检出,则必须使用旧名称,只要它有旧名称。但是,如果您使用git show <commit>:app/OldName.php
或git checkout --ours
将这两个版本中的一个提取到工作树中,则必须使用 new 名称,因为索引现在具有该文件以新名称存储。
如果您 使用git checkout --theirs
或git checkout --ours
分别获取您或您的Bob版本的文件,则无法解决文件,但clobber Git尝试将它们合并到工作树中。如果您想要恢复Git尝试合并它们,请使用git checkout --theirs
。请注意,所有这些都会覆盖文件的工作树版本,而只留下三个未解析的索引槽。
奇怪的是,如果您git checkout -m
从某个特定提交中获取旧版本的文件,将覆盖索引:它会复制提交内容。 s版本的文件索引槽零,破坏插槽1-3条目。该文件现在显示已解决!同样,您可以使用git checkout <commit-id> -- <path>
恢复未解决的状态。 (当然会覆盖工作树版本。)
同样,如果您使用git checkout -m
错误地解决了该文件尚未完全的问题,您可以git add
解决它 - 但是当然,这会覆盖工作树版本。如果您大部分时间都已完成解析,那么您也可以完成解析并重新git checkout -m
结果。这将使用从工作树中新复制的版本替换slot-zero索引条目:文件保持已解析,但为下一次提交准备的版本将更改为最新的git add
版本。
答案 1 :(得分:0)
我通常会选择后一个并且没有面对并发现它。
我认为这取决于你将要实现的目标。
如果您想明确提及您已准确解决冲突的文件,您可以执行第一个。否则你可以做另一个。
答案 2 :(得分:0)
“要提交的更改”列表中指定的文件已经在暂存区域中(也称为索引)。
Git add是用于将您的工作放入索引的内容,它告诉git您希望它们包含在提交中。
因此,已经上传的文件的git add将是多余的。
只有在两个列表中列出文件时才需要在“待提交”列表中添加其他文件(如果您暂存文件然后再修改它们,则有时会发生这种情况。)