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

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

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

扫描二维码登录本站

QQ登录

只需一步,快速开始

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

Git 体系结构和索引文件

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

参加活动:0

组织活动:12

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

4 L& z( a7 a0 c7 q5 E
若要真正了解 Git 在暂存、取消暂存、撤消、提交和签出文件时所做的工作,必须先了解 Git 的体系结构。

4 F6 G: m7 o* _- y" J
1Git 三树体系结构) Q+ d5 y% W" ^# u7 I  Z/ K
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 存储库中。
3 `% }( F+ G6 {9 e# _( N
0.jpg
图 2:Git 三树体系结构利用非常重要的索引文件实现智能化和高效性能

* ?  [/ W8 `! [. ^
请注意,虽然每颗树都存储目录结构和文件,但它们利用的数据结构不同,以便可以保留树专属元数据,并优化存储和检索。第一棵树(工作目录树,也称为“工作树”)显然是 OS 文件和文件夹(除了 OS 级数据结构外,没有其他任何特殊数据结构),满足软件开发者和 Visual Studio 的需求;第二颗树(Git 索引)跨越工作目录和组成 DAG 的提交对象,从而帮助 Git 快速执行工作目录文件内容比较和快速提交;第三棵树 (DAG) 让 Git 能够跟踪可靠历史版本控制系统,同时 Git 还能向其存储在索引和提交对象中的项添加有用的元数据。例如,在索引中存储的元数据有助于检测工作目录中的文件更改,而在提交对象中存储的元数据则有助于跟踪提交签发者和签发原因。
, ^2 D6 f. d" E
本段内容是为了回顾三树体系结构中的三棵树,并引出本文剩余部分重点介绍的主题:已了解工作目录树的运行方式,因为实际上就是已精通使用的 OS 文件系统。如果阅读过我的上一篇文章,应该已非常了解 DAG 的运行方式。那么,此时,缺少的环节就是跨工作目录和 DAG 的索引树(下称“索引”)。实际上,索引的作用非常重要,将是本文剩余部分的唯一主题。
# u. X; p- E/ b" W2 k2 O
2索引的工作原理
  E, I! R' u+ t+ H/ H; l4 R
可能已听说过下面善意的意见:索引是“暂存区域”的代名词。 虽然这样的表述多少有些准确,但却掩盖了索引的真正作用:不仅支持暂存区域,还便于 Git 检测工作目录中的文件更改;协调分支合并过程,以便能够逐个文件解决冲突,并能随时安全地中止合并;将暂存文件和文件夹转换为树对象,这些对象的引用会被写入下一个提交对象。Git 还使用索引来保留工作树中文件的相关信息,以及从 DAG 中检索到的对象的相关信息,从而进一步将索引用作一种缓存。我们将更为全面地研究一下索引。
# d4 Z+ B* G3 D2 Z2 ^
索引实现自己的独立式文件系统,从而能够存储对文件夹和文件的引用,以及关于文件夹和文件的元数据。Git 如何以及何时更新此索引取决于所发出的 Git 命令类型和指定的命令选项(若要试一试,可以使用 Git 更新索引底层命令来自行管理索引),因此这里无法详尽无遗地进行介绍。不过,使用 Visual Studio Git 工具时,不妨留意一下 Git 更新索引以及使用索引中存储信息的主要方式。图 3 展示了 Git 如何在用户暂存文件时在索引中更新工作目录数据,以及如何在用户启动合并(若有合并冲突)、执行克隆/拉取或切换分支时在索引中更新 DAG 数据。另一方面,Git 依赖索引中存储的信息,在用户签发提交后更新 DAG,并在用户执行克隆/拉取或切换分支后更新工作目录。意识到 Git 依赖索引,并且索引跨越多个 Git 操作后,便会开始重视用于修改索引的高级 Git 命令,从而能够有效地巧妙处理 Git 操作。

. t% [7 X* p2 b- i  ]; ?' K: A
0.jpg
图 3:更新索引的主要 Git 操作(绿色)和依赖索引所含信息的 Git 操作(红色)

. H' O; f0 k( a% L! B
我们将在工作目录中新建一个文件,看看此文件被写入索引时会发生些什么。在用户暂存此文件后,Git 会立即使用以下字符串串联公式创建标头:

) Q+ a! _  B8 k
blob{space}{file-length in bytes}{null-termination character}
: P" y  p- V3 r1 C. i5 J' r9 Q# k
然后,Git 将标头串联到文件内容开头。因此,对于包含字符串“Hello”的文本文件,将标头与文件内容串联后生成如下字符串(请注意,字母“H”前面有一个空字符):
  _1 {6 i* l* f
blob 5Hello

$ V1 R" B2 p& U9 m' V* s! A+ z
为了更加明确化,下面展示了此字符串的十六进制版本:

, A" a- d" t2 Z, N62 6C 6F 62 20 35 00 48 65 6C 6C 6F
, K2 I0 u1 y7 U
然后,Git 计算此字符串的 SHA-1:
/ r; ~; {& e/ N! M
5ab2f8a4323abafb10abb68657d9d39f1a775057
* |* q, H$ A$ Y6 L9 J' S- h
接下来,Git 检查现有索引,以确定此文件夹\文件名的条目是否已存在且包含相同的 SHA-1。如果有,Git 会在 .git\objects 文件夹中找到此 blob 对象,并更新它的修改日期时间(Git 绝不会覆盖存储库中的现有对象;而是更新上次修改日期,以便延迟这一新添加的对象被视为垃圾遭到回收的时间)。如果没有,Git 会使用 SHA-1 字符串的前两个字符作为 .git\objects 中的目录名称,并使用剩下的 38 个字符命名 blob 文件,再对此文件进行 zlib 压缩并编写其内容。在我的示例中,Git 会在 .git\objects 中创建名为 5a 的文件夹,再将 blob 对象作为文件 b2f8a4323abafb10abb68657d9d39f1a775057 写入此文件夹。
- T9 |3 q  }4 Y# b4 q
当 Git 以这种方式创建 blob 对象时,大家可能会感到惊讶,因为 blob 对象中明显缺少一个预期的文件属性:文件名! 然而,这是有意而为之。回顾一下,Git 是内容可寻址文件系统,因此它管理的是名为 SHA-1 的 blob 对象,而不是文件。每个 blob 对象通常是由至少一个树对象引用,反过来树对象通常是由提交对象引用。最终,Git 树对象表示暂存的文件的文件夹结构。不过,在用户签发提交之前,Git 不会创建这些树对象。因此,可以得出下列结论:如果 Git 仅使用索引来准备提交对象,还必须为索引中的每个 blob 捕获文件路径引用,而它正是这样做的。其实,即使两个 blob 的 SHA-1 值相同,只要每个映射到不同的文件名或不同的路径/文件值,都将显示为索引中的单独条目。

7 `* R% z8 ~$ K$ Y9 U* H6 v# z  l7 L
Git 还将文件元数据(如文件的创建日期和修改日期)与写入索引的每个 blob 对象一起保存。Git 利用此类信息通过比较文件日期和使用启发,有效地检测工作目录中的文件更改,而不是采用重新计算工作目录中每个文件的 SHA-1 值这种蛮力做法。此类策略可以加快“团队资源管理器 - 更改”窗格中显示信息的速度,或发出高层 Git 状态命令后显示信息的速度。
! }2 ]3 o; A1 l  A9 L
有了工作目录文件对应的索引条目及其相关元数据后,Git 据说就可以“跟踪”文件了,因为它可以很容易地将文件副本与工作目录中保留的副本进行比较。从技术角度来讲,被跟踪的文件也存在于工作目录中,并包含在下一次提交中。这与未受跟踪的文件相反,未受跟踪的文件分为以下两种类型:位于工作目录中但不位于索引中的文件,以及显式指定为不受跟踪的文件(见“索引扩展”部分)。总而言之,借助索引,Git 可以确定跟踪、不跟踪以及不得跟踪哪些文件。
" }6 i( T# m4 e0 @/ K8 K
为了更好地理解索引的具体内容,让我们来看看一个具体示例,从新的 Visual Studio 项目入手。此项目是否复杂并不太重要,只需几个文件就可以充分说明正在发生什么。新建名为 MSDNConsoleApp 的控制台应用程序,并选中“创建解决方案的目录”和“新建 Git 存储库”复选框。单击“确定”,创建解决方案。

; L! V1 }6 b, q  B0 i' R
稍后我将发出一些 Git 命令。因此,若要在系统上运行这些命令,请在工作目录中打开命令提示符窗口,并确保可以在继续操作时使用此窗口。一种为特定 Git 存储库快速打开 Git 命令窗口的方法是,访问“Visual Studio Team”菜单,并选择“管理连接”。此时,将看到本地 Git 存储库列表,以及相应存储库的工作目录路径。右键单击存储库名称,并选择“打开命令提示符”,以启动可用于输入 Git CLI 命令的窗口。
: w6 Q1 z& f9 _: e$ z2 Q) y/ i
创建解决方案后,立即打开“团队资源管理器 - 分支”窗格(图 4 中的标记 1),以确定 Git 是否创建了名为“主分支”的默认分支(标记 2)。右键单击“主分支”(标记 2),并选择“查看历史记录”(标记 3),以查看 Visual Studio 代表用户创建的两个提交对象(标记 4)。第一个对象包含提交消息“添加 .gitignore 和 .gitattributes”;第二个对象包含提交消息“添加项目文件”。
" H' y* x9 e( N8 {3 O
0.jpg
图 4:查看历史记录以确定 Visual Studio 在用户新建项目时所做的工作

5 S* |* p2 d7 H+ |1 b
打开“团队资源管理器 - 更改”窗格。Visual Studio 依赖 Git API 在此窗口中填充项,因为它是 Visual Studio 版 Git 状态命令。当前,此窗口指明工作目录中没有取消暂存的更改。为了做出此决定,Git 将每个索引条目与各个工作目录文件进行比较。有了索引的文件条目和相关的文件元数据后,Git 就有了所需的全部信息,可以确定用户是否进行了任何更改、添加、删除,或是否重命名了工作目录中的任何文件(不包括 .gitignore 文件中提到的任何文件)。
0 `6 D: q. ~* |' Z' [
因此,在方便 Git 智能化确定工作目录树与 HEAD 指向的提交对象的差异方面,索引起到了关键作用。若要详细了解索引提供给 Git 引擎的信息种类,请转到前面打开的命令行窗口,并发出以下底层命令:
" D$ d6 u  T, j
git ls-files --stage

2 y9 a& ~8 l( C6 ?. r' o& N
可以随时发出此命令,从而生成索引中当前包含的文件的完整列表。在我的系统上,此命令的输出如下:
- X" l" y1 A% Y; t/ R- i7 Y+ q5 \
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
1 K# C' D7 X+ v9 d4 _
输出的第一列是八进制的 Unix OS 文件模式。不过,Git 并不支持全部文件模式值。可能只会看到 100644(对于非 EXE 文件)和 100755(对于基于 Unix 的 EXE 文件,适用于 Windows 的 Git 也对可执行文件类型使用 100644)。第二列是文件的 SHA-1 值。第三列是文件的合并暂存值,0 表示没有冲突,1、2 或 3 表示有合并冲突。最后,请注意,七个 blob 对象的路径和文件名全都存储在索引中。Git 使用路径值在下一次提交之前生成树对象(稍后将详细介绍)。

) ], W  J" X5 G) x1 A
现在,让我们来研究一下索引文件本身。由于它是二进制文件,因此我将使用 HexEdit 4(hexedit.com 提供的免费软件十六进制编辑器)查看文件内容(图 5 摘录了部分内容)。
' x, k1 J# s# a2 c: S
0.jpg
图 5:项目的 Git 索引文件的十六进制转储

! {3 t% Z6 L2 o% {! q* o# T7 ~
图 6:Git 索引标头数据格式
索引文件 - 标头条目
00 - 03
' j5 R* g4 D( b$ W(4 字节)
DIRC用于目录缓存条目的固定标头。
4 n, A: ^% Q( N+ u% G, ~所有索引文件的开头都是此条目。
04 - 071 h% n' z  p" e9 B3 H
(4 字节)
Version索引版本号(适用于 Windows 的 Git 5 u8 T: z$ P9 M4 l
当前使用版本 2)。
08 - 11: x7 ^9 M% K+ B; @7 t, p
(4 字节)
条目数作为 4 字节的值,索引最多支持 5 O! z; W, j$ ~0 X0 ]
4,294,967,296 个条目!
3 V( m! s3 T: s% g% R$ s
索引的前 12 个字节包含标头(见图 6)。前 4 个字节始终包含字符 DIRC(“目录缓存”的缩写),这是 Git 索引通常被称为“缓存”的原因之一。接下来的 4 个字节包含索引版本号,默认为版本 2,除非要使用 Git 的特定功能(如稀疏签出)。在这种情况下,可以设置为版本 3 或 4。最后 4 个字节包含索引进一步包含的文件条目数。

- c9 C- c' i5 C' R+ a
12 字节标头后面是 n 个索引项的列表,其中 n 与索引标头描述的条目数一致。图 7 展示了每个索引条目的格式。Git 根据路径/文件名字段按升序排列索引条目。
: D( f$ m" s* j# E3 o% e( N% E2 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 个字节
2 B* U( @5 ^2 y, D: V0 I7 A( w' D(版本 3
0 P% C8 f/ m2 w( d/ i% v7 U或更高版本)
3 X) R4 C! L' F/ `/ ^5 f+ p6 e
标记(从高到低位)( C6 _) e2 s. _) L2 x  L; T& B
1 位:日后使用5 f8 e2 J+ t' T+ n) k# @6 h. p
1 位:跳过工作树标志(稀疏签出)% t/ y* ?# d7 D
1 位:有意添加标志 (git add -N)8 m; X/ D7 B% m: s2 h: m+ I6 i
13 位:未使用,必须为零
长度不固定路径/文件名以空字符结尾
+ `( L4 g, R% ~# R
第一个 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 位置所示)。
2 `& J% o) f9 c2 W0 t
元数据后面是 4 字节的文件内容长度。在图 5 中,此值从 030 行的 00 00 0A 15(十进制为 2,581)开始,我的系统上的 .gitattributes 文件长度为:

" K9 }; V4 Y8 a: o
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

% M& s+ `8 f8 o, `5 L, h$ W+ E) h
偏移 034H 是 blob 对象的 20 字节 SHA-1 值:
3 {7 W9 h" R( t, A1 ^7 \. E: W
1ff0c423042b46cb1d617b81efb715defbe8054d.

- _/ b2 _" O0 h  u5 i$ o
请注意,此 SHA-1 指向 blob 对象,其中包含相关文件 (.gitattributes) 的文件内容。

6 n! W; w0 d, y0 R
048H 是 2 字节值,包含两个 1 位标志、2 位合并暂存值,以及当前索引条目的路径/文件名的 12 位长度。在这两个 1 位标志中,高位指定索引条目是否设置了假设未更改标志(通常使用 Git 更新索引底层命令完成);低位指定是否在路径\文件名条目之前附加两字节的数据(只有当索引版本不小于 3 时,此位才会是 1)。接下来的 2 位保留介于 0 到 3 之间的合并暂存值,如前所述。12 位值包含路径\文件名字符串的长度。
5 ?7 U) F$ `/ ?! m1 ?& [; Y2 @% U
如果设置了扩展标志,那么 2 字节值包含跳过工作树标志和有意添加位标志,以及填充占位符。
  z$ O/ G; y' ^+ V( V
最后,长度不固定的字节序列包含路径\文件名。此值以一个或多个空字符结尾。空字符结尾后面是索引中的下一个 blob 对象,或一个或多个索引扩展条目(我很快将会介绍)。

( x: T  \9 n- B8 ]" A) Q7 ?4 ~' J
我在前面提到过,在用户提交暂存内容前,Git 不会生成树对象。也就是说,索引最初只包含路径/文件名,以及对 blob 对象的引用。不过,只要用户签发提交,Git 就会将索引更新为包含对在上次提交期间创建的树对象的引用。如果在下一次提交期间这些目录引用仍位于工作目录中,那么可以使用缓存的树对象引用,以减少 Git 需要在下一次提交期间完成的工作量。可以看到,索引的作用涉及许多方面,正因为此,它被描述为索引、暂存区域和缓存。
2 \! H( E! ]" n' E8 `6 _+ t1 [3 [
图 7 展示的索引条目只支持 blob 对象引用。Git 使用扩展,以便可以存储树对象。
5 c7 p) s3 R  a) v# U
3索引扩展/ d3 J. \# z# A3 w1 r' U$ t4 k/ J
索引可以包含扩展条目,这些条目存储特殊化数据流,为 Git 引擎提供其他信息,以供它在监视工作目录中的文件和准备下一次提交时参考。为了缓存在上次提交期间创建的树对象,Git 将树扩展对象添加到工作目录的根目录的索引,以及每个子目录的索引。
) _  n) {- l5 M  Y
图 5 中的标记 2 展示了索引的最终字节,并捕获索引中存储的树对象。图 8 展示了树扩展数据的格式。

/ u2 L9 O0 U3 X. {
图 8:Git 索引文件树扩展对象数据格式
索引文件 - 缓存的树扩展标头
4 字节TREE用于缓存的树扩展条目的固定签名。
4 字节表示 TREE 扩展数据长度的 32 位数字/ K7 R1 |9 Z% A. W4 P0 D
缓存的树扩展条目
变量Path以空字符结尾的路径字符串(只有是根树,才为 NULL)。
ASCII 数字条目数ASCII 数字,表示此树条目覆盖的索引中的条目数。
1 个字节20H(空格字符)
+ T6 w1 h' {3 ]0 p3 }# X1 y7 X/ b
ASCII 数字子树数量表示此树的子树数量的 ASCII 数字。
1 个字节0AH(换行符)- ~; ]9 y5 G* y" o( ^) q) |2 L
20 字节树对象的 SHA-1此条目生成的树对象的
+ {5 D, C4 O0 x# g/ t. iSHA-1 值。

8 e7 a: M- \! h. A1 e) ]在偏移 284H 处出现的树扩展数据标头包含字符串“TREE”(标记了缓存的树扩展数据的开头),后跟 32 位值(表示后面的扩展数据的长度)。接下来是各个树条目:第一个条目是树路径的以 NULL 结尾的长度不固定字符串值(或直接为 NUL,如果是根树的话)。后跟 ASCII 值,因此十六进制编辑器中显示“7”,即当前树覆盖的 blob 条目数(因为这是根树,条目数与先前在签发 Git ls-files 暂存命令时看到的条目数相同)。下一个字符是空格,后又跟一个 ASCII 数字,表示当前树的子树数量。
1 w% A9 Z# Q4 a5 j: D& y1 J
我们项目的根树只有 1 个子树:MSDNConsoleApp。此值后面是换行符和树的 SHA-1 值。SHA-1 从偏移 291 处的 0d21e2 开始。

$ s9 s: ]5 t5 O! t! x* J: P
我们将确认 0d21e2 是否就是根树的 SHA-1。为此,请转到命令窗口并输入:
7 [" d. }" D* n- P  c
git log
9 r% w% \3 ?" W) o
这将显示与最近提交相关的详细信息:

5 [2 Q( x" m1 U- Bcommit 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.
4 j0 c' m+ i/ z) I6 ~
最近一次提交的时间戳为 21:24:15,所以就是上次更新索引的提交。我可以使用此提交的 SHA-1 查找根树的 SHA-1 值:

' Z/ f5 W% z  p0 Jgit cat-file -p 51923
; Q8 b- `! H. [7 b' c
输出如下:
& |- N  o7 S0 k' D% x# G
tree 0d21e2f7f760f77ead2cb85cc128efb13f56401dparent dc0d3343fa24e912f08bc18aaa6f664a4a020079author Jonathan Waldman <jonathan.waldman@live.com> 1494296655 -0500committer Jonathan Waldman <jonathan.waldman@live.com> 1494296655 -0500
9 Z$ u, a1 ]$ |8 h' @3 ~
以上树条目就是根树对象。据此可以确认,索引转储中偏移 291H 处的 0d21e2 值实际上就是根树对象的 SHA-1 值。
* Z# a/ [1 Q8 i! c
SHA-1 值后面为其他树条目(从偏移 2A5H 开始)。若要确认根树下缓存的树对象的 SHA-1 值,请运行以下命令:

. q3 a& b* G; \- L/ u$ vgit ls-tree -r -d master
3 ^: w8 g3 v+ I  r
这仅以递归方式显示当前分支上的树对象:
$ J' B* z' y- s" T( `% v8 L
040000 tree c7c367f2d5688dddc25e59525cc6b8efd0df914d    MSDNConsoleApp040000 tree 2723ceb04eda3051abf913782fadeebc97e0123c    MSDNConsoleApp/Properties

. U! l* e' V( q8 U7 x
第一列中的模式值 040000 表明此对象是目录,而不是文件。

$ n1 M, T  v! q# P9 ^+ o5 y1 M
最后,索引的最后 20 个字节包含表示索引本身的 SHA-1 哈希值:就跟预期一样,Git 使用此 SHA-1 值来验证索引的数据完整性。

0 T3 ]/ D3 n" b6 w8 f
虽然我介绍了本文中示例索引文件的所有条目,但更大、更复杂的索引文件成为一种规范。索引文件格式支持附加的扩展数据流,例如:

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

    - @2 w5 D" [! p5 z
+ h# Y7 v/ V' h7 O/ {0 f% u: K
借助索引的扩展功能,可以继续添加索引功能。

/ I, f+ J7 v3 N4 f6 c. b( f
4总结
9 a5 X# ~! O9 C( c- a- u
本文回顾了 Git 三树体系结构,并深入详细地探讨了索引文件的幕后运行机制。我介绍了 Git 为响应特定操作而更新索引,并依赖索引包含的信息,以便执行其他操作。

) R' K0 J9 d: G5 z
可以在不太考虑索引的情况下使用 Git。然而,了解索引可以获取对 Git 核心功能的宝贵见解,同时了解 Git 如何检测工作目录中的文件更改、什么是暂存区域及其非常有用的原因、Git 如何管理合并以及 Git 执行特定操作如此快速的原因。此外,还可以轻松了解签出命令和变基命令的命令行变体,以及软重置、混合重置与硬重置的区别。通过此类功能,可以指定应在发出特定命令时更新索引、工作目录还是两者都更新。了解 Git 工作流、策略和高级操作时,将会看到此类选项。本文旨在让读者认识到索引起到的重要作用,从而可以更好地了解具体利用方式。
% p" R% O+ v% z" P/ [& ?9 t- ?
原创:Jonathan Waldman
" y2 O1 b* K* [" Z. J: E
来自- 北京

参加活动:0

组织活动:0

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

本版积分规则

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

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

Baidu

GMT+8, 2018-11-14 13:06 , Processed in 0.267030 second(s), 34 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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