Skip to content
aplb edited this page Apr 7, 2014 · 24 revisions

GIT И УКАЗАТЕЛИ.

Часть 1. Первый коммит.

В определенной степени работа с Git сводится к работе с графом истории и перемещению указателей. Речь пойдет о ветках/ссылках в Git.

(Часть коммитов сгенерирована рандомно).

Для начала создадим пустой репозиторий. (Тестовый репо на https://github.com/aplb/git-pointers.git)

mkdir git-pointers
cd git-pointers
git init

Содержимое .git/ имеет следующий вид (каталог .git/hooks/ предварительно удален):

.git/
├── branches
├── config
├── description
├── HEAD
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

Далее нас будут интересовать refs/heads и HEAD.

cat .git/HEAD

ref: refs/heads/master

По умолчанию HEAD ссылается на ветку master (при этом, refs/heads/master еще нет). Поскольку коммиты не сделаны, попытка посмотреть историю командой git-log приводит к ошибке (fatal: bad default revision 'HEAD') (из файла revision.c die("bad default revision '%s'", revs->def);)

Предположим, я создал два файла arbitrary.txt и password.txt с произвольным текстом и добавил их в индекс. Обратим внимание, что в директории .git/objects появилось 2 новых объекта.

.git/
├── branches
├── config
├── description
├── HEAD
├── index
├── info
│   └── exclude
├── objects
│   ├── 43
│   │   └── 99a58404431b72c3f91a0fc8567883beb007af
│   ├── 9d
│   │   └── 84fae31b1d7b692acc454896166b37f7879bf4
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

Служебная команда git-cat-file подтверждает, что с помощью sha1-hash закодировано содержимое созданных файлов и являются они blob-ами (один из типов объектов в Git).

git cat-file -t 4399a58404431b72c3f91a0fc8567883beb007af
blob
git cat-file -t 9d84fae31b1d7b692acc454896166b37f7879bf4
blob

а

git cat-file -p 4399a58404431b72c3f91a0fc8567883beb007af
some text here
git cat-file -p 9d84fae31b1d7b692acc454896166b37f7879bf4
5 stars *****

Собираясь сделать первый коммит, вместо git commit -m, я воспользуюсь двумя plumbing командами, чтобы продемонстрировать, каким образом в Git происходит обновление ссылок.

  1. В первую очередь необходимо создать дерево из текущего индекса.
git write-tree
1fc4773412b807079495823801ffe1db8023d6f8

Видим ещё один объект в .git/objects, который является деревом.

├── objects
│   ├── 1f
│   │   └── c4773412b807079495823801ffe1db8023d6f8
│   ├── 43
│   │   └── 99a58404431b72c3f91a0fc8567883beb007af
│   ├── 9d
│   │   └── 84fae31b1d7b692acc454896166b37f7879bf4

Перепроверим тип этого объекта:

git cat-file -t 1fc4773412b807079495823801ffe1db8023d6f8
tree

В это дерево входят добавленные чуть ранее в индекс 2 файла.

git cat-file -p 1fc4773412b807079495823801ffe1db8023d6f8
100644 blob 4399a58404431b72c3f91a0fc8567883beb007af	arbitrary.txt
100644 blob 9d84fae31b1d7b692acc454896166b37f7879bf4	password.txt
  1. После того, как мы создали дерево, которое содержит snapshot состояния проекта на определенный момент, можно смело его зафиксировать посредством еще одной служебной команды commit-tree.
git commit-tree 1fc4773412b807079495823801ffe1db8023d6f8 -m 'initial commit'
95188b2efd42b1390f51337e7f2b72e4d49744ca

В .git/objects появился 4-й объект.

├── objects
│   ├── 1f
│   │   └── c4773412b807079495823801ffe1db8023d6f8
│   ├── 43
│   │   └── 99a58404431b72c3f91a0fc8567883beb007af
│   ├── 95
│   │   └── 188b2efd42b1390f51337e7f2b72e4d49744ca
│   ├── 9d
│   │   └── 84fae31b1d7b692acc454896166b37f7879bf4

Снова проверим тип нового объекта:

git cat-file -t 95188b2efd42b1390f51337e7f2b72e4d49744ca
commit

это действительно коммит;
отобразить его содержимое:

git cat-file -p 95188b2efd42b1390f51337e7f2b72e4d49744ca
tree 1fc4773412b807079495823801ffe1db8023d6f8
author aplb <[email protected]> 1396863114 +0300
committer aplb <xxxxxx> 1396863114 +0300

initial commit

Также информацию об этом коммите можно получить с помощью

git show 95188b2efd42b1390f51337e7f2b72e4d49744ca --pretty=raw

Казалось бы, коммит сделан. Но у нас все еще нет истории, команда git-log по-прежнему завершается сообщением об ошибке. Довести высокоуровневый git commit до конца поможет обновление ссылок.

git update-ref refs/heads/master 95188b2efd42b1390f51337e7f2b72e4d49744ca

(а можно просто git update-ref HEAD 95188b2efd42b1390f51337e7f2b72e4d49744ca, так как HEAD ссылается на master, см. cat .git/HEAD)

Теперь в нашем репозитории создана история изменений.

Часть 2. Ссылки.

Далее я сделал несколько коммитов, история изменений приняла следующий вид.

┏ 95188b2 (49 minutes ago) aplb initial commit
┣ 2c6b8bd (3 seconds ago) aplb A random change of 26097 to foo1.txt
┣ 334b363 (3 seconds ago) aplb A random change of 27179 to foo2.txt
┣ c34c860 (3 seconds ago) aplb A random change of 4498 to foo3.txt
┗━[HEAD]──[master]──797c515 (3 seconds ago) aplb A random change of 17368 to foo4.txt

HEAD ссылается на ветку master, а master указывает на коммит, который был сделан последним.

cat .git/refs/heads/master
797c515ade1de8e2281889371101ee8219e86098

Сейчас можно сделать историю чуть более изощренной. Допустим, нам понадобилось внести ряд изменений в отдельной ветке friday-fix. При этом в ней не нужны изменения последних трех коммитов ветки master. Создадим новую ветку, берущую начало от коммита 2c6b8bd.

git checkout -b friday-fix 2c6b8bd

┏ 95188b2 (58 minutes ago) aplb initial commit
┣━[HEAD]──[friday-fix]──2c6b8bd (9 minutes ago) aplb A random change of 26097 to foo1.txt
┣ 334b363 (9 minutes ago) aplb A random change of 27179 to foo2.txt
┣ c34c860 (9 minutes ago) aplb A random change of 4498 to foo3.txt
┗━[master]──797c515 (9 minutes ago) aplb A random change of 17368 to foo4.txt

Так, мы просто создали новый указатель refs/heads/friday-fix, который ссылается на коммит 2c6b8bd,

cat .git/refs/heads/friday-fix
2c6b8bd92e060ab5e84c05bf2812719ca40f1ce3

а HEAD теперь будет указывать на friday-fix (пока мы не переместимся с помощью checkout).

cat .git/HEAD
ref: refs/heads/friday-fix

В новой ветке я сделал несколько коммитов. История разветвилась. Можно быстро перемещаться с ветки master на friday-fix и обратно, используя git-checkout, при этом обновляться будет указатель HEAD. Вот так можно видеть, на какую ветку указывает HEAD.

┏ 95188b2 (63 minutes ago) aplb initial commit
┣ 2c6b8bd (13 minutes ago) aplb A random change of 26097 to foo1.txt
┣━┓
┃ ┣ 334b363 (13 minutes ago) aplb A random change of 27179 to foo2.txt
┃ ┣ c34c860 (13 minutes ago) aplb A random change of 4498 to foo3.txt
┃ ┗━[master]──797c515 (13 minutes ago) aplb A random change of 17368 to foo4.txt
┣ fa74f9f (4 seconds ago) aplb A random change of 31507 to essentials1.txt
┣ dc65ae7 (4 seconds ago) aplb A random change of 4288 to essentials2.txt
┗━[HEAD]──[friday-fix]──9fc036b (4 seconds ago) aplb A random change of 31771 to essentials3.txt

Однако хотелось бы привести несколько вычурный пример, который покажет силу указателей в Git.

Итак, я выполнил несколько перемещений между ветками master и friday-fix. Воспользуемся командой

git reflog

все изменения HEAD зафиксированы.

9fc036b HEAD@{0}: checkout: moving from master to friday-fix
797c515 HEAD@{1}: checkout: moving from friday-fix to master
9fc036b HEAD@{2}: commit: A random change of 31771 to essentials3.txt
dc65ae7 HEAD@{3}: commit: A random change of 4288 to essentials2.txt
fa74f9f HEAD@{4}: commit: A random change of 31507 to essentials1.txt
2c6b8bd HEAD@{5}: checkout: moving from master to friday-fix
797c515 HEAD@{6}: commit: A random change of 17368 to foo4.txt
c34c860 HEAD@{7}: commit: A random change of 4498 to foo3.txt
334b363 HEAD@{8}: commit: A random change of 27179 to foo2.txt
2c6b8bd HEAD@{9}: commit: A random change of 26097 to foo1.txt
95188b2 

История по-прежнему в порядке. Заметим, ветка master содержит 5 коммитов.

┏ 95188b2 (68 minutes ago) aplb initial commit
┣ 2c6b8bd (19 minutes ago) aplb A random change of 26097 to foo1.txt
┣━┓
┃ ┣ 334b363 (19 minutes ago) aplb A random change of 27179 to foo2.txt
┃ ┣ c34c860 (19 minutes ago) aplb A random change of 4498 to foo3.txt
┃ ┗━[master]──797c515 (19 minutes ago) aplb A random change of 17368 to foo4.txt
┣ fa74f9f (6 minutes ago) aplb A random change of 31507 to essentials1.txt
┣ dc65ae7 (6 minutes ago) aplb A random change of 4288 to essentials2.txt
┗━[HEAD]──[friday-fix]──9fc036b (6 minutes ago) aplb A random change of 31771 to essentials3.txt

В этот момент я, пытаясь продемонстрировать работу git-update-ref, по ошибке выполняю

git update-ref refs/heads/master fa74f9f

Дерево истории:

┏ 95188b2 (72 minutes ago) aplb initial commit
┣ 2c6b8bd (23 minutes ago) aplb A random change of 26097 to foo1.txt
┣━[master]──fa74f9f (9 minutes ago) aplb A random change of 31507 to essentials1.txt
┣ dc65ae7 (9 minutes ago) aplb A random change of 4288 to essentials2.txt
┗━[HEAD]──[friday-fix]──9fc036b (9 minutes ago) aplb A random change of 31771 to essentials3.txt

Git сделал именно то, о чем я его попросил. Указатель (ветка) master теперь ссылается на один из коммитов ветки friday-fix (см. cat .git/refs/heads/master). В ветке master всего 3 коммита, вместо 5. При этом работа из трех коммитов master-ской ветки потеряна. Вот они:

┣ 334b363 (19 minutes ago) aplb A random change of 27179 to foo2.txt
┣ c34c860 (19 minutes ago) aplb A random change of 4498 to foo3.txt
┗ 797c515 (19 minutes ago) aplb A random change of 17368 to foo4.txt

И здесь может помочь git-reflog. Всё, что нужно, это sha коммита, куда мы хотим вернуться:
797c515 (19 minutes ago) aplb A random change of 17368 to foo4.txt

git update-ref refs/heads/master 797c515

(или git update-ref refs/heads/master HEAD@{6})

Искомое состояние истории восстановлено, master указывает на верхушку прежней ветки.

┏ 95188b2 (78 minutes ago) aplb initial commit
┣ 2c6b8bd (29 minutes ago) aplb A random change of 26097 to foo1.txt
┣━┓
┃ ┣ 334b363 (29 minutes ago) aplb A random change of 27179 to foo2.txt
┃ ┣ c34c860 (29 minutes ago) aplb A random change of 4498 to foo3.txt
┃ ┗━[master]──797c515 (29 minutes ago) aplb A random change of 17368 to foo4.txt
┣ fa74f9f (16 minutes ago) aplb A random change of 31507 to essentials1.txt
┣ dc65ae7 (16 minutes ago) aplb A random change of 4288 to essentials2.txt
┗━[HEAD]──[friday-fix]──9fc036b (16 minutes ago) aplb A random change of 31771 to essentials3.txt

Вот и всё. Переключаться между ветками, искать потерянные изменения и вручную обновлять ссылки в Git просто.

Clone this wiki locally