git checkout --ours when file spec includes deleted file

jgovgodb  于 2023-09-29  发布在  Git
关注(0)|答案(2)|浏览(139)

合并时,我们保留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文件--这正是我们不想要的。
我们如何更新我们的脚本来处理这个问题?

3gtaxfhh

3gtaxfhh1#

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

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

作为一个人,你必须做到以下几点。让我们假设您运行了以下代码,正如我在Who is "us"/"ours" and "them"/"theirs" according to Git?中所解释和推荐的:

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端的 deleted 文件,所以我们必须从我们的工作树(工作文件系统)手动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 ing您删除的文件,您将看到以下输出(这里的第一行包含$ char之后的实际命令):

$ 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端的所有合并冲突(在我的回答中阅读更多内容:根据Git,谁是“我们”,谁是“他们”?).
  2. |&管道 * stderr输出 * 以及 * stdout输出,因为git命令可能打印出的错误消息是stderr,这就是我们需要管道的。
  3. gawk '{ print $3 }'只打印每行的第三个空格分隔字段,这意味着它捕获error: path 'path/to/some/dir/file1.cpp' does not have our version'path/to/some/dir/file1.cpp'部分。
  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.

参考资料:

1.对于awk/gawk:

  1. My git-diffn.sh "git diff with line numbers" script(我从来不记得awk语法,所以我只看以前的例子,包括我自己的)。
  2. https://en.wikipedia.org/wiki/AWK
    1.官方GNU AWK用户指南
    1.使用| xargs git rm:Git rm多个文件?
    1.使用|&管道传输 * stdout * 和 * stderr:在bash中同时连接stdout和stderr?
    1.为什么使用'git rm'而不是'rm'来删除文件?
5q4ezhmt

5q4ezhmt2#

第一:

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做glob扩展。这可能会影响到你的具体情况,我不确定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的索引是你构建下一个提交的地方。在合并过程中,它也是解决冲突的地方。

合并时索引中的条目

在正常(非合并)情况下,索引对每个被跟踪的文件都有一个条目。如果文件 F 在当前(HEAD)提交和工作树中,则索引具有针对 F 的条目。最初这个索引条目版本匹配HEAD版本。你修改工作树中的文件,然后git add工作树版本将其复制到索引中代替HEAD版本;然后下一个git commit将保存索引版本。
在冲突合并期间,文件 F 有冲突,索引有 * 最多三个**条目,而不是通常的一个。这些条目分别放在插槽1、2和3中。(插槽0是为正常的、不冲突的条目保留的。)插槽1用于 merge base 版本。插槽2用于--ours,插槽3用于--theirs,您可以将这些名称用于2和3,但插槽1没有名称。
在以下情况下会发生合并冲突:

  • 在我们的和他们的基础版本中修改了相同的行(这是修改/修改冲突),或者
  • 没有基本版本,只有我们的和他们的(这是创建/创建冲突),或者
  • 我们删除了文件,他们改变了一些东西,甚至只是名称(这是删除/修改或删除/重命名冲突),或者
  • 他们删除了文件,我们做了一些修改:这也是一个修改/删除或重命名/删除冲突,其中交换了伙伴。

对于修改/修改冲突,填充所有三个插槽。对于其他三种类型的冲突,一个插槽是空的:合并基槽为空(创建/创建),或--ours为空(删除/X),或--theirs为空(X/删除)。
--ours插槽为空时,git checkout --ours步骤失败。当--ours插槽非空时成功:它将X1 M15 N1 X版本提取到工作树中。
Git对任何delete/X或X/delete冲突的默认操作是在工作树中保留幸存的版本。也就是说,如果插槽3(他们的)为空,则工作树文件匹配插槽2条目,但是如果插槽2(我们的)为空,则工作树文件匹配插槽3条目。
在这种情况下,您可以选择通过扫描空的“slot 2“s并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匹配的文件:

  • 如果存在stage-zero条目,Git认为它正确解析了文件。将hash ID与HEAD提交中的hash ID进行比较,因为Git可能根本没有正确解析文件;在这种情况下,用来自HEAD提交的一个替换索引blob散列。详情请参见the git update-index documentation。您应该能够使用--cacheinfo,尽管我还没有用未合并的索引条目测试过它。
  • 否则,存在阶段1、2和/或3条目。如果存在阶段2条目,则将其用作分辨率,即如上所述将其馈送到git update-index。如果没有第二阶段的条目,使用git update-index来删除这些条目(使用0来删除模式,以及任何内容,包括全零哈希值;如果模式为0,则散列是不相关的)。

对所有pom.xml路径执行此操作后,任何剩余的非零stage索引条目都表示应该将合并冲突传递回用户。否则,你可能已经准备好了。

(快速浏览一下http://gitpython.readthedocs.io/en/stable/reference.html#module-git.index.base,就会发现在GitPython中可以很容易地做到这一点,但我没有使用它的经验。
最后警告:我完全没有使用Maven的经验,但我认为pom.xml文件是控制各种事情的XML文件,Git合并得很差(最后一种情况几乎适用于所有XML文件)。不过,我一点也不清楚,仅仅使用“我们的”版本是正确的。

相关问题