gpt4 book ai didi

git - Git粒度—解决一行中的差异

转载 作者:行者123 更新时间:2023-12-02 14:08:55 25 4
gpt4 key购买 nike

是否可以将基于git行或diff的粒度增加到字/字母分辨率?每行多条语句或使用git编写纯文本都是值得的。

最佳答案

在根据评论重新阅读问题时,我想我明白了您的初衷,因此我将给出一个真实的答案(与Ismail Badawi's one line comment相对)。

Git作为简单的存储

Git有很多作品,其中一些作品比其他作品更好玩。在最低级别上,Git充当纯粹的基于内容的数据存储:具有键/值对的数据库,其中键本身只是(并且始终是)值的哈希值。也就是说,您无法选择密钥,在您提供值之后会为您提供密钥。

在这个非常低级的键/值数据存储区的正上方是一个类似的低级接口,其中存储的值具有类型。四种类型的对象是“斑点”,用于保存您的实际文件。 “树”,允许Git将真实,有用的文件名(例如file.pyimage.jpg之类)映射到诸如b02f09aabdd957329476837f406588710142aebd的不可读的哈希中; “提交”,从无法理解的哈希转换为有关提交的元数据,包括表示提交时文件的树;和带注释标签的对象,在这里我们可以忽略它们。

在这些之上,Git提供了参考。引用是一种既包含分支又包含标签的通用形式,包括更多的内容(远程跟踪分支,Git的“注释”,“隐藏”等)。这些将诸如master之类的人类可读名称转换为不可读的哈希,Git然后使用该哈希获取提交,Git使用该提交获取树的哈希,Git使用该提交获取文件的哈希。

这是Git首先真正有用的级别。此时,Git可以存储所有任意数据:它不必由行甚至字组成;二进制格式(包括JPEG图像)在这里起作用。唯一的约束是,每个文件都必须哈希到其自己的唯一值,除非它与该文件的先前版本相同。 (举个例子,即使hash("a file\nwith two lines\n")hash("a file\nwith two lines\n")中的file1.txt匹配file2.txt也可以;如果hash("a file\nwith two lines!\n")的值相同,那是不可以的,因为我们只是添加了一个字节:第二行的感叹号。无论文件名如何,我们都必须为此文件的内容获取一个新的,不同的哈希值。)

当您进行新的提交时,Git只关心内容。具体来说,在git commit时,Git根据文件的暂存版本(索引中的内容)打包所有跟踪文件的内容。文件名(包括带有子目录的路径名,如果需要的话)全部进入一堆树对象,文件内容本身进入blob对象,或在数据库中已有的blob对象重用。

(对于大多数提交来说,后者是很常见的,因为通常每个提交中有数十个,数百个甚至数万个文件,但是每个新提交都会使大多数文件与上一个提交保持不变。索引/分段-area对于未更改的文件Shakespeare-Sonnet-107.txt具有与最近50次提交相同的blob哈希,因此我们在新提交中再次使用相同的blob。)

Git作为压缩方案

如果您要使用Git做的就是存储一堆文件树的一堆版本(换句话说,比源控制系统更多的是备份系统),那么在进行一些更改后,Git压缩文件的效率如何( s),变得有趣。

对象存储的基本形式完美地压缩了相同的文件:如上所述,无论有多少提交包含名为Shakespeare-Sonnet-107.txt的文件,只要内容逐字节相同,我们就只需重复使用其ID的blob是该文件内容的哈希值。2

相同的基本存储方法(所谓的“松散对象”)使用Zlib压缩数据。这对于文本来说效果很好,在这种情况下,大文本文件可能会压缩到其原始大小的10%,但对二进制文件则效果不佳(压缩差异很大,我见过原始大小的1/3至1/2),并且对于已经压缩的任何东西都非常差。但是,对于文本文件和编程语言源文件,我们通常可以做得更好。

大多数版本控制系统都提供所谓的delta compression,其最简单的形式就是“区分旧的和新的,并将将一个更改为另一个的指令转换为文件的存储形式”。也就是说,我们在两个文件(通常是上一版本和下一个版本,以线性提交链)上运行string-to-string correction algorithm,而不是存储整个新文件,而是说“删除第3行,添加新的第47行”或其他内容。

Git至少以一种版本控制系统的独特方式执行增量压缩。它永远不会简单地说:“哦,好吧,这是blah.py的新版本,我将针对最新的blah.py进行压缩。”取而代之的是,它在游戏后期压缩对象,从单个对象(以及重新打包,其他已打包的对象,尽管规则变得复杂4)中制作“打包文件”,并通过各种试探法选择彼此打包的对象。

此特定代码基础上的算法是xdelta的修改版本。这适用于任意二进制数据,并且不依赖于换行符。

Git作为版本控制

但是,如果我们想使用Git进行真正的版本控制-毕竟它是专为它而设计的-我们必须更进一步。我们将有许多特定于版本控制的任务,但是有两个非常大的任务正在研究发生了什么变化,并合并来自不同开发路径的变化。

为了了解发生了什么变化,Git为我们提供了git diffgit showgit log。所有这些都使用相同的基本差异引擎:给定两次提交,它匹配那些提交中的文件,然后匹配匹配文件中的行(啊哈,这里是“行”的所在!)。

git diff的输出,因此也是git showgit log -p的输出,都是非常面向行的。如果更改一行中的单词,它将显示更改后的整行。您可以为git diff提供标记(因此,为git showgit log提供标记),在Git找到这些面向行的更改后,它们将指示它们显示行中哪些单词是实际上不同。从Git 2.9版开始,行内显示得到了进一步的改进:有一个new diff-highlight script可以准确显示更改的内容。 (所有这些都在下面的面向行的diff之上工作:例如,当您忽略空格更改时,这会显示出来,在这里您将看到空的diff块,而不是根本没有diff。)

请注意,使用单词或字符显示对包中的提交blob(完整存储)或带修饰符的对象的内部格式没有影响(xdelta不面向行)。这些纯粹是显示选项。

三向合并

除了上述所有考虑因素之外,如果您要使用Git的合并功能,则不必;例如,一些使用Perforce的人使用p4merge而不是Git的内置合并-您需要知道它们是通过运行两个常规的,因此是面向行的git diff开头的。

特别是,当您运行git merge <other>时,Git会将<other>解析为提交ID,然后在当前(HEAD)提交与其他提交之间找到合并基础。此合并基础commit5作为起点。 Git进行两个比较:一个从基本到HEAD,另一个从相同的基本到<other>。然后,Git组合两个差异,将组合的更改应用于基本提交中的文件。

由于这些差异是面向行的,因此,如果您构建更改以使其与行相对应,则合并过程通常会更加顺利。因此,与git diff本身一样,在这里您可能希望文件非常面向行。

如上所述,尽管编写合并脚本有点棘手,但您不必使用Git的内部合并,并且那些使用外部合并的人总是在various rough edges上绊脚石。



1这意味着只有一个索引/临时区域。实际上,有一个主索引,通常只看到一个索引,但是有很多极端的情况,例如git commit -a,其中的其他索引会临时存在,并弄乱了该模型。

2技术上,ID为hash("blob %d\0%s" % (len(bytes), bytes)),以Python形式书写。也就是说,文件内容的哈希ID以单词“ blob”,一个空格,内容长度的十进制表示形式,ASCII NUL字节以及最后的实际内容开头。这样可以保证您不能仅仅通过编写一个内容与某些现有提交3相同的文件来破坏Git。这里的问题是,可以测试每个对象的类型,而不是从上下文中假设其类型,因此,如果提交和常规文件具有相同的哈希,这将迫使单个基础Git存储库对象同时具有类型commit和键入blob,这是不允许的。

3例如,您可以使用git rev-parse HEAD查看当前提交的内容,其ID是git cat-file -p HEAD打印的内容。将git cat-file的输出写入常规文件,然后将此新文件写入git add,您将获得与git rev-parse HEAD所显示内容不匹配的新ID,这都是因为内部每个对象都以其类型名称作为前缀,再加上该空间大小和NUL序列。

4要使它们沸腾很多,一个包对象只能引用同一包内的其他对象,所谓的“瘦”包除外,后者用于在网络上进行传输。

5假设有一个合并基础。如果有多个,则默认递归策略通过合并基于合并的候选对象构造一个。通常,只有一个合并基础,我们不必为此担心。有时根本没有合并基础。在这种情况下,Git 2.9已将默认值更改为抱怨并失败,而不是从空树中合并。

关于git - Git粒度—解决一行中的差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37906514/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com