如何将文件另存为 git 树而不是 blob?

How to save a file as a git tree instead of a blob?

由于许多文件格式都具有树状结构(例如 XML、tar,即使是 MP3,如果您考虑将标签和框架拆分为叶子),我想知道是否有让 git 将它们存储为树对象而不是 blob 的方法,以利用结构,例如用于比较和合并。

到目前为止,我考虑过使用钩子或 smudge/clean-filters,但两者都有我想避免的缺点:

那么有什么明智的方法可以做到这一点吗?或者我应该坚持使用 blob 而不是修改 merge/diff 驱动程序?

如果您尝试在 Git 中添加任何二进制文件(如 XML、tar、MP3),它们将被区别对待。

Git 用于非二进制文件。如果您打算定期包含二进制文件,请考虑使用二进制存储解决方案,例如 Git LFS。

Git 本身试图与内容形式无关。也就是说,初步估计它只关心原始数据——甚至不关心文本与二进制数据,只关心 "here is some data as a collection of files; please store it as such."(我认为 Linus 的最初设想不包括 CR/LF 转换,只要它是永远不会打开,它不会损坏二进制数据。)

这种不可知论很快就瓦解了。比较一个提交与另一个提交是从比较文件开始的,但是除了简单的 "pathname p/a/t/h in commit A must mean the same file as pathname p/a/t/h in commit B"——当两个路径都存在并且 do 命名相同的内容时效果很好——我们很快发现我们需要比较相似但不相同的文件,并希望在某种结构基础上这样做:例如,面向行或单词 diff。而且,为了处理重命名问题,如果 p/a/t/h 变为 p/t/h 或相反,我们可能希望将这些文件相互匹配,即使它们只有 90% 相似度。

(其他 VCS 会在每次提交时记录一些其他类型的文件标识,而不仅仅是路径名,通过记录目录操作或为文件分配唯一的内部 ID。Git 没有,所以它必须依赖于这个相似性检测系统。Git 的相似性检测器很特别:它不是完全面向行的,因此它可以处理二进制文件,但它确实检测行边界以消除 \r\n vs \n 从其相似性检测器更改。)

无论如何,您当然可以采用Git并修改它以添加"like trees"但具有不同风格的新对象类型。这将使您能够分离这些结构化文件。它的效果如何基本上似乎是一个研究课题。不过,仅仅将它们塞进 树显然效果不佳:您永远不会知道某个树实例是 "derived tree" 还是 "real tree"。为避免更改 Git 的某些核心代码,您或许可以在 Git 读取和写入其索引的位置插入真实与 derived/synthetic 树转换,并编码 "real" vs "synthetic" 进入 "file names".

如果存储在每个合成子树中的原始数据本身是二进制的,那么您将 运行 陷入 diffs 无法使用的常见问题。 pack 文件存储格式(基于 xdelta)不是面向行的,但是使用路径名的 pack heuristics 可能不会很好地执行,因此您可能需要修改那些也是。您从 xdelta 获得的压缩量取决于输入数据中的 Shannon entropy:二进制与文本在这里实际上不是问题,除非典型的文本输入具有相当低的熵;二进制输入往往难以预测。