Git
Chapters ▾ 2nd Edition

10.3 Git на ниско ниво - Git референции

Git референции

Ако искате да видите историята на хранилището си достъпна през къмит 1a410e например, може да изпълните нещо като git log 1a410e, но все пак трябва да сте запомнили, че именно 1a410e е къмитът, който ви интересува. Вместо това, би било по-лесно ако имахте файл, в който да съхраните тази SHA-1 стойност под някакво смислено име и след това да използвате това име като изходна точка.

В Git тези опростени имена се наричат “references” или просто “refs” и може да намерите файловете, които ги съхраняват в директорията .git/refs. В текущия ни проект тази директория не съдържа файлове, но съдържа проста структура:

$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f

За да създадете проста референция, може да направите това:

$ echo 1a410efbd13591db07496601ebc7a059dd55cfe9 > .git/refs/heads/master

Сега можете да използвате head референцията, която току що създадохте вместо SHA-1 стойността в Git командите:

$ git log --pretty=oneline master
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit

Не се препоръчва тези файлове да бъдат редактирани ръчно, вместо това Git предоставя по-безопасната команда git update-ref:

$ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9

Това в общи линии е един клон в Git: прост указател към head на линия работа. За да създадете клон от втория къмит:

$ git update-ref refs/heads/test cac0ca

Този клон сега ще съдържа само работата от този къмит назад:

$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit

Сега Git базата данни концептуално изглежда така:

Обекти в Git директорията с включени head референции за клонове.
Figure 152. Обекти в Git директорията с включени head референции за клонове.

Когато изпълнявате команда като git branch <branch>, Git всъщност изпълнява update-ref за да добави SHA-1 чексумата на последния къмит на текущия клон в референцията, която искате да създадете.

HEAD

Сега изниква въпроса как при изпълнение на git branch <branch> Git знае коя е SHA-1 стойността на последния къмит? Отговорът е файла HEAD.

HEAD файлът е symbolic референция към текущия клон. Под symbolic референция се има предвид, че за разлика от нормалните референции, тя не съдържа SHA-1 стойност, а вместо това указател към друга референция.

В някои редки случаи обаче, HEAD файлът все пак може да съдържа SHA-1 стойност на git обект. Това се случва при извличане на таг, къмит или отдалечен клон, при което хранилището ви попада в "detached HEAD" състояние.

Ако погледнете файла, обикновено виждате нещо такова:

$ cat .git/HEAD
ref: refs/heads/master

Ако изпълним git checkout test, Git обновява файла така:

$ cat .git/HEAD
ref: refs/heads/test

При изпълнение на git commit се създава къмит обект и се указва, че родителят на този къмит обект съответства на SHA-1 стойността, към която сочи референцията в HEAD.

Можете и ръчно да редактирате този файл, но и тук съществува по-безопасна алтернатива под формата на командата git symbolic-ref. Можете да прочетете стойността на HEAD така:

$ git symbolic-ref HEAD
refs/heads/master

Със същата команда можете и да я промените:

$ git symbolic-ref HEAD refs/heads/test
$ cat .git/HEAD
ref: refs/heads/test

Не можете да направите symbolic референция извън стила на refs:

$ git symbolic-ref HEAD test
fatal: Refusing to point HEAD outside of refs/

Тагове

Току що разгледахме трите основни обектни типа в Git (blobs, trees и commits), но има и още един. Обектът tag е много подобен на commit обекта — съдържа информация за човека създал тага, дата, указател и съобщение. Основната разлика е, че таг обекта сочи към къмит, вместо към дърво. Това е подобно на branch референция, но никога не се премества — винаги сочи към един и същи къмит, но му дава по-информативно име.

Както видяхме в Основи на Git, има два вида тагове: annotated и lightweight. Можете да направите lightweight таг така:

$ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d

Това е lightweight тагът — референция, която не се мести. Annotated таговете, обаче, са малко по-сложни. Ако създадете annotated таг, Git създава таг обект и след това създава референция сочеща към него, вместо директно към къмита. Може да видите това създавайки annotated таг (с опцията -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 <schacon@gmail.com> Sat May 23 16:48:58 2009 -0700

test tag

Отбележете как реда object сочи към SHA-1 стойността на къмита, който беше тагнат. Също така отбележете, че не е необходимо да сочи към къмит, можете да тагвате всеки Git обект. В сорс кода на Git например, поддържащият проекта разработчик е добавил своя GPG public key като blob обект и след това го е тагнал. Можете да видите публичния ключ изпълнявайки следното в клонирано хранилище:

$ git cat-file blob junio-gpg-pub

В хранилището на Linux ядрото също има таг обект, който не сочи към къмит — първият създаден таг сочи към първоначалното дърво на импорта на сорс кода.

Remotes

Третият тип референции са remote референциите. Ако добавите remote и публикувате към него, Git записва последната публикувана стойност за всеки клон в директорията refs/remotes. Можете да добавите remote origin и да публикувате master клона в него:

$ git remote add origin git@github.com: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 git@github.com:schacon/simplegit-progit.git
  a11bef0..ca82a6d  master -> master

След това, можете да видите стойността за master клона в отдалечената origin референция последния път, когато сте комуникирали със сървъра във файла refs/remotes/origin/master:

$ cat .git/refs/remotes/origin/master
ca82a6dff817ec66f44342007202690a93763949

Remote референциите се отличават от клоновете (refs/heads референциите) основно по това, че се третират като read-only. Можете да изпълните git checkout към такава референция, но Git няма да насочи HEAD към нея, така че никога няма да я обновите с commit команда. Git ги управлява като bookmarks към последния известен статус на клоновете им в съответните сървъри.