请选择 进入手机版 | 继续访问电脑版

IT运维管理,ITIL,ITSS,ITSM,ISO20000-ITIL先锋论坛

 找回密码
 点击获取邀请码 - 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
查看: 432|回复: 1

Git 体系结构和索引文件

[复制链接]
来自- 广东广州

参加活动:0

组织活动:12

发表于 2017-9-9 15:19:15 | 显示全部楼层 |阅读模式 来自- 广东广州
我曾介绍过 Git 如何使用有向无环图 (DAG) 来组织存储库的提交对象。此外,我还研究过提交对象可以指代的 blob、树和标记对象。在这篇文章的最后,我还介绍了分支,包括 HEAD 和 head 的区别。阅读本文前必须先阅读这篇文章,因为本文将介绍 Git“三树”体系结构及其索引文件的重要性。进一步了解这些 Git 内部结构将有助于积累基础知识,提高 Git 用户的工作效率,并有助于用户在研究各种基于 Visual Studio IDE 图形 Git 工具的 Git 操作时获取新见解。

9 R) r& i$ n8 W2 @
回顾一下,之前有介绍过,Visual Studio 使用 Git API 与 Git 进行通信,以及 Visual Studio IDE Git 工具简化并取代了基础 Git 引擎的功能。对于要实现版本控制工作流而不依赖 Git 命令行接口 (CLI) 的开发者来说,这是一大福音。哎,但 IDE 的实用 Git 抽象有时可能会引起混乱。以下面的基本工作流为例:将项目添加到 Git 源控件,修改并暂存项目文件,再提交暂存文件。为此,需要打开“团队资源管理器 - 更改”窗格来查看更改后文件列表,再选择要暂存的文件。请注意图 1 中的最左侧图像,其中显示了我在工作目录中更改的两个文件(标记 1)。
3 n" `- @  w+ z/ H* j
图 1:“团队资源管理器 - 更改”窗格可以在“更改”和“暂存更改”部分中显示相同的文件
; [# G6 ?  Y, s
在下一张靠右的图像中,我暂存了其中一个更改后文件:Program.cs(标记 2)。当我这样做时,Program.cs 似乎已从“更改”列表中“移动”到了“暂存更改”列表中。如果我进一步修改,并在工作目录中保存 Program.cs 副本,那么此文件会继续出现在“暂存更改”部分中(标记 3),但同时也会出现在“更改”部分中(标记 4)! 如果不了解 Git 的幕后运行机制,可能会感到困惑,解疑释惑的关键在于有两个 Program.cs“副本”:一个位于工作文件夹中,另一个位于对象的 Git 内部数据库中。即使发现这一点,可能也无法知道在取消暂存副本、尝试暂存 Program.cs 的第二个更改副本、撤消对工作副本的更改或切换分支时会发生些什么。

, {: k$ n! Z6 H. j
若要真正了解 Git 在暂存、取消暂存、撤消、提交和签出文件时所做的工作,必须先了解 Git 的体系结构。
0 r& i/ m! }2 l8 Q
1Git 三树体系结构
2 n3 \. o2 K2 \* U3 F/ P8 l
Git 实现的是三树体系结构(在此上下文中,“树”指的是目录结构和文件)。在图 2(Git 三树体系结构利用非常重要的索引文件实现智能化和高效性能)中从左向右看,第一棵树是工作目录(包含隐藏 .git 文件夹的 OS 目录)中的文件和文件夹集合;第二棵树通常存储在.git 文件夹根目录中一个名为 index 的二进制文件中;第三棵树由代表 DAG 的 Git 对象组成(回顾一下,名为 SHA-1 的 Git 对象位于 .git\objects 中用两个十六进制数命名的文件夹内,也可以存储在 .git\objects\pack 的“包”文件中,以及 .git\objects\info\alternates 文件定义的文件路径中)。请注意,Git 存储库是由 .git 文件夹中的所有文件进行定义。人们通常将 DAG 称为“Git 存储库”,但这并不太准确:因为索引和 DAG 都包含在 Git 存储库中。

  Z8 e! z6 u  z6 T& ^# d, {
图 2:Git 三树体系结构利用非常重要的索引文件实现智能化和高效性能

  N( @9 {9 r8 u7 ~' W# L
请注意,虽然每颗树都存储目录结构和文件,但它们利用的数据结构不同,以便可以保留树专属元数据,并优化存储和检索。第一棵树(工作目录树,也称为“工作树”)显然是 OS 文件和文件夹(除了 OS 级数据结构外,没有其他任何特殊数据结构),满足软件开发者和 Visual Studio 的需求;第二颗树(Git 索引)跨越工作目录和组成 DAG 的提交对象,从而帮助 Git 快速执行工作目录文件内容比较和快速提交;第三棵树 (DAG) 让 Git 能够跟踪可靠历史版本控制系统,同时 Git 还能向其存储在索引和提交对象中的项添加有用的元数据。例如,在索引中存储的元数据有助于检测工作目录中的文件更改,而在提交对象中存储的元数据则有助于跟踪提交签发者和签发原因。

6 S, K! Q0 N% n, W9 T; t
本段内容是为了回顾三树体系结构中的三棵树,并引出本文剩余部分重点介绍的主题:已了解工作目录树的运行方式,因为实际上就是已精通使用的 OS 文件系统。如果阅读过我的上一篇文章,应该已非常了解 DAG 的运行方式。那么,此时,缺少的环节就是跨工作目录和 DAG 的索引树(下称“索引”)。实际上,索引的作用非常重要,将是本文剩余部分的唯一主题。

5 O6 V: J1 A0 O) @* S- G6 X
2索引的工作原理' w$ |/ H! v) F; G+ |) k
可能已听说过下面善意的意见:索引是“暂存区域”的代名词。 虽然这样的表述多少有些准确,但却掩盖了索引的真正作用:不仅支持暂存区域,还便于 Git 检测工作目录中的文件更改;协调分支合并过程,以便能够逐个文件解决冲突,并能随时安全地中止合并;将暂存文件和文件夹转换为树对象,这些对象的引用会被写入下一个提交对象。Git 还使用索引来保留工作树中文件的相关信息,以及从 DAG 中检索到的对象的相关信息,从而进一步将索引用作一种缓存。我们将更为全面地研究一下索引。
' w# \6 j8 @' w( }, j/ @5 V
索引实现自己的独立式文件系统,从而能够存储对文件夹和文件的引用,以及关于文件夹和文件的元数据。Git 如何以及何时更新此索引取决于所发出的 Git 命令类型和指定的命令选项(若要试一试,可以使用 Git 更新索引底层命令来自行管理索引),因此这里无法详尽无遗地进行介绍。不过,使用 Visual Studio Git 工具时,不妨留意一下 Git 更新索引以及使用索引中存储信息的主要方式。图 3 展示了 Git 如何在用户暂存文件时在索引中更新工作目录数据,以及如何在用户启动合并(若有合并冲突)、执行克隆/拉取或切换分支时在索引中更新 DAG 数据。另一方面,Git 依赖索引中存储的信息,在用户签发提交后更新 DAG,并在用户执行克隆/拉取或切换分支后更新工作目录。意识到 Git 依赖索引,并且索引跨越多个 Git 操作后,便会开始重视用于修改索引的高级 Git 命令,从而能够有效地巧妙处理 Git 操作。
5 M! ?' Q/ B5 e% Q( k
图 3:更新索引的主要 Git 操作(绿色)和依赖索引所含信息的 Git 操作(红色)
) s  B0 y$ ]! J7 u
我们将在工作目录中新建一个文件,看看此文件被写入索引时会发生些什么。在用户暂存此文件后,Git 会立即使用以下字符串串联公式创建标头:

  U* v3 d9 Q( a+ N0 ^- q3 j" f' r
blob{space}{file-length in bytes}{null-termination character}
1 h; ~7 O! Y. A: U% Q0 A+ U1 o
然后,Git 将标头串联到文件内容开头。因此,对于包含字符串“Hello”的文本文件,将标头与文件内容串联后生成如下字符串(请注意,字母“H”前面有一个空字符):
- X  ^! X; z- n  Q1 p$ Y6 R
blob 5Hello

0 X& l  a/ ?4 t6 P4 n$ W2 D8 ]
为了更加明确化,下面展示了此字符串的十六进制版本:

+ V0 h; K2 d- P$ G, Y! O62 6C 6F 62 20 35 00 48 65 6C 6C 6F

: O+ C) f: u) q% N
然后,Git 计算此字符串的 SHA-1:

, P+ C" t+ T2 J/ A) j4 K5ab2f8a4323abafb10abb68657d9d39f1a775057

  c- y! @7 W: A7 K
接下来,Git 检查现有索引,以确定此文件夹\文件名的条目是否已存在且包含相同的 SHA-1。如果有,Git 会在 .git\objects 文件夹中找到此 blob 对象,并更新它的修改日期时间(Git 绝不会覆盖存储库中的现有对象;而是更新上次修改日期,以便延迟这一新添加的对象被视为垃圾遭到回收的时间)。如果没有,Git 会使用 SHA-1 字符串的前两个字符作为 .git\objects 中的目录名称,并使用剩下的 38 个字符命名 blob 文件,再对此文件进行 zlib 压缩并编写其内容。在我的示例中,Git 会在 .git\objects 中创建名为 5a 的文件夹,再将 blob 对象作为文件 b2f8a4323abafb10abb68657d9d39f1a775057 写入此文件夹。

- K" q# V2 I* ~9 f
当 Git 以这种方式创建 blob 对象时,大家可能会感到惊讶,因为 blob 对象中明显缺少一个预期的文件属性:文件名! 然而,这是有意而为之。回顾一下,Git 是内容可寻址文件系统,因此它管理的是名为 SHA-1 的 blob 对象,而不是文件。每个 blob 对象通常是由至少一个树对象引用,反过来树对象通常是由提交对象引用。最终,Git 树对象表示暂存的文件的文件夹结构。不过,在用户签发提交之前,Git 不会创建这些树对象。因此,可以得出下列结论:如果 Git 仅使用索引来准备提交对象,还必须为索引中的每个 blob 捕获文件路径引用,而它正是这样做的。其实,即使两个 blob 的 SHA-1 值相同,只要每个映射到不同的文件名或不同的路径/文件值,都将显示为索引中的单独条目。

% A8 B5 @. W' ?! E
Git 还将文件元数据(如文件的创建日期和修改日期)与写入索引的每个 blob 对象一起保存。Git 利用此类信息通过比较文件日期和使用启发,有效地检测工作目录中的文件更改,而不是采用重新计算工作目录中每个文件的 SHA-1 值这种蛮力做法。此类策略可以加快“团队资源管理器 - 更改”窗格中显示信息的速度,或发出高层 Git 状态命令后显示信息的速度。
7 o; w' c; [$ _
有了工作目录文件对应的索引条目及其相关元数据后,Git 据说就可以“跟踪”文件了,因为它可以很容易地将文件副本与工作目录中保留的副本进行比较。从技术角度来讲,被跟踪的文件也存在于工作目录中,并包含在下一次提交中。这与未受跟踪的文件相反,未受跟踪的文件分为以下两种类型:位于工作目录中但不位于索引中的文件,以及显式指定为不受跟踪的文件(见“索引扩展”部分)。总而言之,借助索引,Git 可以确定跟踪、不跟踪以及不得跟踪哪些文件。
. ]0 Y* N' g6 X
为了更好地理解索引的具体内容,让我们来看看一个具体示例,从新的 Visual Studio 项目入手。此项目是否复杂并不太重要,只需几个文件就可以充分说明正在发生什么。新建名为 MSDNConsoleApp 的控制台应用程序,并选中“创建解决方案的目录”和“新建 Git 存储库”复选框。单击“确定”,创建解决方案。
1 b4 w+ Y3 T' p
稍后我将发出一些 Git 命令。因此,若要在系统上运行这些命令,请在工作目录中打开命令提示符窗口,并确保可以在继续操作时使用此窗口。一种为特定 Git 存储库快速打开 Git 命令窗口的方法是,访问“Visual Studio Team”菜单,并选择“管理连接”。此时,将看到本地 Git 存储库列表,以及相应存储库的工作目录路径。右键单击存储库名称,并选择“打开命令提示符”,以启动可用于输入 Git CLI 命令的窗口。

9 h% _6 I( K! V: D" g; S. W# h4 Z
创建解决方案后,立即打开“团队资源管理器 - 分支”窗格(图 4 中的标记 1),以确定 Git 是否创建了名为“主分支”的默认分支(标记 2)。右键单击“主分支”(标记 2),并选择“查看历史记录”(标记 3),以查看 Visual Studio 代表用户创建的两个提交对象(标记 4)。第一个对象包含提交消息“添加 .gitignore 和 .gitattributes”;第二个对象包含提交消息“添加项目文件”。
/ n1 a. h) m' E, w$ w
图 4:查看历史记录以确定 Visual Studio 在用户新建项目时所做的工作

3 v. f$ `) [3 _" d0 w
打开“团队资源管理器 - 更改”窗格。Visual Studio 依赖 Git API 在此窗口中填充项,因为它是 Visual Studio 版 Git 状态命令。当前,此窗口指明工作目录中没有取消暂存的更改。为了做出此决定,Git 将每个索引条目与各个工作目录文件进行比较。有了索引的文件条目和相关的文件元数据后,Git 就有了所需的全部信息,可以确定用户是否进行了任何更改、添加、删除,或是否重命名了工作目录中的任何文件(不包括 .gitignore 文件中提到的任何文件)。

0 a/ c% H- O% ?, t& l' z9 Z1 D
因此,在方便 Git 智能化确定工作目录树与 HEAD 指向的提交对象的差异方面,索引起到了关键作用。若要详细了解索引提供给 Git 引擎的信息种类,请转到前面打开的命令行窗口,并发出以下底层命令:

) J1 M0 F: G( ^. wgit ls-files --stage
. n# O5 ^. I& S2 D1 d- q# V# e# P
可以随时发出此命令,从而生成索引中当前包含的文件的完整列表。在我的系统上,此命令的输出如下:

0 D0 [# A' V: v/ E100644 1ff0c423042b46cb1d617b81efb715defbe8054d 0       .gitattributes100644 3c4efe206bd0e7230ad0ae8396a3c883c8207906 0       .gitignore100644 f18cc2fac0bc0e4aa9c5e8655ed63fa33563ab1d 0       MSDNConsoleApp.sln100644 88fa4027bda397de6bf19f0940e5dd6026c877f9 0       MSDNConsoleApp/App.config100644 d837dc8996b727d6f6d2c4e788dc9857b840148a 0       MSDNConsoleApp/MSDNConsoleApp.csproj100644 27e0d58c613432852eab6b9e693d67e5c6d7aba7 0       MSDNConsoleApp/Program.cs100644 785cfad3244d5e16842f4cf8313c8a75e64adc38 0       MSDNConsoleApp/Properties/AssemblyInfo.cs

1 h+ i- |2 K2 x8 \8 y3 ~
输出的第一列是八进制的 Unix OS 文件模式。不过,Git 并不支持全部文件模式值。可能只会看到 100644(对于非 EXE 文件)和 100755(对于基于 Unix 的 EXE 文件,适用于 Windows 的 Git 也对可执行文件类型使用 100644)。第二列是文件的 SHA-1 值。第三列是文件的合并暂存值,0 表示没有冲突,1、2 或 3 表示有合并冲突。最后,请注意,七个 blob 对象的路径和文件名全都存储在索引中。Git 使用路径值在下一次提交之前生成树对象(稍后将详细介绍)。
6 y7 p! _) g7 j9 l7 D: k! v: V2 W# }
现在,让我们来研究一下索引文件本身。由于它是二进制文件,因此我将使用 HexEdit 4(hexedit.com 提供的免费软件十六进制编辑器)查看文件内容(图 5 摘录了部分内容)。
# _- W# v' `" k; v5 x
图 5:项目的 Git 索引文件的十六进制转储
( b* j- v2 c2 D  q# L# f
图 6:Git 索引标头数据格式
索引文件 - 标头条目
00 - 03
5 B" B6 J2 Y0 [( l  y0 Y+ O2 k(4 字节)
DIRC用于目录缓存条目的固定标头。
* P# Y* W- M! H% d- e# O所有索引文件的开头都是此条目。
04 - 07
- A- q# k, a% W& M(4 字节)
Version索引版本号(适用于 Windows 的 Git
: G1 x1 R' O6 e7 [/ U9 p当前使用版本 2)。
08 - 11% f2 B4 H% Y/ f( B
(4 字节)
条目数作为 4 字节的值,索引最多支持 ' W. m) Q, v) Y* D, \
4,294,967,296 个条目!

: {: @$ M$ }$ n( z' I索引的前 12 个字节包含标头(见图 6)。前 4 个字节始终包含字符 DIRC(“目录缓存”的缩写),这是 Git 索引通常被称为“缓存”的原因之一。接下来的 4 个字节包含索引版本号,默认为版本 2,除非要使用 Git 的特定功能(如稀疏签出)。在这种情况下,可以设置为版本 3 或 4。最后 4 个字节包含索引进一步包含的文件条目数。
. l8 N& i7 e) }) B0 J& k% ]
12 字节标头后面是 n 个索引项的列表,其中 n 与索引标头描述的条目数一致。图 7 展示了每个索引条目的格式。Git 根据路径/文件名字段按升序排列索引条目。
+ ~& X3 D4 J8 `( L3 @% k8 I( O3 z
图 7:Git 索引文件 - 索引条目数据格式
索引文件 - 索引条目
4 字节32 位创建时间(以秒为单位)与 1970 年 1 月 1 日 00:00:00 之间相隔的秒数。
4 字节32 位创建时间 - 纳秒组成部分创建时间的纳秒组成部分(以秒为单位)。
4 字节32 位修改时间(以秒为单位)与 1970 年 1 月 1 日 00:00:00 之间相隔的秒数。
4 字节32 位修改时间 - 纳秒组成部分修改时间的纳秒组成部分(以秒为单位)。
4 字节设备与文件相关的元数据,源自 Unix OS 上使用的文件属性。
4 字节Inode
4 字节mode
4 字节用户 ID
4 字节组 ID
4 字节文件内容长度文件内容字节数。
20 字节SHA-1相应 blob 对象的 SHA-1 值。
2 个字节标记(从高到低位)1 位:假设有效/假设未更改标志;1 位:扩展标志(如果版本低于 3,必须为 0;如果为 1,在路径\文件名前面附加 2 个字节);2 位:合并暂存;12 位:路径\文件名长度(如果小于 0xFFF)
2 个字节 ) l1 k, a; O; v
(版本 3 7 G# _% ~" X& [* F. Y0 R& Q* G
或更高版本)
! d# p1 N  G% Q0 c" H! E0 s
标记(从高到低位)
1 R! ]. t0 X( L& x1 o! C# _1 位:日后使用$ u: ~' C  C% g( C" D
1 位:跳过工作树标志(稀疏签出)
9 j2 R+ o0 x- i/ o; {( |1 位:有意添加标志 (git add -N)
: ]) G( x9 k: M, [13 位:未使用,必须为零
长度不固定路径/文件名以空字符结尾

- o. v0 z( l) D第一个 8 字节表示文件创建时间(与 1970 年 1 月 1 日 00:00:00 之间相隔的秒数)。第二个 8 字节表示文件修改时间(与 1970 年 1 月 1 日 00:00:00 之间相隔的秒数)。接下来是五个与主机 OS 相关的文件属性元数据的 4 字节值(设备、inode、模式、用户 ID 和组 ID)。唯一仅限 Windows 的值是模式,值通常是八进制的 100644,我在前面介绍 ls-files 命令输出时提到过(这会转换为 4 字节 814AH 值,如图 5 中的 26H 位置所示)。

# l7 l8 P* O. r# t* X# y$ [8 N
元数据后面是 4 字节的文件内容长度。在图 5 中,此值从 030 行的 00 00 0A 15(十进制为 2,581)开始,我的系统上的 .gitattributes 文件长度为:

$ }+ D0 C; H# o/ Q3 J, u3 i
05/08/2017  09:24 PM    <DIR>          .05/08/2017  09:24 PM    <DIR>          ..05/08/2017  09:24 PM             2,581 .gitattributes05/08/2017  09:24 PM             4,565 .gitignore05/08/2017  09:24 PM    <DIR>          MSDNConsoleApp05/08/2017  09:24 PM             1,009 MSDNConsoleApp.sln               3 File(s)          8,155 bytes               3 Dir(s)  92,069,982,208 bytes free
' ?% V1 |' m$ K5 _: x5 m
偏移 034H 是 blob 对象的 20 字节 SHA-1 值:
) _; H/ d" c& s0 r: F/ M' U- Y
1ff0c423042b46cb1d617b81efb715defbe8054d.
4 ?+ e8 n6 w% O9 U
请注意,此 SHA-1 指向 blob 对象,其中包含相关文件 (.gitattributes) 的文件内容。
* J, V5 p& B9 ^5 S, }! G. H" H
048H 是 2 字节值,包含两个 1 位标志、2 位合并暂存值,以及当前索引条目的路径/文件名的 12 位长度。在这两个 1 位标志中,高位指定索引条目是否设置了假设未更改标志(通常使用 Git 更新索引底层命令完成);低位指定是否在路径\文件名条目之前附加两字节的数据(只有当索引版本不小于 3 时,此位才会是 1)。接下来的 2 位保留介于 0 到 3 之间的合并暂存值,如前所述。12 位值包含路径\文件名字符串的长度。
0 m8 r  A) J( _1 }) ^" a4 U/ L7 K
如果设置了扩展标志,那么 2 字节值包含跳过工作树标志和有意添加位标志,以及填充占位符。
" c! H7 N6 ?( F; p. b  [
最后,长度不固定的字节序列包含路径\文件名。此值以一个或多个空字符结尾。空字符结尾后面是索引中的下一个 blob 对象,或一个或多个索引扩展条目(我很快将会介绍)。
9 X4 u$ s$ G( ~& P$ c: W
我在前面提到过,在用户提交暂存内容前,Git 不会生成树对象。也就是说,索引最初只包含路径/文件名,以及对 blob 对象的引用。不过,只要用户签发提交,Git 就会将索引更新为包含对在上次提交期间创建的树对象的引用。如果在下一次提交期间这些目录引用仍位于工作目录中,那么可以使用缓存的树对象引用,以减少 Git 需要在下一次提交期间完成的工作量。可以看到,索引的作用涉及许多方面,正因为此,它被描述为索引、暂存区域和缓存。

% o/ ~6 }1 v0 z$ a4 f7 G3 d
图 7 展示的索引条目只支持 blob 对象引用。Git 使用扩展,以便可以存储树对象。

) N  ~; ?. ^3 G1 Z0 z
3索引扩展
9 g0 \+ M' H5 x# Y; ^) F- H! K" B, I
索引可以包含扩展条目,这些条目存储特殊化数据流,为 Git 引擎提供其他信息,以供它在监视工作目录中的文件和准备下一次提交时参考。为了缓存在上次提交期间创建的树对象,Git 将树扩展对象添加到工作目录的根目录的索引,以及每个子目录的索引。
( g' D) M9 I% c6 O( U, l& Z( H1 ?
图 5 中的标记 2 展示了索引的最终字节,并捕获索引中存储的树对象。图 8 展示了树扩展数据的格式。
* t- p, h9 s$ l- O
图 8:Git 索引文件树扩展对象数据格式
索引文件 - 缓存的树扩展标头
4 字节TREE用于缓存的树扩展条目的固定签名。
4 字节表示 TREE 扩展数据长度的 32 位数字8 Y' W3 ^) w: c9 }8 i( |. I" }4 n
缓存的树扩展条目
变量Path以空字符结尾的路径字符串(只有是根树,才为 NULL)。
ASCII 数字条目数ASCII 数字,表示此树条目覆盖的索引中的条目数。
1 个字节20H(空格字符)+ P5 T- o' m* e+ r
ASCII 数字子树数量表示此树的子树数量的 ASCII 数字。
1 个字节0AH(换行符)/ s% }/ o9 d+ m. g, S/ ^; y7 n
20 字节树对象的 SHA-1此条目生成的树对象的
7 l7 L/ O! i. A0 ^) NSHA-1 值。
: U* B1 ]7 F! D  f7 p' w; X+ l- a
在偏移 284H 处出现的树扩展数据标头包含字符串“TREE”(标记了缓存的树扩展数据的开头),后跟 32 位值(表示后面的扩展数据的长度)。接下来是各个树条目:第一个条目是树路径的以 NULL 结尾的长度不固定字符串值(或直接为 NUL,如果是根树的话)。后跟 ASCII 值,因此十六进制编辑器中显示“7”,即当前树覆盖的 blob 条目数(因为这是根树,条目数与先前在签发 Git ls-files 暂存命令时看到的条目数相同)。下一个字符是空格,后又跟一个 ASCII 数字,表示当前树的子树数量。
# F# H! m  n" e" I/ H( C: D6 P
我们项目的根树只有 1 个子树:MSDNConsoleApp。此值后面是换行符和树的 SHA-1 值。SHA-1 从偏移 291 处的 0d21e2 开始。

8 W3 Q+ p7 O- @$ @
我们将确认 0d21e2 是否就是根树的 SHA-1。为此,请转到命令窗口并输入:
" G. A& W6 g! k& A: w4 b
git log
; @+ @; g: Y+ z: Y2 x) F9 t
这将显示与最近提交相关的详细信息:
  H' s2 f3 _2 r4 I
commit 5192391e9f907eeb47aa38d1c6a3a4ea78e33564Author: Jonathan Waldman <jonathan.waldman@live.com>Date:   Mon May 8 21:24:15 2017 -0500  Add project files.commit dc0d3343fa24e912f08bc18aaa6f664a4a020079Author: Jonathan Waldman <jonathan.waldman@live.com>Date:   Mon May 8 21:24:07 2017 -0500  Add .gitignore and .gitattributes.

& b1 Y# r- C3 P" \0 f
最近一次提交的时间戳为 21:24:15,所以就是上次更新索引的提交。我可以使用此提交的 SHA-1 查找根树的 SHA-1 值:
  G0 \3 H) R2 p; t* R0 M0 q
git cat-file -p 51923
9 B. r! i: L% K9 s
输出如下:
+ W) _* z6 ~( W7 Y/ R
tree 0d21e2f7f760f77ead2cb85cc128efb13f56401dparent dc0d3343fa24e912f08bc18aaa6f664a4a020079author Jonathan Waldman <jonathan.waldman@live.com> 1494296655 -0500committer Jonathan Waldman <jonathan.waldman@live.com> 1494296655 -0500

% V" }5 q- K2 D; |5 }3 P- W- H* q1 y
以上树条目就是根树对象。据此可以确认,索引转储中偏移 291H 处的 0d21e2 值实际上就是根树对象的 SHA-1 值。
# n, D. Y" V' y7 C/ b1 _# d3 i: L
SHA-1 值后面为其他树条目(从偏移 2A5H 开始)。若要确认根树下缓存的树对象的 SHA-1 值,请运行以下命令:
0 a$ I" N  v2 `/ N1 B8 ^$ [
git ls-tree -r -d master

% O2 y- K: o% Z6 H3 @
这仅以递归方式显示当前分支上的树对象:
8 s5 w2 V; t7 e2 }( k8 [
040000 tree c7c367f2d5688dddc25e59525cc6b8efd0df914d    MSDNConsoleApp040000 tree 2723ceb04eda3051abf913782fadeebc97e0123c    MSDNConsoleApp/Properties

4 I- L; a1 M  Y
第一列中的模式值 040000 表明此对象是目录,而不是文件。

$ t7 g- B9 _* i& n# M( K1 M' q6 E7 u
最后,索引的最后 20 个字节包含表示索引本身的 SHA-1 哈希值:就跟预期一样,Git 使用此 SHA-1 值来验证索引的数据完整性。
# Z' a* ~3 J. b2 m3 }1 f" p
虽然我介绍了本文中示例索引文件的所有条目,但更大、更复杂的索引文件成为一种规范。索引文件格式支持附加的扩展数据流,例如:
$ ?9 y2 R5 ~& Y  P7 D. o
  • 支持合并操作和合并冲突解决方法的数据流。它的签名为“REUC”(用于解决撤消冲突)。
  • 用于维护未受跟踪的文件(这些是未包含在跟踪范围内的文件,在 .gitignore 和 .git\info\exclude 中指定,并由 core.excludesfile 指向的文件指定)的缓存的数据流。它的签名为“UNTR”。
  • 支持拆分索引模式的数据流,以便加快非常大的索引文件的索引更新速度。它的签名为“link”。
    & U6 t. K- B" p  C& i! r

" \/ v2 u" @& c. d5 j& f
借助索引的扩展功能,可以继续添加索引功能。

4 B# m5 o7 {' ?
4总结) h4 u) K: z4 Y0 t2 k* \9 Z3 p9 \' {
本文回顾了 Git 三树体系结构,并深入详细地探讨了索引文件的幕后运行机制。我介绍了 Git 为响应特定操作而更新索引,并依赖索引包含的信息,以便执行其他操作。
$ h/ @3 P, J5 g, q
可以在不太考虑索引的情况下使用 Git。然而,了解索引可以获取对 Git 核心功能的宝贵见解,同时了解 Git 如何检测工作目录中的文件更改、什么是暂存区域及其非常有用的原因、Git 如何管理合并以及 Git 执行特定操作如此快速的原因。此外,还可以轻松了解签出命令和变基命令的命令行变体,以及软重置、混合重置与硬重置的区别。通过此类功能,可以指定应在发出特定命令时更新索引、工作目录还是两者都更新。了解 Git 工作流、策略和高级操作时,将会看到此类选项。本文旨在让读者认识到索引起到的重要作用,从而可以更好地了解具体利用方式。
# x: B; f) ^, M# Z, U9 ]; c
原创:Jonathan Waldman

- c: G1 x5 n3 b/ d& B0 R+ p* r

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?点击获取邀请码 - 立即注册

x
来自- 北京

参加活动:0

组织活动:0

发表于 2018-5-2 16:36:55 | 显示全部楼层 来自- 北京
好好学习学习,谢谢

本版积分规则

选择云运维时代的王牌讲师-长河老师,助你轻松入门ITIL Foundation培训课程

QQ|小黑屋|手机版|Archiver|ITIL先锋论坛五万运维人社区 ( 粤ICP备17056641号|网站地图

Baidu

GMT+8, 2018-6-22 04:13 , Processed in 0.234536 second(s), 36 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表