Что в git index содержится ТОЧНО?

178

Что именно содержит индекс Git, и какую команду можно использовать для просмотра содержимого индекса?


Обновить

Спасибо за все ваши ответы. Я знаю, что индекс действует как промежуточная область, и то, что фиксируется, находится в индексе, а не в рабочем дереве. Мне просто интересно, из чего состоит индексный объект. Я предполагаю, что это может быть список имя файла / имя каталога, пары SHA-1, что-то вроде виртуального дерева?

Есть ли в терминологии Git какая-либо сантехническая команда, которую я могу использовать для отображения содержимого индекса?

mochidino
источник
3
Вы должны читать и смотреть диаграммы - очень полезно: gitguys.com/topics/whats-the-deal-with-the-git-index
kernix
1
@kernix срок действия домена истек. Не очень полезно больше.
Нарендра-Чоудхари
2
обновленная ссылка: web.archive.org/web/20160822072849/http://www.gitguys.com/…
ndequeker

Ответы:

162

Книга Git содержит статью о том, что включает в себя индекс :

Индекс представляет собой двоичный файл (обычно хранящийся в нем .git/index), содержащий отсортированный список имен путей, каждый с разрешениями и SHA1 объекта BLOB-объекта; git ls-filesможет показать вам содержимое индекса:

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

Проблема Racy 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) :

См. Коммит 77ff112 , коммит 3255089 , коммит abb4bb8 , коммит c780b9c , коммит 3b1d9e0 , коммит 371ed0d (10 октября 2018 г.) от Ben Peart ( benpeart) .
См. Коммит 252d079 (26 сентября 2018 г.) Нгуена Тхая Нгука Дуй ( pclouds) .
(Слиты Junio C Hamano - gitster- в фиксации e27bfaa , 19 октября 2018)

ieot: добавить расширение таблицы смещения записи индекса (IEOT)

Этот патч позволяет решить проблему загрузки индекса процессором, добавив к нему дополнительные данные, что позволит нам эффективно выполнять многопоточную загрузку и преобразование записей кэша.

Это достигается путем добавления (необязательного) расширения индекса, которое представляет собой таблицу смещений, к блокам записей кэша в файле индекса.

Чтобы это работало для индексов 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 мая 2019) от Jeff King ( peff) .
(Слиты Junio C Hamano - gitster- в фиксации c0e78f7 , 13 июня 2019)

read-cache: удалить неиспользуемый параметр из многопоточной загрузки

load_cache_entries_threaded()Функция принимает src_offsetпараметр , который он не использует. Это происходит с момента его создания в 77ff112 ( read-cacheзагрузка записей кэша в рабочих потоках, 2018-10-10, Git v2.20.0-rc0).

Копая в списке рассылки, этот параметр был частью более ранней итерации серии , но стал ненужным, когда код переключился на использование расширения IEOT.

VonC
источник
6
О важности индекса в модели Git см. Stackoverflow.com/questions/1450348/…
VonC
Первая ссылка выше указывает на версию git-scm, в которой нет статьи об индексе. Я думаю, что намерение было указать здесь: schacon.github.io/gitbook/7_the_git_index.html
Крис Гизинг
1
@ KrisGiesing Спасибо за ссылку. Я обновил ответ.
VonC
@VonC Если индекс - это место, где готовится следующий коммит, почему «git ls-files -s» ничего не возвращает после коммита? В индексе должно быть что-то большее, чем вы указали в своем ответе.
mljrg
@mljrg не уверен, что я следую за тобой: после коммита этап (где готовился коммит) будет пустым, поскольку коммит уже сделан, не так ли?
VonC
62

Побитовый анализ

Я решил провести небольшое тестирование, чтобы лучше понять формат и исследовать некоторые области более подробно.

Результаты ниже одинаковы для версий 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. Это указывает на то, что:

  • что indexуказывает на содержимое файла, так как git add bсоздается объект блоб
  • он хранит метаданные в индексном файле, а не в объекте дерева, поскольку был только один объект: BLOB-объект (в обычных объектах Git метаданные BLOB-объектов хранятся в дереве)

анализ HD

Теперь давайте посмотрим на сам индекс:

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,.

Далее начинается список записей индекса, определяемых структурой 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 e81000 в шестнадцатеричном формате: общее значение для однопользовательских настроек.

    Все эти метаданные, большинство из которых отсутствуют в древовидных объектах, позволяют Git проверять, быстро ли изменился файл, не сравнивая все содержимое.

  • в начале строки 30:: 00 00 00 02размер файла: 2 байта ( aи \nиз echo)

  • 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 будет знать, что конфликт был помечен как разрешенный. ТОДО: проверь это.

    • 12-битная длина пути, который будет следовать 0 01:: только 1 байт, поскольку путь былb

  • 2-байтовые расширенные флаги. Имеет смысл только если «расширенный флаг» был установлен на базовые флаги. ДЕЛАТЬ.

  • 62(ASCII b): путь переменной длины. Длина определяется в предыдущих флагах, здесь всего 1 байт b.

Затем идет 00: 1-8 байтов заполнения нулями, чтобы путь заканчивался нулем, а индекс заканчивался кратным 8 байтам. Это происходит только до версии 4 индекса.

Расширения не использовались. Git знает об этом, потому что в файле не хватит места для контрольной суммы.

Наконец, есть 20-байтовая контрольная сумма ee 33 c0 3a .. 09 ab 49 94для содержимого индекса.

Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
1
Очень интересно. +1. Это хорошо иллюстрирует мой собственный ответ . Интересно, изменились бы эти результаты с последней версией Git 2.1+?
VonC
3
@NielsBom да, это будет работать также. При интерпретации программ я предпочитаю использовать два подхода: сначала эмпирический, чтобы увидеть, какие результаты он генерирует, и только потом читать исходный код. В противном случае можно попасть в крайние случаи исходного кода, которые даже не появляются на простых выходных данных. Конечно, я посмотрел на исходные структуры, чтобы помочь мне, и каждый TODO может быть решен в моем чтении, как манипулируют этими структурами, что является трудной частью.
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
1
@CiroSantilli: Если я изменяю индекс в шестнадцатеричном редакторе и обновляю его 20-байтовую контрольную сумму, есть ли команда для обновления sha1, который хранится в других объектах? (git жалуется, что подпись индекса sha1 повреждена) . Также данные индекса хранятся совершенно другим способом при отправке через push-запросы.
user2284570
1
@CiroSantilli 六四 事件 法轮功 纳米比亚 威 视: Цели безопасности. Просто ищем хорошо известные атаки файлов растровых изображений, применяемые к git базе данных / объектам. (конечно, я знаю, что большинство реализаций недавно позаботились об этой перспективе, но, вероятно, не обо всех).  Поэтому я особенно ищу структуры двоичных данных, которые сообщают длину массива. (что касается текстовых буферов, кажется, что нулевое завершение является нормой для определения количества строк)
user2284570
1
Что касается git add, по вашему TODO: вы правы. Если у вас есть записи индекса верхнего уровня (конфликт) по заданному пути, то при git addэтом пути все записи индекса верхнего уровня будут удалены, а копия рабочего каталога будет добавлена ​​на этапе 0. (Разрешение конфликта).
Эдвард Томсон
11

Индекс Git является промежуточной областью между вашим рабочим каталогом и вашим хранилищем. Вы можете использовать индекс для создания набора изменений, которые вы хотите зафиксировать вместе. Когда вы создаете коммит, то, что фиксируется, это то, что в данный момент находится в этом индексе, а не то, что находится в вашем рабочем каталоге.

Чтобы увидеть, что находится внутри индекса, введите команду:

git status

Когда вы запускаете git status, вы можете видеть, какие файлы размещены (в данный момент в вашем индексе), которые изменены, но еще не подготовлены, а какие полностью не отслежены.

Вы можете прочитать это . Поиск Google выдает много ссылок, которые должны быть достаточно самодостаточными.

user225312
источник
7
git statusне перечисляет все файлы из индекса. В нем перечислены только те файлы, которые отличаются между индексом и рабочим каталогом. Чтобы увидеть все файлы в индексе, вам необходимо использовать git ls-files.
Акаш Агравал
1
@AkashAgrawal, git status делает в списке факт индексных файлов, независимо от того, отличаются ли они между указательным и WORKDIR.
Acumenus
3
да, он перечисляет НЕКОТОРЫЕ из индексных файлов, но он не показывает вам все, что находится внутри индекса, о чем говорит его утверждение в ответе. Это все равно что сказать, что в коробке 2 зеленых шарика и 3 красных шарика. Чтобы увидеть, что внутри коробки, вытащите 2 зеленых шарика. То, что сказал Акаш, является наиболее точным, чтобы увидеть все файлы в индексе, используйте 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 index - это двоичный файл (обычно хранящийся в нем .git/index), содержащий отсортированный список имен путей, каждый с правами доступа и SHA1 объекта BLOB-объекта;

git ls-filesможет показать вам содержимое индекса. Обратите внимание, что слова index, stageи cacheэто то же самое в Git: они используются взаимозаменяемо

введите описание изображения здесь

Git index или Git cache имеет 3 важных свойства:

  1. Индекс содержит всю информацию, необходимую для генерации одного (однозначно определенного) объекта дерева.
  2. Индекс обеспечивает быстрое сравнение между определенным им объектом дерева и рабочим деревом.
  3. Он может эффективно представлять информацию о конфликтах слияния между различными объектами дерева, позволяя связать каждое имя пути с достаточной информацией о задействованных деревьях, чтобы вы могли создать трехстороннее слияние между ними.

Источник :

  1. https://mincong.io/2018/04/28/git-index/
  2. https://medium.com/hackernoon/understanding-git-index-4821a0765cf
Saikat
источник