通过设置非零阶段条目值手动将具有合并冲突的文件添加到 git 索引

Manually adding a file with a merge-conflict to the git index by setting non-zero stage entry values

我在 git 存储库中提交了两个文件,一个 original 文件和一个 derived 文件。 derived 基于 original 但有一些修改(即 diff original derived 产生一些小输出)。

每当我修改 original 文件时,我也想通过脚本半自动地将相同的更改应用到 derived。有一个有用的 git 命令,叫做 git merge-file 可以让我做到这一点。

我的情况是我想将索引中 original 的更改(即 original 已修改但尚未提交)应用到 derived 文件,所以我做了一些事情像这样:

git cat-file -p HEAD:original >orignal.base
git merge-file derived original.base original
rm -f original.base

现在 derived 应该具有应用于 original 的相同更改(并且 diff original derived 应该或多或少没有变化)。

但是,有可能发生合并冲突,在这种情况下git-merge-file returns一个非零值,并留下输出文件(derived) 与合并冲突标记(<<<<<<<=======>>>>>>>)。这就是我的问题所在。

我想要做的是,在合并冲突的情况下,而不是让 derived 在索引中被标记为 'modified',我希望它显示为 'both modified'就像发生合并冲突时(不太像 git merge,更像 git stash apply)。

我查看了所有可用的 git 命令,有一些像 git read-tree -m 几乎可以满足我的要求,但用于树而不是 blob。

所以我的想法是使用类似 git update-index 的东西。阅读一下 git 索引的工作原理,like in this question,索引中的文件有一个所谓的 'stage entries',通常为 0,例如对于上述文件,我们会有

$ git ls-files --stage
100644 cd2732a3aeeb97c20b5dc809cc6350fd7fbfb944 0       derived
100644 4b48deed3a433909bfd6b6ab3d4b91348b6af464 0       original

但是在合并冲突之后(例如在失败之后 git stash apply 它看起来像这样:

$ git ls-files --stage
100644 cd2732a3aeeb97c20b5dc809cc6350fd7fbfb944 1       derived
100644 68bc18a75908806fd6c9c816b7370f4797a6be15 2       derived
100644 d4cda09e1616383549548d7212cdcb86b4dda596 3       derived
100644 4b48deed3a433909bfd6b6ab3d4b91348b6af464 0       original

其中 1 是基础文件,2 是本地修改的文件,3 是隐藏文件。工作目录中的文件包含 git merge-file 也产生的合并冲突标记。但是git update-index不支持设置这些'stage values'

所以我想做的是以下内容,以防 git merge-file 失败:

想要这样做的原因是:

有没有办法实现我想做的事情

谢谢。

作为bk2204 said in a comment,这是错误的处理方式。但是让我们回答标题问题。如果您 想要创建一个冲突的索引条目,git update-index 确实是正确的(也是唯一的)工具:

  • 根据定义,当且仅当索引条目具有非零阶段编号时,它才是冲突/未合并的。
  • 只有 git update-index 能够插入具有非零阶段编号的索引条目。

But git update-index does not support setting these 'stage values' ...

这种说法是错误的。但是,设置一个非零的分段编号并不容易。仔细阅读 the git update-index documentation,我们发现只有一种方法可以做到这一点:

--index-info
     Read index information from stdin.

USING --INDEX-INFO

--index-info is a more powerful mechanism that lets you feed multiple entry definitions from the standard input, and designed specifically for scripts. It can take inputs of three formats:

  1. mode SP type SP sha1 TAB path

    This format is to stuff git ls-tree output into the index.

  2. mode SP sha1 SP stage TAB path

    This format is to put higher order stages into the index file and matches git ls-files --stage output.

[format 3 snipped; boldface above is mine]

To place a higher stage entry to the index, the path should first be removed by feeding a mode=0 entry for the path, and then feeding necessary input lines in the third format.

进一步注意,因为您必须将 哈希 ID 放入索引中,所以您必须首先确保您想要的数据以 blob object 的形式存在。为此,请使用 git hash-object -w -t blob(尽管您可以省略 -t blob,因为这是默认值)。

So what I want to do is the following, in case git merge-file fails:

  • Add the file original.base as 1 derived (base)

因为 original.base 已经有一个哈希 ID(例如,4b48deed3a433909bfd6b6ab3d4b91348b6af464),您可以直接使用它。假设此时在 $hash1 中。

  • the file original as 2 derived ('our' modification)

这也有一个现有的哈希 ID,比方说 $hash2

  • and the original derived file as 3 derived ('their' modification) into the index

为此,您必须再次导出文件(我想我可能有 mis-read 问题中的内容)和 运行 git hash-object:

hash3=$(git hash-object -w < original-derived-data)

您现在可以删除文件(此处 original-derived-data)。

  • (keep the derived file with the merge-conflict markers in the worktree)

为此,不需要任何内容​​。现在您有了三个哈希 ID:

TAB=$'\t'  # make sure your shell supports this syntax

git update-index --index-info <<end
0 blob $hash1 0${TAB}derived
100644 blob $hash1 1${TAB}derived
100644 blob $hash2 2${TAB}derived
100644 blob $hash3 3${TAB}derived
end

“here-doc”(<<end) 将扩展 $ 个变量,因为我们没有引用 end-word。 0 blob $hash1 0${TAB}derived 行中的散列是无关紧要的:mode 0 是我们真正想要的,如果有条目则删除该条目。其余三行使用现有数据的现有哈希 ID($hash1$hash2)和新 computed-and-saved 数据的新哈希 ID($hash3).

请注意,默认情况下,从您使用 git hash-object -w 计算索引开始,您只有 14 天的时间将 $hash3 插入索引。如果您的脚本需要比 运行 更长的时间,git gc 可能 运行 并删除您编写的 object。当然,如果您的脚本需要 14 天才能达到 运行,那么其他地方可能就大错特错了。 :-)