Git 合并冲突:添加解决后要提交的更改?
Git Merge Conflict: Add Changes to be committed when resolved?
我经常遇到以下Merge Conflict问题并解决它,但我想问一下我的解决方案是否正确。就个人而言,鉴于功能测试已通过,我没有看到任何问题,但如果能对此有深入了解会很好。
所以,假设我们有两个分支:
develop
feature
我正在更改 feature
分支中的代码,然后创建一个 PR 以合并到 develop
。但是,Github
或 Bitbucket
的 diff 工具表明我们遇到了合并冲突;编码中很常见的情况。
然后我采取以下步骤:
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:
部分中包含的文件?
我通常会做第一个,但如果能再考虑一下就好了。
我经常做后一种,但没有遇到过问题。
我认为这取决于您要实现的目标。
如果您想明确提及您在哪些文件中完全解决了冲突,您可以执行第一个。否则你可以做另一个。
"changes to be committed" 列表中命名的文件已经在暂存区(也称为索引)中。
Git add 是用来将您的工作放入索引的,它告诉 git 您希望将它们包含在提交中。
因此,git 添加已经暂存的文件将是多余的。
仅当文件在两个列表中都列出时才需要在 "to be committed" 列表中额外添加文件(如果暂存文件然后再修改它们有时会发生这种情况)。
TL;DR:您不需要重新添加 "changes to be committed".
中的文件
Git的"index"
Git 有一个叫做 index 或 staging area 的东西,有时甚至是 缓存(如git diff --cached
)。它没有得到很好的解释,部分原因是它很复杂。幸运的是,索引的 use 非常简单,除了(唉)在合并期间。 :-)
索引中的内容最好描述为 您将进行的下一次提交。还有助于记住 Git 始终具有 当前commit,命名为 HEAD
,以及(--bare
存储库除外),一个 work-tree 或 working tree,这是您准备好文件以供查看、编译、编辑以及您对文件执行的任何其他操作的地方。
最初,您的工作树、索引和 HEAD
提交全部匹配(当您第一次克隆存储库时)。您不能直接在索引中触摸文件,因此您可以在工作树中进行任何更改,甚至创建新文件,您可以在工作树中完成所有工作。然后,要将修改后的文件放回索引中,您需要在路径上 运行 git add
,以便 Git 将修改后的文件复制到索引中。要将新文件放入索引,您还要在路径上 运行 git add
。
如果您出于某种原因感到非常兴奋并且现在 运行 git commit
,Git 会读取您当前的索引并将其打包为新的提交。新的提交将作为其工作树快照,索引中的每个文件现在处于索引中的状态。
如果那个状态 匹配 HEAD
提交,git commit
只是说: "Huhwha? Why you try to make new commit now?" 所以大概你已经改变了索引中的一些东西至此。 git status
将向您显示索引中 与 HEAD
提交 不同的内容:这些是 "changes to be committed".
正在合并
运行 git merge
有时会泄露索引的秘密。 in 索引中的每个条目实际上有四个 slots,它们已编号。通常,您只会看到为 "normal" 文件保留的插槽零,准备提交。
合并旨在合并 两组(或有时更多,但我们坚持使用两组)更改。例如,也许您和 Bob 从相同的文件开始。然后您对某些文件进行了一些更改,并且 git add
-ed 并提交了源的新快照。与此同时,Bob 对一些文件进行了一些更改——甚至可能是相同的文件——并且还 git add
编辑并提交了,现在是时候合并你的更改和 Bob 的更改了。
要执行合并——将某些文件合并为动词——Git 首先找到 合并基础,即你和鲍勃是同步的。然后 Git 运行 两个 git diff
:一个从那个合并基到 你的 最新提交,一个从同一个合并基到 Bob 的最新提交。这显示 "what you changed" 和 "what Bob changed".
Git 然后尽最大努力 合并 这些更改(包括,在这种情况下,找出并遵循文件重命名,并找出如果只有 一个 重命名了一些文件——但大多数合并冲突不包括文件重命名,除非你写了很多 Java 代码...)。但是,有时 Git 无法自行完成此操作。在这种特殊情况下,Git 将合并工作交还给您,此时您会看到索引中的所有 slots。
索引槽 1 是 Git 存储文件的 base 版本的地方。那是你们俩开始的通用版本。
索引槽 2 是文件的 HEAD
或 --ours
版本。 (有时也称为 "local" 版本。)
索引槽 3 是文件的另一个或 --theirs
(或有时 "remote")版本。
这几乎就是它的全部内容:三个插槽包含三个版本,最后是 "unmerged paths"。您的工作树中的文件包含 Git 最初的合并工作,以及冲突标记和 --ours
和 --theirs
版本的一些混搭。如果将 merge.conflictstyle
设置为 diff3
,Git 也将包括文件的 base 版本的部分,存储在索引槽 1 中。它是现在由您来解决合并问题。
找到正确的解决方案并更新文件的工作树版本后,您必须 git add
路径。这会将工作树版本存储到索引槽零中,从而清除槽 1-3 中的三个条目。由于插槽 0 包含正常的待提交版本,因此文件现在已准备好提交。
对于根本没有任何变化的文件,或只是你们中的一个人改变了一些东西,或其中Git认为它正确地结合了您的更改和 Bob 的更改,那些 文件已经在工作树和索引(的槽零)中更新。如果结果与当前 HEAD
提交不同,git status
将文件显示为 "changes to be committed".
最后几位
我在上面说过,这就是几乎的全部内容。关于索引槽的最后几件事是:
有些位置可能是空的。假设您 和 Bob 都添加了一个文件。在这种情况下,您将得到 "add/add conflict"。什么版本的文件是通用基础版本?当然还有 none:你们都添加了一个 new 文件。所以在这种情况下,基本插槽(插槽 1)是空的。
如果您删除了一个文件而 Bob 更改了它,也会发生同样的情况。在这种情况下,--ours
插槽,插槽 2 是空的,而插槽 1 和 3 包含基本版本和 Bob 的版本。由您决定是保留 Bob 的版本、删除文件还是使用第三个变体,但再次只填充了三个插槽中的两个。
如果你们中的一个或两个重命名一个文件,则三个位置中的文件版本可能来自具有不同名称的文件 在较早的提交中。例如,您的输出显示:
renamed: app/OldName.php -> app/NewName.php
这里没有冲突,但如果有的话,至少有一个 app/NewName.php
的插槽会被其他提交版本的 app/OldName.php
填充。
这个主要是你去看旧版的时候。如果在单独的克隆或工作树中,您签出其中一个文件尚未重命名的提交——或者如果您使用 git show <commit>:app/OldName.php
查看文件而不签出它——您必须使用 old 旧名称所在的名称。但是,如果您使用 git checkout --ours
或 git checkout --theirs
将这两个版本中的一个提取到工作树中,则必须使用 new 名称,因为索引现在以新名称存储文件。
如果您做使用git checkout --ours
或git checkout --theirs
分别获取您的或Bob的文件版本,这确实不解析文件,但破坏 Git 试图将它们合并到工作树中。如果您想恢复 Git 合并它们的尝试,请使用 git checkout -m
。请注意,所有这些都会覆盖文件的工作树版本,同时单独保留三个未解析的索引槽。
但是,奇怪的是,如果您 git checkout <commit-id> -- <path>
从某个特定的提交中获取文件的旧版本,那么 会覆盖 索引:它将文件的提交版本复制到索引槽零,破坏槽 1-3 条目。该文件现在似乎已解决!同样,您可以使用 git checkout -m
恢复未解决的状态。 (当然,这会覆盖工作树版本。)
同样,如果你错误地用git add
解析文件,但还没有完全,你可以git checkout -m
取消解析它——当然,这会覆盖工作树版本。如果你已经大部分解决了,你还不如完成解决并重新git add
结果。这用从工作树中新复制的版本替换了零槽索引条目:文件保持解析状态,但准备好下一次提交的版本更改为最新的 git add
-ed 版本。
我经常遇到以下Merge Conflict问题并解决它,但我想问一下我的解决方案是否正确。就个人而言,鉴于功能测试已通过,我没有看到任何问题,但如果能对此有深入了解会很好。
所以,假设我们有两个分支:
develop
feature
我正在更改 feature
分支中的代码,然后创建一个 PR 以合并到 develop
。但是,Github
或 Bitbucket
的 diff 工具表明我们遇到了合并冲突;编码中很常见的情况。
然后我采取以下步骤:
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:
部分中包含的文件?
我通常会做第一个,但如果能再考虑一下就好了。
我经常做后一种,但没有遇到过问题。
我认为这取决于您要实现的目标。
如果您想明确提及您在哪些文件中完全解决了冲突,您可以执行第一个。否则你可以做另一个。
"changes to be committed" 列表中命名的文件已经在暂存区(也称为索引)中。
Git add 是用来将您的工作放入索引的,它告诉 git 您希望将它们包含在提交中。
因此,git 添加已经暂存的文件将是多余的。
仅当文件在两个列表中都列出时才需要在 "to be committed" 列表中额外添加文件(如果暂存文件然后再修改它们有时会发生这种情况)。
TL;DR:您不需要重新添加 "changes to be committed".
中的文件Git的"index"
Git 有一个叫做 index 或 staging area 的东西,有时甚至是 缓存(如git diff --cached
)。它没有得到很好的解释,部分原因是它很复杂。幸运的是,索引的 use 非常简单,除了(唉)在合并期间。 :-)
索引中的内容最好描述为 您将进行的下一次提交。还有助于记住 Git 始终具有 当前commit,命名为 HEAD
,以及(--bare
存储库除外),一个 work-tree 或 working tree,这是您准备好文件以供查看、编译、编辑以及您对文件执行的任何其他操作的地方。
最初,您的工作树、索引和 HEAD
提交全部匹配(当您第一次克隆存储库时)。您不能直接在索引中触摸文件,因此您可以在工作树中进行任何更改,甚至创建新文件,您可以在工作树中完成所有工作。然后,要将修改后的文件放回索引中,您需要在路径上 运行 git add
,以便 Git 将修改后的文件复制到索引中。要将新文件放入索引,您还要在路径上 运行 git add
。
如果您出于某种原因感到非常兴奋并且现在 运行 git commit
,Git 会读取您当前的索引并将其打包为新的提交。新的提交将作为其工作树快照,索引中的每个文件现在处于索引中的状态。
如果那个状态 匹配 HEAD
提交,git commit
只是说: "Huhwha? Why you try to make new commit now?" 所以大概你已经改变了索引中的一些东西至此。 git status
将向您显示索引中 与 HEAD
提交 不同的内容:这些是 "changes to be committed".
正在合并
运行 git merge
有时会泄露索引的秘密。 in 索引中的每个条目实际上有四个 slots,它们已编号。通常,您只会看到为 "normal" 文件保留的插槽零,准备提交。
合并旨在合并 两组(或有时更多,但我们坚持使用两组)更改。例如,也许您和 Bob 从相同的文件开始。然后您对某些文件进行了一些更改,并且 git add
-ed 并提交了源的新快照。与此同时,Bob 对一些文件进行了一些更改——甚至可能是相同的文件——并且还 git add
编辑并提交了,现在是时候合并你的更改和 Bob 的更改了。
要执行合并——将某些文件合并为动词——Git 首先找到 合并基础,即你和鲍勃是同步的。然后 Git 运行 两个 git diff
:一个从那个合并基到 你的 最新提交,一个从同一个合并基到 Bob 的最新提交。这显示 "what you changed" 和 "what Bob changed".
Git 然后尽最大努力 合并 这些更改(包括,在这种情况下,找出并遵循文件重命名,并找出如果只有 一个 重命名了一些文件——但大多数合并冲突不包括文件重命名,除非你写了很多 Java 代码...)。但是,有时 Git 无法自行完成此操作。在这种特殊情况下,Git 将合并工作交还给您,此时您会看到索引中的所有 slots。
索引槽 1 是 Git 存储文件的 base 版本的地方。那是你们俩开始的通用版本。
索引槽 2 是文件的 HEAD
或 --ours
版本。 (有时也称为 "local" 版本。)
索引槽 3 是文件的另一个或 --theirs
(或有时 "remote")版本。
这几乎就是它的全部内容:三个插槽包含三个版本,最后是 "unmerged paths"。您的工作树中的文件包含 Git 最初的合并工作,以及冲突标记和 --ours
和 --theirs
版本的一些混搭。如果将 merge.conflictstyle
设置为 diff3
,Git 也将包括文件的 base 版本的部分,存储在索引槽 1 中。它是现在由您来解决合并问题。
找到正确的解决方案并更新文件的工作树版本后,您必须 git add
路径。这会将工作树版本存储到索引槽零中,从而清除槽 1-3 中的三个条目。由于插槽 0 包含正常的待提交版本,因此文件现在已准备好提交。
对于根本没有任何变化的文件,或只是你们中的一个人改变了一些东西,或其中Git认为它正确地结合了您的更改和 Bob 的更改,那些 文件已经在工作树和索引(的槽零)中更新。如果结果与当前 HEAD
提交不同,git status
将文件显示为 "changes to be committed".
最后几位
我在上面说过,这就是几乎的全部内容。关于索引槽的最后几件事是:
有些位置可能是空的。假设您 和 Bob 都添加了一个文件。在这种情况下,您将得到 "add/add conflict"。什么版本的文件是通用基础版本?当然还有 none:你们都添加了一个 new 文件。所以在这种情况下,基本插槽(插槽 1)是空的。
如果您删除了一个文件而 Bob 更改了它,也会发生同样的情况。在这种情况下,
--ours
插槽,插槽 2 是空的,而插槽 1 和 3 包含基本版本和 Bob 的版本。由您决定是保留 Bob 的版本、删除文件还是使用第三个变体,但再次只填充了三个插槽中的两个。如果你们中的一个或两个重命名一个文件,则三个位置中的文件版本可能来自具有不同名称的文件 在较早的提交中。例如,您的输出显示:
renamed: app/OldName.php -> app/NewName.php
这里没有冲突,但如果有的话,至少有一个
app/NewName.php
的插槽会被其他提交版本的app/OldName.php
填充。这个主要是你去看旧版的时候。如果在单独的克隆或工作树中,您签出其中一个文件尚未重命名的提交——或者如果您使用
git show <commit>:app/OldName.php
查看文件而不签出它——您必须使用 old 旧名称所在的名称。但是,如果您使用git checkout --ours
或git checkout --theirs
将这两个版本中的一个提取到工作树中,则必须使用 new 名称,因为索引现在以新名称存储文件。如果您做使用
git checkout --ours
或git checkout --theirs
分别获取您的或Bob的文件版本,这确实不解析文件,但破坏 Git 试图将它们合并到工作树中。如果您想恢复 Git 合并它们的尝试,请使用git checkout -m
。请注意,所有这些都会覆盖文件的工作树版本,同时单独保留三个未解析的索引槽。但是,奇怪的是,如果您
git checkout <commit-id> -- <path>
从某个特定的提交中获取文件的旧版本,那么 会覆盖 索引:它将文件的提交版本复制到索引槽零,破坏槽 1-3 条目。该文件现在似乎已解决!同样,您可以使用git checkout -m
恢复未解决的状态。 (当然,这会覆盖工作树版本。)同样,如果你错误地用
git add
解析文件,但还没有完全,你可以git checkout -m
取消解析它——当然,这会覆盖工作树版本。如果你已经大部分解决了,你还不如完成解决并重新git add
结果。这用从工作树中新复制的版本替换了零槽索引条目:文件保持解析状态,但准备好下一次提交的版本更改为最新的git add
-ed 版本。