Git索引到底包含什么,我可以使用什么命令来查看索引的内容?


谢谢你的回答。我知道索引作为一个暂存区,提交的内容在索引中而不是工作树中。我只是好奇一个索引对象由什么组成。我猜它可能是一个文件名/目录名列表,SHA-1对,一种虚拟树?

在Git术语中,是否有任何可以用来列出索引内容的管道命令?


当前回答

这是你真正需要的,使用这个命令。

$ binwalk指数

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
1717          0x6B5           Unix path: /company/user/user/delete.php
1813          0x715           Unix path: /company/user/user/get.php
1909          0x775           Unix path: /company/user/user/post.php
2005          0x7D5           Unix path: /company/user/user/put.php
3373          0xD2D           Unix path: /urban-airship/channel/channel/post.php
3789          0xECD           Unix path: /urban-airship/named-user/named-user/post.php
3901          0xF3D           Unix path: /user/categories/categories/delete.php
4005          0xFA5           Unix path: /user/categories/categories/get.php
4109          0x100D          Unix path: /user/categories/categories/put.php
4309          0x10D5          Unix path: /user/favorites/favorites/delete.php

其他回答

Git索引是工作目录和存储库之间的临时区域。您可以使用索引构建一组希望一起提交的更改。创建提交时,提交的是当前在这个索引中的内容,而不是工作目录中的内容。

要查看索引内的内容,发出命令:

git status

当您运行git status时,您可以看到哪些文件是暂存的(当前在您的索引中),哪些文件已被修改但尚未暂存,以及哪些文件完全未被跟踪。

你可以看看这个。谷歌搜索会抛出许多链接,这些链接应该是相当自给自足的。

这是你真正需要的,使用这个命令。

$ binwalk指数

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
1717          0x6B5           Unix path: /company/user/user/delete.php
1813          0x715           Unix path: /company/user/user/get.php
1909          0x775           Unix path: /company/user/user/post.php
2005          0x7D5           Unix path: /company/user/user/put.php
3373          0xD2D           Unix path: /urban-airship/channel/channel/post.php
3789          0xECD           Unix path: /urban-airship/named-user/named-user/post.php
3901          0xF3D           Unix path: /user/categories/categories/delete.php
4005          0xFA5           Unix path: /user/categories/categories/get.php
4109          0x100D          Unix path: /user/categories/categories/put.php
4309          0x10D5          Unix path: /user/favorites/favorites/delete.php

Git书中包含了一篇关于索引包含什么的文章:

索引是一个二进制文件(通常保存在.git/index中),包含一个排序的路径名列表,每个路径名都有权限和一个blob对象的SHA1;Git ls-files可以显示索引的内容:

$ git ls-files --stage
100644 63c918c667fa005ff12ad89437f2fdc80926e21c 0   .gitignore
100644 5529b198e8d14decbe4ad99db3f7fb632de0439d 0   .mailmap

Racy git问题给出了该结构的更多细节:

索引是git中最重要的数据结构之一。 它通过记录路径列表及其对象名称来表示一个虚拟工作树状态,并作为一个staging区域来写出要提交的下一个树对象。 这个状态是“虚拟的”,因为它不需要匹配工作树中的文件,通常也不匹配。


2021年11月:参见Derrick Stolee (Microsoft/GitHub)的“用Git的稀疏索引让你的单库存感觉很小”

The Git index is a critical data structure in Git. It serves as the “staging area” between the files you have on your filesystem and your commit history. When you run git add, the files from your working directory are hashed and stored as objects in the index, leading them to be “staged changes”. When you run git commit, the staged changes as stored in the index are used to create that new commit. When you run git checkout, Git takes the data from a commit and writes it to the working directory and the index. In addition to storing your staged changes, the index also stores filesystem information about your working directory. This helps Git report changed files more quickly.


要了解更多,请参阅。“git / git /文档/技术/ index-format.txt”:

Git索引文件的格式如下

所有二进制数都是网络字节顺序。 除非另有说明,这里描述的是版本2。

A 12-byte header consisting of: 4-byte signature: The signature is { 'D', 'I', 'R', 'C' } (stands for "dircache") 4-byte version number: The current supported versions are 2, 3 and 4. 32-bit number of index entries. A number of sorted index entries. Extensions: Extensions are identified by signature. Optional extensions can be ignored if Git does not understand them. Git currently supports cached tree and resolve undo extensions. 4-byte extension signature. If the first byte is 'A'..'Z' the extension is optional and can be ignored. 32-bit size of the extension Extension data 160-bit SHA-1 over the content of the index file before this checksum.


mljrg评论:

如果索引是下一次提交准备的地方,为什么"git ls-files -s"在提交后不返回任何东西?

因为索引表示被跟踪的内容,并且在提交之后,被跟踪的内容与上次提交是相同的(git diff——cached不返回任何内容)。

所以git ls-files -s列出了所有跟踪的文件(输出中的对象名称,模式位和阶段号)。

该列表(跟踪的元素)由提交的内容初始化。 切换分支时,索引内容被重置为刚才切换到的分支引用的提交。


Git 2.20(2018年Q4)添加了一个索引项偏移表(IEOT):

参见Ben Peart (benpeart)的commit 77ff112, commit 3255089, commit abb4bb8, commit c780b9c, commit 3b1d9e0, commit 371ed0d(2018年10月10日)。 参见提交252d079(2018年9月26日)由nguyThái ngeconc Duy (pclouds)。 (由Junio C Hamano—gitster—在commit e27bfaa中合并,2018年10月19日)

ieot:添加ieot (Index Entry Offset Table)扩展

This patch enables addressing the CPU cost of loading the index by adding additional data to the index that will allow us to efficiently multi- thread the loading and conversion of cache entries. It accomplishes this by adding an (optional) index extension that is a table of offsets to blocks of cache entries in the index file. To make this work for V4 indexes, when writing the cache entries, it periodically"resets" the prefix-compression by encoding the current entry as if the path name for the previous entry is completely different and saves the offset of that entry in the IEOT. Basically, with V4 indexes, it generates offsets into blocks of prefix-compressed entries.

用新的索引。线程配置设置,索引加载现在更快。


作为(使用IEOT)的结果,提交7bd9631清理Git 2.23(2019年Q3)的read-cache.c load_cache_entres_threading()函数。

参见Jeff King (peff)的commit 8373037、commit d713e88、commit d92349d、commit 113c29a、commit c95fc72、commit 7a2a721、commit c016579、commit be27fb7、commit 13a1781、commit 7bd9631、commit 3c1dce8、commit cf7a901、commit d64db5b、commit 76a7bc0(2019年5月9日)。 (由Junio C Hamano—gitster—在commit c0e78f7中合并,2019年6月13日)

Read-cache:从线程加载中删除未使用的参数

load_cache_entries_threading()函数的作用是:带src_offset参数 它不使用。自77ff112 (read-cache:在工作线程上加载缓存项,2018-10-10,Git v2.20.0-rc0)开始时就存在了。 在邮件列表中,该参数是该系列早期迭代的一部分,但当代码切换到使用IEOT扩展时,就不需要了。


在Git 2.29 (Q4 2020)中,格式描述调整为最近的SHA-256工作。

参见commit 8afa50a, commit 0756e61, commit 123712b, commit 5b6422a (15 Aug 2020) by Martin Ågren (none)。 (由Junio C Hamano—gitster—在commit 74a395c中合并,2020年8月19日)

index-format.txt:文件SHA-256索引格式 署名:Martin Ågren

在SHA-1存储库中,我们使用SHA-1,在SHA-256存储库中,我们使用SHA-256,然后用更中性的东西取代“SHA-1”的所有其他用途。 避免引用“160位”哈希值。

技术/索引格式现在包括在它的手册页:

所有二进制数都是网络字节顺序。 在使用传统SHA-1的存储库中,校验和和对象id (对象名)都是使用SHA-1计算的。 类似地,在SHA-256存储库中,这些值是使用SHA-256计算的。 除非另有说明,这里描述的是版本2。

在回应@ciro-santilli-%e9%83%9d%e6%b5%b7% 4%b8%9c%e5%86% a07 %8a%b6% 97% 855 %85%ad%e5%9b%9b%e4%ba%8b%e4%bb%b6% b3% 95b %e5% e5%8a%9f的指数,am分享输出为其中一个TODO。

如果你添加,那么阶段0将被添加到该路径的索引中,git将知道冲突已被标记为已解决。托朵:检查这个。”

更具体地说,是不同的合并阶段。

0:普通文件,不存在合并冲突 1:基础 2:我们的 3:他们的

关于各个阶段的详细数字表示,在这种情况下一个文件有冲突。

$ git ls-files -s
100644 f72d68f0d10f6efdb8adc8553a1df9c0444a0bec 0       vars/buildComponent.groovy

$ git stash list
stash@{0}: WIP on master: c40172e turn off notifications, temporarily

$ git stash apply
Auto-merging vars/commonUtils.groovy
Auto-merging vars/buildComponent.groovy
CONFLICT (content): Merge conflict in vars/buildComponent.groovy

$ git ls-files -s
100644 bc48727339d36f5d54e14081f8357a0168f4c665 1       vars/buildComponent.groovy
100644 f72d68f0d10f6efdb8adc8553a1df9c0444a0bec 2       vars/buildComponent.groovy
100644 24dd5be1783633bbb049b35fc01e8e88facb20e2 3       vars/buildComponent.groovy

逐位分析

我决定做一些测试,以更好地理解格式,并更详细地研究一些领域。

下面的结果与Git版本1.8.5.2和2.3相同。

我已经标记了一些我不确定/没有发现TODO的地方:请随时补充这些地方。

正如其他人提到的,索引存储在.git/index下,而不是作为标准树对象,它的格式是二进制的,并记录在:https://github.com/git/git/blob/master/Documentation/technical/index-format.txt

定义索引的主要结构体位于cache.h,因为索引是用于创建提交的缓存。

设置

当我们开始一个测试存储库时:

git init
echo a > b
git add b
tree --charset=ascii

.git目录如下:

.git/objects/
|-- 78
|   `-- 981922613b2afb6025042ff6bd878ac1994e85
|-- info
`-- pack

如果我们得到唯一对象的内容:

git cat-file -p 78981922613b2afb6025042ff6bd878ac1994e85

得到a,这表明:

索引指向文件内容,因为git add b创建了一个blob对象 它将元数据存储在索引文件中,而不是树对象中,因为只有一个对象:blob(在常规Git对象中,blob元数据存储在树中)

高清分析

现在让我们看看索引本身:

hd .git/index

给:

00000000  44 49 52 43 00 00 00 02  00 00 00 01 54 09 76 e6  |DIRC.... ....T.v.|
00000010  1d 81 6f c6 54 09 76 e6  1d 81 6f c6 00 00 08 05  |..o.T.v. ..o.....|
00000020  00 e4 2e 76 00 00 81 a4  00 00 03 e8 00 00 03 e8  |...v.... ........|
00000030  00 00 00 02 78 98 19 22  61 3b 2a fb 60 25 04 2f  |....x.." a;*.`%./|
00000040  f6 bd 87 8a c1 99 4e 85  00 01 62 00 ee 33 c0 3a  |......N. ..b..3.:|
00000050  be 41 4b 1f d7 1d 33 a9  da d4 93 9a 09 ab 49 94  |.AK...3. ......I.|
00000060

接下来我们将得出结论:

  | 0           | 4            | 8           | C              |
  |-------------|--------------|-------------|----------------|
0 | DIRC        | Version      | File count  | ctime       ...| 0
  | ...         | mtime                      | device         |
2 | inode       | mode         | UID         | GID            | 2
  | File size   | Entry SHA-1                              ...|
4 | ...                        | Flags       | Index SHA-1 ...| 4
  | ...                                                       |

首先是头文件,定义在:struct cache_header:

44 49 52 43: dirc。为什么这是必要的? 00 00 00 02:格式版本:2。索引格式随着时间的推移而发展。目前有多达4个版本。在GitHub上不同计算机之间协作时,索引的格式不应该成为问题,因为裸存储库不存储索引:它是在克隆时生成的。 00 00 00 01:索引上的文件数:只有一个,b。

接下来开始一个由struct cache_entry定义的索引项列表,这里我们只有一个。它包含:

a bunch of file metadata: 8 byte ctime, 8 byte mtime, then 4 byte: device, inode, mode, UID and GID. Note how: ctime and mtime are the same (54 09 76 e6 1d 81 6f c6) as expected since we haven't modified the file The first bytes are seconds since EPOCH in hex: date --date="@$(printf "%x" "540976e6")" Gives: Fri Sep 5 10:40:06 CEST 2014 Which is when I made this example. The second 4 bytes are nanoseconds. UID and GID are 00 00 03 e8, 1000 in hex: a common value for single user setups. All of this metadata, most of which is not present in tree objects, allows Git to check if a file has changed quickly without comparing the entire contents. at the beginning of line 30: 00 00 00 02: file size: 2 bytes (a and \n from echo) 78 98 19 22 ... c1 99 4e 85: 20 byte SHA-1 over the previous content of the entry. Note that according to my experiments with the assume valid flag, the flags that follow it are not considered in this SHA-1. 2 byte flags: 00 01 1 bit: assume valid flag. My investigations indicate that this poorly named flag is where git update-index --assume-unchanged stores its state: https://stackoverflow.com/a/28657085/895245 1 bit extended flag. Determines if the extended flags are present or not. Must be 0 on version 2 which does not have extended flags. 2 bit stage flag used during merge. Stages are documented in man git-merge: 0: regular file, not in a merge conflict 1: base 2: ours 3: theirs During a merge conflict, all stages from 1-3 are stored in the index to allow operations like git checkout --ours. If you git add, then a stage 0 is added to the index for the path, and Git will know that the conflict has been marked as solved. TODO: check this. 12 bit length of the path that will follow: 0 01: 1 byte only since the path was b 2 byte extended flags. Only meaningful if the "extended flag" was set on the basic flags. TODO. 62 (ASCII b): variable length path. Length determined in the previous flags, here just 1 byte, b.

然后是一个00:1-8字节的零填充,这样路径将以空结束,索引将以8字节的倍数结束。这只发生在索引版本4之前。

没有使用扩展。Git知道这一点,因为文件中没有足够的空间用于校验和。

最后有一个20字节的校验和ee 33 c0 3a ..09 ab 49 94的索引内容。