Git - це сховище об'єктів, адресованих за хешем вмісту, і набір вказівників на них.
Чотири типи об'єктів:
- blob - вміст файлу (без імені й прав).
- tree - каталог: список імен з посиланнями на blob-и й вкладені tree.
- commit - посилання на кореневий tree (повний знімок проєкту), на батьківські коміти, автор, дата, повідомлення.
- tag (анотований) - іменований вказівник на об'єкт з власними метаданими.
Ідентифікатор кожного об'єкта - хеш його вмісту (SHA-1, у нових репозиторіях можливий SHA-256). Однаковий файл у двох комітах - той самий blob, збережений один раз. Змінити старий коміт неможливо: інший вміст - інший хеш, тобто вже інший об'єкт.
Гілка - файл з одним хешем. refs/heads/main містить хеш останнього коміту. Новий коміт просто переписує цей хеш. Тому створення гілки миттєве й нічого не копіює.
HEAD - вказівник на поточну гілку (ref: refs/heads/main) або напряму на коміт («detached HEAD»).
git cat-file -p HEAD # вміст коміту: tree, parent, author
git cat-file -p HEAD^{tree} # дерево каталогу
cat .git/refs/heads/main # гілка - це просто хеш
Що з цього випливає:
- Коміт зберігає знімок, а не різницю. Diff Git обчислює на льоту, порівнюючи дерева. (Для економії місця об'єкти пакуються в pack-файли з дельта-стисненням, але це деталь зберігання.)
- Rebase не «переносить» коміти - він створює нові з іншими батьками й хешами.
- Видалення гілки не видаляє коміти, лише вказівник - звідси можливість їх відновити.