Если вас интересует история репозитория начиная с определённого коммита, например 1a410e
, то для её отображения вы можете воспользоваться командой git log 1a410e
, однако при этом вам всё ещё необходимо помнить хеш коммита 1a410e
, который является начальной точкой истории.
Было бы неплохо, если бы существовал файл, в который можно было бы сохранить значение SHA-1 под простым именем, а затем использовать это имя вместо хеша SHA-1.
В Git такие файлы называются ссылками («references» или, сокращённо, «refs») и расположены в каталоге .git/refs
.
В нашем проекте этот каталог пока пуст, но в нём уже прослеживается некая структура:
$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f
Чтобы создать новую ссылку, которая поможет вам запомнить SHA-1 последнего коммита, технически, достаточно выполнить примерно следующее:
$ echo 1a410efbd13591db07496601ebc7a059dd55cfe9 > .git/refs/heads/master
Теперь в командах Git вместо SHA-1 можно использовать только что созданную ссылку:
$ git log --pretty=oneline master
1a410efbd13591db07496601ebc7a059dd55cfe9 Third commit
cac0cab538b970a37ea1e769cbbde608743bc96d Second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d First commit
Тем не менее, редактировать файлы ссылок вручную не рекомендуется, вместо этого Git предоставляет более безопасную команду update-ref
на случай, если вам потребуется изменить ссылку:
$ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9
Вот что такое, по сути, ветка в Git — простой указатель или ссылка на последний коммит в цепочке. Для создания ветки, соответствующей предыдущему коммиту, можно выполнить следующее:
$ git update-ref refs/heads/test cac0ca
Данная ветка будет содержать лишь коммиты по указанный, но не те, что были созданы после него:
$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d Second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d First commit
Теперь база данных Git схематично выглядит так, как показано на рисунке:
При выполнении команды git branch <branch>
, в действительности Git запускает команду update-ref
, которая добавляет SHA-1 хеш последнего коммита текущей ветки в файл с именем указанной ветки.
Как же Git получает хеш последнего коммита при выполнении git branch <имя ветки>
?
Ответ кроется в файле HEAD.
Файл HEAD — это символическая ссылка на текущую ветку. Символическая ссылка отличается от обычной тем, что она содержит не сам хеш SHA-1, а указатель на другую ссылку.
В некоторых случаях файл HEAD может содержать SHA-1 хеш какого-либо объекта. Это происходит при извлечении тега, коммита или удалённой ветки, что приводит репозиторий в состояние "detached HEAD".
Если вы заглянете внутрь HEAD, то увидите следующее:
$ cat .git/HEAD
ref: refs/heads/master
Если выполнить git checkout test
, Git обновит содержимое файла:
$ cat .git/HEAD
ref: refs/heads/test
При выполнении git commit
Git создаёт коммит, указывая его родителем объект, SHA-1 которого содержится в файле, на который ссылается HEAD.
При желании, можно вручную редактировать этот файл, но лучше использовать команду symbolic-ref
.
Получить значение HEAD этой командой можно так:
$ git symbolic-ref HEAD
refs/heads/master
Изменить значение HEAD можно так:
$ git symbolic-ref HEAD refs/heads/test
$ cat .git/HEAD
ref: refs/heads/test
Символическую ссылку на файл вне .git/refs
поставить нельзя:
$ git symbolic-ref HEAD test
fatal: Refusing to point HEAD outside of refs/
Мы рассмотрели три основных типа объектов Git, но есть ещё один. Объект тега очень похож на объект коммита: он содержит имя своего автора, дату, сообщение и указатель. Разница же в том, что объект тега указывает на коммит, а не на дерево. Он похож на ветку, которая никогда не перемещается: он всегда указывает на один и тот же коммит, просто давая ему понятное имя.
Как мы знаем из главы ch02-git-basics-chapter.asc, теги бывают двух типов: аннотированные и легковесные. Легковесный тег можно создать следующей командой:
$ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d
Вот и всё, легковесный тег — это ветка, которая никогда не перемещается.
Аннотированный тег имеет более сложную структуру.
При создании аннотированного тега Git создаёт специальный объект и указывающую на него ссылку, а не просто указатель на коммит.
Мы можем увидеть это, создав аннотированный тег, используя опцию -a
:
$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'Test tag'
Вот значение SHA-1 созданного объекта:
$ cat .git/refs/tags/v1.1
9585191f37f7b0fb9444f35a9bf50de191beadc2
Теперь выполним git cat-file -p
для этого хеша:
$ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2
object 1a410efbd13591db07496601ebc7a059dd55cfe9
type commit
tag v1.1
tagger Scott Chacon <[email protected]> Sat May 23 16:48:58 2009 -0700
Test tag
Обратите внимание, что в поле object
записан SHA-1 помеченного коммита.
Также стоит отметить, что это поле не обязательно должно указывать на коммит; вы можете пометить любой объект в Git.
Например, в исходниках Git сопровождающий проекта добавил свой публичный GPG-ключ в блоб и пометил его.
Увидеть этот ключ можно, выполнив команду:
$ git cat-file blob junio-gpg-pub
В репозитории ядра Linux также есть тег, указывающий не на коммит: самый первый тег указывает на дерево первичного импорта.
Третий тип ссылок, который мы рассмотрим — ссылки на удалённые ветки.
Если вы добавили удалённый репозиторий и отправили в него какие-нибудь изменения, Git сохранит последнее отправленное значение SHA-1 в каталоге refs/remotes
для каждой отправленной ветки.
Например, можно добавить удалённый репозиторий origin
и отправить туда ветку master
:
$ git remote add origin [email protected]:schacon/simplegit-progit.git
$ git push origin master
Counting objects: 11, done.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (7/7), 716 bytes, done.
Total 7 (delta 2), reused 4 (delta 1)
To [email protected]:schacon/simplegit-progit.git
a11bef0..ca82a6d master -> master
Позже вы сможете посмотреть, где находилась ветка master
с сервера origin
во время последней синхронизации с ним, заглянув в файл refs/remotes/origin/master
:
$ cat .git/refs/remotes/origin/master
ca82a6dff817ec66f44342007202690a93763949
Ссылки на удалённые ветки отличаются от веток (ссылок в refs/heads
) тем, что они считаются неизменяемыми.
Это означает, что вы можете переключиться на любую из таких веток с помощью git checkout
, но Git не установит HEAD на неё, а значит вы не сможете фиксировать свои изменения в ней с помощью git commit
.
Git воспринимает удалённые ветки как закладки на последние известные состояния веток на удалённых серверах.