Каждый коммит создает новый объект дерева в git? - PullRequest
1 голос
/ 21 апреля 2019

Я изучаю внутренние компоненты git и то, как объектная модель git работает "под капотом".

Если я изменю какой-то файл и передам его в локальный репозиторий git, то будет создан новый git commit object. Каждый объект фиксации ассоциируется с tree object. Каждый объект дерева содержит SHA1 файлов (BLOB-объектов), на которые он указывает. Значит ли это, что каждый новый коммит (при условии, что в нем есть какое-то изменение файла) всегда будет генерировать новый объект дерева (который будет иметь другой SHA1, чем все предыдущие деревья, даже если они указывают на один и тот же каталог в файловой системе)?

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

Ответы [ 3 ]

4 голосов
/ 21 апреля 2019

Верны ли мои рассуждения об этом?

Практически - да - но смотри ниже

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

@ Лассе уже упоминал git commit --allow-empty как способ повторного использования last дерева, но это довольно необычная команда. Довольно распространенная команда - git commit --amend, когда вы просто хотите исправить последнее сообщение о коммите.

Также обратите внимание: существующие деревья можно использовать повторно, и эти деревья не обязательно должны быть последним коммитом. Обычный сценарий - git rebase --interactive, и он просто переписывает сообщения коммита (аналогично git commit --amend, но для коммитов дальше от HEAD).

Другой сценарий: рассмотрим последовательность коммитов:

commit 0
commit A
commit B
commit C
revert C  # will reuse tree from B
revert B  # will reuse tree from A
revert A  # will reuse tree from 0

В этом случае старые деревья также используются повторно.

Следующий сценарий: git merge -s ours (не путать с git merge -X ours) объединит другую ветку, но проигнорирует любые изменения. Другими словами: коммит-слияние и первый родительский элемент совместно используют одно и то же дерево.

Швейцарский армейский нож для выполнения странных вещей - конечно - git filter-branch, где вы можете переписать коммиты несколькими способами, но оставить деревья нетронутыми.

3 голосов
/ 21 апреля 2019

Давайте сделаем все шаг за шагом.

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

Однако, если вы 5-6 коммитов в конце строки сможете восстановить содержимое файла до того, каким оно было ранее, его новый хеш уже будет существовать в хранилище, и, следовательно, дополнительный файл не будет добавлен. Вместо этого все, что будет ссылаться на этот файл, будет использовать хеш, но при этом ссылается на «старый» файл.

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

Другими словами, в вышеописанном сценарии, в котором мы восстановили файл, если мы в конечном итоге восстановим содержимое всех файлов в хранилище обратно в состояние, которое они имели в предыдущем коммите, хеш нового дерева уже существует, и новый объект дерева не будет добавлен. Вместо этого, что бы ни ссылалось на это дерево, коммит, скорее всего, будет использовать хеш и ссылаться на «старое» дерево.

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

Чтобы добавить коммит без изменений файла, известный как «пустой коммит», вы можете использовать эту команду git:

git commit --allow-empty

Вы можете выбрать такие вещи, как -m "message" или тому подобное, как обычно.

Вот пример:

λ git init .
Initialized empty Git repository in D:/Temp/.git/

λ echo a >test.txt                                                             
λ git add .                                                                    
λ git commit -m test1                                                          
[master (root-commit) dc613fe] test1                                           
 1 file changed, 1 insertion(+)                                                
 create mode 100644 test.txt                                                   

λ git commit -m test2 --allow-empty                                            
[master c197192] test2                                                         

λ git lg                                                                       
* c197192: (7 seconds ago) test2 (HEAD -> master)                              
| Lasse Vågsæther Karlsen <lasse@vkarlsen.no> (Sat, 20 Apr 2019 23:28:44 +0200)
|                                                                              
* dc613fe: (17 seconds ago) test1                                              
  Lasse Vågsæther Karlsen <lasse@vkarlsen.no> (Sat, 20 Apr 2019 23:28:34 +0200)

Теперь, если я выведу содержимое этих двух коммитов:

λ git cat-file -p c197192
tree 35b422a71005d59dd6af858a3425b608b63f7b5a
parent dc613fe57276009b399d8152a657cb971fad605a
author Lasse Vågsæther Karlsen <lasse@vkarlsen.no> 1555795724 +0200
committer Lasse Vågsæther Karlsen <lasse@vkarlsen.no> 1555795724 +0200

test2

λ git cat-file -p dc613fe
tree 35b422a71005d59dd6af858a3425b608b63f7b5a
author Lasse Vågsæther Karlsen <lasse@vkarlsen.no> 1555795714 +0200
committer Lasse Vågsæther Karlsen <lasse@vkarlsen.no> 1555795714 +0200

test1

Вы можете видеть, что они оба ссылаются на один и тот же объект дерева, который выглядит следующим образом:

λ git cat-file -p 35b422a71005d59dd6af858a3425b608b63f7b5a
100644 blob f5eea678d87a8664e4c76e12d3ef5c4ff775ad58    test.txt
0 голосов
/ 21 апреля 2019

Если содержимое дерева (каталоги имеют одинаковое имя и расположение, файлы имеют одинаковое имя и одинаковое содержимое) совпадает с предыдущей ревизией, дерево будет «дубликатом», и поэтому оно будет teoревизии, указывающие на то же дерево

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...