如何告诉git从一个特定分支中获取所有冲突的文件?
将branch1合并到branch2时出现合并冲突。我可以说从branch1获取所有冲突的文件,而不是添加每个文件。
答案 0 :(得分:3)
你可以。
你可能不应该,但你可以。 (最多,您可能应使用-X ours
或-X theirs
。)
下面,在完成所有设置之后,确保阅读此答案的任何人都能理解正在进行的操作,这是简单的 - 尽管有时太简单的方式来选择"我们的& #34;或者"他们的"每个冲突的文件。
$ git status # make sure everything is clean
[git status is here]
$ git checkout branch2 # get onto branch2
[git's message about checking out the branch is here]
$ git merge branch1 # merge from branch1 into branch2, as in your text
... conflict messages ...
您现在有一些成功的合并和一些冲突。例如,公共合并库可能包含文件a.a
,b.b
,c.c
,依此类推,直到z.z
,因此,与通用合并相比基本提交,前三个(a.a
,b.b
和c.c
)在branch1
和后三个(b.b
,c.c
中进行了修改,d.d
)已在branch2
中修改。显然,对a.a
的更改没有冲突,对d.d
的更改没有冲突,但可能b.b
和c.c
冲突,在{{1}中进行了多次修复} branch1
和branch2中b.b
的一个不同修复,但两个修复重叠。 b.b
发生了类似的事情,导致那里的冲突。
您现在希望通过从c.c
(或{{1}获取文件版本来放弃在branch2
(或branch1
)中所做的所有修复 }} 分别)。您可以使用此命令:
branch1
或:
branch2
但这需要知道有问题的两个文件是git checkout branch1 -- b.b c.c
和git checkout branch2 -- b.b c.c
。
你也可以这样做:
b.b
或:
c.c
这些几乎与使用名称git checkout --ours -- b.b c.c
和git checkout --theirs -- b.b c.c
的{{1}}命令相同,但有两个很大的区别。我们稍后会谈到这些。
我在上面提到了 merge base ,没有定义它。知道合并基础是什么是一个好主意,这样你就知道Git在做什么,从而知道你正在做什么。有一个precise definition (which is also a bit complicated),但简单来说,当我们查看两个分支上的提交链时, merge base 是最近的共同祖先提交。也就是说,如果我们绘制(部分)提交图,我们通常会看到类似这样的内容:
git checkout
两个分支有两个不同的提示提交(分支名称指向)。这些提交指向他们的父提交,指向他们的父母,依此类推。也就是说,每个branch1
(表示提交图中的提交节点)都有一个指向其父提交的左箭头。 ( merge 提交有两个或更多这样的箭头;上面显示了一个简单的情况,其中没有合并。)最终这两个分支应该(通常)会合,并从那个点向左 - 向后及时 - 所有提交现在都在两个分支上。
最右边的此类提交,我将其绘制为branch2
而不是 o--o--o--o <-- branch1
/
...--o--*
\
o--o--o <-- branch2
,是合并基础。
在我们进入自动部分之前,我们需要看一下Git如何定义索引或临时区域。
索引基本上是#34; Git将在下一次提交中加入#34;。也就是说,它对于将在提交中的每个文件都有一个条目。当您刚刚提交时,索引为每个(跟踪的)文件都有一个条目,该条目与您刚刚提交的跟踪文件相匹配。无论如何,每次提交都有一个完整的工作树快照 - 所有跟踪文件 - 并且该快照是从索引中创建的,索引仍然与刚才的相同。
当您o
文件时,您将*
以获得一些新内容,然后o
将新 git add
内容放入索引中。现有的b.b
和git add b.b
等等都保持不变,但b.b
将替换为工作树版本。
(如果您a.a
是一个全新的文件,例如c.c
,则该新文件会进入索引,现在b.b
已被跟踪&#34;。如果您git add
一个文件,Git在索引中放入一个特殊的&#34; white-out&#34;条目,将文件标记为&#34;将被排除在下一个提交之外&#34;即使它&#34; #39;目前仍然被跟踪。在大多数情况下,你可以忽略这些小细节,只需将索引视为&#34;然而你可以做下一次提交&#34;。
在合并期间,索引会扮演一个新角色。
对于每个跟踪文件,索引中实际上有四个个插槽,而不仅仅是一个。通常Git只使用插槽零(0),这是普通的,非合并冲突的文件。当Git在合并期间遇到冲突时,它会将每个文件的多个副本放入索引,插槽1,2和/或3中,并将插槽0留空。
插槽1用于文件的合并基础版本。插槽2保存本地(当前或README
)分支版本,插槽3保存另一个(待合并或README
)版本。在上面的示例中,我们执行了git rm
,因此广告位2保存--ours
--theirs
版本,广告位3保存git checkout branch2
版branch2
(因为我们b.b
)。同时,插槽1保存合并基础版本,来自提交branch1
。
其中一些插槽可能为空:例如,如果在两个分支中添加了名为b.b
的文件,因此它没有合并基础版本,则插槽1将为空;将只有slot-2和slot-3条目。
git merge branch1
的{{1}}和*
标志告诉new.new
使用插槽2或插槽3.没有--ours
标志来提取版本1(合并基础版本),但它 可能得到它;有关--theirs
语法,请参阅the gitrevisions documentation。
你当然也可以使用分支名称,但这两种方式略有不同,正如我们稍后会看到的那样。
要查找未合并的文件,我们只需找到处于此未合并状态的任何路径。一种方法是使用git status
,但这有点棘手。一种更好的方法是使用所谓的&#34;管道命令&#34;,特别是git ls-files
。 git checkout
命令具有git checkout
以显示所有索引条目及其阶段编号。我们想知道哪些文件存在于阶段1,2和/或3中的任何一个:
--
产生如下输出:
:n:path
输出格式具有阶段编号(0到3),后跟文字选项卡,后跟文件名,因此我们要排除阶段0,收集阶段1-3名称。有很多方法可以做到这一点,例如ls-files
,但我们还需要拆分文件名并使其唯一(因为如果它在所有3个插槽中都会出现多次) )。我们可以使用--stage
一次完成所有操作:
git ls-files --stage
(这段代码仍然存在轻微缺陷,因为它的名称包含空格的文件是错误的。修复它留作练习。)
更复杂的awk(或其他语言)脚本会检查每个路径实际上有三个版本,然后执行某些操作 - 这取决于您的目标 - 对于其他情况。
请注意,[mass snip]
100755 2af1beec5f6f330072b3739e3c8b855f988173a9 0 t/t6020-merge-df.sh
100755 213deecab1e81685589f9041216b7044243acff3 0 t/t6021-merge-criss-cross.sh
100755 05ebba7afa29977196370d178af728ec1d0d9a81 0 t/t6022-merge-rename.sh
[more mass snip]
有一个标记grep -v $'0\t'
或awk
,以显示仅未合并的文件。输出仍然是git ls-files --stage | awk '$3 != 0 { path[$4]++; } END { for (i in path) print i }'
格式(实际上git ls-files
强制-u
,就像the documentation所说的那样)。我们可以使用它而不是检查stage-number。
我们有一个未合并文件列表后,我们只需在其上运行--unmerged
或--stage
:
--unmerged
在此之后,我们仍然需要将它们标记为已解决,如果我们使用--stage
。
git checkout --ours --
等我们上面说git checkout --theirs --
。然后,我们必须git ls-files --unmerged | \
awk '{ path[$4]++; } END { for (i in path) print i }' |
xargs git checkout --ours --
标记已解决的文件,即通过将--ours
的工作树版本放入stage-slot-zero来清除阶段1-3。
如果我们--ours
,我们不必须git checkout --ours -- b.b
。
你可能想知道为什么。我当然这样做。我可以告诉你,这是因为git add b.b
将b.b
的版本从git checkout branch2 -- b.b
指向的提交复制到stage-slot-zero,清除插槽1-3,然后从进入工作树;但git add b.b
将git checkout branch2 -- b.b
的版本从stage-slot-3(b.b
)复制到工作树中。
也就是说,branch2
有时将复制到索引然后将其复制到工作树中,有时只需将索引槽中的副本复制到工作中-树。我无法解释的是为什么 git checkout --ours -- b.b
有时必须首先写入索引,有时不会写入,而不是&#34;编写代码后编写代码更容易一堆其他代码。&#34;换句话说,这里没有明显的主设计原则。你可以创建一个:&#34;如果要从树中读取b.b
,它应该写入索引,写入slot-zero并清除1-3;但如果它只能从索引中读取,则不应写入索引。&#34;但这似乎很随意。
--ours
等当Git准备合并并设置所有这些索引槽时,它也会重命名检测。假设我们git checkout
,git checkout
和checkout
代替文件--ours
到a.a
。假设z.z
a.a
成为sillyname.b
,c.c
成为sillyname
。
Git通常会自动检测此重命名。然后,它将使用在该特定提交中命名的文件的内容填充插槽1,2和3。由于我们位于b.b
且该文件现在名为branch1
,因此索引名称将为bettername.b
。插槽1中的条目将是提交branch2
&#39; s branch2
的条目。插槽2中的条目(我们的版本)将适用于我们的bettername.b
。插槽3中的条目是另一个(bettername.b
)版本,将用于*
。
同样,它们都是我们的新名称,因此我们需要sillyname.b
或bettername.b
。如果我们尝试--theirs
,Git会抱怨它在b.b
中找不到git checkout --ours -- bettername.b
。实际上,它不能:它在那里被称为git checkout --theirs -- bettername.b
。
有时您可能不关心,或者可能想要发现这种重命名检测。在这种情况下,您可能希望使用分支名称,而不是git checkout branch1 -- bettername.b
或bettername.b
参数。你只需要意识到差异。
branch1
和b.b
不同(可能是您想要的)我在上面提到了--ours
中--theirs
有多个修复程序的情况,-X ours
中只有一个修复程序(与-X theirs
中的修补程序冲突)。< / p>
冲突可能只发生在一个地区。也就是说,假设有两个较小的拼写错误与更重要的修复无关。如果我们使用b.b
版branch1
,我们将失去两个拼写修复程序,即使我们采用了高级代码修复程序。
如果我们告诉 merge 命令使用branch1
,那说明在发生冲突的情况下,请使用我们的更改,但是如果对{{1}进行了一些更改我们没有做任何事情,继续进行他们的更改。在这种情况下,我们将提取他们的两个拼写修复。
因此,branch2
与以后的--ours
非常不同。
如果你想重新合并冲突 - 如果你解决了一个文件,但又有了第二个想法 - b.b
还有另一个标志-X ours
,那就是这样做:
b.b
这&#34;重新合并&#34;该文件,将冲突标记放回。(它实际上使用了一组特殊的&#34; undo&#34;索引条目,因为合并基础版本可能是虚拟合并基础而不是单个提交。它只能在提交合并之前使用;之后,您必须重新执行整个合并。)
答案 1 :(得分:0)
由-s/--strategy
git merge
命令选项指定的合并策略可以告诉git应该使用哪个分支。您还可以看到here