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

Mar*_*eon 9 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"
Run Code Online (Sandbox Code Playgroud)

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

d:\code>git checkout --ours **/pom.xml pom.xml
error: path 'blah/pom.xml' does not have our version
Run Code Online (Sandbox Code Playgroud)

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

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

Gab*_*les 18

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

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

作为一个人,您需要执行以下操作。假设您运行了以下命令,正如我在《根据 Git 来看谁是“我们”/“我们的”和“他们”/“他们的”?》中解释和推荐的那样。:

git checkout --ours -- path/to/some/dir
Run Code Online (Sandbox Code Playgroud)

...但没有成功!它没有做任何事情。相反,它输出这些错误:

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
Run Code Online (Sandbox Code Playgroud)

问题是这些是侧面删除的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
Run Code Online (Sandbox Code Playgroud)

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

git checkout --ours -- path/to/some/dir
Run Code Online (Sandbox Code Playgroud)

作品!完毕。

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
Run Code Online (Sandbox Code Playgroud)

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

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

$ 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'
Run Code Online (Sandbox Code Playgroud)

解释git checkout --ours -- path/to/some/dir |& gawk '{ print $3 }' | xargs git rm

  1. git checkout --ours -- path/to/some/dir从侧面接受所有合并冲突--ours(在我的回答中阅读更多内容:根据 Git,谁是“我们”,谁是“他们”?)。
  2. |&输出输出进行stderr管道传输,因为 git 命令可能打印出的错误消息是我们需要管道传输的。stdoutstderr
  3. gawk '{ print $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
git cherry-pick --continue
# etc.
Run Code Online (Sandbox Code Playgroud)

参考:

  1. 对于 awk/gawk:
    1. 我的git-diffn.sh“带有行号的 git diff”脚本(我永远记不住 awk 语法,所以我只看以前已知的示例,包括我自己的示例)。
    2. https://en.wikipedia.org/wiki/AWK
    3. 官方 GNU AWK 用户指南
  2. 使用| xargs git rmGit rm 几个文件?
  3. 使用|&管道传输stdoutstderr:在 bash 中管道化 stdout 和 stderr
  4. 为什么使用“git rm”而不是“rm”来删除文件?

  • 运算符“|&”意味着 _stderr_ 和 _stdout_ 都通过管道传输到第二个命令。但是,它并非在所有 shell 上都可用。在 `bash` 上,只有 4+ 版本支持它。对于较旧的 shell,请使用: `git checkout --ours -- path/to/some/dir 2>&1 | gawk '{ print $3 }' | xargs git rm` `2>&1` 运算符意味着获取 _syserr_ 并将其通过管道传输到相同的 _stdout_,获得相同的结果。 (2认同)

tor*_*rek 1

第一的:

git merge origin/remote_branch
Run Code Online (Sandbox Code Playgroud)

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

下一个:

git checkout --ours **/pom.xml pom.xml
Run Code Online (Sandbox Code Playgroud)

这依赖于你的 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
Run Code Online (Sandbox Code Playgroud)

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

让我们进入这一切中 Git 特定的部分,即索引。请记住,Git 的索引是您构建下一次提交的位置。在合并期间,您还可以在此处解决冲突。

合并期间索引中的条目

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

在冲突合并期间,文件F发生冲突,索引最多包含三个F条目,而不是通常的条目。这些条目位于插槽 1、2 和 3 中。(插槽 0 是为正常的、不冲突的条目保留的。)插槽 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 条目匹配。

您可以选择通过扫描空的“slot 2”并git rm针对这种情况读取文件来处理此问题:

git ls-files --stage | fancy-script-or-program
Run Code Online (Sandbox Code Playgroud)

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

本质上,您可以通读整个索引,查找基本名称(final 后面的所有内容/)匹配的文件pom.xml

  • 如果存在零阶段条目,Git 认为它正确解析了该文件。将哈希 ID 与提交中的哈希 ID 进行比较HEAD,因为 Git 可能根本没有真正正确解析该文件;在这种情况下,请将索引 blob 哈希替换为HEAD提交中的哈希。有关详细信息,参阅文档。git update-index您应该能够使用--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 文件都是如此)。不过,我根本不清楚仅使用“我们的”版本是否正确。