git checkout - 当文件规范包含已删除的文件

时间:2017-04-21 21:03:25

标签: git merge git-checkout

当我们合并时,我们保留Maven pom.xml文件的本地版本:

git merge origin/remote_branch
git checkout --ours **/pom.xml pom.xml
git add **/pom.xml pom.xml
git commit -m "Merge"

除非在本地分支中删除了pom.xml文件,否则这很有效。在运行上面的命令#2后,我们得到一个错误:

d:\code>git checkout --ours **/pom.xml pom.xml
error: path 'blah/pom.xml' does not have our version

...并且在此错误之后,下一个命令#3 git add **/pom.xml pom.xml有效地添加了远程pom.xml文件 - 这正是我们想要的。

我们如何更新我们的脚本来处理这个?

2 个答案:

答案 0 :(得分:2)

运行命令error: path 'some/file' does not have our version后出现错误git checkout --ours **/some_file2.xml some_file2.xml如何解决

1.A.作为人类,以下是步骤

作为人类,您需要执行以下操作。假设您运行了以下代码,as I explain and recommend here

git checkout --ours -- path/to/some/dir

...它没有用!它什么也没做。相反,它输出这些错误:

<块引用>
error: path 'path/to/some/dir/file1.cpp' does not have our version
error: path 'path/to/some/dir/file2.cpp' does not have our version
error: path 'path/to/some/dir/file3.cpp' does not have our version

问题在于这些是our端的删除文件,因此我们必须从我们的工作树(工作文件系统)中手动git rm每个文件,以手动强制我们的工作树匹配这些文件的 our 端:

git rm path/to/some/dir/file1.cpp
git rm path/to/some/dir/file2.cpp
git rm path/to/some/dir/file3.cpp

# OR (same thing)
git rm path/to/some/dir/file1.cpp path/to/some/dir/file2.cpp \
path/to/some/dir/file3.cpp

现在,重新运行您的 checkout --ours 命令,它会正常工作!:

git checkout --ours -- path/to/some/dir

有效!完成。

1.B.编写上述过程的脚本有点困难,但方法如下

让我们编写上面的内容。毫无疑问,有很多方法可以做到这一点,但这是我能找到的最简单的方法:

# 1. attempt to run `git checkout --ours` the first time,
# collecting any filenames which errored out, if any, and 
# `git rm` them all.
git checkout --ours -- path/to/some/dir \
|& gawk '{ print $3 }' | xargs git rm

# 2. Now run it again. If it worked the first time above already, 
# no big deal--running it again causes no problems. If it failed
# above though, the above command just ran `git rm` on all those
# failed files, so now this time it will succeed!
git checkout --ours -- path/to/some/dir

完成!当然,您也可以将第一次尝试的输出存储到文件中,并且仅在第一次尝试失败时才运行第二次尝试(意味着输出不是空字符串),但我会留给您.

示例输出:通过 git rm 删除已删除的文件,您将看到以下输出(此处的第一行包含 $ 字符后的实际命令):

$ git checkout --ours -- path/to/some/dir |& gawk '{ print $3 }' | xargs git rm
path/to/some/dir/file1.cpp: needs merge
path/to/some/dir/file2.cpp: needs merge
path/to/some/dir/file3.cpp: needs merge
rm 'path/to/some/dir/file1.cpp'
rm 'path/to/some/dir/file2.cpp'
rm 'path/to/some/dir/file3.cpp'
git checkout --ours -- path/to/some/dir |& gawk '{ print $3 }' | xargs git rm

解释

  1. git checkout --ours -- path/to/some/dir 接受来自 --ours 方的所有合并冲突(在我的回答中了解更多信息:Who is "us" and who is "them" according to Git?)。
  2. |& 管道两者 stderr 输出以及 stdout 输出,因为可能打印的错误消息通过 git 命令输出到 stderr 这就是我们需要通过管道传输的内容。
  3. gawk '{ print $3 }' 仅打印每行的第 3 个空格分隔字段,例如,这意味着它捕获 'path/to/some/dir/file1.cpp'error: path 'path/to/some/dir/file1.cpp' does not have our version 部分。
  4. | xargs git rm 将所有这些文件通过管道传送到 git rm 以“git 删除”它们。

2.收尾

现在您可以添加这些自动修复的文件并继续该过程:

git add path/to/some/dir 
git status 

# Use the appropriate one of these based on whatever operation 
# you were in at the time when the conflicts happened.
git merge --continue 
git rebase --continue
git revert --continue
# etc.

参考:

  1. 对于 awk/gawk:
    1. My git-diffn.sh "git diff with line numbers" script(我永远不记得 awk 语法,所以我只查看以前已知的示例,包括我自己的示例)。
    2. https://en.wikipedia.org/wiki/AWK
    3. Official GNU AWK user guide
  2. 使用 | xargs git rmGit rm several files?
  3. 使用 |&both stdout stderr 进行管道传输:Piping both stdout and stderr in bash?

答案 1 :(得分:1)

首先:

git merge origin/remote_branch

应该读取git merge --no-commit以确保Git在没有合并冲突的情况下不提交这些更改,否则您的后续步骤没有多大意义。请注意,如果--theirs提交已更改某些pom.xml文件并且您尚未更改它们,或者Git认为它已成功合并您的更改及其更改,则根本不存在合并冲突。 (如果你想在其中一种情况下使用它们,那也有点棘手,但你似乎总是想要使用--ours版本。)

下一步:

git checkout --ours **/pom.xml pom.xml

这依赖于你的shell(大概是bash或类似的)来扩展**你想要的方式;你可能想引用星号,并让Git做全局扩展。这可能会影响你的特定情况,而且我不确定Git在合并冲突期间如何处理这个问题,所以在你做这样的事情之前,你需要仔细试验。

  

除非在本地分支中删除了pom.xml文件,否则这很有效。在运行上面的命令#2后,我们得到一个错误:

d:\code>git checkout --ours **/pom.xml pom.xml
error: path 'blah/pom.xml' does not have our version

正确:对于这种情况,如果要保留删除的文件,则需要覆盖Git的默认操作,即选择将其版本保留在索引和工作树中。

让我们跳转到所有这一切的Git特定部分, index 。请记住,Git的索引是您构建 next 提交的地方。在合并期间,您也可以解决冲突。

合并期间索引中的条目

在正常(非合并)情况下,索引为每个跟踪文件都有一个条目。如果文件 F 在当前(HEAD)提交和工作树中,则索引具有 F 的条目。最初,此索引条目版本与HEAD版本匹配。您可以在工作树中修改文件,然后git add工作树版本将其复制到HEAD版本的索引中;然后下一个git commit将保存索引版本。

在冲突合并期间,文件 F 发生冲突时,索引 F 最多三个条目,而不是通常的条目。这些条目位于插槽号1,2和3中。(插槽零保留用于正常的,不冲突的条目。)插槽1用于合并基础版本。插槽2用于--ours,插槽3用于--theirs,您可以将这些名称用于2和3,但插槽1没有名称。

合并冲突发生在:

  • 相对于基本版本(这是修改/修改冲突),我们和他们的相同行被修改了,或者
  • 没有基本版本,只有我们和他们的(这是创建/创建冲突)或
  • 我们删除了文件,他们更改了一些内容,甚至只是名称(这是删除/修改或删除/重命名冲突),或者
  • 他们删除了文件,我们更改了一些内容:这也是修改/删除或重命名/删除冲突,合作伙伴互换。

对于修改/修改冲突,将填充所有三个插槽。对于其他三种类型的冲突,一个槽为空:合并基槽为空(创建/创建),或--ours为空(删除/ X),或--theirs为空(X /删除)。

git checkout --ours广告位为空时,--ours步骤失败。当--ours插槽非空时它会成功:它会将--ours版本提取到工作树中。

Git对任何删除/ X或X /删除冲突的默认操作是在工作树中保留幸存的版本。也就是说,如果它的第3个插槽(他们的)为空,则工作树文件与插槽2条目匹配,但如果它的插槽2(我们的)是空的,工作树文件与插槽3条目匹配。

你可以选择通过扫描空的&#34;插槽2&#34;和git rm来处理这种情况:

git ls-files --stage | fancy-script-or-program

如果您将其编写为Python程序,请使用git ls-files -z --stage使其易于机器解析。您甚至可以完全停止使用git checkout --ours,并停止依赖shell或Git globbing,并编写完全在脚本中解析pom.xml文件的规则。

基本上,您可以阅读整个索引,查找基本名称(最终/之后的所有内容)与pom.xml匹配的文件:

  • 如果存在阶段零条目,Git认为它正确解析了文件。将哈希ID与HEAD提交中的哈希ID进行比较,因为毕竟Git可能没有正确解析文件;在这种情况下,将索引blob哈希替换为HEAD提交中的一个。有关详细信息,请参阅the git update-index documentation。您应该能够使用--cacheinfo,尽管我没有使用未合并的索引条目对其进行测试。

  • 否则,有第1阶段,第2阶段和/或第3阶段。如果存在第2阶段条目,请将其用作分辨率,即如上所述将其提供给git update-index。如果没有第2阶段条目,请使用git update-index 删除条目(使用0作为模式,以及任何内容,包括全部 - 零哈希,用于哈希;如果模式为0,则哈希无关。

使用所有pom.xml路径完成此操作后,任何剩余的非零阶段索引条目都会指示您应该传递给用户的合并冲突。否则,您可能已准备好提交。

(对http://gitpython.readthedocs.io/en/stable/reference.html#module-git.index.base的快速扫描表明,这可以在GitPython中相当容易地完成,但我没有使用它的经验。)

最后的警告:我对Maven没有任何经验,但我认为pom.xml文件是控制各种事物的XML文件,并且Git合并得很差(几乎所有XML文件的最后都是如此)。对我来说,只使用&#34;我们的&#34;但版本是正确的。