git索引完全包含什么?


183

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


更新资料

感谢您的所有答复。我知道索引充当临时区域,提交的内容在索引中而不是工作树中。我只是对索引对象的组成感到好奇。我想这可能是文件名/目录名称,SHA-1对的列表,也许是一种虚拟树?

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



3
您应该阅读并观看图表-很有帮助: gitguys.com/topics/whats-the-deal-with-the-git-index
kernix 2013年

1
@kernix域已过期。不再是很有帮助。
narendra-choudhary

Answers:


164

Git书中包含有关索引包含的内容的文章:

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

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

情趣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(Q4 2018)添加了一个索引条目偏移表(IEOT)

请参阅Ben Peart()的提交77ff112提交3255089提交abb4bb8提交c780b9c提交3b1d9e0提交371ed0d(2018年10月10日。 见提交252d079通过(2018年9月26日)阮泰玉维战((由Junio C Hamano合并--commit 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(Q3 2019)的功能。

请参阅提交8373037提交d713e88提交d92349d提交113c29a提交c95fc72提交7a2a721提交c016579提交be27fb7提交13a1781提交7bd9631提交3c1dce8提交cf7a901提交d64db5b提交76a7bc0(09年5月7日)(Jeff (peff
(通过合并JUNIOÇ滨野- 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(提交8afa50a提交0756e61提交123712b提交5b6422a(2020年8月15日(通过合并JUNIOÇ滨野- -提交74a395c 8月19日2020)none
gitster

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

签字人:马丁·奥格伦

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

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

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

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


6
关于重要性,如果在GIT模型中的指标,请参阅stackoverflow.com/questions/1450348/...
VonC

上面的第一个链接指向git-scm的版本,该版本在索引上没有文章。我认为目的是要指向这里:schacon.github.io/gitbook/7_the_git_index.html
Kris Giesing

1
@KrisGiesing谢谢您的链接。我已经更新了答案。
VonC 2013年

@VonC如果索引是准备下一次提交的地方,为什么在提交后“ git ls-files -s”不返回任何内容?关于索引的内容肯定比您在答案中所写的要多。
mljrg 2014年

@mljrg不知道我跟着你:提交,(其中提交正在编写)的舞台将是空的,因为提交已经完成,不会是?
VonC 2014年

63

一点一点分析

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

下面的结果与Git版本1.8.5.2和相同2.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。这表明:

  • indexgit 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 43DIRC。TODO:为什么这是必要的?

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

  • 00 00 00 01:索引上的文件数:仅一个,b

接下来启动由struct cache_entry定义的索引条目列表。这里只有一个。它包含了:

  • 一堆文件元数据:8字节ctime,8字节mtime,4字节:设备,索引节点,模式,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 e8,十六进制为1000:这是单个用户设置的常用值。

    所有这些元数据(大多数不存在于树对象中)使Git可以检查文件是否快速更改,而无需比较整个内容。

  • 在一行的开头3000 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字节的校验和。


1
很有意思。+1。那很好地说明了我自己的答案。我不知道这些结果是否会随着最新的Git 2.1+而改变。
VonC 2014年

3
@NielsBom是的,那也可以。在解释程序时,我更喜欢采用两种方法:首先凭经验查看其生成的输出,然后才阅读源代码。否则,可能会陷入源代码边缘情况,甚至不会出现在简单输出中。当然,我确实查看了源代码结构以帮助指导我,并且阅读每个结构如何操纵这些TODO都可以解决,这是困难的部分。
西罗Santilli郝海东冠状病六四事件法轮功2015年

1
@CiroSantilli六四事件法轮功纳米比亚威视:如果我在十六进制编辑器中修改索引并更新它的20字节校验和,是否有命令更新存储在其他对象中的sha1?(git抱怨索引的sha1签名已损坏)。当通过推送请求发送索引数据时,索引数据也以完全不同的方式存储。
user2284570

1
@CiroSantilli六四事件法轮功纳米比亚威视:安全目的。只是寻找众所周知的应用于git数据库/对象的光栅图像文件攻击。(当然,我知道大多数实现最近都在考虑这种观点,但可能不是全部)。  因此,我特别在寻找能说明数组长度的二进制数据结构。(关于文本缓冲区,似乎空终止是告知行数的标准)
user2284570 2015年

1
关于git add,根据您的意见TODO:您是正确的。如果给定路径上有高级索引条目(发生冲突),则在git add该路径上时,所有高级索引条目都将被删除,而工作目录副本将在stage处添加0。(解决冲突)。
爱德华·汤姆森

11

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

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

git status

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

您可以阅读此内容。Google搜索会抛出许多链接,这些链接应该足够自给。


7
git status不列出索引中的所有文件。它仅列出索引目录和工作目录之间不同的那些文件。要查看索引中的所有文件,您需要使用git ls-files
Akash Agrawal 2014年

1
@AkashAgrawal, git status 确实事实上列表索引文件,不论他们是否指数和WORKDIR不同。
Acumenus 2014年

3
是的,它列出了一些索引文件,但是并没有向您显示索引中的所有内容,这就是他在回答中所说的。这就像说一个盒子里有2个绿色球和3个红色球。要查看包装盒中的内容,请拉出2个绿色的球。Akash所说的最准确,要查看索引中的所有文件,请使用git ls-files。
dave4jr

3
确实。 git status列出索引中的文件(是),但不列出索引中的所有文件。解释git status 实际的工作方式将是一个问题的有益答案,尽管可能不是这个问题。
爱德华·汤姆森

1
git status显示工作树状态(工作树和索引之间的差异)。它实际上没有显示索引。git-scm.com/docs/git-status
wisbucky

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

0

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

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

在此处输入图片说明

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

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

资料来源

  1. https://mincong.io/2018/04/28/git-index/
  2. https://medium.com/hackernoon/understanding-git-index-4821a0765cf

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.