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

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

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

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
查看: 471|回复: 1

Git 体系结构和索引文件

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

参加活动:0

组织活动:12

发表于 2017-9-9 15:19:15 | 显示全部楼层 |阅读模式 来自- 广东广州
我曾介绍过 Git 如何使用有向无环图 (DAG) 来组织存储库的提交对象。此外,我还研究过提交对象可以指代的 blob、树和标记对象。在这篇文章的最后,我还介绍了分支,包括 HEAD 和 head 的区别。阅读本文前必须先阅读这篇文章,因为本文将介绍 Git“三树”体系结构及其索引文件的重要性。进一步了解这些 Git 内部结构将有助于积累基础知识,提高 Git 用户的工作效率,并有助于用户在研究各种基于 Visual Studio IDE 图形 Git 工具的 Git 操作时获取新见解。
& j$ I7 R/ ~0 k3 u1 N! n6 w
回顾一下,之前有介绍过,Visual Studio 使用 Git API 与 Git 进行通信,以及 Visual Studio IDE Git 工具简化并取代了基础 Git 引擎的功能。对于要实现版本控制工作流而不依赖 Git 命令行接口 (CLI) 的开发者来说,这是一大福音。哎,但 IDE 的实用 Git 抽象有时可能会引起混乱。以下面的基本工作流为例:将项目添加到 Git 源控件,修改并暂存项目文件,再提交暂存文件。为此,需要打开“团队资源管理器 - 更改”窗格来查看更改后文件列表,再选择要暂存的文件。请注意图 1 中的最左侧图像,其中显示了我在工作目录中更改的两个文件(标记 1)。
7 l# `; a/ r0 H: Y( }, Q. t
图 1:“团队资源管理器 - 更改”窗格可以在“更改”和“暂存更改”部分中显示相同的文件
5 G% u+ }: b) T. t' J% O" B
在下一张靠右的图像中,我暂存了其中一个更改后文件:Program.cs(标记 2)。当我这样做时,Program.cs 似乎已从“更改”列表中“移动”到了“暂存更改”列表中。如果我进一步修改,并在工作目录中保存 Program.cs 副本,那么此文件会继续出现在“暂存更改”部分中(标记 3),但同时也会出现在“更改”部分中(标记 4)! 如果不了解 Git 的幕后运行机制,可能会感到困惑,解疑释惑的关键在于有两个 Program.cs“副本”:一个位于工作文件夹中,另一个位于对象的 Git 内部数据库中。即使发现这一点,可能也无法知道在取消暂存副本、尝试暂存 Program.cs 的第二个更改副本、撤消对工作副本的更改或切换分支时会发生些什么。
5 P; X5 d; H4 R7 `5 f- v2 v4 @% x
若要真正了解 Git 在暂存、取消暂存、撤消、提交和签出文件时所做的工作,必须先了解 Git 的体系结构。

! A$ y" y1 @7 i( ?! J
1Git 三树体系结构
6 B2 Q% n$ r0 }- ]' c) g2 Q
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 存储库中。
, f! f/ K5 J& \8 E9 {. p7 t0 s& f
图 2:Git 三树体系结构利用非常重要的索引文件实现智能化和高效性能
" i# g# |! R7 i- M  V5 M
请注意,虽然每颗树都存储目录结构和文件,但它们利用的数据结构不同,以便可以保留树专属元数据,并优化存储和检索。第一棵树(工作目录树,也称为“工作树”)显然是 OS 文件和文件夹(除了 OS 级数据结构外,没有其他任何特殊数据结构),满足软件开发者和 Visual Studio 的需求;第二颗树(Git 索引)跨越工作目录和组成 DAG 的提交对象,从而帮助 Git 快速执行工作目录文件内容比较和快速提交;第三棵树 (DAG) 让 Git 能够跟踪可靠历史版本控制系统,同时 Git 还能向其存储在索引和提交对象中的项添加有用的元数据。例如,在索引中存储的元数据有助于检测工作目录中的文件更改,而在提交对象中存储的元数据则有助于跟踪提交签发者和签发原因。
& _2 {2 U. \% }& g5 R
本段内容是为了回顾三树体系结构中的三棵树,并引出本文剩余部分重点介绍的主题:已了解工作目录树的运行方式,因为实际上就是已精通使用的 OS 文件系统。如果阅读过我的上一篇文章,应该已非常了解 DAG 的运行方式。那么,此时,缺少的环节就是跨工作目录和 DAG 的索引树(下称“索引”)。实际上,索引的作用非常重要,将是本文剩余部分的唯一主题。

4 _# V, N) U' W' F! P9 x
2索引的工作原理6 o* f( J8 x8 K- O5 l7 b' X
可能已听说过下面善意的意见:索引是“暂存区域”的代名词。 虽然这样的表述多少有些准确,但却掩盖了索引的真正作用:不仅支持暂存区域,还便于 Git 检测工作目录中的文件更改;协调分支合并过程,以便能够逐个文件解决冲突,并能随时安全地中止合并;将暂存文件和文件夹转换为树对象,这些对象的引用会被写入下一个提交对象。Git 还使用索引来保留工作树中文件的相关信息,以及从 DAG 中检索到的对象的相关信息,从而进一步将索引用作一种缓存。我们将更为全面地研究一下索引。

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

" G% g% ]+ k) p4 I2 \: ~
图 3:更新索引的主要 Git 操作(绿色)和依赖索引所含信息的 Git 操作(红色)
/ c: v& E3 C, p3 g8 k1 d4 U
我们将在工作目录中新建一个文件,看看此文件被写入索引时会发生些什么。在用户暂存此文件后,Git 会立即使用以下字符串串联公式创建标头:
# ^  T  l% \4 B3 E* v
blob{space}{file-length in bytes}{null-termination character}

) B% o' `+ ?9 C1 R5 j
然后,Git 将标头串联到文件内容开头。因此,对于包含字符串“Hello”的文本文件,将标头与文件内容串联后生成如下字符串(请注意,字母“H”前面有一个空字符):
% A1 @- o# w# I: i
blob 5Hello
+ l: j3 K5 S# n1 P$ B
为了更加明确化,下面展示了此字符串的十六进制版本:

, O' @  L0 a7 v# f! a2 Z$ W62 6C 6F 62 20 35 00 48 65 6C 6C 6F

* m$ k/ {  d4 m7 B$ U
然后,Git 计算此字符串的 SHA-1:

: h$ a8 J, O  E3 o) [, E* F5ab2f8a4323abafb10abb68657d9d39f1a775057

* j- c2 t& X6 P" F+ o
接下来,Git 检查现有索引,以确定此文件夹\文件名的条目是否已存在且包含相同的 SHA-1。如果有,Git 会在 .git\objects 文件夹中找到此 blob 对象,并更新它的修改日期时间(Git 绝不会覆盖存储库中的现有对象;而是更新上次修改日期,以便延迟这一新添加的对象被视为垃圾遭到回收的时间)。如果没有,Git 会使用 SHA-1 字符串的前两个字符作为 .git\objects 中的目录名称,并使用剩下的 38 个字符命名 blob 文件,再对此文件进行 zlib 压缩并编写其内容。在我的示例中,Git 会在 .git\objects 中创建名为 5a 的文件夹,再将 blob 对象作为文件 b2f8a4323abafb10abb68657d9d39f1a775057 写入此文件夹。
# U9 f4 k2 Z" |$ Z( w0 @( t
当 Git 以这种方式创建 blob 对象时,大家可能会感到惊讶,因为 blob 对象中明显缺少一个预期的文件属性:文件名! 然而,这是有意而为之。回顾一下,Git 是内容可寻址文件系统,因此它管理的是名为 SHA-1 的 blob 对象,而不是文件。每个 blob 对象通常是由至少一个树对象引用,反过来树对象通常是由提交对象引用。最终,Git 树对象表示暂存的文件的文件夹结构。不过,在用户签发提交之前,Git 不会创建这些树对象。因此,可以得出下列结论:如果 Git 仅使用索引来准备提交对象,还必须为索引中的每个 blob 捕获文件路径引用,而它正是这样做的。其实,即使两个 blob 的 SHA-1 值相同,只要每个映射到不同的文件名或不同的路径/文件值,都将显示为索引中的单独条目。
) g' t8 @( E) ^1 V" |
Git 还将文件元数据(如文件的创建日期和修改日期)与写入索引的每个 blob 对象一起保存。Git 利用此类信息通过比较文件日期和使用启发,有效地检测工作目录中的文件更改,而不是采用重新计算工作目录中每个文件的 SHA-1 值这种蛮力做法。此类策略可以加快“团队资源管理器 - 更改”窗格中显示信息的速度,或发出高层 Git 状态命令后显示信息的速度。

" {3 ?9 R& L5 U: n* M. R
有了工作目录文件对应的索引条目及其相关元数据后,Git 据说就可以“跟踪”文件了,因为它可以很容易地将文件副本与工作目录中保留的副本进行比较。从技术角度来讲,被跟踪的文件也存在于工作目录中,并包含在下一次提交中。这与未受跟踪的文件相反,未受跟踪的文件分为以下两种类型:位于工作目录中但不位于索引中的文件,以及显式指定为不受跟踪的文件(见“索引扩展”部分)。总而言之,借助索引,Git 可以确定跟踪、不跟踪以及不得跟踪哪些文件。
6 j) [- E  _& Z+ ^
为了更好地理解索引的具体内容,让我们来看看一个具体示例,从新的 Visual Studio 项目入手。此项目是否复杂并不太重要,只需几个文件就可以充分说明正在发生什么。新建名为 MSDNConsoleApp 的控制台应用程序,并选中“创建解决方案的目录”和“新建 Git 存储库”复选框。单击“确定”,创建解决方案。
3 X( E: x7 y+ _; q- S
稍后我将发出一些 Git 命令。因此,若要在系统上运行这些命令,请在工作目录中打开命令提示符窗口,并确保可以在继续操作时使用此窗口。一种为特定 Git 存储库快速打开 Git 命令窗口的方法是,访问“Visual Studio Team”菜单,并选择“管理连接”。此时,将看到本地 Git 存储库列表,以及相应存储库的工作目录路径。右键单击存储库名称,并选择“打开命令提示符”,以启动可用于输入 Git CLI 命令的窗口。
! g: E# x: Q% f) i
创建解决方案后,立即打开“团队资源管理器 - 分支”窗格(图 4 中的标记 1),以确定 Git 是否创建了名为“主分支”的默认分支(标记 2)。右键单击“主分支”(标记 2),并选择“查看历史记录”(标记 3),以查看 Visual Studio 代表用户创建的两个提交对象(标记 4)。第一个对象包含提交消息“添加 .gitignore 和 .gitattributes”;第二个对象包含提交消息“添加项目文件”。
4 ?( l: K- Y$ r4 x& U
图 4:查看历史记录以确定 Visual Studio 在用户新建项目时所做的工作

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

1 [; ]$ U  e: f7 h2 f0 M. y
因此,在方便 Git 智能化确定工作目录树与 HEAD 指向的提交对象的差异方面,索引起到了关键作用。若要详细了解索引提供给 Git 引擎的信息种类,请转到前面打开的命令行窗口,并发出以下底层命令:
+ T. W$ e# |% I8 w' {0 O
git ls-files --stage
* }) e+ I2 I* K! H( U
可以随时发出此命令,从而生成索引中当前包含的文件的完整列表。在我的系统上,此命令的输出如下:

1 s9 i% e  t- }' m. t100644 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

% j- u( O! E$ x+ _  R6 e
输出的第一列是八进制的 Unix OS 文件模式。不过,Git 并不支持全部文件模式值。可能只会看到 100644(对于非 EXE 文件)和 100755(对于基于 Unix 的 EXE 文件,适用于 Windows 的 Git 也对可执行文件类型使用 100644)。第二列是文件的 SHA-1 值。第三列是文件的合并暂存值,0 表示没有冲突,1、2 或 3 表示有合并冲突。最后,请注意,七个 blob 对象的路径和文件名全都存储在索引中。Git 使用路径值在下一次提交之前生成树对象(稍后将详细介绍)。

" l. ~+ G  X6 W( s
现在,让我们来研究一下索引文件本身。由于它是二进制文件,因此我将使用 HexEdit 4(hexedit.com 提供的免费软件十六进制编辑器)查看文件内容(图 5 摘录了部分内容)。

4 p$ n; p- \8 g, L5 d1 C! \: o
图 5:项目的 Git 索引文件的十六进制转储

/ U4 O( h. ?* k% H" t) P6 Z
图 6:Git 索引标头数据格式
索引文件 - 标头条目
00 - 03/ q1 ^" y" e! O7 d
(4 字节)
DIRC用于目录缓存条目的固定标头。6 i  |- @6 H6 q& w3 S5 t
所有索引文件的开头都是此条目。
04 - 07# s4 ?) u' K8 U/ j4 s, O5 F3 u
(4 字节)
Version索引版本号(适用于 Windows 的 Git
& g1 O$ u4 |6 j5 Y; l" p2 ?当前使用版本 2)。
08 - 111 c% o0 {7 P/ P' r$ N& N/ c7 j
(4 字节)
条目数作为 4 字节的值,索引最多支持 ; k, A7 R4 k, ^6 W: p+ t
4,294,967,296 个条目!
$ \8 h4 b7 k' A: M4 m3 n+ C
索引的前 12 个字节包含标头(见图 6)。前 4 个字节始终包含字符 DIRC(“目录缓存”的缩写),这是 Git 索引通常被称为“缓存”的原因之一。接下来的 4 个字节包含索引版本号,默认为版本 2,除非要使用 Git 的特定功能(如稀疏签出)。在这种情况下,可以设置为版本 3 或 4。最后 4 个字节包含索引进一步包含的文件条目数。

9 s1 {, o, S/ E$ k& U
12 字节标头后面是 n 个索引项的列表,其中 n 与索引标头描述的条目数一致。图 7 展示了每个索引条目的格式。Git 根据路径/文件名字段按升序排列索引条目。

" [. P8 {  \. ^3 T/ e% p' F8 M0 F
图 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 个字节 7 }* P" z  n8 ~- F* m; Q
(版本 3
, D; Q/ f0 C7 P; M: P* t或更高版本). X5 p  g+ i9 y: j0 t* g  S! F; v
标记(从高到低位)
" k. A) f! [* H3 p! r" ?: n1 N4 v. u1 位:日后使用
, v$ s( G8 N2 _1 R: h, I. r) @0 q1 位:跳过工作树标志(稀疏签出)
  p$ g. ~8 x" N( a* s1 位:有意添加标志 (git add -N): K/ O  ~" T; y1 A; u2 I
13 位:未使用,必须为零
长度不固定路径/文件名以空字符结尾

: T) r4 l' {+ [5 M' H/ ~第一个 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 位置所示)。

8 o. o" L, \/ }2 X" Q; r
元数据后面是 4 字节的文件内容长度。在图 5 中,此值从 030 行的 00 00 0A 15(十进制为 2,581)开始,我的系统上的 .gitattributes 文件长度为:
. t7 Y3 D# l8 @2 m9 W* U  C
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

) L. a" U# e5 {( d- {
偏移 034H 是 blob 对象的 20 字节 SHA-1 值:
* e8 h* W+ y7 b6 ~) H) e# f/ E
1ff0c423042b46cb1d617b81efb715defbe8054d.
- t- [4 q# L! e% k' y
请注意,此 SHA-1 指向 blob 对象,其中包含相关文件 (.gitattributes) 的文件内容。
; b. M5 g, W0 B  |! o' {+ `
048H 是 2 字节值,包含两个 1 位标志、2 位合并暂存值,以及当前索引条目的路径/文件名的 12 位长度。在这两个 1 位标志中,高位指定索引条目是否设置了假设未更改标志(通常使用 Git 更新索引底层命令完成);低位指定是否在路径\文件名条目之前附加两字节的数据(只有当索引版本不小于 3 时,此位才会是 1)。接下来的 2 位保留介于 0 到 3 之间的合并暂存值,如前所述。12 位值包含路径\文件名字符串的长度。
+ c. b3 b! W6 s  G# J8 t
如果设置了扩展标志,那么 2 字节值包含跳过工作树标志和有意添加位标志,以及填充占位符。

  @9 l6 e. R( \' o: |. G9 Z8 J7 F
最后,长度不固定的字节序列包含路径\文件名。此值以一个或多个空字符结尾。空字符结尾后面是索引中的下一个 blob 对象,或一个或多个索引扩展条目(我很快将会介绍)。
! k3 W7 R* G5 r. a6 m' }+ k
我在前面提到过,在用户提交暂存内容前,Git 不会生成树对象。也就是说,索引最初只包含路径/文件名,以及对 blob 对象的引用。不过,只要用户签发提交,Git 就会将索引更新为包含对在上次提交期间创建的树对象的引用。如果在下一次提交期间这些目录引用仍位于工作目录中,那么可以使用缓存的树对象引用,以减少 Git 需要在下一次提交期间完成的工作量。可以看到,索引的作用涉及许多方面,正因为此,它被描述为索引、暂存区域和缓存。

& W/ \' ~( I3 k& J
图 7 展示的索引条目只支持 blob 对象引用。Git 使用扩展,以便可以存储树对象。

$ Z  {$ F8 Y2 z
3索引扩展& n5 @( U  }+ V; ?. g
索引可以包含扩展条目,这些条目存储特殊化数据流,为 Git 引擎提供其他信息,以供它在监视工作目录中的文件和准备下一次提交时参考。为了缓存在上次提交期间创建的树对象,Git 将树扩展对象添加到工作目录的根目录的索引,以及每个子目录的索引。
2 ^, d9 A3 x  w  r5 K: N- }
图 5 中的标记 2 展示了索引的最终字节,并捕获索引中存储的树对象。图 8 展示了树扩展数据的格式。

0 ~2 _: G* d9 e& w- b+ n
图 8:Git 索引文件树扩展对象数据格式
索引文件 - 缓存的树扩展标头
4 字节TREE用于缓存的树扩展条目的固定签名。
4 字节表示 TREE 扩展数据长度的 32 位数字& Y/ M) b( P( b' }* d9 c  {" V! W
缓存的树扩展条目
变量Path以空字符结尾的路径字符串(只有是根树,才为 NULL)。
ASCII 数字条目数ASCII 数字,表示此树条目覆盖的索引中的条目数。
1 个字节20H(空格字符). f" |6 f- E! d5 X
ASCII 数字子树数量表示此树的子树数量的 ASCII 数字。
1 个字节0AH(换行符)
) [' D9 k( l2 f' Q
20 字节树对象的 SHA-1此条目生成的树对象的 . [7 |$ M, R5 {% [) @
SHA-1 值。

7 l3 {7 z! `& J5 I在偏移 284H 处出现的树扩展数据标头包含字符串“TREE”(标记了缓存的树扩展数据的开头),后跟 32 位值(表示后面的扩展数据的长度)。接下来是各个树条目:第一个条目是树路径的以 NULL 结尾的长度不固定字符串值(或直接为 NUL,如果是根树的话)。后跟 ASCII 值,因此十六进制编辑器中显示“7”,即当前树覆盖的 blob 条目数(因为这是根树,条目数与先前在签发 Git ls-files 暂存命令时看到的条目数相同)。下一个字符是空格,后又跟一个 ASCII 数字,表示当前树的子树数量。

. O; @/ c) j8 F. M+ }
我们项目的根树只有 1 个子树:MSDNConsoleApp。此值后面是换行符和树的 SHA-1 值。SHA-1 从偏移 291 处的 0d21e2 开始。
8 ~& S. F$ \. U4 {. E+ ^8 @
我们将确认 0d21e2 是否就是根树的 SHA-1。为此,请转到命令窗口并输入:

7 e1 g. W2 a0 B9 {git log
7 I& o; p. O0 r9 K9 I
这将显示与最近提交相关的详细信息:
! r- v; G$ I- J; [
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.
7 b1 o  h) u/ N3 ?6 \
最近一次提交的时间戳为 21:24:15,所以就是上次更新索引的提交。我可以使用此提交的 SHA-1 查找根树的 SHA-1 值:
+ x$ Z, W/ B4 I' k: ?: C1 Q; a7 f
git cat-file -p 51923
+ N/ J+ h$ Y5 U* N+ X( x" `
输出如下:
1 T. x: Y* q6 ]# _- i; k
tree 0d21e2f7f760f77ead2cb85cc128efb13f56401dparent dc0d3343fa24e912f08bc18aaa6f664a4a020079author Jonathan Waldman <jonathan.waldman@live.com> 1494296655 -0500committer Jonathan Waldman <jonathan.waldman@live.com> 1494296655 -0500

' ~/ }  X4 w- z7 y
以上树条目就是根树对象。据此可以确认,索引转储中偏移 291H 处的 0d21e2 值实际上就是根树对象的 SHA-1 值。

4 p+ p0 }6 r9 N8 L( J, o; b
SHA-1 值后面为其他树条目(从偏移 2A5H 开始)。若要确认根树下缓存的树对象的 SHA-1 值,请运行以下命令:
: H, k$ O6 j) a
git ls-tree -r -d master
+ x, g. `2 p4 B) i! Q; f
这仅以递归方式显示当前分支上的树对象:

* t" M/ ?0 _6 ?) U* U! a( U" ^040000 tree c7c367f2d5688dddc25e59525cc6b8efd0df914d    MSDNConsoleApp040000 tree 2723ceb04eda3051abf913782fadeebc97e0123c    MSDNConsoleApp/Properties

! ~, R* [" b! {5 U
第一列中的模式值 040000 表明此对象是目录,而不是文件。

& {# p1 Q' F1 h, `' |
最后,索引的最后 20 个字节包含表示索引本身的 SHA-1 哈希值:就跟预期一样,Git 使用此 SHA-1 值来验证索引的数据完整性。

0 G' Q  o* @& S
虽然我介绍了本文中示例索引文件的所有条目,但更大、更复杂的索引文件成为一种规范。索引文件格式支持附加的扩展数据流,例如:

7 a' j5 T! Q- W0 N: @
  • 支持合并操作和合并冲突解决方法的数据流。它的签名为“REUC”(用于解决撤消冲突)。
  • 用于维护未受跟踪的文件(这些是未包含在跟踪范围内的文件,在 .gitignore 和 .git\info\exclude 中指定,并由 core.excludesfile 指向的文件指定)的缓存的数据流。它的签名为“UNTR”。
  • 支持拆分索引模式的数据流,以便加快非常大的索引文件的索引更新速度。它的签名为“link”。
    " U' D4 y8 V: R) `

6 q8 B: E- g& b+ l/ `$ o" M
借助索引的扩展功能,可以继续添加索引功能。
+ f, T" k4 Z9 N+ ~; F1 \5 ?$ C
4总结
' @4 p9 X+ V2 f+ v; _
本文回顾了 Git 三树体系结构,并深入详细地探讨了索引文件的幕后运行机制。我介绍了 Git 为响应特定操作而更新索引,并依赖索引包含的信息,以便执行其他操作。
0 X: y$ O, [( D
可以在不太考虑索引的情况下使用 Git。然而,了解索引可以获取对 Git 核心功能的宝贵见解,同时了解 Git 如何检测工作目录中的文件更改、什么是暂存区域及其非常有用的原因、Git 如何管理合并以及 Git 执行特定操作如此快速的原因。此外,还可以轻松了解签出命令和变基命令的命令行变体,以及软重置、混合重置与硬重置的区别。通过此类功能,可以指定应在发出特定命令时更新索引、工作目录还是两者都更新。了解 Git 工作流、策略和高级操作时,将会看到此类选项。本文旨在让读者认识到索引起到的重要作用,从而可以更好地了解具体利用方式。
0 ]0 C4 q' ^6 _. \, i
原创:Jonathan Waldman

$ e5 e  q7 r, i( Q5 I; C

本帖子中包含更多资源

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

x
来自- 北京

参加活动:0

组织活动:0

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

本版积分规则

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

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

Baidu

GMT+8, 2018-7-19 17:35 , Processed in 0.192297 second(s), 36 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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