在git rebase解决冲突时如何从一个分支中选择文件?

19
给定一个包含两个分支 masterfeature 的 git repo。当使用 rebase master 将 feature 分支合并到 master 分支时,假设文件 a.txt 存在冲突,需要在继续 rebase 之前解决它。
我知道可以通过三个步骤来解决冲突:
  1. 手动打开编辑器,解决冲突
  2. 调用 git add a.txt 告诉 git 已经解决了冲突
  3. 调用 git rebase --continue 继续 rebase
有没有一种方法可以避免第一步,告诉 git 我想要从 master 分支获取文件的版本或者从 feature 分支获取文件的版本,而不需要执行上面的第一步和第二步。
3个回答

17

是的,事实上有不止一种方法可以做到。

rebase和merge(以及cherry-pick)命令都采用相同的strategy-X标志传递给底层的git合并机制。对于recursive策略,在出现在合并的分支中都修改了的文件的情况下,-Xours-Xtheirs选择一个或另一个“方面”。

或者当合并停止并发生冲突时,您可以使用带有--ours--theirs标志的git checkout,从其中一个侧面选择版本。(您也可以使用其他命令来执行此操作;此处,我将坚持使用--ours--theirs作为与使用合并机制的命令匹配的参数。)

这当然是不同的,因为您可以更改选择:

$ git checkout main
Switched to branch 'main'
$ git merge branch
... conflicts in files A and B ...
$ git checkout --ours -- A    # takes main:A
$ git checkout --theirs -- B  # takes branch:B

注意这与“我们的策略”(上面展示了使用ours选项的recursive策略)非常不同。使用“我们的策略”,会发生完全不同的事情。让我们先不使用它,再次进行相同的合并:

$ git checkout main && git merge branch
... conflicts ...
$ git checkout --ours -- A B  # take main:A and main:B
假设有第三个文件 C,Git可以自动合并它。当你执行上述操作时,Git会将 C 合并并保留 main:Amain:B。但是如果你使用 git merge --strategy=ours branch,Git会选择 main:Amain:Bmain:C,而且会放弃 branch:C 的更改而不是自动合并它们。
上面使用了 git merge 是因为它使得 "ours" 和 "theirs" 工作正确。然而我不喜欢 Git 给它们起的这些名字,因为在进行变基时,我们的版本和他们的版本会交换,因为变基是通过切换到 “另一个” 分支并执行一系列挑选操作来完成的。
$ git checkout mine; git rebase theirs

通过进行(非常)粗略的等价物来在下方工作:

$ git checkout theirs; git cherry-pick theirs..mine

然后,重新调整分支标签的位置,以便分支theirs实际上不会移动。(在内部情况并不像这么糟糕:-),但它确实成功地使--ours意味着 "their",而--theirs则意味着"our",这是相当糟糕的外部表现)。


1
感谢您解释为什么rebase会交换ours和theirs标签。 - ams
@torek,我一直很喜欢你的回答。问题:git merge --strategy=ours 是否等同于 git merge --ours --file_with_conflict1 --file_with_conflict2 ... --file_with_conflict_n,其中 'file_with_conflict1',... 'file_with_conflict_n' 是所有具有合并冲突的文件? - Diana Vazquez Romo
@Diana:git merge 不支持 --ours。你是不是想用 git merge-file?(它的用法不同,但确实有 --ours 选项。) - torek
@torek 哎呀!抱歉,让我在这里重新表达我的问题:a. git merge --strategy=oursb. git merge 然后 git checkout --ours --file_with_conflict1 --file_with_conflict2 ... --file_with_conflict_n,其中 'file_with_conflict1',... 'file_with_conflict_n' 是所有具有合并冲突的文件?(a) 和 (b) 是等价的吗? - Diana Vazquez Romo
1
@Diana:啊。不,因为没有 -s oursgit merge 会尝试合并每个文件。其中一些将成功合并,因此结合了工作。其他文件将产生冲突;对于那些文件的 git checkout --ours 将放弃他们的工作,只留下我们的工作。所以我们将得到一个混合:一些文件仅来自我们的工作(我们更改了文件而他们没有更改,或者存在冲突),一些仅来自他们的工作(他们更改了文件而我们没有更改),还有一些合并的文件(合并没有冲突)。但是带有 -s ours,Git 将忽略他们的工作。 - torek

9

您可以使用:

git checkout --ours -- path/to/file

或者:

git checkout --theirs -- path/to/file

在合并或变基期间选择冲突文件的特定版本;由于变基有点奇怪,因此在这种情况下使用--theirs会选择feature版本,而--ours会选择master版本。

实际上,你不需要使用git add,因为checkout会通过索引写入,将我们或它们的版本复制到索引中的“slot 0”,然后再写入工作树。(执行git add也是无害的。) - torek
哦,真的吗?我还以为我也得把它暂存起来,才能将其作为解决方案进行暂存。我会更新答案的,谢谢! - Ash Wilson

1
我相信你所需要的是能够消除所有三个步骤的方法,即: git rebase master -X theirs 这将自动解决冲突并将优先权赋予于 feature(当前正在检出的分支),或者 git rebase master -X ours 在rebase中,ours和theirs的意义与预期相反,如选项描述中所述。详情请参见http://git-scm.com/docs/git-rebase

1
如果有两个冲突的文件a.txt和b.txt,我可能想从主分支选择a.txt,而b.txt则需要手动合并。您建议的命令是否适用于所有冲突文件? - ams
在这种情况下,Ash Wilson的答案是最好的选择 :) - GreenAsJade

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接