209

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


感谢您的所有回答。我知道索引充当了一个暂存区,并且提交的是索引而不是工作树。我只是对索引对象的组成感到好奇。我猜它可能是文件名/目录名列表、SHA-1 对、一种虚拟树?

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

4

7 回答 7

181

Git book 包含一篇关于索引包含什么的文章:

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

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

Racy git 问题提供了有关该结构的更多详细信息:

索引是 git 中最重要的数据结构之一。
它通过记录路径列表及其对象名称来表示虚拟工作树状态,并用作暂存区以写出下一个要提交的树对象。
从某种意义上说,状态是“虚拟的”,它不一定必须而且通常不会匹配工作树中的文件。


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

https://github.blog/wp-content/uploads/2021/11/Fig-1-working-directory-index-commit-history.png

Git 索引是 Git 中的一个关键数据结构。它充当文件系统上的文件和提交历史记录之间的“暂存区”。

  • 当你运行git add时,你工作目录中的文件被散列并作为对象存储在索引中,导致它们被“分阶段更改”。
  • 当您运行git commit时,存储在索引中的暂存更改用于创建新的提交。
  • 当你运行时git checkout,Git 从提交中获取数据并将其写入工作目录和索引。

除了存储您的分阶段更改之外,索引还存储有关您的工作目录的文件系统信息。
这有助于 Git 更快地报告更改的文件。


要了解更多信息,请参阅。“ git/git/Documentation/technical/index-format.txt ”:

Git 索引文件具有以下格式

所有二进制数都按网络字节顺序排列。除非另有说明,否则此处描述的是
版本 2 。

  • 一个 12 字节的标头,包括:
  • 4字节签名
    签名为{ ' D', ' I', ' R', ' C' }(代表“ dircache”)
  • 4 字节版本号
    当前支持的版本为 2、3 和 4。
  • 32 位索引条目数。
  • 一些已排序的索引条目
  • 扩展
    扩展由签名标识。
    如果 Git 不理解可选扩展,可以忽略它们。
    Git 当前支持缓存树和解析撤消扩展。
  • 4 字节扩展签名。如果第一个字节是' A'..' Z',则扩展是可选的,可以忽略。
  • 扩展的 32 位大小
  • 扩展数据
  • 此校验和之前的索引文件内容的 160 位 SHA-1。

mljrg 评论

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

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

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

该列表(跟踪的元素)使用提交的内容进行初始化。
当您切换分支时,索引内容将重置为您刚刚切换到的分支引用的提交。


Git 2.20(2018 年第四季度)添加了索引条目偏移表(IEOT)

请参阅Ben Peart ( )的提交 77ff112提交 3255089提交 abb4bb8提交 c780b9c提交 3b1d9e0提交 371ed0d(2018 年 10 月 10 日) 。 请参阅Nguyễn Thái Ngọc Duy ( ) 的提交 252d079(2018 年 9 月 26 日(由Junio C Hamano 合并 -- --提交 e27bfaa中,2018 年 10 月 19 日)benpeart
pclouds
gitster

ieot:添加索引条目偏移表 (IEOT) 扩展

该补丁通过向索引添加额外数据来解决加载索引的 CPU 成本问题,这将使我们能够高效地多线程加载和转换缓存条目。

它通过添加一个(可选的)索引扩展来实现这一点,该扩展是索引文件中缓存条目块的偏移量表。

为了使这适用于 V4 索引,在写入缓存条目时,它会定期“重置”前缀压缩,方法是对当前条目进行编码,就好像前一个条目的路径名完全不同一样,并将该条目的偏移量保存在 IEOT .
基本上,使用 V4 索引,它会生成前缀压缩条目块的偏移量。

使用新的 index.threads 配置设置,索引加载现在更快。


结果(使用 IEOT),提交 7bd9631清理了read-cache.c load_cache_entries_threaded()Git 2.23(2019 年第三季度)的功能。

请参阅提交8313037提交D92349D提交113C29ACommit C95FC72Commit 7a2a721提交C016579Commit Be27FB7提交13A1781Commit 7BD9631Commit 3C1DCE8,提交CF7A901 ,Jeff King(2019年5月9日)提交的D64DB5B提交3C1DCE8( peff)
(由Junio C Hamano 合并 -- gitster--提交 c0e78f7中,2019 年 6 月 13 日)

读取缓存:从线程加载中删除未使用的参数

load_cache_entries_threaded()函数接受一个src_offset它不使用的参数。这自77ff112成立以来就一直存在(read-cache:在工作线程上加载缓存条目,2018-10-10,Git v2.20.0-rc0)。

在邮件列表中挖掘,该参数是该系列早期迭代的一部分,但当代码切换到使用 IEOT 扩展时变得不必要了。


在 Git 2.29(2020 年第四季度)中,格式描述会根据最近的 SHA-256 工作进行调整。

请参阅Martin Ågren ( ) 的commit 8afa50acommit 0756e61commit 123712bcommit 5b6422a(2020 年 8 月 15 日(由Junio C Hamano 合并——提交 74a395c中,2020 年 8 月 19 日)none
gitster

index-format.txt: 文档 SHA-256 索引格式

签字人:Martin Ågren

记录在 SHA-1 存储库中,我们使用 SHA-1,在 SHA-256 存储库中,我们使用 SHA-256,然后将“SHA-1”的所有其他用途替换为更中性的东西。
避免提及“160 位”哈希值。

technical/index-format现在在其手册页中包含:

所有二进制数都按网络字节顺序排列。
在使用传统 SHA-1 的存储库中,下面提到的校验和和对象 ID(对象名称)都是使用 SHA-1 计算的。
同样,在 SHA-256 存储库中,这些值是使用 SHA-256 计算的。

除非另有说明,否则此处描述的是版本 2。

于 2010-11-03T12:23:19.787 回答
65

点点滴滴分析

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

下面的结果对于 Git 版本1.8.5.22.3.

我已经标记了我不确定/没有找到的TODO点:请随时补充这些点。

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

定义索引的主要结构位于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. 这表明:

  • index指向文件内容的点,因为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. TODO:为什么这是必要的?

  • 00 00 00 02: 格式版本: 2. 索引格式随着时间的推移而发展。目前存在的版本最高为 4。在 GitHub 上的不同计算机之间进行协作时,索引的格式不应该成为问题,因为裸存储库不存储索引:它是在克隆时生成的。

  • 00 00 00 01:索引上的文件数:只有一个,b.

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

  • 一堆文件元数据:8 字节ctime,8 字节mtime,然后是 4 字节:设备、inode、模式、UID 和 GID。

    注意如何:

    • ctime和预期mtime的一样 ( 54 09 76 e6 1d 81 6f c6) 因为我们没有修改文件

      第一个字节是自 EPOCH 以十六进制表示的秒数:

      date --date="@$(printf "%x" "540976e6")"
      

      给出:

      Fri Sep  5 10:40:06 CEST 2014
      

      那是我做这个例子的时候。

      第二个 4 个字节是纳秒。

    • UID 和 GID 是00 00 03 e81000(十六进制):单用户设置的常用值。

    所有这些元数据(其中大部分都不存在于树对象中)允许 Git 检查文件是否已快速更改,而无需比较整个内容。

  • 在行首30::00 00 00 02文件大小:2字节(a\necho

  • 78 98 19 22 ... c1 99 4e 85: 20 字节 SHA-1 超过条目的先前内容。请注意,根据我对假设有效标志的实验,此 SHA-1 中不考虑其后面的标志。

  • 2字节标志:00 01

    • 1 位:假设有效标志。我的调查表明,这个名字不好的标志是git update-index --assume-unchanged存储其状态的地方:https ://stackoverflow.com/a/28657085/895245

    • 1 位扩展标志。确定扩展标志是否存在。必须0在没有扩展标志的版本 2 上。

    • 合并期间使用的 2 位阶段标志。阶段记录在man git-merge

      • 0: 常规文件,不在合并冲突中
      • 1: 根据
      • 2: 我们的
      • 3: 他们的

      在合并冲突期间,从 1 到 3 的所有阶段都存储在索引中,以允许像git checkout --ours.

      如果您git add是 ,则将阶段 0 添加到路径的索引中,Git 将知道冲突已被标记为已解决。TODO:检查这个。

    • 将遵循的路径的 12 位长度0 01::仅 1 个字节,因为路径是b

  • 2 字节扩展标志。只有在基本标志上设置了“扩展标志”时才有意义。去做。

  • 62(ASCII b):可变长度路径。长度在前面的标志中确定,这里只有 1 个字节,b.

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

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

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

于 2014-09-12T10:42:18.653 回答
12

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

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

git status

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

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

于 2010-11-03T07:25:48.647 回答
3

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

git ls-files可以显示索引的内容。请注意,Git 中的单词indexstagecache是相同的:它们可以互换使用。

在此处输入图像描述

Git 索引或 Git 缓存具有 3 个重要属性:

  1. 该索引包含生成单个(唯一确定的)树对象所需的所有信息。
  2. 索引可以在它定义的树对象和工作树之间进行快速比较。
  3. 它可以有效地表示有关不同树对象之间合并冲突的信息,允许每个路径名与有关所涉及的树的足够信息相关联,您可以在它们之间创建三向合并。

来源

  1. https://mincong.io/2018/04/28/git-index/
  2. https://medium.com/hackernoon/understanding-git-index-4821a0765cf
于 2020-07-12T14:03:54.400 回答
1

这是您真正需要的,请使用此命令。

$ binwalk index

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
于 2019-01-24T16:47:37.840 回答
0

回应@ciro-santilli-%e9%83%9d%e6%b5%b7%e4%b8%9c%e5%86%a0%e7%8a%b6%e7%97%85%e5%85%ad %e5%9b%9b%e4%ba%8b%e4%bb%b6%e6%b3%95%e8%bd%ae%e5%8a%9f 详细深入查看索引,我分享输出一的TODO

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

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

  • 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
于 2020-10-10T03:43:00.810 回答
0

只是想把git ls-tree放在戒指中。

索引是 git 中最重要的数据结构之一。
它通过记录路径列表及其对象名称来表示虚拟工作树状态,并用作暂存区以写出下一个要提交的树对象。
从某种意义上说,状态是“虚拟的”,它不一定必须而且通常不会匹配工作树中的文件。

如果我签出一个特殊的提交,是否可以说git ls-tree准确地告诉我应该存在哪些工作文件/对象?在 ls-tree 的上下文中,我们说的是哪种树?

例子

git ls-tree -r -l HEAD
git ls-tree -r -l commit-hash

顺便说一句:ls-tree 也适用于没有检出 (-n) 的克隆存储库,其中 ls-files 不返回任何内容。

https://stackoverflow.com/a/56242906/2623045

https://stackoverflow.com/a/67567058/2623045

于 2021-05-17T17:41:28.947 回答