Вот и мне посчастливилось познакомиться с git. Каюсь, пользуясь Subversion, я знал, как в IDEA или TortoiseSVN сделать то, что мне надо, но даже не представлял, что происходит за сценой. В данном случае я решил подойти к git более ответственно и хорошенько изучить его перед использованием. Сейчас я знаю какие команды надо использовать для выполнения задуманного, но не знаю, как это сделать в IDEA или TortoiseSVN.
Но я решил пойти еще дальше и узнать, что происходит в самой директории .git. Там оказалось все настолько интересно и просто, что я решил поделиться этим с вами.
Вот так выглядит .git после команды git init.
Это не все, что вы можете увидеть, так как при дальнейшей работе появятся другие файлы и директории. Например, так выглядит .git на моем рабочем проекте.
Я не буду описывать назначение каждого файла, а остановлюсь на основных моментах.
Самыми важными элементами являются objects, refs, HEAD, index. Для того чтобы понять, что это и с чем их едят, мы создадим несколько файлов, каталог, и добавим их в наш репозиторий.
Объекты
Первоначально каталог оbjects содержит пустые подкаталоги pack и info и никаких файлов.
Создадим в рабочей директории файл test.txt с содержимым «test file version 1».
$ echo ‘test file version 1’ > test.txt
Добавим этот файл в индекс.
$ git add test.txt
Теперь посмотрим что изменилось у нас в репозитории.
$ find .git/objects
.git/objects/27/703ec79a98c1d097d5b1cd320befffa376e826
В директорию objects добавился файл. Следует сказать, что все файлы в данной директории являются git объектами определенного типа.
Давайте посмотрим содержимое и тип данного объекта с помощью команды cat-file.
$ git cat-file -p 2770
test file version 1
$ git cat-file -t 2770
blob
Данный объект имеет тип blob. Это и есть начальное представление данных в Git — один файл на единицу хранения с именем, которое вычисляется как SHA1 хеш содержимого и заголовка объекта. Первые два символа SHA определяют подкаталог файла, остальные 38 — имя. Данный объект просто хранит снимок содержимого файла test.txt.
Далее видим, что появился файл index. Это бинарный файл, содержащий список индексированных файлов и связанных с ними blob объектов.
$ git ls-files --stage
100644 27703ec79a98c1d097d5b1cd320befffa376e826 0 test.txt
То есть индекс содержит всю необходимую информацию для создания tree объекта при последующем коммите. Tree объект — это еще один тип объекта в git. Чуть позже мы с ним познакомимся.
А теперь добавим каталог new и файл new/new.txt
$ mkdir new
$ echo "new file" > new/new.txt
$ git add .
$ find .git/objects -type f
.git/objects/27/703ec79a98c1d097d5b1cd320befffa376e826
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Давайте узнаем тип нового объекта и его содержимое.
$ git cat-file -p fa49
new file
$ git cat-file -t fa49
blob
И заглянем снова в index.
$ git ls-files --stage
100644 fa49b077972391ad58037050f2a75f74e3671e92 0 new/new.txt
100644 27703ec79a98c1d097d5b1cd320befffa376e826 0 test.txt
А теперь все это закоммитим.
$ git commit -m "first commit"
[master (root-commit) cae1990] first commit
2 files changed, 2 insertions(+), 0 deletions(-)
create mode 100644 new/new.txt
create mode 100644 test.txt
Теперь наш репозиторий содержит 5 объектов.
$ find .git/objects -type f
.git/objects/27/703ec79a98c1d097d5b1cd320befffa376e826
.git/objects/49/66bf4e5c88c5f9d149b45bb2f3099644701d93
.git/objects/ca/e19909974ee9e64f5787fe4ee89b9b8fe94ccf
.git/objects/eb/85079ce7fd354ffc630f4a8e2991196cb3807f
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Добавилось еще три файла. Посмотрим что это за файлы.
$ git cat-file -t 4966
tree
$ git cat-file -p 4966
040000 tree eb85079ce7fd354ffc630f4a8e2991196cb3807f new
100644 blob 27703ec79a98c1d097d5b1cd320befffa376e826 test.txt
$ git cat-file -t eb85
tree
$ git cat-file -p eb85
100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt
Это другой тип объектов git — tree объект. Объект данного типа содержит одну или более записей, указывающей на другое дерево или на blob объект.
И наконец, последний тип объекта — объект-коммит.
$ git cat-file -p cae1
tree 4966bf4e5c88c5f9d149b45bb2f3099644701d93
author Ivan Ivanov <i_ivanov@adam.net> 1335783964 +0300
committer Ivan Ivanov <i_ivanov@adam.net> 1335783964 +0300
first commit
Мы видим дерево верхнего уровня, о котором я уже упоминал ранее, имя автора и коммитера и сообщение коммита.
Сделаем новое изменение(в test.txt изменим текст на «test file version 2»)и закоммитим. Кроме ссылки на дерево появилась еще ссылка на предшествующий коммит.
$ git cat-file -p b303
tree 42e998096f18d4249dc00ec89eaaadc44a8bf3cb
parent cae19909974ee9e64f5787fe4ee89b9b8fe94ccf
author Ivan Ivanov <i_ivanov@adam.net> 1335786789 +0300
committer Ivan Ivanov <i_ivanov@adam.net> 1335786789 +0300
second commit
Чтобы все стало на свои места, нарисуем граф объектов
Ссылки
В git ссылка — это файл-указатель с простым именем, который содержит значение хеша SHA-1. Данные файлы размещаются в каталоге .git/refs/
$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/heads/master
.git/refs/tags
Так как у нас только одна ветка master, то и ссылка тоже только одна, которая указывает на последний коммит.
Давайте сделаем бранч release, указывающий на первый коммит.
$ git branch release cae1990
$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/heads/master
.git/refs/heads/release
.git/refs/tags
$ cat .git/refs/heads/release
cae19909974ee9e64f5787fe4ee89b9b8fe94ccfa
Вот по сути что такое ветка в git — простой указатель на определенный коммит.
Посмотрим более наглядно
HEAD
Данный файл содержит ссылку не на хеш, а на текущую ветку.
$ cat .git/HEAD
ref: refs/heads/master
Если перейти на другую ветку, то и содержание данного файла изменится.
$ git co release
Switched to branch 'release'
$ cat .git/HEAD
ref: refs/heads/release
Вместо итога
А есть еще метки, удаленные ссылки, директория info и много много вкусного.
Автор: andreich3