如何在冲突中将文件分为我们的和他们的



I有一种方法可以获得文件的两个副本,第一个副本具有远程内容,第二个副本具有本地内容

我认为没有git选项可以将两个版本的文件保存在两个副本中。

但您可以通过以下命令轻松实现此目标:

git merge the_branch
git checkout --theirs -- path/to/file ; mv path/to/file path/to/file.theirs
git checkout --ours -- path/to/file ; mv path/to/file path/to/file.ours
git checkout -m -- path/to/file

最后你有三个文件:

  • file.theirs及其版本
  • file.ours,与您的版本
  • file,具有版本和冲突标记

TL;DR

考虑使用git mergetool(尽管我自己从来没有这样做过)或git show(我有时会这样做)。还可以考虑将merge.conflictStyle设置为diff3,这将使Git写入冲突的工作树副本、基础版本以及左侧和右侧版本。我发现,打开这个选项后,我几乎从不需要来查看这两个输入。(但只有几乎从不。)

长(ish)

事实上,文件已经存在于所有三个版本中,但它们都在索引中,而不是在工作树中。因此,诀窍是将它们从索引中取出,在索引中,它们采用的是仅限Git的特殊格式,放入您的工作树中,在那里您可以查看并使用它们。

让我们简要而详细地看一下git merge在发生冲突时是如何工作的。要想发生冲突,我们必须做这样的事情:

$ git checkout ours
Switched to branch 'ours'
$ git merge theirs

Git会查看提交图,发现ourstheirs已经分叉,但有一个共同的合并基础提交:1

o--...--L   <-- ours (HEAD)
/
...--o--B

o--...--R   <-- theirs

提交L左侧本地,或--ours提交。提交R右侧远程或者--theirs提交。提交B这里是合并基础。然后,Git实际上执行了两个git diff命令,其中一个是为了找出自合并基础以来我们发生了什么变化:

git diff --find-renames <hash-of-B> <hash-of-L>   # what we changed
git diff --find-renames <hash-of-B> <hash-of-R>   # what they changed

然后,合并尝试组合这两组更改,使用与提交B相关的内容作为组合更改的基础。然而,我们更改了一些文件,他们更改了同一个文件,我们的更改与他们的更改发生了冲突,因此我们发生了合并冲突。

在这一点上,Git所做的是将文件的所有三个副本放入索引中,位于非零的暂存槽编号:

  • 阶段1包含合并基:文件B:P,其中B是基提交,P是文件的路径名(无论如何都可以在提交中找到——我们可能已经重命名了文件!)
  • 阶段2包含我们的文件版本:fileL:P
  • 阶段3包含他们的文件版本:fileR:P

工作树包含Git试图用冲突标记合并这两组更改的尝试。请注意,此版本与三个输入版本中的任何一个都不相同!合并的某些部分可能已经解决。冲突更改的默认样式是merge,它只显示左侧(B-vs-L)和右侧(B-vs-R。在许多情况下,这已经足够了,但当更改纯粹是删除时,了解B中的哪一行被删除通常非常有帮助,而这不是你可以简单推断的。将冲突样式设置为diff3会使Git在两个更改部分之间也记录B代码部分。


1可能存在多个合并基提交。在这种情况下,Git默认通过合并合并基来构建新的合并基提交。这个过程有点混乱,但幸运的是,它很少发生,即使真的发生了,也很少让事情变得更糟。

提取三个版本

  • 您可以使用git checkout-index提取三个版本中的任何一个,尽管使用此命令有点困难:

    git checkout-index --all -- path/to/file
    

    这会将这三个文件都写入到具有时髦临时名称的文件中,然后您必须重命名这些文件。(这个主题有几种变体,但都有点烦人。)

  • 您可以使用git mergetool会自动提取所有三个版本,然后在所有版本上调用您选择的合并工具命令。

  • 或者,您可以手动提取一个或两个文件。DogEata的答案中的方法是有效的,但如果你不担心换行问题,这种方法会更短:

    git show :1:path/to/file > path/to/file.base
    git show :2:path/to/file > path/to/file.ours
    git show :3:path/to/file > path/to/file.theirs
    

    这使用git show命令和gitrevisions语法来访问索引副本,将它们显示为标准输出,并将输出重定向到新文件。

请注意,git add写入插槽0

Git从几个方面知道合并正在进行中,但还没有完成,但最重要的是,在索引中的某个路径的插槽1、2和/或3中有这些文件。当您计算出该路径的正确内容,并将其写入工作树中的该路径时,您将运行:

git add path/to/file

将文件复制回索引中,采用正常格式的工作树副本并压缩为索引中的特殊Git专用格式。

如果文件在插槽0的索引中,与正常情况一样,这只会用固定的工作树版本覆盖旧的索引副本。当索引中有多个文件副本使用编号较高的插槽时,git add仍然写入插槽0,但这一次,它完全删除编号较高的条目。现在该文件已解析。

如果使用git mergetoolgit mergetool命令可以自动为您运行git add,从而隐藏额外的步骤。有些人觉得这特别方便。

Cherry从官方文档和其他评论中挑选了一些想法,我提出了一个可能对您有所帮助的小函数。

这个脚本也涵盖了重基本冲突的情况,由于某种原因,使用git checkout --ours的解决方案无法处理这些冲突。

在我的特定用例中,每当我运行命令时,我都有兴趣将所有文件分开,根据您的需要进行调整。

专为鱼类设计的功能:

function git-conflict-split
for file in (git ls-files -u | cut -c 51- | sort -u)
rm $file
git checkout-index --stage 3 -- $file
mv $file $file.THEIRS
git checkout-index --stage 2 -- $file
mv $file $file.OURS
git checkout-index --stage 1 -- $file
echo "meld $file.OURS $file $file.THEIRS"
end
end

相关内容

最新更新