tor*_*rek 12
根据评论重新阅读这个问题,我想我看到你最初得到的是什么,所以我会提出一个真正的答案(对Ismail Badawi的一行评论).
Git有很多作品,其中一些比其他作品更好.在最低级别,Git充当基于内容的纯数据存储:具有键/值对的数据库,其中键本身简单(并且始终)是值的散列.也就是说,您无法选择密钥,在提供值后会为您提供密钥.
在这个非常低级别的键/值数据存储区之上是类似的低级接口,其中存储的值具有类型.四种类型的对象是"blob",它们保存您的实际文件; "树",它允许Git将真实有用的文件名,file.py或类似的东西映射image.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 commitGit会根据文件的分阶段版本(即1索引中的任何内容)打包所有跟踪文件的内容.文件的名称(包括带有子目录的路径名,如果需要)都会进入一堆树对象,文件的内容本身会进入blob对象,或者当它们已经存在于数据库中时重新使用现有的blob对象.
(后者在大多数提交中都很常见,因为通常每次提交都有几十个,或几百个,甚至几万个文件,但每个新提交都会使大多数文件与之前的提交保持不变.索引/暂存-area对于未更改的文件Shakespeare-Sonnet-107.txt具有与最近50次提交相同的blob-hash ,因此我们只是在新提交中再次使用相同的blob.)
如果你想用Git做的就是存储一些文件树的版本 - 更多的是备份系统而不是源代码控制系统,换句话说 - 那么Git在压缩文件时效率如何,经过一些改变后( s),变得有趣.
对象存储的基本形式完美地压缩相同的文件:如上所述,无论提交的文件包含多少个Shakespeare-Sonnet-107.txt,只要内容是逐字节相同的,我们只需重新使用其ID为哈希的blob该文件的内容.2
这种相同的基本存储方法 - 所谓的"松散对象" - 使用Zlib压缩数据.这适用于文本,其中大文本文件可能压缩到其原始大小的10%,但它对二进制文件不太好(压缩变化很大,我看到原始大小的1/3到1/2)和任何已压缩的东西都很糟糕.但是,对于文本文件和编程语言源文件,我们通常可以做得更好.
大多数版本控制系统都提供所谓的增量压缩,其最简单的形式就是"差异新旧,并将指令从一个转换为另一个转换为文件的存储形式".也就是说,我们在一个线性提交链中对两个文件(通常是上一个和下一个版本)运行字符串到字符串校正算法 - 而不是存储整个新文件,我们只是说"删除第3行,添加新行" 47"或其他什么.
对于版本控制系统,Git至少以不寻常的方式执行增量压缩.它从不简单地说"噢,好吧,这是一个新的略有不同的版本blah.py,我会压缩最近的blah.py".相反,它在游戏后期压缩对象,从单个对象中制作"打包文件"(并且,对于重新打包,其他已经打包的对象,尽管规则在这里变得复杂4)并且选择要通过各种方式相互打包的对象启发式.
在该特定代码的基础上的算法是xdelta的修改版本.这适用于任意二进制数据,不依赖于换行符.
如果我们想使用Git进行真正的版本控制,那么它毕竟是为它设计的 - 我们必须进一步观察.我们将有许多特定于版本控制的任务,但是两个非常大的任务正在查看已发生的变化并合并来自不同开发路径的变更.
要查看发生了什么变化,Git的给我们git diff,git show和git log.所有这些使用相同的基本差异引擎:给定两个提交,它匹配这些提交中的文件,然后匹配行 - aha,这里是"行"进来的地方! - 在匹配的文件中.
从输出git diff,因此也git show并git log -p,非常多线为主.如果更改一行中的单词,则会将整行显示为已更改.还有,你可以提供的标志git diff(因此git show和git log),将,Git会发现这些面向线路的更改后,引导他们的线(S)中显示哪些字(S)实际上是不同的.从Git版本2.9开始,内部显示进一步得到了改进:有一个新的diff-highlight脚本可以准确显示更改的内容.(这些都在下面的面向行的diff上面工作:当你忽略空格变化时,这会显示出来,例如,你会看到空的差异,而不是没有差异.)
请注意,使用单词或字符显示对内部格式没有影响,commit-blobs(存储完整)或包中的deltified对象(xdelta不是面向行的).这些是纯粹的显示选项.
除了上述所有考虑因素之外,如果您打算使用Git的合并设施 - 您不必使用; 例如,Perforce的一些人使用p4merge而不是Git内置的合并 - 你需要知道这些开始是运行两个常规的,因此是面向行git diff的.
特别是,当您运行时git merge <other>,Git解析<other>为提交ID,然后在current()提交和另一个提交之间找到合并基础HEAD.此合并基础提交5作为起点.Git产生两个差异:一个来自基地HEAD,一个来自同一个基地<other>.Git然后组合两个差异,将组合的更改应用于基本提交中的文件.
由于这些差异是面向行的,因此如果构造更改以使它们与行对应,则组合过程通常会更顺利.因此,就像它git diff本身一样,您可能希望您的文件非常注重面向行.
如上所述,你不需要使用Git的内部合并,虽然编写合并脚本有点棘手,而那些使用外部合并的人总是在各种粗糙的边缘上磕磕绊绊.
1这意味着只有一个索引/暂存区域.事实上,有一个主要索引,一般情况下你只看到一个索引,但是有一些极端情况,例如git commit -a,其他索引会暂时存在,并且混乱了这个模型.
2从技术上讲,ID是hash("blob %d\0%s" % (len(bytes), bytes))用Python-esque形式编写的.也就是说,通过以单词"blob",一个空格,内容长度的十进制表示,ASCII NUL字节,最后是实际内容开始,找到文件内容的哈希ID.这可以保证您不能通过简单地编写一个内容与某些现有提交的内容相同的文件来破坏Git ,例如3.这里的问题是可以测试每个对象的类型,而不是从上下文中假设类型,因此如果提交和常规文件具有相同的哈希,这将强制单个底层Git存储库对象同时具有类型提交和输入blob,这是不允许的.
3例如,您可以查看当前提交的内容,其ID是git rev-parse HEAD打印的内容git cat-file -p HEAD.将输出git cat-file写入常规文件和git add这个新文件,你将获得一个与git rev-parse HEADshow-all 不匹配的新ID,因为在内部,每个对象都以其类型名称为前缀,加上空间大小和 - NUL序列.
4要将它们煮沸很多,一个包对象只能引用同一包内的其他对象,除了所谓的"瘦"包,它们用于通过网络传输.
5假设有一个合并基础.如果有多个,则默认递归策略通过合并基于合并的候选者来构造一个.通常只有一个合并基础,我们不必担心这一点.偶尔根本没有合并基地; 在这种情况下,Git 2.9已将默认值更改为抱怨和失败,而不是从空树中合并.
| 归档时间: |
|
| 查看次数: |
1356 次 |
| 最近记录: |