如何仅接受"他们的"分支中特定目录的git合并冲突?

5
这是我的情况:git merge master 导致出现50个带有合并冲突的文件。我想接受其中45个来自master的文件,并完成合并,我想要手动解决剩余的5个文件中的冲突。这45个文件都在some/dir目录中。而其他5个文件则散布在其他地方。如果我只想接受所有50个冲突的文件中来自master的更改,我会执行以下命令:
git merge -X theirs master
# OR (same thing):
git merge --strategy-option theirs master

但是我不想要那样,正如我所说的。我希望得到更像这样的东西:

git merge -X theirs master some/dir

使 Git 在 some/dir 中的所有冲突情况下自动选择“theirs”(主分支)方案,同时允许手动修复冲突。然而,这个功能并不存在。

因此,我的解决方法如下:

简要说明:

开始合并,仅手动修复我需要的少量文件,而不是所有冲突的文件。备份我刚刚修复过的任何文件。中止合并。使用 git merge -X theirs master 重新执行合并以自动接受所有文件中的所有冲突的 master 更改。手动将我修复的几个文件复制回代码库,并提交它们。

这避免了手动修复50个文件,当只有5个文件确实需要注意时,这可能非常乏味和耗时。

详细步骤:

  1. Start a normal merge:

     git merge master
    
  2. Manually resolve conflicts in just the 5 files I want to do that in, and save each file. Then, MAKE COPIES OF THEM to a location outside of the repo (or at least in a folder that is ignored by the repo). I now have copies of those 5 files I manually resolved. Here is one way to accomplish this:

     mkdir -p temp  # create `temp` dir inside the repo.
    
     # Note: if you're not aware, the ".git/info/exclude" file in your repo is
     # an **untracked** form of a ".gitignore" file for the repo. We will use 
     # this file below.
    
     # Add a `/temp/` entry to the ".git/info/exclude" file to keep
     # the repo from tracking the /temp/ dir, but withOUT using the
     # shared ".gitignore" file since this is my own personal setting
     # not everyone else on the team necessarily wants.
     echo -e "\n# don't track this temporary folder for my arbitrary use\n/temp/" \
     >> .git/info/exclude
    
     # Now manually make copies of your 5 files you just resolved conflicts in:
     cp some/dir/file1.cpp temp
     cp some/dir/file2.cpp temp
     cp some/dir/file3.cpp temp
     cp some/dir/file4.cpp temp
     cp some/dir/file5.cpp temp
    
  3. Do git merge --abort to abort the merge, then do git merge -X theirs master to redo the merge, except this time automatically accepting all of master's changes for all 50 files (ie: for all conflicts).

  4. Now, manually copy my manually-resolved backup copies from above back into the repo on top of their respective files:

     cp temp/file1.cpp some/dir
     cp temp/file2.cpp some/dir
     cp temp/file3.cpp some/dir
     cp temp/file4.cpp some/dir
     cp temp/file5.cpp some/dir
    
  5. Finally, do git add -A and git commit --amend to amend them to the merge commit, and voila! I have the equivalent of an automatic resolution on the 45 files, and the manual resolution on the 5 files.

有更好的方法吗?

我认为这种方式相当有效,并且效果很好,但我愿意学习其他替代方案,特别是如果它们更快或更容易。

更新这里是答案目前最受欢迎的答案实际上是不正确的,在某些非常重要的情况下会产生错误的行为,尽管它是最受欢迎的。

相关但不是重复问题:

  1. 合并两个分支,如何接受一个分支的所有冲突
  2. 使用git接受"theirs"或"mine"的简单工具

我认为你可能正在寻找这个答案,从例如git ls-files -u获取剩余的未合并文件,在每个文件上就地运行脚本解析器,然后添加结果。 - jthill
3个回答

7

在解决冲突时,您可以直接从其他分支检出文件。

所以,在执行git merge并解决需要修复的文件的冲突后,执行git checkout <branch you're merging> -- some/dir,这将仅移动其他分支中更改的文件,并使它们准备好提交。我相信这也会使它们准备好提交。

如果有来自ours分支的更改,您只需在--之后列出文件而不是检出整个目录即可。


请参阅我的答案中的合并冲突解决示例部分:根据Git,“我们”和“他们”是谁? - Gabriel Staples
1
顺便说一下,这个很好用。我刚测试过了!我从来不知道你可以像 some/dir 这样传递目录给 git。我一直以为你必须逐个指定文件。 - Gabriel Staples
1
@GabrielStaples 目录的一个限制是它们不能是空的。Git 只跟踪文件,因此空目录会导致问题。 - Schleis
Schleis,结果你的答案并不正确,很遗憾。我已经在我的新回答中详细说明了问题和正确的解决方案。 - Gabriel Staples

3

在6个月前我给这个答案点了赞并标为正确,但今天我发现在一次非常糟糕的合并过程中,需要用到这个答案的时候它是错误的。假设你想要使用以下命令把最新的更改从master更新到你的feature_branch:

git checkout feature_branch
git merge master
# conflicts result...

...而且还有很多冲突。您会看到其中大约有25个是在 some/dir 目录内部的,您想要从 feature_branch 保留所有冲突的更改,所以您可以执行以下三个命令中的任何一个(在这种情况下都是相同的):

git checkout -- some/dir
# OR
git checkout HEAD -- some/dir
# OR
git checkout feature_branch -- some/dir

好的,你搞砸了!mastergit merge中的--theirs一侧)也在some/dir中添加了一些变更和新文件,其中许多与你的feature_branch变更(git merge中的--ours一侧)没有任何冲突,并且你希望它们都存在(这就是将最新的master合并到feature_branch的全部意义!),但现在它们全部消失了,因为你刚刚用来自feature_branchsome/dir覆盖了它们。这不是我们想要的结果!

因此,这里是正确的答案:

# RIGHT ANSWER
# NB: you must be **in the middle of resolving conflicts in a `git merge`**
# in order for this to have the behavior I describe here. 

# **Keep conflicting changes** for all conflicts within files inside
# some/dir, from the "--ours" (`feature_branch` in this case) side.
# This does NOT delete files added by the `master` (`--theirs`) side which 
# have **no conflicts** with your `feature_branch` (`--ours`) side. 
git checkout --ours -- some/dir
git add some/dir
git merge --continue

再次强调,这与下面这个不是同一件事情:

# WRONG ANSWER

# **Overwrite this entire `some/dir` directory** with everything from
# the `feature_branch` (`--ours`) side. 
# This DOES delete files added by the `master` (`--theirs`) side which 
# have **no conflicts** with your `feature_branch` (`--ours`) side,
# which is NOT what we want.
git checkout feature_branch -- some/dir
git add some/dir
git merge --continue

所以,这里是完整的正确答案:

# merge latest master into feature_branch to get those upstream changes
# from other people into your feature_branch
git fetch origin master:master
git checkout feature_branch
git merge master 

# conflicts result here...

# Keep all changes from `feature_branch` for conflicts in some/dir
git checkout --ours -- some/dir
# OR, keep all changes from `master` for conflicts in some/dir
git checkout --theirs -- some/dir

git add some/dir
git merge --continue

完成!

有关详细信息和清晰度,请参见我的答案:根据Git,"us"和"them"分别指谁?。这在该答案的“警告”部分中有所涵盖,此答案是更全面的例子,并更好地显示了问题。


2
@onlinespending,看到你对我的回答进行的评论和踩票,我认为你误解了我的问题和我的回答。我的回答实际上是正确的,而目前得到最多赞的回答实际上是错误的。我的问题陈述如下:“如何仅针对某个目录从“他们”的分支接受git合并冲突?”--意思是:合并除了某个目录以外的所有内容,对于该目录,我们只接受其中一方(--ours--theirs)的内容。我的回答正好展示了这一点:合并除了some/dir目录的内容以外的所有内容,对于该目录,我们只接受其中一方(--ours--theirs)的内容。 - Gabriel Staples
1
@onlinespending,你的意思是什么?我不想要三方合并。我想将最新的master合并到我的feature_branch中,对于除了some/dir中的所有文件,都进行双方合并,而在冲突的情况下,我只想保留一方的some/dir。你是在尝试解决不同的问题吗? - Gabriel Staples
1
不,我只是认为您在这个目录中没有文件具有两个分支之间的非冲突更改(再次提醒,是在某个/目录下的一个文件)。如果有的话,您就会明白我的意思。我怀疑您只是在某个/目录下有一些有冲突的文件和一些没有冲突的文件。但是,在某个/目录下的单个文件中,两个分支之间没有不同的更改。 - onlinespending
我已经尝试过在合并的中间和外部进行操作。在完成合并后,冲突仍然存在。我确实看到了在“我们”的文件版本中添加的非冲突行。但是一旦发出“git checkout --theirs -- somefile”命令,它就会被破坏。 - onlinespending
尝试一下。在branch1中添加并提交一个带有几行内容的文件。切换到branch2并向该文件添加新行(非冲突性更改)并编辑现有行。回到branch1并编辑相同的现有行(冲突性更改)。返回branch2并“git合并branch1”。你会自然地看到一个冲突。合并后的文件将显示非冲突性添加的行以及两个分支中都编辑的那一行之间的冲突。现在发出“git checkout --theirs -- thisfile”命令。它将选择theirs并销毁那个非冲突性添加的行。这不是真正的三方合并。 - onlinespending
显示剩余10条评论

0

我认为@Schleis的答案很好。如果你的repo已经使用了压缩合并,另一种选择是……在你开始合并之前,只需要执行:

git checkout gabriel.staples/MyBranch
git checkout develop path/You/Want/To/Accept/Everything
git add path/You/Want/To/Accept/Everything
git commit -m "Partially merge develop at SHA XXXXX (squash-style)"
git merge develop
<resolve conflicts>
git add path/To/Your/Hand/Resolved/Files
git merge --continue

如果你真的想要,你也可以使用以下方法将上述内容更接近于真正的合并(给它一个父级):

git commit-tree aboveMergeSHA^{tree} -p `git merge-base develop pseudoMergeSHA` -p develop -m "Merge develop at SHA XXXXX"

但是在那个时候,我认为你最好使用@Schleis的答案。如果不必要,我不喜欢手动操作父级元素...


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