【问题标题】:git checkout --ours when file spec includes deleted filegit checkout --ours 当文件规范包含已删除的文件时
【发布时间】:2017-09-19 00:05:01
【问题描述】:

当我们合并时,我们会保留 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 文件——这正是我们想要的。

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

【问题讨论】:

    标签: git merge git-checkout


    【解决方案1】:

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

    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 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'
    
    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. |& 管道 both stderr 输出 以及 stdout 输出,因为 git 命令可能打印出的错误消息是 @987654349 @ 这就是我们需要传递的内容。
    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 remove”它们。

    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 rm:Git rm several files?
    3. 使用|& 管道both 标准输出 标准错误:Piping both stdout and stderr in bash?
    4. Why use 'git rm' to remove a file instead of 'rm'?

    【讨论】:

    • 运算符 |& 表示 stderrstdout 都通过管道传递给第二个命令。但是,它并非在所有 shell 上都可用。在 bash 只有版本 4+ 支持它。对于较旧的 shell,请使用:git checkout --ours -- path/to/some/dir 2>&1 | gawk '{ print $3 }' | xargs git rm 2>&1 运算符意味着获取 syserr 并将其通过管道传输到相同的 stdout,结果相同。
    【解决方案2】:

    第一:

    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 中。(插槽 0 为正常的、不冲突的条目保留。)插槽 1 用于 merge base 版本。插槽 2 用于--ours,插槽 3 用于--theirs,您可以将这些名称用于 2 和 3,但插槽 1 没有名称。

    在以下情况下发生合并冲突:

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

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

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

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

    您可以选择通过扫描空的“slot 2”和git rming 文件来处理这种情况:

    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 文件都是如此)。不过,我完全不清楚仅使用“我们的”版本是否正确。

    【讨论】:

      猜你喜欢
      • 2020-05-19
      • 2011-08-30
      • 1970-01-01
      • 2014-09-06
      • 2011-06-26
      • 2016-09-09
      • 2019-02-02
      • 2021-03-03
      • 2021-07-27
      相关资源
      最近更新 更多