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

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

 找回密码
 微信、QQ、手机号一键注册

扫描二维码登录本站

QQ登录

只需一步,快速开始

艾拓先锋
搜索
查看: 680|回复: 1

Git 体系结构和索引文件

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

参加活动:0

组织活动:12

发表于 2017-9-9 15:19:15 | 显示全部楼层 |阅读模式 来自- 广东广州
我曾介绍过 Git 如何使用有向无环图 (DAG) 来组织存储库的提交对象。此外,我还研究过提交对象可以指代的 blob、树和标记对象。在这篇文章的最后,我还介绍了分支,包括 HEAD 和 head 的区别。阅读本文前必须先阅读这篇文章,因为本文将介绍 Git“三树”体系结构及其索引文件的重要性。进一步了解这些 Git 内部结构将有助于积累基础知识,提高 Git 用户的工作效率,并有助于用户在研究各种基于 Visual Studio IDE 图形 Git 工具的 Git 操作时获取新见解。
4 }  F" n& B+ {" y4 p2 f! ~
回顾一下,之前有介绍过,Visual Studio 使用 Git API 与 Git 进行通信,以及 Visual Studio IDE Git 工具简化并取代了基础 Git 引擎的功能。对于要实现版本控制工作流而不依赖 Git 命令行接口 (CLI) 的开发者来说,这是一大福音。哎,但 IDE 的实用 Git 抽象有时可能会引起混乱。以下面的基本工作流为例:将项目添加到 Git 源控件,修改并暂存项目文件,再提交暂存文件。为此,需要打开“团队资源管理器 - 更改”窗格来查看更改后文件列表,再选择要暂存的文件。请注意图 1 中的最左侧图像,其中显示了我在工作目录中更改的两个文件(标记 1)。
/ b+ I& p( i, T$ W/ Z
0.jpg
图 1:“团队资源管理器 - 更改”窗格可以在“更改”和“暂存更改”部分中显示相同的文件

# H9 g4 ]2 j$ }! z
在下一张靠右的图像中,我暂存了其中一个更改后文件:Program.cs(标记 2)。当我这样做时,Program.cs 似乎已从“更改”列表中“移动”到了“暂存更改”列表中。如果我进一步修改,并在工作目录中保存 Program.cs 副本,那么此文件会继续出现在“暂存更改”部分中(标记 3),但同时也会出现在“更改”部分中(标记 4)! 如果不了解 Git 的幕后运行机制,可能会感到困惑,解疑释惑的关键在于有两个 Program.cs“副本”:一个位于工作文件夹中,另一个位于对象的 Git 内部数据库中。即使发现这一点,可能也无法知道在取消暂存副本、尝试暂存 Program.cs 的第二个更改副本、撤消对工作副本的更改或切换分支时会发生些什么。
) m. N+ j" f2 b; H! i3 d
若要真正了解 Git 在暂存、取消暂存、撤消、提交和签出文件时所做的工作,必须先了解 Git 的体系结构。
3 I, ~" y' c: k7 e
1Git 三树体系结构
& P, |0 ]* g5 m- m7 I
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 存储库中。
0 r7 x0 l* h% H& M$ n+ y
0.jpg
图 2:Git 三树体系结构利用非常重要的索引文件实现智能化和高效性能
5 [3 A1 h! ?5 r# R- D: B
请注意,虽然每颗树都存储目录结构和文件,但它们利用的数据结构不同,以便可以保留树专属元数据,并优化存储和检索。第一棵树(工作目录树,也称为“工作树”)显然是 OS 文件和文件夹(除了 OS 级数据结构外,没有其他任何特殊数据结构),满足软件开发者和 Visual Studio 的需求;第二颗树(Git 索引)跨越工作目录和组成 DAG 的提交对象,从而帮助 Git 快速执行工作目录文件内容比较和快速提交;第三棵树 (DAG) 让 Git 能够跟踪可靠历史版本控制系统,同时 Git 还能向其存储在索引和提交对象中的项添加有用的元数据。例如,在索引中存储的元数据有助于检测工作目录中的文件更改,而在提交对象中存储的元数据则有助于跟踪提交签发者和签发原因。
2 |' c( }+ _4 |# a& H
本段内容是为了回顾三树体系结构中的三棵树,并引出本文剩余部分重点介绍的主题:已了解工作目录树的运行方式,因为实际上就是已精通使用的 OS 文件系统。如果阅读过我的上一篇文章,应该已非常了解 DAG 的运行方式。那么,此时,缺少的环节就是跨工作目录和 DAG 的索引树(下称“索引”)。实际上,索引的作用非常重要,将是本文剩余部分的唯一主题。

3 u/ ]8 C& c( I3 E/ D9 o+ b
2索引的工作原理
3 ~; }. t; B  ^! S3 o6 W# \
可能已听说过下面善意的意见:索引是“暂存区域”的代名词。 虽然这样的表述多少有些准确,但却掩盖了索引的真正作用:不仅支持暂存区域,还便于 Git 检测工作目录中的文件更改;协调分支合并过程,以便能够逐个文件解决冲突,并能随时安全地中止合并;将暂存文件和文件夹转换为树对象,这些对象的引用会被写入下一个提交对象。Git 还使用索引来保留工作树中文件的相关信息,以及从 DAG 中检索到的对象的相关信息,从而进一步将索引用作一种缓存。我们将更为全面地研究一下索引。

" ?. E3 z0 j7 C
索引实现自己的独立式文件系统,从而能够存储对文件夹和文件的引用,以及关于文件夹和文件的元数据。Git 如何以及何时更新此索引取决于所发出的 Git 命令类型和指定的命令选项(若要试一试,可以使用 Git 更新索引底层命令来自行管理索引),因此这里无法详尽无遗地进行介绍。不过,使用 Visual Studio Git 工具时,不妨留意一下 Git 更新索引以及使用索引中存储信息的主要方式。图 3 展示了 Git 如何在用户暂存文件时在索引中更新工作目录数据,以及如何在用户启动合并(若有合并冲突)、执行克隆/拉取或切换分支时在索引中更新 DAG 数据。另一方面,Git 依赖索引中存储的信息,在用户签发提交后更新 DAG,并在用户执行克隆/拉取或切换分支后更新工作目录。意识到 Git 依赖索引,并且索引跨越多个 Git 操作后,便会开始重视用于修改索引的高级 Git 命令,从而能够有效地巧妙处理 Git 操作。

% j' P- H, \& ?/ a! _  G) u
0.jpg
图 3:更新索引的主要 Git 操作(绿色)和依赖索引所含信息的 Git 操作(红色)

7 a, Q8 R; B0 }7 ]; r+ }) k: u
我们将在工作目录中新建一个文件,看看此文件被写入索引时会发生些什么。在用户暂存此文件后,Git 会立即使用以下字符串串联公式创建标头:

, D8 F! T( ]2 _7 `& E* K
blob{space}{file-length in bytes}{null-termination character}
# a6 E2 s/ k# i) k, }
然后,Git 将标头串联到文件内容开头。因此,对于包含字符串“Hello”的文本文件,将标头与文件内容串联后生成如下字符串(请注意,字母“H”前面有一个空字符):
: ~8 E( c' f: t5 V& s# Z/ a- u
blob 5Hello

7 E9 f* \  Q) y! s
为了更加明确化,下面展示了此字符串的十六进制版本:
$ c0 z! A0 u% ^$ |: x
62 6C 6F 62 20 35 00 48 65 6C 6C 6F

# q0 t* }1 a: q' g4 r
然后,Git 计算此字符串的 SHA-1:

! W" X# z( Y1 \! h7 R% x) V0 o5ab2f8a4323abafb10abb68657d9d39f1a775057

& g- a; M- ~6 `3 x7 t
接下来,Git 检查现有索引,以确定此文件夹\文件名的条目是否已存在且包含相同的 SHA-1。如果有,Git 会在 .git\objects 文件夹中找到此 blob 对象,并更新它的修改日期时间(Git 绝不会覆盖存储库中的现有对象;而是更新上次修改日期,以便延迟这一新添加的对象被视为垃圾遭到回收的时间)。如果没有,Git 会使用 SHA-1 字符串的前两个字符作为 .git\objects 中的目录名称,并使用剩下的 38 个字符命名 blob 文件,再对此文件进行 zlib 压缩并编写其内容。在我的示例中,Git 会在 .git\objects 中创建名为 5a 的文件夹,再将 blob 对象作为文件 b2f8a4323abafb10abb68657d9d39f1a775057 写入此文件夹。
: ?8 E, w  j8 U+ k2 r
当 Git 以这种方式创建 blob 对象时,大家可能会感到惊讶,因为 blob 对象中明显缺少一个预期的文件属性:文件名! 然而,这是有意而为之。回顾一下,Git 是内容可寻址文件系统,因此它管理的是名为 SHA-1 的 blob 对象,而不是文件。每个 blob 对象通常是由至少一个树对象引用,反过来树对象通常是由提交对象引用。最终,Git 树对象表示暂存的文件的文件夹结构。不过,在用户签发提交之前,Git 不会创建这些树对象。因此,可以得出下列结论:如果 Git 仅使用索引来准备提交对象,还必须为索引中的每个 blob 捕获文件路径引用,而它正是这样做的。其实,即使两个 blob 的 SHA-1 值相同,只要每个映射到不同的文件名或不同的路径/文件值,都将显示为索引中的单独条目。
# K( C8 m6 i2 A
Git 还将文件元数据(如文件的创建日期和修改日期)与写入索引的每个 blob 对象一起保存。Git 利用此类信息通过比较文件日期和使用启发,有效地检测工作目录中的文件更改,而不是采用重新计算工作目录中每个文件的 SHA-1 值这种蛮力做法。此类策略可以加快“团队资源管理器 - 更改”窗格中显示信息的速度,或发出高层 Git 状态命令后显示信息的速度。
$ _: D2 K* P2 n" V: D
有了工作目录文件对应的索引条目及其相关元数据后,Git 据说就可以“跟踪”文件了,因为它可以很容易地将文件副本与工作目录中保留的副本进行比较。从技术角度来讲,被跟踪的文件也存在于工作目录中,并包含在下一次提交中。这与未受跟踪的文件相反,未受跟踪的文件分为以下两种类型:位于工作目录中但不位于索引中的文件,以及显式指定为不受跟踪的文件(见“索引扩展”部分)。总而言之,借助索引,Git 可以确定跟踪、不跟踪以及不得跟踪哪些文件。

* S0 [+ W8 U# }1 {4 f% p4 f
为了更好地理解索引的具体内容,让我们来看看一个具体示例,从新的 Visual Studio 项目入手。此项目是否复杂并不太重要,只需几个文件就可以充分说明正在发生什么。新建名为 MSDNConsoleApp 的控制台应用程序,并选中“创建解决方案的目录”和“新建 Git 存储库”复选框。单击“确定”,创建解决方案。
# t; n) W. H! m8 h
稍后我将发出一些 Git 命令。因此,若要在系统上运行这些命令,请在工作目录中打开命令提示符窗口,并确保可以在继续操作时使用此窗口。一种为特定 Git 存储库快速打开 Git 命令窗口的方法是,访问“Visual Studio Team”菜单,并选择“管理连接”。此时,将看到本地 Git 存储库列表,以及相应存储库的工作目录路径。右键单击存储库名称,并选择“打开命令提示符”,以启动可用于输入 Git CLI 命令的窗口。

7 J9 N: K: E) `2 _
创建解决方案后,立即打开“团队资源管理器 - 分支”窗格(图 4 中的标记 1),以确定 Git 是否创建了名为“主分支”的默认分支(标记 2)。右键单击“主分支”(标记 2),并选择“查看历史记录”(标记 3),以查看 Visual Studio 代表用户创建的两个提交对象(标记 4)。第一个对象包含提交消息“添加 .gitignore 和 .gitattributes”;第二个对象包含提交消息“添加项目文件”。
$ Z% ]. v$ m, H2 A
0.jpg
图 4:查看历史记录以确定 Visual Studio 在用户新建项目时所做的工作
: j. ~; ^; ]! \( M5 X/ B  G
打开“团队资源管理器 - 更改”窗格。Visual Studio 依赖 Git API 在此窗口中填充项,因为它是 Visual Studio 版 Git 状态命令。当前,此窗口指明工作目录中没有取消暂存的更改。为了做出此决定,Git 将每个索引条目与各个工作目录文件进行比较。有了索引的文件条目和相关的文件元数据后,Git 就有了所需的全部信息,可以确定用户是否进行了任何更改、添加、删除,或是否重命名了工作目录中的任何文件(不包括 .gitignore 文件中提到的任何文件)。
  d7 R$ C( x" ~" J2 }( [9 i: X
因此,在方便 Git 智能化确定工作目录树与 HEAD 指向的提交对象的差异方面,索引起到了关键作用。若要详细了解索引提供给 Git 引擎的信息种类,请转到前面打开的命令行窗口,并发出以下底层命令:
( y% ^' R9 n8 Q* w. R
git ls-files --stage
. C: G9 w! W  E) F6 a* W4 |+ O
可以随时发出此命令,从而生成索引中当前包含的文件的完整列表。在我的系统上,此命令的输出如下:
9 |6 b2 @0 n/ z
100644 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
/ l! N7 E' J$ n& v, M
输出的第一列是八进制的 Unix OS 文件模式。不过,Git 并不支持全部文件模式值。可能只会看到 100644(对于非 EXE 文件)和 100755(对于基于 Unix 的 EXE 文件,适用于 Windows 的 Git 也对可执行文件类型使用 100644)。第二列是文件的 SHA-1 值。第三列是文件的合并暂存值,0 表示没有冲突,1、2 或 3 表示有合并冲突。最后,请注意,七个 blob 对象的路径和文件名全都存储在索引中。Git 使用路径值在下一次提交之前生成树对象(稍后将详细介绍)。
' ?$ ~5 R& \' p6 v6 \7 @& D# x1 |- {
现在,让我们来研究一下索引文件本身。由于它是二进制文件,因此我将使用 HexEdit 4(hexedit.com 提供的免费软件十六进制编辑器)查看文件内容(图 5 摘录了部分内容)。

( W: t  K: o6 i) c  e8 r  J
0.jpg
图 5:项目的 Git 索引文件的十六进制转储
' T! E" \( x# ?
图 6:Git 索引标头数据格式
索引文件 - 标头条目
00 - 03
) F: o. _, U: I, a; a1 J. |: K(4 字节)
DIRC用于目录缓存条目的固定标头。7 _: ^! C8 C) T# r! ^& t- z
所有索引文件的开头都是此条目。
04 - 07
8 R, g0 L7 C; J* a(4 字节)
Version索引版本号(适用于 Windows 的 Git
2 G$ G5 V3 K# g3 r9 F: @: Z: f( F当前使用版本 2)。
08 - 11
1 o3 K( t0 r; U- s9 [9 l(4 字节)
条目数作为 4 字节的值,索引最多支持
! I' \5 r; I! e( P4,294,967,296 个条目!

9 y) l4 w/ T2 V  `  }5 Z$ q- m索引的前 12 个字节包含标头(见图 6)。前 4 个字节始终包含字符 DIRC(“目录缓存”的缩写),这是 Git 索引通常被称为“缓存”的原因之一。接下来的 4 个字节包含索引版本号,默认为版本 2,除非要使用 Git 的特定功能(如稀疏签出)。在这种情况下,可以设置为版本 3 或 4。最后 4 个字节包含索引进一步包含的文件条目数。

5 N& ~8 h) M  p1 b* P, A  }* ]. R
12 字节标头后面是 n 个索引项的列表,其中 n 与索引标头描述的条目数一致。图 7 展示了每个索引条目的格式。Git 根据路径/文件名字段按升序排列索引条目。

% f& _# Q9 }+ y' u0 e3 W: a5 \8 ^
图 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 个字节
1 Y, R. V% e7 o$ u) N, v2 o(版本 3
5 _; z: f) n5 b& m0 T或更高版本)( Q$ @1 C' Y) G
标记(从高到低位)" C2 t2 \/ l6 g0 Y2 f  W
1 位:日后使用
# l. u" l& ~7 Y) _4 ^% Y" n: l1 位:跳过工作树标志(稀疏签出)
1 M" f- u- i" H% b, v" c) _* P1 位:有意添加标志 (git add -N)
3 [1 U' S4 K! E13 位:未使用,必须为零
长度不固定路径/文件名以空字符结尾
2 `# C* Q) @- o0 h: M
第一个 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 位置所示)。

3 ^3 H2 q8 @1 R1 g' L
元数据后面是 4 字节的文件内容长度。在图 5 中,此值从 030 行的 00 00 0A 15(十进制为 2,581)开始,我的系统上的 .gitattributes 文件长度为:

" ]; n2 ]3 `4 ~! X/ f, |  f
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
( B4 a/ ?% Z0 `, g- \9 V+ U) W+ Q) |8 s" t: h
偏移 034H 是 blob 对象的 20 字节 SHA-1 值:
: F  i3 O/ u% E! X+ T: z$ G" b
1ff0c423042b46cb1d617b81efb715defbe8054d.

6 e0 V8 w- Q& b  i
请注意,此 SHA-1 指向 blob 对象,其中包含相关文件 (.gitattributes) 的文件内容。
9 S" ]% X5 w* Z3 u, S9 D
048H 是 2 字节值,包含两个 1 位标志、2 位合并暂存值,以及当前索引条目的路径/文件名的 12 位长度。在这两个 1 位标志中,高位指定索引条目是否设置了假设未更改标志(通常使用 Git 更新索引底层命令完成);低位指定是否在路径\文件名条目之前附加两字节的数据(只有当索引版本不小于 3 时,此位才会是 1)。接下来的 2 位保留介于 0 到 3 之间的合并暂存值,如前所述。12 位值包含路径\文件名字符串的长度。

8 H" Y  e! J2 W0 ]/ ~
如果设置了扩展标志,那么 2 字节值包含跳过工作树标志和有意添加位标志,以及填充占位符。
+ ^- p, ~- T* R1 B/ P5 K, K( [' [5 I
最后,长度不固定的字节序列包含路径\文件名。此值以一个或多个空字符结尾。空字符结尾后面是索引中的下一个 blob 对象,或一个或多个索引扩展条目(我很快将会介绍)。
3 W$ N! [, M: v. N: z
我在前面提到过,在用户提交暂存内容前,Git 不会生成树对象。也就是说,索引最初只包含路径/文件名,以及对 blob 对象的引用。不过,只要用户签发提交,Git 就会将索引更新为包含对在上次提交期间创建的树对象的引用。如果在下一次提交期间这些目录引用仍位于工作目录中,那么可以使用缓存的树对象引用,以减少 Git 需要在下一次提交期间完成的工作量。可以看到,索引的作用涉及许多方面,正因为此,它被描述为索引、暂存区域和缓存。
! p; V- ~5 H9 o' O' W9 {$ _
图 7 展示的索引条目只支持 blob 对象引用。Git 使用扩展,以便可以存储树对象。

6 w# J: V/ Y5 p% a
3索引扩展! r$ t( s4 X. j  `& u$ J
索引可以包含扩展条目,这些条目存储特殊化数据流,为 Git 引擎提供其他信息,以供它在监视工作目录中的文件和准备下一次提交时参考。为了缓存在上次提交期间创建的树对象,Git 将树扩展对象添加到工作目录的根目录的索引,以及每个子目录的索引。
; ^" u; s3 ]; D; Q, s( y6 U4 U
图 5 中的标记 2 展示了索引的最终字节,并捕获索引中存储的树对象。图 8 展示了树扩展数据的格式。

* }+ \& a/ Z) a
图 8:Git 索引文件树扩展对象数据格式
索引文件 - 缓存的树扩展标头
4 字节TREE用于缓存的树扩展条目的固定签名。
4 字节表示 TREE 扩展数据长度的 32 位数字9 c7 S) A2 F& v/ F; Y, f
缓存的树扩展条目
变量Path以空字符结尾的路径字符串(只有是根树,才为 NULL)。
ASCII 数字条目数ASCII 数字,表示此树条目覆盖的索引中的条目数。
1 个字节20H(空格字符)) U# \1 ~1 I, Q9 Q# H
ASCII 数字子树数量表示此树的子树数量的 ASCII 数字。
1 个字节0AH(换行符)
: ]- [4 u  s+ I, k5 `
20 字节树对象的 SHA-1此条目生成的树对象的
8 h  Q  O/ {* C; b9 Y! A6 }SHA-1 值。
; c0 y$ {3 |& I+ A* f5 T
在偏移 284H 处出现的树扩展数据标头包含字符串“TREE”(标记了缓存的树扩展数据的开头),后跟 32 位值(表示后面的扩展数据的长度)。接下来是各个树条目:第一个条目是树路径的以 NULL 结尾的长度不固定字符串值(或直接为 NUL,如果是根树的话)。后跟 ASCII 值,因此十六进制编辑器中显示“7”,即当前树覆盖的 blob 条目数(因为这是根树,条目数与先前在签发 Git ls-files 暂存命令时看到的条目数相同)。下一个字符是空格,后又跟一个 ASCII 数字,表示当前树的子树数量。
  ?9 y6 ^3 n& k+ @& d; F
我们项目的根树只有 1 个子树:MSDNConsoleApp。此值后面是换行符和树的 SHA-1 值。SHA-1 从偏移 291 处的 0d21e2 开始。
9 i5 I) @  I9 ], g! h% Z" n+ q
我们将确认 0d21e2 是否就是根树的 SHA-1。为此,请转到命令窗口并输入:
4 E" H; @' U) y& k$ p+ h+ \
git log

' T0 A, Y8 T8 V+ _0 G5 T- z
这将显示与最近提交相关的详细信息:

: }: ?+ ^# C4 S" vcommit 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.

& O" u; t9 i; J* `
最近一次提交的时间戳为 21:24:15,所以就是上次更新索引的提交。我可以使用此提交的 SHA-1 查找根树的 SHA-1 值:

+ [' L& M' D+ f9 k/ D5 V3 @* a( E5 agit cat-file -p 51923
7 F& Z! X% `% w% Z! G
输出如下:
- M+ D9 @. ]4 s) G: q0 L
tree 0d21e2f7f760f77ead2cb85cc128efb13f56401dparent dc0d3343fa24e912f08bc18aaa6f664a4a020079author Jonathan Waldman <jonathan.waldman@live.com> 1494296655 -0500committer Jonathan Waldman <jonathan.waldman@live.com> 1494296655 -0500
( q6 {4 W5 q) P) l: r6 \" P' _+ ^
以上树条目就是根树对象。据此可以确认,索引转储中偏移 291H 处的 0d21e2 值实际上就是根树对象的 SHA-1 值。

% m$ T, Q9 u3 L8 l0 M
SHA-1 值后面为其他树条目(从偏移 2A5H 开始)。若要确认根树下缓存的树对象的 SHA-1 值,请运行以下命令:
! u& a* h- I* m+ j5 ~5 y: ?
git ls-tree -r -d master
$ w- `0 J0 \. |7 z4 R
这仅以递归方式显示当前分支上的树对象:
' p% H- H4 j$ U; G  n8 T
040000 tree c7c367f2d5688dddc25e59525cc6b8efd0df914d    MSDNConsoleApp040000 tree 2723ceb04eda3051abf913782fadeebc97e0123c    MSDNConsoleApp/Properties

# s3 {: e1 g, ~+ E' M; b+ o8 |5 m  F
第一列中的模式值 040000 表明此对象是目录,而不是文件。
, x' I: }* {. z* }# h9 t6 w
最后,索引的最后 20 个字节包含表示索引本身的 SHA-1 哈希值:就跟预期一样,Git 使用此 SHA-1 值来验证索引的数据完整性。
4 ~$ x0 b, r5 @$ w& n
虽然我介绍了本文中示例索引文件的所有条目,但更大、更复杂的索引文件成为一种规范。索引文件格式支持附加的扩展数据流,例如:

2 z. W& y) X' d' I1 m$ e' X
  • 支持合并操作和合并冲突解决方法的数据流。它的签名为“REUC”(用于解决撤消冲突)。
  • 用于维护未受跟踪的文件(这些是未包含在跟踪范围内的文件,在 .gitignore 和 .git\info\exclude 中指定,并由 core.excludesfile 指向的文件指定)的缓存的数据流。它的签名为“UNTR”。
  • 支持拆分索引模式的数据流,以便加快非常大的索引文件的索引更新速度。它的签名为“link”。

    " a) I! C/ ?- v% F/ b
: s( }* L) r. \, A( D! F
借助索引的扩展功能,可以继续添加索引功能。
7 [4 P2 n' @0 q* P% a! i
4总结" l3 F8 A' [4 q' A$ e' t4 |  a* q
本文回顾了 Git 三树体系结构,并深入详细地探讨了索引文件的幕后运行机制。我介绍了 Git 为响应特定操作而更新索引,并依赖索引包含的信息,以便执行其他操作。
$ w' \5 w# o1 _# q4 Z* c: B
可以在不太考虑索引的情况下使用 Git。然而,了解索引可以获取对 Git 核心功能的宝贵见解,同时了解 Git 如何检测工作目录中的文件更改、什么是暂存区域及其非常有用的原因、Git 如何管理合并以及 Git 执行特定操作如此快速的原因。此外,还可以轻松了解签出命令和变基命令的命令行变体,以及软重置、混合重置与硬重置的区别。通过此类功能,可以指定应在发出特定命令时更新索引、工作目录还是两者都更新。了解 Git 工作流、策略和高级操作时,将会看到此类选项。本文旨在让读者认识到索引起到的重要作用,从而可以更好地了解具体利用方式。

6 c2 ^+ I5 ?/ A8 X( l; D5 J) y
原创:Jonathan Waldman

( w' I& F4 Q( p' p
来自- 北京

参加活动:0

组织活动:0

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

本版积分规则

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

QQ|小黑屋|手机版|Archiver|艾拓先锋网 ( 粤ICP备11099876号-1|网站地图

Baidu

GMT+8, 2018-12-14 00:06 , Processed in 0.280236 second(s), 34 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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