Вся информация требуемая чтобы представить историю проекта хранится в особым образом организованных файлах. Все файлы хранят ссылаются друг на друга с помощью 40-значного "имени объекта" и это имя выглядит так:
6ff87c4664981e4397625791c8ea3bbb5f2279a3
Вы увидите эти 40-значные строки повсюду в Git. В каждом случае имя вычисляется как SHA1 значение содержимого объекта. SHA1 хэш это криптографическая хэш-функция. Для нас это значит то, что практически нереально найти два разных объекта с одинаковым именем. Это дает огромную выгоду; такую как:
Каждый объект состоит из трех частей - тип, размер , содержимое. Размер это просто объем содержимого, а содержимое зависит от типа объекта. Существуют 4 разных типа объекта: "блоб", "дерево", "коммит", и "таг".
Почти все в Git построено вокруг манипуляций этой простой структурой состоящей из четырех различных типов объектов. Это что-то вроде своеобразной файловой-системы надстроенной над файловой-системой компьютера.
Важное замечание: в отличии от других распространенных систем контроля версий с которым вы возможно знакомы. Subversion, CVS, Perforce, Mercurial другие подобные им используют Delta Storage Systems (приемы на базе дельта алгоритмов http://ru.wikipedia.org/wiki/Дельта-кодирование) - они хранят разницу между двумя следующими друг за другом комитами. Git не делает этого - он хранит снапшот своего рода точный снимок всех файлов и директорий, состояния всего дерева в момент коммита. Очень важно понимать эту концепцию когда испльзуете Git.
Блоб обычно хранит содержимое файла.
Вы можете использовать git show чтобы исследовать содержимое блоба. Предположим у нас есть SHA-значение блоба, таким образом чтобы просмотреть его содержимое выполните выполнить:
$ git show 6ff87c4664
Note that the only valid version of the GPL as far as this project
is concerned is _this_ particular version of the license (ie v2, not
v2.2 or v3.x or whatever), unless explicitly otherwise stated.
...
Объект "блоб" это всего лишь некоторая порция бинарных данных. Он ни на что не ссылается у него нет каких либо атрибутов, нет даже имени файла.
Поскольку блоб полностью определяется его собственным содержимым, то если два файла в директории или даже в разных версиях репозитория имеют одинаковое содержимое, они будут разделять один и тот же блоб объект. Объект полностью независит от его расположения в дереве каталогов, и переименование файла не изменит объект с которым этом файл связан.
Дерево это простой объект который заключает в себе группу указателей на блобы и другие деревья - обычно представляет содержимое директорий или поддиректорий.
Команда git show более общая, и также может быть использована чтобы исследовать дерево объектов, но git ls-tree даст вам больше подробностей. Предположим у нас есть SHA значение дерева, тогда мы можем исследовать его следующим образом:
$ git ls-tree fb3a8bdd0ce
100644 blob 63c918c667fa005ff12ad89437f2fdc80926e21c .gitignore
100644 blob 5529b198e8d14decbe4ad99db3f7fb632de0439d .mailmap
100644 blob 6ff87c4664981e4397625791c8ea3bbb5f2279a3 COPYING
040000 tree 2fb783e477100ce076f6bf57e4a6f026013dc745 Documentation
100755 blob 3c0032cec592a765692234f1cba47dfdcc3a9200 GIT-VERSION-GEN
100644 blob 289b046a443c0647624607d471289b2c7dcd470b INSTALL
100644 blob 4eb463797adc693dc168b926b6932ff53f17d0b1 Makefile
100644 blob 548142c327a6790ff8821d67c2ee1eff7a656b52 README
...
Как вы можете видеть, объект дерево содержит список записей. Каждая запись состоит из вида, типа объекта, SHA1 значения, и имени соответственно. Записи отсортированы по имени. Так выглядит содержимое одной директории дерева.
Ссылка на объект в дереве может быть как блобом (файлом по сути) так и деревом (поддиректорией). Поскольку имена всех объектов, деревьев и блобов, совпадает с SHA хэш-значением их содержимого, то SHA значения двух деревьев будут идентичны только если их содержимое (включая, рекурсивно, содержимое всех поддиректорий) идентично.
Это свойство позволяет git быстро найти отличия между двумя родственными объектами типа дерево, так как git может игнорировать объекты с одинаковыми именами.
Замечание: деревья могут также содержать записи коммитов. Более продробно от этом в секции Подмодули.)
Отметьте для себя, что все файлы имеют права 644 или 755: фактически git обращает внимание только на бит исполнения.
Объект "коммит" связывает физическое состояние дерева с описпнием того каким образом мы пришли к этому и почему.
Вы можете использовать параметр --pretty=raw с git show или git log чтобы исследовать коммит:
$ git show -s --pretty=raw 2be7fcb476
commit 2be7fcb4764f2dbcee52635b91fedb1b3dcf7ab4
tree fb3a8bdd0ceddd019615af4d57a53f43d8cee2bf
parent 257a84d9d02e90447b149af58b271c19405edb6a
author Dave Watson <dwatson@mimvista.com> 1187576872 -0400
committer Junio C Hamano <gitster@pobox.com> 1187591163 -0700
Fix misspelling of 'suppress' in docs
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Как вы можете это видеть, коммит определяется:
Заметьте что коммит сам по себе не содержит никакой информации о том что изменилось; все изменения вычисляются при сравнении содержимого дерева на которое ссылается создаваемый коммит и дерева на которое ссылается его родитель. Git не пытается явно регистрировать переименования файлов хотя может идентифицировать случаи где существование одниковых файловых данных в измененном пути предложит переименовать. (Просмотрите, например параметр -M к команде git diff).
Коммит обычно создается git commit. Эта команда создает коммит - родитель которого текущая ветка HEAD, и чье дерево взято из содержимого сохраненного в данный момент в индексе.
Теперь когда мы рассмотрели 3 главных объекта (блоб, дерево и коммит), давайте теперь посмотрим как они объединяются.
Если у нас есть простой проект со след. структурой директории:
$>tree
.
|-- README
`-- lib
|-- inc
| `-- tricks.rb
`-- mylib.rb
2 directories, 3 files
И мы выполнили коммит всего этого в репозиторий Git, это будет выглядеть след. образом:
Вы можете видеть что мы создали объект дерево для каждой директории (включая корневую) и объект блоб для каждого файла. Затем, мы имеем объект коммит указывающий на кореневую директорию, и мы можем отследить как наш проект выглядел в момент коммита.
Объект таг содержит имя объекта (называетмого просто 'объект'), тип объекта, имя тага, или разработчика ("таггер") который создал таг, и сообщение, которое может содержить подпись. Это можно увидеть выполнив git cat-file:
$ git cat-file tag v1.5.0
object 437b1b20df4b356c9342dac8d38849f24ef44f27
type commit
tag v1.5.0
tagger Junio C Haеmano <junkio@cox.net> 1171411200 +0000
GIT 1.5.0
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQBF0lGqwMbZpPMRm5oRAuRiAJ9ohBLd7s2kqjkKlq1qqC57SbnmzQCdG4ui
nLE/L9aUXdWeTFPron96DLA=
=2E+0
-----END PGP SIGNATURE-----
Просмотрите документацию команды git tag чтобы изучить как создавать и проверять объекты таг. (Заметьте что git tag может также использоваться чтобы создавать "легковесные таги", которые не являеются объектами таг вообще, это просто ссылки чьи имена начинаются с "refs/tags/").