1. Как Git обнаруживает, что нужно создать дерево?
Когда вы фиксируете, git создает древовидную иерархию для содержимого индекса, а затем создает коммит, ссылающийся на корень этой древовидной иерархии. После операции git-add хранилище содержит объекты BLOB-объектов для всех добавленных файлов, а индекс содержит ссылки на BLOB-объекты в паре с именами путей. Здесь еще нет объектов из дерева.
Когда вы фиксируете (технически, во время операции дерева записи), git рекурсивно создает набор деревьев, используя информацию индекса. Он начинается с деревьев, которые содержат только капли, определяет их идентификаторы и записывает объекты дерева. Затем он поднимается на каждый уровень и создает следующий набор деревьев, поскольку это не может произойти до того, как станут известны идентификаторы поддерева. Затем он сохраняет дерево корневого уровня.
Операция фиксации разбита на этапы дерева записи и дерева фиксации. Шаг дерева записи использует текущее состояние индекса для идентификации и (при необходимости) хранения всех деревьев. Шаг дерева коммитов создает новый коммит, ссылающийся на все родительские коммиты и только что созданное корневое дерево.
2. Что хранится под деревом в любой момент?
Когда вы узнаете, как использовать git, основное внимание будет уделено ориентированному ациклическому графу (DAG) коммитов: каждый коммит содержит указатель на предыдущий коммит, и вы можете вернуться назад во времени, следуя этим ссылкам. Это имеет смысл, поскольку пользовательский интерфейс связан с коммитами, а деревья действительно вторичны.
Деревья также образуют DAG, но разница в том, что они не представляют историю коммитов. Точно так же, как блоб, когда дерево создано, его идентификатор всегда будет указывать на это дерево с этим содержимым. Если какой-либо из BLOB-объектов или деревьев, перечисленных в дереве, будет изменен или удален, у него будет новый идентификатор, а само дерево будет иметь новое имя в следующем коммите.
3. Новое дерево "ревизия" создается каждый раз, когда капля под этим деревом модифицируется?
Хорошо, допустим, ваш репозиторий выглядит так:
foo/
a.txt
b.txt
bar/
a.txt
b.txt
и все файлы пусты. Тогда в хранилище есть три объекта, не считая фиксации:
Дерево верхнего уровня:
$ git cat-file -p ebf247ec5ebc97b12cd7a56db330141568edb946
040000 tree 2bdf04adb23d2b40b6085efb230856e5e2a775b7 bar
040000 tree 2bdf04adb23d2b40b6085efb230856e5e2a775b7 foo
Дерево с двумя каплями:
$ git cat-file -p 2bdf04adb23d2b40b6085efb230856e5e2a775b7
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 a.txt
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 b.txt
Пустой блоб:
$ git cat-file -p e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
Сначала я объясню, почему деревья foo
и bar
хранятся в одном объекте, затем внесу изменения и посмотрю, что произойдет.
Идентификатор SHA1 дерева полностью определяется его содержимым, как и BLOB-объект. Обратите внимание, что его имя не задействовано, что означает, что переименование дерева воссоздает его родителя, но само дерево восстанавливать не нужно. Если вы вставите вышеприведенный вывод в git mktree
, git ответит именем объекта получившегося дерева. Под капотом mktree
выдает SHA1, как этот код рубина:
>> require 'digest/sha1'
>> sha1 = ['e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'].pack 'H*'
>> contents = "100644 a.txt\0#{sha1}100644 b.txt\0#{sha1}"
>> data = "tree #{contents.length}\0#{contents}"
>> Digest::SHA1.hexdigest(data)
"2bdf04adb23d2b40b6085efb230856e5e2a775b7"
Теперь я собираюсь изменить 'bar / b.txt' и исследовать новый набор деревьев:
$ echo hello > bar/b.txt
$ git add bar/b.txt
$ git write-tree
5fa578acc6695bf2af2975ed0ffa7ab448b52c22
$ git cat-file -p 5fa578acc6695bf2af2975ed0ffa7ab448b52c22
040000 tree 9a514e08691a9f636665a43a1c89dc1920dab0fa bar
040000 tree 2bdf04adb23d2b40b6085efb230856e5e2a775b7 foo
Поскольку ничего, кроме «foo», не изменилось, оно сохраняется как то же самое дерево. Для крупных сооружений это огромная космическая победа. Существует новое дерево для 'bar', так как я изменил его:
$ git cat-file -p 9a514e08691a9f636665a43a1c89dc1920dab0fa
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 a.txt
100644 blob ce013625030ba8dba906f756967f9e9ca394464a b.txt
$ git cat-file -p ce013625030ba8dba906f756967f9e9ca394464a
hello
Опять же, ничего в объектах дерева ничего не говорит о ревизиях или фиксациях. Если дерево и его дочерние элементы неизменны от одного коммита к другому, они будут представлены одним и тем же объектом. Если в одном коммите есть два одинаковых дерева, они также будут представлены одним и тем же объектом.
Что касается индекса, то между ним и деревьями существует только минимальная связь. Одним из важных отличий является то, что в индексе хранятся имена и пути BLOB-объектов, используется плоский список и вообще не упоминаются деревья.
$ git ls-files -s
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 bar/a.txt
100644 ce013625030ba8dba906f756967f9e9ca394464a 0 bar/b.txt
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 foo/a.txt
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 foo/b.txt
Когда данные копируются из дерева в индекс, древовидная структура выравнивается. Когда данные копируются из индекса в деревья, они перестраиваются.
Ссылки