Техника и логика Git, стоящие за "git status"
Что именно происходит, когда git status
ищет, есть ли какие-либо изменения в локальной папке?
Насколько я понимаю, каждый файл "регистрируется" через хеш-код (а точнее: sha1) и git status
"просто" пытается сопоставить до сих пор зарегистрированные хэши с вычисленными на лету, и если есть что-то другое, это считается изменением статуса. Я не очень уверен в этом, если честно, если я ошибаюсь, я бы хотел исправить. В любом случае возникают вопросы:
- Где можно найти какие хеши? Есть много хэшей для репо определенных вещей, но где именно я могу найти зарегистрированный хэш для каждого файла?
- Что происходит с этими хэшами, если выполняется одна из следующих команд:
git add
,git commit -am
,git gc
2 ответа
Чтобы понять это, вам сначала нужно разобраться с объектами git store, все они идентифицируются по их хэшу SHA1. Это коммиты, деревья и капли.
Коммит содержит сообщение о коммите, коммитер, дату, SHA1 родительского коммита (ов) и SHA1 дерева (плюс некоторая дополнительная информация).
Дерево представляет собой каталог. Он содержит имена (и другие метаданные) файлов и каталогов, которые он содержит. Для каждого файла он также содержит SHA1 соответствующего большого двоичного объекта, а для каждого подкаталога он содержит SHA1 другого дерева.
Blob представляет содержимое файла без имени или каких-либо других метаданных.
Сейчас, git status
сравнивает три дерева:
- Тот, который принадлежит текущему коммиту (
HEAD
обычно последний коммит в текущей ветке). - Тот, что в области постановки. Это где файлы идут после вас
git add
их и используется для подготовки коммита до того, как вы его на самом деле делаете. - Ваше рабочее дерево. Так выглядит каталог на вашем диске.
Вот почему, если вы редактируете файл (скажем, a.txt), git add
это, отредактируйте его еще немного, а затем используйте git status
, вы получите вывод, как это:
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: a.txt
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: a.txt
#
Теперь к вашим актуальным вопросам:
Где можно найти какие хеши? Есть много хэшей для репо определенных вещей, но где именно я могу найти зарегистрированный хэш для каждого файла?
Они хранятся в дереве объектов. Например, чтобы увидеть объект дерева текущего коммита (HEAD
), используйте git ls-tree HEAD
:
$ git ls-tree HEAD
100644 blob 9c59e24b8393179a5d712de4f990178df5734d99 a.txt
Вы можете видеть, что корневой каталог репо содержит один файл (blob
) под названием a.txt с SHA1 9c59e24b8393179a5d712de4f990178df5734d99.
Вы можете использовать эту же команду для просмотра SHA1 подкаталогов и файлов в этих подкаталогах, подробности см. В документации к команде.
Чтобы вычислить SHA1 некоторого файла на диске, вы можете использовать git hash-object
,
Что происходит с этими хэшами, если выполняется одна из следующих команд
Вы должны помнить, что SHA1 основаны на содержимом объекта. И каждый объект полностью неизменен, поэтому SHA1 какого-либо объекта никогда не изменяется. Но многие операции могут создавать новые объекты, и они могут, например, также переходить на какой-либо объект.
git add
берет дерево в промежуточной области, модифицирует его, добавляя или изменяя некоторые файлы в соответствии с параметрами команды, и сохраняет измененное дерево обратно в промежуточную область.git commit
берет дерево в области подготовки и создает коммит, который указывает на это дерево. Новый коммит также имеет текущую дату, вы как коммитер и текущий коммит как его родитель. Затем команда изменяет текущую ветвь так, чтобы она указывала на новый коммит.git commit -a
это просто ярлык дляgit add
с последующимgit commit
,git gc
просматривает все объекты, которые он хранит, и удаляет те, которые недоступны. Доступные объекты - это подсказки всех ветвей, тегов или текущего коммита, а также всех объектов, на которые они ссылаются, рекурсивно. Недавно использованные коммиты (и объекты, на которые они ссылаются) также не удаляются, потому что они доступны через reflog.