GIT - краткое руководство (RU)

Руководство пользователя GIT

(для версии 1.5.3 и выше)

Перевод http://www.kernel.org/pub/software/scm/git/docs/user-manual.html

Предисловие

Git это быстрая распределённая система управления версиями.

Данное руководство предназначено для чтения тем, кто имеет базовые знания UNIX и навыки работы с командной строкой, но не имеет знания по git.
В Главе 1 «Репозитории и Ветки» и в Главе 2 «Изучение истории git «объясняется, как получить и изучить проект используя GIT.
Читая эти главы, Вы узнаете, как собрать и проверить конкретную версию программного проекта, найти регрессии, и т.п.
Людям, занимающимся актуальной разработкой, также будет полезна Глава 3 «Разработка с Git» и глава 4 «Совместная разработка».
Дальнейшие главы охватывают более специализированные темы.
Всестороннюю справочную документацию можно получить с помощью man страниц.
Для любой из команд, например команды, “git clone”, просто наберите

$ man git-clone

Оглавление документа Глава 1
1. Как получить git репозиторий
2. Как проверить различные версии проекта
3. Понятие истории
3.1. Понятие истории: Коммиты
3.2. Понятие истории: Коммиты, родители и доступность
3.3. Понятие истории: Диаграммы истории
3.4. Понятие истории: Что такое ветка?
4. Действия с ветками
5. Изучение старой версия без создания новой ветки
6. Изучение веток удалённого репозитория
7. Имена веток, ярлыков, а также другие ссылки
8. Обновление репозитория через git fetch
9. Извлечение веток из других репозиториев

Оглавление документа Глава 2
1. Как используя bisect найти регрессию
2. Именование коммитов
3. Создание тегов
4. Просмотр версий
5. Формирование сравнений (diff)
6. Просмотр старых версии файлов
7. Примеры
7.1. Подсчёт числа коммитов в ветке :
7.2. Проверка наличия истории между двумя точками ветки
7.3. Поиск первой помеченной версии с решением определённой проблемы
7.4. Просмотр уникальных коммитов в данной ветке
7.5. Создание журнала изменений (changelog) и тарбола релиза
7.6. Поиск ссылок коммитов на файл с заданным содержанием

Оглавление документа Глава 3
1. Задать ваше имя для git
2. Создание нового репозитория
3. Как создать коммит
4. Создание хорошего описания коммита
5. Игнорирование файлов
6. Как осуществить слияние
7. Разрешение конфликтов объединений
7.1. Получение помощи при разрешения конфликтов слияния
8. Отмена слияния
9. Быстрое слияние вперёд
10. Исправление ошибок
10.1. Исправление ошибки новым коммитом
10.2. Исправление ошибки путём переписывания истории
10.3. Извлечение старой версии файла
10.4. Временный отказ от проделанной работы
11. Обеспечение эффективной деятельности
12. Обеспечение надёжности
12.1. Проверка репозитория от порчи
12.2. Восстановление потерянных изменений
12.2.1. Журнал reflog
12.3. Изучение зависших объектов

Глава 1. Репозитории и ветки

1. Как получить git репозиторий
2. Как проверить различные версии проекта
3. Понятие истории
3.1. Понятие истории: Коммиты
3.2. Понятие истории: Коммиты, родители и доступность
3.3. Понятие истории: Диаграммы истории
3.4. Понятие истории: Что такое ветка?
4. Действия с ветками
5. Изучение старой версия без создания новой ветки
6. Изучение веток удалённого репозитория
7. Имена веток, ярлыков, а также другие ссылки
8. Обновление репозитория через git fetch
9. Извлечение веток из других репозиториев

1. Как получить git репозиторий

Будет полезно иметь git хранилище для экспериментов, пока вы читаете это руководство.

Лучший способ получить его с помощью команды git-clone(1), которая загрузит копию существующего репозитория. Если у Вы ещё не сообразили как это сделать, приведу несколько интересных примеров:

# взять сам git (примерно. 10MB закачки):
$ git clone git://git.kernel.org/pub/scm/git/git.git
# Ядро linux (примерно. 150MB закачки):
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git

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

Команда clone создаёт новый каталог с именем проекта ( “git” или “linux-2.6” в приведённых выше примерах выше).

После того как вы перейдёте в каталог с репозиторием, вы увидите, что он содержит копии файлов проекта, образуя рабочее дерево, а также содержит специальные каталог с именем “.git”, в котором содержится вся история проекта .

2. Как проверить различные версии проекта

Git это наилучший инструмент для хранении истории коллекции файлов.
Он хранит историю в виде сжатой коллекции взаимосвязанных отпечатков (снапшотов) содержимого проекта. В git каждая такая версия называется коммит.

Эти отпечатки, не обязательно, обычно образуют одну линию разработки от старейшего снапшота, до новейшего; вместе с тем, они могут образовывать параллельные линии разработки, называемые ветками, которые можно объединять и разделять.

Один Git репозиторий может отслеживать развитие в нескольких ветках.
Это позволяет сохранять список основных вершин (head) которые ссылаются на последние коммиты каждой ветки; команда git-branch показывает список вершин веток:

$ git branch
* master

Свежие склонированные репозитории содержат одну вершину ветки, по умолчанию имеющую название “master”, и стартовый рабочий каталог в соответствии с вершиной ветки master.

Большинство проектов также используют теги.

Тэги, подобно вершинам, являются ссылками в истории проекта, и могут быть перечислены командой git-tag(1):

$ git tag -l
v2.6.11
v2.6.11-tree
v2.6.12
v2.6.12-rc2
v2.6.12-rc3
v2.6.12-rc4
v2.6.12-rc5
v2.6.12-rc6
v2.6.13
...

Предполагается, что тэги это постоянные точки указывающие на некоторые версии проекта, в то время как вершины отмечают текущее развитие разработки проекта.

Создать новую вершину ветки указывающую на одну из этих версий и проверить её можно с с помощью git checkout :

$ git checkout -b new v2.6.13

Рабочий каталог теперь отражает то содержимое проекта которое было при установке тега v2.6.13, а git-branch(1) показывает две ветви, причём звёздочкой помечена ветка указанная в команде git checkout :

$ git branch
master
* new

Если вы решите, что вы хотите увидеть версию 2.6.17, вы можете изменить текущую ветку на точку v2.6.17 вместо текущей

$ git reset --hard v2.6.17

Заметим, что если текущая вершина ветки была лишь ссылкой на конкретный момент истории, а затем была сброшена вами это может привести к потери части истории, поэтому используйте эту команду осторожно.

3. Понятие истории

3.1. Понятие истории: Коммиты

Каждое изменение в истории проекта представлено в виде фиксации (коммита).
Команда git-show(1) показывает самые последние коммиты текущей ветки:

$ git show
commit 17cf781661e6d38f737f15f53ab552f1e95960d7
Author: Linus Torvalds <torvalds@ppc970.osdl.org.(none)>
Date: Tue Apr 19 14:11:06 2005 -0700
Remove duplicate getenv(DB_ENVIRONMENT) call

Noted by Tony Luck.

diff --git a/init-db.c b/init-db.c
index 65898fa..b002dc6 100644

a/init-db.c
+++ b/init-db.c
@@ -7,7 +7,7 @@

int main(int argc, char **argv)
{

– char *sha1_dir = getenv(DB_ENVIRONMENT), *path;
+ char *sha1_dir, *path;

int len, i;

if (mkdir(«.git», 0755) < 0) {

Как вы можете увидеть, в коммите указатся кто сделал последние изменений, что сделал и почему.

Каждый коммит имеет 40-значный шестнадцатеричный идентификатор, который иногда называются «именем объекта» или «SHA1 идентификатором», и показывается в первой строке вывода “git show”.

Обычно, вы можете, ссылаться на коммит через короткое имя, такое как тег или имя ветви, но и имя образуемое SHA1 идентификатором часто тоже бывает полезным.

Самое главное в нём, что это уникальное имя для этого коммита: так, если вы скажите кому-нибудь другому название объекта (например, в электронной почте), то вам гарантируется, что это имя будет относиться к тому же коммиту что и в у вас (предполагается, что этот коммит есть у всех).
Это имя объекта вычисляется как хэш его содержимого коммита, и вам гарантируется, что содержимое коммита не может изменится без изменения его имени.

Фактически в Главе 7 «Концепция Git» мы увидим, что всё хранящиеся в истории git, включая файлы данных и содержимое каталога, хранится в объектах с именем которого является хэш его содержимого.

3.2. Понятие истории: Коммиты, родители и доступность

Каждый коммит (за исключением самого первого коммита в проекте) имеет родительские коммит, которые показывает, что произошло до этого коммита.
После цепочки родителей, в конечном итоге мы перейдём обратно в начальное состояние проекта.
Тем не менее, коммиты не образуют простой список; git позволяет разделять линии разработки, а затем объединять их снова, и точки, где два направления разработки объединяются в одно называются «слиянием» (merge).

Коммит, представляющий собой слияние, может иметь более одного родителя, представляющих из себя самые последние коммиты в каждой линии разработки, ведущих к этому коммиту.

Лучший способ узнать, как это работает, запустите команду gitk (1); gitk работает сейчас на git хранилище, и показывает слияния коммитов и поможет вам понять, каким образом git организует историю.

Далее, мы говорим, что коммит Х «доступен» от коммита Y, если коммит Х предок коммита Y. Эквивалентно, можно сказать, что Y является потомком X, или что существует цепь родства (родителей), идущая от коммита Y до коммита X.

3.3. Понятие истории: Диаграммы истории

Мы будем иногда представлять git историю с помощью диаграмм, как показано ниже. Коммиты показаны буквой “o”, а связь между ними — линиями оканчивающимися – / и \. Время идет слева направо. Если нам нужно говорить о конкретном коммите символ “o” может быть заменён другой буквой или номером :

ooo «-- Branch A
/

ooo «-- master

\
ooo «-- Branch B

3.4. Понятие истории: Что такое ветка?

Когда нам нужно быть точными, мы будем использовать слово «ветка» (branch) которое означает линию разработки, и «вершина ветки» (или просто «вершина» (head)) означает ссылку на последний коммит на ветке .
В приведенном выше примере, вершина ветки имеет имя «А» и является указателем на один конкретный коммит, но когда мы ссылаемся на линию из трех коммитов до этой точки, мы называем эту цепочку коммитов «веткой А».

Однако, когда понятно к чему относится термин, мы просто будем использовать термин «ветвь» как для ветви, так и только для вершины этой ветви.

4. Действия с ветками

Создать, удалить и измененить ветку можно очень быстро и очень просто, вот сумма команд:

git branch
список всех веток

git branch «branch»

создать новую ветку с именем «branch», ссылающуюся на некотрую точку в истории
текущей ветки

git branch «branch» «start-point»

создать новую ветку с именем «branch», ссылающуюся на точку «start-point», которая
может быть указана так, как вы хотите – через имя ветки или имя тэга.

git branch -d «branch»

удалить ветвь «branch», если удаляемая вами ветвь имеет точки вне доступности от
текущей ветки, то эта команда завершится неудачно с предупреждением.

git branch -D «branch»

даже если точки ветви недоступны из текущей ветви, вы можете знать, что коммит
доступен из другой ветки или тэга В этом случае можно с уверенностью использовать эту
команду, чтобы удалить ветвь.

git checkout «branch»

сделать ветку «branch» текущей, при этом обновляется рабочий каталог, отражая
состояние ветки «branch»

git checkout -b «new» «start-point»

создать новую ветку «new» ссылающуюся на «start-point», и сделать её текущей.

Специальный символ “HEAD” всегда означает вершину текущей ветки. Фактически, git использует файл с именем “HEAD” в .git каталоге, чтобы запомнить, текущую ветку :

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

5. Изучение старой версия без создания новой ветки

Команда git-checkout, как правило, ожидает что вы укажете вершину ветки, но она также принимает произвольный коммит, например, вы можете проверить коммит, на который указывает тег:

$ git checkout v2.6.17

Примечание: переход к “v2.6.17”, который не входит в локальную ветвь. Если вы хотите создать новую ветку через checkout, вы можете сделать (сейчас или позже), используя команды checkout. Пример:

git checkout -b «new_branch_name»
HEAD is now at 427abfa... Linux v2.6.17

HEAD ссылается через имя SHA1 на коммит, а не на ветку, и git branch показывает, что вы вне ветки:

$ cat .git/HEAD
427abfa28afedffadfca9dd8b067eb6d36bac53f
$ git branch
(no branch)
master

В данном случае мы можем сказать, что вершина «отделена».

Это простой способ проверить конкретную версию не имея новой ветки. Вы можете создать новую ветку (или тэг) для этой версии позже, если вы решите сделать это.

6. Изучение веток удалённого репозитория

Ветвь «master», которая была создана во время клонирования является копией HEAD клонированного репозитория.

Этот репозиторий, возможно, также имеет другие ветки, и в вашем локальном репозитории есть ветки, с помощью которых можно проследить каждую из этих удаленных веток, используя оппцию «-r» git-branch(1):

$ git branch -r
origin/HEAD
origin/html
origin/maint
origin/man
origin/master
origin/next
origin/pu
origin/todo

Вы не можете проверить их удалённо, но вы можете проверить их ветви в вашем репозитории, используя теги:

$ git checkout -b my-todo-copy origin/todo

Заметим, что название “origin” это просто название, которое git использует по умолчанию для обозначения репозитория, откуда был клонирован текущий репозиторий.

7. Имена веток, ярлыков, а также другие ссылки

Ветки, удалённое отслеживание веток и тэги – все это ссылки на коммиты. Все ссылки имеют полное имя с слеш-разделителем (косой чертой) и путем, которое начинается с “refs”; фактически раньше мы пользовались короткими именами (сокращениями):

* ветка «тест» это сокращение «refs/heads/test».
* тег “v2.6.18”' это сокращение «refs/tags/v2.6.18”.
* «origin/master» это сокращение «refs/remotes/origin/master»..

Полное имя может пригодиться, если, например, существует и тег, и ветвь с одним и тем-же именем. (Вновь созданные ссылки на самом деле хранятся в каталоге .git/refs по пути заданным их именем. Однако для эффективности они также могут быть упакованы вместе в один общий файл, см. git-pack-refs(1) ).

Другой, часто полезный ярлык это “HEAD”, на него можно сослаться только используя название репозитория. Так, например, “origin”, как правило, ярлык на HEAD ветки в репозитории “origin”.

Для полного списка путей которые git проверяют по ссылкам, и о порядке их использования для решения выбора, когда имеются несколько ссылок с одним сокращением, см. в выводе команды git-rev-parse(1) раздел SPECIFYING REVISIONS (Указание ревизий) .

8. Обновление репозитория через git fetch

В конечном итоге разработчик склонировав репозиторий и выполнив в нём определённую работу, создаёт новый коммит и наращивает ветку до нового коммита.

Команда “git fetch”, без дополнительных аргументов, будет обновлять все отслеживаемые ветки до последней версии найденной в этом репозитории.
Это касается не только ваших веток, это коснётся и даже ветки “master”, которая была создана при вашем клонировании.

9. Извлечение веток из других репозиториев

Вы также можете отслеживать ветки из других репозиториев, отличных от вашего источника клонирования используя git-remote(1) :

$ git remote add linux-nfs git://linux-nfs.org/pub/nfs-2.6.git
$ git fetch linux-nfs
* refs/remotes/linux-nfs/master: storing branch 'master' ...
commit: bf81b46

Новая ветка удалённого отслеживания будут сохранена с коротким именем, которое вы задали в команде «git remote add», в данном случае c именем «linux-nfs»:

$ git branch -r
linux-nfs/master
origin/master

Если вы запустите «git fetch «remote»" позже, то отслеживаемая ветка с именем «remote» будет обновлена.

Если проанализировать файл .git/config, то можно увидить, как git добавляет новые блоки:

$ cat .git/config
...
[remote “linux-nfs”]
url = git://linux-nfs.org/pub/nfs-2.6.git
fetch = +refs/heads/*:refs/remotes/linux-nfs/*
...

Как git отслеживает удалённые ветки;
Вы можете модифицировать или удалять параметры конфигурации, редактируя файл .git/config в текстовым редакторе. (См. раздел “CONFIGURATION FILE” в выводе команды git-config(1) для изучения деталей).

Глава 2. Изучение Git истории

Оглавление документа Изучение Git истории
1. Как используя bisect найти регрессию
2. Именование коммитов
3. Создание тегов
4. Просмотр версий
5. Формирование сравнений (diff)
6. Просмотр старых версии файлов
7. Примеры
7.1. Подсчёт числа коммитов в ветке :
7.2. Проверка наличия истории между двумя точками ветки
7.3. Поиск первой помеченной версии с решением определённой проблемы
7.4. Просмотр уникальных коммитов в данной ветке
7.5. Создание журнала изменений (changelog) и тарбола релиза
7.6. Поиск ссылок коммитов на файл с заданным содержанием

Git – это один из наилучших инструментов для хранения истории множества файлов.

Это объясняется тем, что он хранит сжатые снапшоты содержащие файла иерархии, вместе с «коммитами», которые отслеживают связи между ними.

Git является чрезвычайно гибким и быстрым инструментом для изучения истории проекта.

Начнём с одного специализированного инструмента, которое обычно используется для поиска программной ошибки («бага») в проекте.

1. Как используя bisect найти регрессию

Предположим, версия 2.6.18 вашего проекта была рабочей, но в версия «мастер» возникла ошибка.

Иногда лучшим способом найти причину такой регрессии, состоит в том, чтобы выполнить поиск с отдельного коммита в истории проекта который является причиной проблемы. Команда git-bisect(1) может помочь вам сделать это:

$ git bisect start
$ git bisect good v2.6.18
$ git bisect bad master
Bisecting: 3537 revisions left to test after this
[65934a9a028b88e83e2b0f8b36618fe503349f8e] BLOCK: Make USB storage depend on SCSI rather than selecting it [try #6]

Если вы запускаете сейчас “git branch” вы увидите, что git временно переместился в новую ветку с названием “bisect”. Эта ветка указывает на коммит ( с именем снапшота 65934…), который доступен из «мастер», но из v2.6.18. Откомпилируйте и протестировать его и посмотрите есть-ли в нём проблема. Допустим, что есть. Тогда:

$ git bisect bad
Bisecting: 1769 revisions left to test after this
[7eff82c8b1511017ae605f0c99ac275a7e21b867] i2c-core: Drop useless bitmaskings

Извлекаем для проверки более старую версию. Продолжая аналогично, проверяем на каждом этапе версию и говорим git`у хорошая это версия, или плохая. Можно заметить что каждый раз число подозрительных версий сокращается примерно вдвое. После примерно 13 тестов (в данном случае), получаем идентификатор «виновного» коммита. Затем можно проанализировать коммит с помощью git-show(1), найти кто записал этот коммит, и направить ему по e-mail баг-рапорт об ошибке с идентификатором этого коммита. Наконец, запустив :

$ git bisect reset

возвращаемся к ветке удаляя перед этим временную ветвь «bisect».

Заметим, что версия, которую извлекает вам для проверки git-bisect является лишь предложением, и Вы можете сразу пробовать другую версию, если считаете что это более правильно. Например, время от времени вы можете «приземлиться» в коммит который не связан c предыдущей последовательностью, запустив

$ git bisect visualize

если запустить gitk, то ярлык коммита в этом случае будет отмечен маркером “bisect”.

Выбрав безопасной просмотр ближайшего коммита, замечаем его идентификатор и проверив его уходим из него командой (Chose a safe-looking commit nearby, note its commit id, and check it out with):

$ git reset --hard fb47ddb2db...

затем проверив его, запускаем соответственно «bisect good» или «bisect bad» и продолжаем.

2. Именование коммитов

Мы уже видели несколько способов именования коммитов:

* Шестнадцатеричное 40-значное имя объекта (снапшот).
* Имя ветки: относится к коммиту на вершине данной ветки
* Имя тэга: ссылка на коммит на который учитывает тэг (мы видели ветки и тэги на которые ссылаются в специальных случаях).
* HEAD: ссылка на вершину текущей ветки

Есть много других имён, см .в странице man git-rev-parse(1) раздел “SPECIFYING REVISIONS” с полным перечнем способов задания имени версий.

Некоторые примеры:

$ git show fb47ddb2 # первые несколько символов имени объекта
# Как правило, достаточно указать его несколько которые делают его уникальным.
$ git show HEAD^ # родители коммита HEAD
$ git show HEAD^^ # «дедушки» коммита HEAD
$ git show HEAD4 # «пра-прадедушки»

Напомним, что если произошло слияние, то коммит может иметь больше чем одного родителя; по умолчанию, ^ и ~ следует за первым родителем, перечисленным в коммите, но вы также можете сделать другой выбор :

$ git show HEAD^1 # показать первого родителя HEAD
$ git show HEAD^2 # показать второго родителя HEAD

В дополнение к HEAD, существует ряд других специальных имён для коммитов:

Слияния (будет обсуждаться позже) это такие операции подобные git-reset которые изменяют текущий проверяемый коммит и как правило устанавливают имя ORIG_HEAD на значение HEAD перед выполнением операции.

Операция git-fetch всегда сохраняет вершину последней извлекаемой ветки в имени FETCH_HEAD. Например, если вы запускаете git fetch без задания локальной ветки в качестве цели операции

$ git fetch git://example.com/proj.git theirbranch

взятые коммиты всё-равно будет доступны через FETCH_HEAD.

Когда мы обсуждаем слияния мы также видим особых имя MERGE_HEAD, которое относится к другой ветви которую мы объединяем с текущей веткой.
Команда git-rev-parse(1) — это команда низкого уровня, которая иногда полезна для перевода некоторых имён коммита в его имя объекта (имя снапшота) :

$ git rev-parse origin
e05db0fd4f31dde7005f075a84f96b360d05984b

3. Создание тегов

Мы можем создать тег к ссылке на особый коммит. После запуска

$ git tag stable-1 1b2e1d63ff

вы можете использовать имя stable-1 как ссылку на коммит 1b2e1d63ff.

Это создаст «легковесный» тег. Если вы хотели бы также включать комментарии в тег, и, возможно, подписать его криптографическими, то вы должны создайте вместо тэга объект, подробнее см.. man страницу git-tag(1).

4. Просмотр версий

Команда git-log(1) может показать лист коммитов.

Собственно она показывает все коммиты доступные из родительского коммита: но она так-же может обработать специфичные запросы :

$ git log v2.5.. # коммиты от v2.5 (только доступные)
$ git log test..master # доступные коммиты от master но не из test
$ git log master..test # ...доступные от test но не из master (две точки)
$ git log master...test # ...или от test или от master (три точки)
# но не из обоих

$ git log --since="2 weeks ago" # коммиты за последние две недели
$ git log Makefile # коммиты в которых менялся Makefile
$ git log fs/ # ... в которых изменился какой-нибудь файл в fs/
$ git log -S'foo()' # коммиты, которые добавили или удалили любые файлы

# имеющую строку 'foo()'

И, конечно, вы можете комбинировать все это; следующая команда находит коммиты из v2.5 в которых изменяются Makefile и изменения в любых файлов в fs/:

$ git log v2.5.. Makefile fs/

Также вы можете использовать git log, для просмотра патчей:

$ git log -p

См. описание опции "—pretty" в man странице git-log(1) страницу и о других опциях.

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

5. Формирование сравнений (diff)

Вы можете создавать сравнения (diff) между любыми двумя версиями используя git-diff(1):

$ git diff master..test

Эта команда создаст текст различий (diff) между двумя ветками test и master.

Если вы хотите найти различия от их общего предка до test, вы можете использовать три точки вместо двух:

$ git diff master...test

Иногда вы хотите вместо различий создать набор патчей, для этого можно использовать git format-patch(1):

$ git format-patch master..test

будет сгенерирован файл патча для совершения каждого коммита доступного из test, до master.

6. Просмотр старых версии файлов

Вы всегда можете просмотреть старую версию файла, перейдя к коммиту с его первой версией..

Но иногда бывает удобнее иметь возможность просмотреть старую версию одного файла, не проверив ничего другого; это делает команда:

$ git show v2.5:fs/locks.c

До двоеточия может быть любое имя коммита, а после него может быть любой путь к файлу из репозитория git.

7. Примеры

7.1. Подсчёт числа коммитов в ветке :

Предположим, вы хотите узнать, сколько коммитов вы создали на “mybranch”, после отделения от “origin”:

$ git log --pretty=oneline origin..mybranch | wc -l

Альтернативно, можно часто видеть вид всего сделанного командой низкого уровня git-rev-list(1), которая перечисляет SHA1 снапшоты всех всех имеющихся коммитов ветки:

$ git rev-list origin..mybranch | wc -l

7.2. Проверка наличия истории между двумя точками ветки

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

$ git diff origin..master

вам скажет что содержание проекта одно и то-же в обоих ветках; теоретически, однако возможно, что содержание этого проекта могло быть получено двумя различными исторических маршрутами. Вы можете сравнить имена объектов (снапшоты) :

$ git rev-list origin
e05db0fd4f31dde7005f075a84f96b360d05984b
$ git rev-list master
e05db0fd4f31dde7005f075a84f96b360d05984b

Или вы вспомните об операторе ... (три точки), который выбирает все коммиты, доступные от каждой из ссылок но не доступные от обеих:

$ git log origin...master

Команда вернёт no commits (нет коммитов), когда две ветви эквивалентны

7.3. Поиск первой помеченной версии с решением определённой проблемы

Предположим, вы знаете, что коммит e05db0fd фиксирует определённые проблемы.

Вы хотите найти ближайшее помеченный тегом релиз, который содержит это исправление.

Конечно, могут существовать более одного варианта ответа на вопрос, если история ветки после коммита 05db0fd имеет несколько «ранних» тегов релизов.

Можно визуально просмотреть коммиты после e05db0fd:

$ gitk e05db0fd..

Вы также можете использовать git-name-rev(1) которая даст коммит с именем любого тега найденного среди потомков коммита :

$ git name-rev --tags e05db0fd
e05db0fd tags/v1.5.0-rc1^023 e05db0fd tags/v1.5.0-rc1 ^ 0 ~ 23

Команда git-describe(1) делает противоположное, выдавая имя версии используя тег, на которым базируется коммит.

$ git describe e05db0fd
v1.5.0-rc0–260-ge05db0f

Иногда вам может помочь догадка, как может называться тег, которые мог возникнуть после решения проблемы.

Если вы просто хотите проверить содержит-ли помеченная тегом версия данный коммит, вы могли бы использовать git-merge-base(1):

$ git merge-base e05db0fd v1.5.0-rc1
e05db0fd4f31dde7005f075a84f96b360d05984b

Команда merge-base находит общего предка у заданных коммитов, и всегда возвращает или тот или другой коммит, когда один является потомком другого; вышеприведённые результаты свидетельствуют о том, что на самом деле e05db0fd предок v1.5.0-rc1 .

Альтернативно отметим :,

$ git log v1.5.0-rc1..e05db0fd

будет производить пустой вывод, только если в v1.5.0-rc1 включает e05db0fd, потому что он совершает только результаты, которые не доступны из v1.5.0-rc1.

Как ещё один вариант, команда git-show-branch(1) перечисляет коммиты доступные её аргументам которые показываются левой стороне дисплея, указывая на каким аргументам, что доступно.

Таким образом, вы можете запустить-то вроде

$ git show-branch e05db0fd v1.5.0-rc0 v1.5.0-rc1 v1.5.0-rc2
! [e05db0fd] Fix warnings in sha1_file.c – use C99 printf format if
available
! [v1.5.0-rc0] GIT v1.5.0 preview
! [v1.5.0-rc1] GIT v1.5.0-rc1
! [v1.5.0-rc2] GIT v1.5.0-rc2
...

Затем поиски строки, которая выглядит так

+ ++ [e05db0fd] Fix warnings in sha1_file.c – use C99 printf format if available

Которая показывает, что e05db0fd доступен из себя, из v1.5.0-rc1, и из v1.5.0-rc2, но не из v1.5.0-rc0.

7.4. Просмотр уникальных коммитов в данной ветке

Предположим, вы хотели бы видеть все доступные коммиты от имени вершины ветки “master”, но недоступные от любой другой вершины ветки вашего репозитория.

Мы можем перечислить все вершины в этом репозитории командой git-show-ref(1):

$ git show-ref --heads]
bf62196b5e363d73353a9dcf094c59595f3153b7 refs/heads/core-tutorial
db768d5504c1bb46f63ee9d6e1772bd047e05bf9 refs/heads/maint
a07157ac624b2524a059a3414e99f6f44bebc1e7 refs/heads/master
24dbc180ea14dc1aebe09f14c8ecf32010690627 refs/heads/tutorial-2
1e87486ae06626c2f31eaa63d26fc0fd646c8af2 refs/heads/tutorial-fixes

Мы можем получить только имя вершины ветки, удалить “master” из вывода, с помощью стандартных утилит cut и grep:

$ git show-ref --heads | cut -d' ' -f2 | grep -v '^refs/heads/master'
refs/heads/core-tutorial
refs/heads/maint
refs/heads/tutorial-2
refs/heads/tutorial-fixes

И так-же мы можем создать запрос чтобы увидеть все доступные коммиты от вершины «master», но не от этих других вершин:

$ gitk master --not $( git show-ref --heads | cut -d' ' -f2 | grep -v '^refs/heads/master' )

Очевидно, возможны бесконечные вариации на эту тему; например, чтобы увидеть все доступные коммиты некоторые от некоторой вершины но не от но без включения тэгов репозитория :

$ gitk $( git show-ref --heads ) --not $( git show-ref --tags )

(См. git-rev-parse(1) для объяснения синтаксиса выбора коммитов таких, как —not).

7.5. Создание журнала изменений (changelog) и тарбола релиза

Команда git-archive(1) может создать tar или zip архив любой версии проекта, например:

$ git archive --format=tar --prefix=project/ HEAD | gzip >latest.tar.gz

Будет использоваться вершина HEAD для создания tar архива, в котором каждому имени файла будет предшествовать путь «project/».
Если Вы создаёте релиз новой версии вашего в вашем проекте, вы можете одновременно создать файл изменений (changelog) который включены в анонс релиза.
Линуса Торвальдса, например, делает новых версиях ядра путём пометки их тегом, а затем выполняя:

$ release-script 2.6.12 2.6.13-rc6 2.6.13-rc7

где-релиз скрипта скрипт, выглядит наподобии :

#!/bin/sh
stable="$1"
last="$2"
new="$3"
echo "# git tag v$new"
echo «git archive --prefix=linux-$new/ v$new | gzip -9 > ../linux-$new.tar.gz»
echo «git diff v$stable v$new | gzip -9 > ../patch-$new.gz»
echo «git log --no-merges v$new ^v$last > ../ChangeLog-$new"
echo «git shortlog --no-merges v$new ^v$last > ../ShortLog"
echo «git diff --stat --summary -M v$last v$new > ../diffstat-$new"

а потом он просто вставляет выход команд после проверки того, что отмечено OK.

7.6. Поиск ссылок коммитов на файл с заданным содержанием

Кто-то дал вам в руки копию файла, и спросил в каком коммите файл был изменён уверенный, что вы содержите содержимое файла до и после коммита Вы можете найти это командой :

$ git log --raw --abbrev=40 --pretty=oneline | grep -B 1 `git hash-object filename`

Понять как это работает, как далее требует дополнительного изучения. Страницы man git-log(1), git-diff-tree(1), и git-hash-object(1) могут помочь вам разобраться подробнее.

Глава 3. Применение Git в разработке

1. Задать ваше имя для git
2. Создание нового репозитория
3. Как создать коммит
4. Создание хорошего описания коммита
5. Игнорирование файлов
6. Как осуществить слияние
7. Разрешение конфликтов объединений
7.1. Получение помощи при разрешения конфликтов слияния
8. Отмена слияния
9. Быстрое слияние вперёд
10. Исправление ошибок
10.1. Исправление ошибки новым коммитом
10.2. Исправление ошибки путём переписывания истории
10.3. Извлечение старой версии файла
10.4. Временный отказ от проделанной работы
11. Обеспечение эффективной деятельности
12. Обеспечение надёжности
12.1. Проверка репозитория от порчи
12.2. Восстановление потерянных изменений
12.2.1. Журнал reflog
12.3. Изучение зависших объектов

1. Задать ваше имя для git

Прежде чем что-нибудь создавать и коммитить, вы должны представиться git. Простейший способ сделать это состоит в том, чтобы убедиться, что следующие строки уже содержатся в файле с именем .gitconfig вашего домашнего каталога:

[user]
name = Your Name Comes Here
email = you@yourdomain.example.com

(См. Раздел “CONFIGURATION FILE” git-config(1) для получения дополнительных сведений о файле конфигурации.)

2. Создание нового репозитория

Создание нового репозитория с нуля очень просто:

$ mkdir project
$ cd project
$ git init

Если у вас есть некоторое первоначальное наполнение (например, тарбол):

$ tar -xzvf project.tar.gz
$ cd project
$ git init
$ git add . # Включить все ниже . в первый коммит
$ git commit

3. Как создать коммит

Создание нового коммита состоит из трех этапов:

1. Создание некоторых изменений в рабочем каталоге, используя ваш любимый редактор.
2. Указание git`у о ваших изменениях.
3. Создание коммита, используя то, что вы указали git на втором шаге.

На практике вы можете можно интерактивно повторять 1 и 2 шаги столько раз, сколько хотите; чтобы отследить то, что вы хотите включить в коммит на шаге 3, git сохраняет отпечаток структуры изменённого содержания в специальной области «индекс» (index).

В начале, содержимое индекса будет идентично тому, что уже есть в HEAD. Команда “git diff – cached” показывает разницу между HEAD и индексом, поэтому нет смысла запускать её в этой точке.

Измененить индекс просто:

Для обновления индекса изменённым содержимым файла, используйте

$ git add path/to/file

Для добавления содержимого нового файла в индекс, используйте

$ git add path/to/file

Для удаления файл из индекса и из рабочего дерева,

$ git rm path/to/file

После каждого шага можно проверить изменения :

$ git diff --cached

Команда git diff --cached всегда показывает разницу между HEAD и файлом индекса – это то, что попадает в проект, если вы сделаете коммит сейчас.

$ git diff

показывает разницу между рабочим деревом и файлом индекса. Заметьте, что “git add” всегда добавляет только текущие содержание файла индекса; дальнейшие изменения в один и тот же файл будут игнорироваться, если вы не запустите git-add ещё раз.

Когда все будет готово, просто запустите

$ git commit

и git даст приглашение для ввода сообщения о коммите и создаст новый коммит

Убедитесь, что это вы всё сделали правильно.

$ git show

Cпециальная опция -a

$ git commit -a

обновляет индекс со всеми файлами, которые вы изменили, удалили или создали и создаёт коммит в один шаг.

Набор команд, полезных для отслеживания того, что Вы собираетесь зафиксировать коммитом:

$ git diff --cached # разница между HEAD и индексом, которая
# зафиксируется, если делать коммит сейчас
$ git diff # разница между индексом и вашим рабочим каталогом которые не
# зафиксируются, если делать коммит сейчас
$ git diff HEAD # разница между HEAD и рабочим каталогом, которая будет зафиксирована
# если сделать сейчас commit -a
$ git status # краткий суммарный отчёт о состоянии индекса, рабочего каталога и HEAD.

Вы также можете использовать git-gui(1) для создания коммита, просмотра изменений в индексе и в рабочем каталоге, индивидуально выбрать кусок изменений для включения в индекс (правым кнопкой мыши на кусок diff и выбрав пункт “Stage Hunk For Commit”).

4. Создание хорошего описания коммита

Хотя это и не требуется, приветствуется стиль оформления сообщения о коммите в котором оно начинается со строки с кратким описанием изменений (менее 50 символа), а уже потом после пустуй строки подробным описанием изминений. Это позволяет превращать коммит в email – первую строку помещаем в тему письма, а остальное в его тело.

5. Игнорирование файлов

При работе с проект часто созжаются файлы, которые вы-бы не хотели отслеживать с помощью git. Это обычно файлы созданные процессом сборки и временные резервные копии файлов, созданных вашим редактором. Естественно, нет смысла отслеживать такие файлы и законно возникает вопрос – как сделать так, чтобы вызов “git add” не добавлял их. Отслеживание их быстро начинает раздражатью Из-за изменений этих файлов становятся практически бесполезны команды “git add .» и “git commit -a”, а изменения этих файлов засоряют вывод команды “git status”.

Вы можете указать git игнорировать определенные файлы, создав файл с именем .gitignore в верхнем уровне вашего рабочего каталога, например, с таким содержанием:

# Строки, начинающиеся с '#' считаются комментариев.
# Игнорировать любой файл с именем foo.txt.
foo.txt
# Игнорировать (генерируются) html файлы,
*.html *. html
# Кроме foo.html который включается в индекс вручную.
!foo.html ! foo.html
# Игнорировать файлы с расширением *.a и *.o
*.[oa]

См. gitignore (5) для детального объяснения синтаксиса. Вы также можете разместить файлы .gitignore в другие каталоги вашего рабочего каталога, и они будут распространяться на эти каталоги и их подкаталоги. Файлы .gitignore могут быть добавлены в репозиторий подобно другим файлам (просто запустив git add .gitignore, как обычно), удобно также сделать так, что-бы при клонировании репозитория другими пользователями по этим шаблонам эти файлы исключались из копирования. Если вы хотите, чтобы исключающие шаблоны влияли только в некоторых репозиториях (вместо каждого репозитория для данного проекта), вы можете поместить шаюлоны в файл вашего репозитория с именем .git/info/exclude, или в файл, задаваемый значением переменной core.excludesfile вашей конфигурации.

Некоторые git команды могут также исключатm шаблоны непосредственно в командной строке. Подробнее см. man gitignore (5) .

6. Как осуществить слияние

Вы можете объединить две разделённые ветви разработки командой git-merge(1):

$ git merge branchname

при этом сливается ветка разработки “branchname» c текущей веткой. Если есть конфликты, например, если один и тот же файл изменяется двумя различными способами в удалённой ветке и в локальной ветке, вы будете предупреждены; вывод будет выглядеть примерно так:

$ git merge next
100% (4/4) done
Auto-merged file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit the result.

Перевод сообщения:

# Авто-слияние file.txt
# Конфликт (содержимого): Конфликт слияния в file.txt
# Автоматическое слияние с ошибкой; устраните конфликты, а затем включите результат
# в коммит.

Маркеры кофликта остаются в проблемных файлах, и после того, как вы разрешите конфликты вручную, вы можете обновить содержимое индекса и запустить git commit, как при обычном изменении файлов.

Если рассматривать результирующий коммит используя gitk, можно увидеть, что он имеет двух родителей один из которых вершина текущей ветки, а другой – вершина другой ветки.

7. Разрешение конфликтов объединений

Если слияние не произошло автоматически, git помещает индекс и рабочий катвалог в специальное состояние содержащее всю необходимую информацию чтобы помочь разрешить конфликт слияния. Файлы с конфликтами помечены в индексе специально, так, что до тех пор, пока вы решите проблему и не обновите индекс, совершить git-commit(1) не удастся:

$ git commit
file.txt: needs merge file.txt
#необходимо объединить file.txt

Кроме того, в git-status(1) будут перечислены эти файлы, как “unmerged” (необъединённые), а файлы с конфликтами будет помечены маркерами конфликта, например:

<<<<<<< HEAD:file.txt
Hello world
=======
Goodbye
>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt

Все, что вам необходимо сделать, это отредактировать файлы для разрешения конфликтов, а затем дать команды

$ git add file.txt
$ git commit

Заметим, что сообщение коммита будет уже заполнен ддя вас некоторой информацией о слиянии. Конечно, вы можете просто использовать это сообщение по умолчанию без изменений, однако вы можете, если посчитаете нужным, добавить свои комментарии.

Выше описоно всё, что вам необходимо знать для решения простых слияния.
Но git также может предоставить дополнительную информацию, чтобы помочь урегулировать более сложные конфликты:

7.1. Получение помощи при разрешения конфликтов слияния

Все изменения, которые git смог объединить автоматически уже добавлены в файл индекса, так что git-diff(1) показывает только конфликты. Используется особый синтаксис:

$ git diff
diff --cc file.txt
index 802992c,2b60207..0000000
--- a/file.txt
+++ b/file.txt
@@@ -1,1 -1,1 +1,5 @@@
++<<<<<<< HEAD:file.txt
+Hello world
++=======
+ Goodbye
++>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt

Напомним, что комммит, который будут зафиксирован после того, как мы урегулировали конфликт будет иметь двух родителей, а не одного: один из родителей будет HEAD – вершина текущей ветки; а другой будут вершиной другой ветви, значение которой временно сохраняется в переменной MERGE_HEAD. В ходе слияния, индекс содержит три версии каждого файла.
Каждая из этих трех «стадий файла» представляет различные версии файла:

$ git show :1:file.txt # файл в общий предке обеих ветвей
$ git show :2:file.txt # Версия HEAD, но и в том числе любое
# неконфликтное изменение из MERGE_HEAD
$ git show :3:file.txt # версия с MERGE_HEAD, но и в том числе любое
# не конфликтующее изменение от HEAD.

Начиная со стадии 2 и стадии 3 версии были обновлены с неконфликтными изменениями, и остающиеся разлияия между ними являются существенной для анализа слияния, и которую можно получить командой git-diff(1) для из индекса для показа этих оставшихся конфликтов.

Команда git diff показывает различия между версией рабочего каталога file.txt и версиями стадии 2 и стадии 3.
Таким образом заменяя в каждой строке один "+" или “-", он теперь использует две колонки: первая колонка используется для различия между первым родителем и рабочий каталогом, а вторая отмечает различия между вторым родителем и рабочий каталогом. (См. “COMBINED DIFF FORMAT” раздела git-diff-files(1) для деталей формата.)

После разрешения конфликта обычным способом (но до обновления индекса), сравнения это будет выглядеть следующим образом:

$ git diff
diff --cc file.txt
index 802992c,2b60207..0000000
a/file.txt
+++ b/file.txt
@@@ -1,1 -1,1 +1,1 @@@
– Hello world
-Goodbye
++Goodbye world

Здесь видно, что наше версия решение исключить “Hello world” от первого родителя, удалить “Goodbye” от второго родителя, и добавить: “Goodbye world”, которое ранее отсутствовало в обоих предках.

В некоторых особые опции diff позволяют находить различия рабочего каталога от любого из этих этапов:

$ git diff -1 file.txt # различий с этапом 1
$ git diff --base file.txt # как выше
$ git diff -2 file.txt # различий c этапом 2
$ git diff --ours file.txt # как выше
$ git diff -3 file.txt # различий c этапом 3
$ git diff --theirs file.txt # как выше.

Командами git-log(1) и gitk[1] также обеспечивается помощь при слиянии:

$ git log --merge
$ gitk --merge

Они будут показывать все коммиты, которые существуют только в HEAD и в MERGE_HEAD, и которых касаются необъеденяемый файл.

Вы также можете использовать git-mergetool(1) которая позволяет объединить необъеденённые файлы с помощью внешних средств, таких как emacs или kdiff3.

Каждый раз, когда вы урегулировать конфликты в файле и обновляя индекс:

$ git add file.txt

различные этапы этого файла будет «коллапсировать», после чего git-diff уже не будут (по умолчанию) показывать различия для этого файла.

8. Отмена слияния

Если вы не смогли слить ветви и решили просто отказаться и отказаться, вы всегда можете вернуться к состоянию до слияния :

$ git reset --hard HEAD

Или, если вы уже зафиксировали слияние коммитом, что вы хотите его отменить,

$ git reset --hard ORIG_HEAD

Однако эта последняя команда может быть опасной в некоторых случаях когда ваш коммит имеет уже коммиты потомки, или если этот коммит сам является слиянием с другой ветвью, тогда удаление коммита запутает будущие слияния.

9. Быстрое слияние вперёд

Существует один особый случай, не упомянутый выше, которая третируется по-разному. Нормально, результатом слияния является коммит слияния с двумя родителями, в точке объединения на двух линий разработки.

Однако, если текущая ветка является потомком другой и каждый коммит всегда один и всегда содержат коммит предыдущего, то можно просто выполнив команду git`ом “fast forward” (Быстрое движение вперёд) вершина текущей ветки продвинется вперед до точки вершины объединения в новую ветвь, без каких-либо новых созданий коммитов.

10. Исправление ошибок

Если вы сделали ошибку в рабочем каталоге, но пока еще не зафиксировали вашу ошибку, вы можете вернуть рабочий каталог в прошлое состояние командой

$ git reset --hard HEAD

Если вы сделаете коммит и позже вы решили отказаться от него, то у есть два принципиально различных способа решения проблемы:

  1. Вы можете создавать новые коммит, в котором отменить всё, что вы сделали в старом коммите. Это правильный вариант, если ваши ошибки уже были преданы гласности.
  2. Ни в коем случае не делайте этого, если вы уже сделали историю гласной; git обычно не ожидает изменения истоии проекта, и не может корректно выполнять частые слияния с веткой, у которой имело изменение истории. Напрример, от удаляемого вами коммита кто-то уже мог создать отдельную ветку разработки и такая ветка не сможет быть в дальнейшем корректно слита с вашим проектом.

10.1. Исправление ошибки новым коммитом

Создание нового коммита, который отменяет ранее сделанные исправления, очень легко, просто дайте команду git-revert(1) с ссылкой на плохой коммит; например, вернуться к последниме коммиту:

$ git revert HEAD

Это позволит создать новыЙ коммит, который “откатывает” изменения HEAD. Вам будет предоставлена возможность отредактировать сообщение для совершения нового коммита.

Вы также можете вернуть последние изменения, например, предпоследние:

$ git revert HEAD^

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

Если последние изменения пересекаются с изменениями будет восстановления, Вам будет предложено устранить конфликты вручную, как и в случае удалени конфликтов a merge .

10.2. Исправление ошибки путём переписывания истории

Если взять проблематичный коммит является последним коммитом, и вы еще не сделали его публичным, вы можете просто удалить его, используя git-reset .

Кроме того, вы можете отредактироватьсодержимое рабочего каталога и обновлять индекс для устранения ошибки, так-же, как если бы вы хотели создать новый коммит, а запустив

$ git commit --amend

который заменит старый коммит новым путём включения ваших изменений, и давая вам возможность изменить вначале старое сообщение коммита.
Опять же, вы никогда не должны делать это с коммитом который уже был объединён в другую ветвь, в этом случае лучше использовать git-revert(1).
Кроме того, можно заменить более старые коммиты в истории, но это более сложная тема и мы оставим её его для другой главы.

10.3. Извлечение старой версии файла

В процессе отмены предыдущих плохие изменений, вы можете обнаружить, что было-бы полезно проверить более старую версию файла командой git-checkout(1). Мы использовали уже git checkout раньще, что-бы сменить текущую ветку, но эта команда работает совершенно по-другому, если в качестве аргумента получает путь до файла :

$ git checkout HEAD^ path/to/file

заменяет содержимое указанного файла, и из коммита HEAD^, а также обновляет соответственно индекс. Команда не меняет текущую ветвь. Если вы просто хотите взглянуть на старую версию файла без изменения рабочего каталога, вы можете сделать git-show(1):

$ git show HEAD^:path/to/file

который будут отображён с учетом версии файла.

10.4. Временный отказ от проделанной работы

Допустим вы находитесь в середине работы над чем-то сложным, и обнаруживаете не очевидную, но тривиальную ошибку. Вы хотите её исправить, прежде чем продолжать работу дальше. Вы можете применить git-stash(1) для сохранения текущего состояние вашей работы, и после устранения ошибки (или, по желанию после перехода на другие ветки и возврата обратно), даёте команду восстановления для продолжения вашей прерванной работы.

W – точка, где вы работали,
H — текущая HEAD,
I — точка коммита с исправлением выявленной тривиальной ошибки.

$ git stash “work in progress for foo feature”

Эта команда сохраняет все изменения “в тайнике” (stash), и возвращает рабочий каталог и индекс к HEAD. Затем вы можете исправить, тривиальную проблему и зафиксировать её решение.

... исправляем тривиальные ошибки ...
$ git commit -a -m “blorpl: typofix”

После этого вы можете вернуться к тому, что вы делали до git stash командой:

$ git stash apply

11. Обеспечение эффективной деятельности

Размер репозитории git зависит от сжатия сохранённой истории информации. Сжатие не даёт занять репозиторию слишком много места на диске и в памяти. Сжатие не выполняется автоматически. Поэтому вы должны периодически запускать git-gc(1) :

$ git gc

для повторения сжатия архива. Это может занять очень много времени, поэтому вы, возможно, предпочтете запустить git-gc, тогда, когда это вам не будет мешать работать.

12. Обеспечение надёжности

12.1. Проверка репозитория от порчи

Команда git-fsck(1) запускает ряд логических самопроверок репозитория, и создаёт отчёт о всех имеющихся проблемах. Этот процесс может занять некоторое время. Наиболее распространенными это предупреждение о «зависших» объектах:

$ git fsck
dangling commit 7281251ddd2a61e38657c827739c57015671a6b3
dangling commit 2706a059f258c6b245f298dc4ff2ccd30ec21a63
dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5
dangling blob 218761f9d90712d37a9c5e36f406f92202db07eb
dangling commit bf093535a34a4d35731aa2bd90fe6b176302f14f
dangling commit 8e4bec7f2ddaa268bef999853c25755452100f8e
dangling tree d50bb86186bf27b681d25af89d3b5b68382e4085
dangling tree b24c2473f1fd3d91352a624795be026d64c8841f
....

Зависание объектов не проблема. В худшем случае они бесполезно занимают место на диске. Они иногда могут стать последним средством в восстановлении потерянных наработок – см. Раздел “Dangling objects” для более детальной информации.

12.2. Восстановление потерянных изменений

12.2.1. Журнал reflog

Скажем вы изменили ветвь git-reset(1) —hard а потом сообразили, что ветвь была была только ссылкой на точку в истории. К счастью, git также ведет журнал, называемый “reflog”, о всех предыдущих значений каждой ветви. Так что в этом случае вы все ещё можете найти старую историю, используя, например,

$ git log master@{1}

Эта команда выдаёт список коммитов о предыдущего версиях вершины ветки “master”. Cинтаксис может быть использован с любыми командами git, которые принимают коммит, а не только с git log. Некоторые примеры:

$ git show master@{2} # Смотрим где ветвь указывает 2,
$ git show master@{3} # 3, ... изменения назад.
$ gitk master@{yesterday} # Смотрим что изменилось вчера,
$ gitk master@{"1 week ago"} # ... неделю назад
$ git log --walk-reflogs master # смотрим содержимое reflog ветки master

Посмотреть reflog сохранённый в HEAD, можно так

$ git show HEAD@{"1 week ago"}

Команда покажет, на что указывало HEAD неделю назад, а не то, что текущая ветвь указала неделю назад. Это позволяет просматривать историю о том, с чем вы работали .
Reflogs хранятся по умолчанию в течение 30 дней, после чего он будет обрезан. См. git-reflog(1) и git-gc(1) чтобы узнать, как управлять этим обрезанием, и смотрите секцию “SPECIFYING REVISIONS” git-rev-parse(1) для изучения подробностей.

Заметьте, что reflog история сильно отличантся от обычной истории git истории.

Обычная история отражает совместное использование репозитория всех, кто работает с этим проектом, а reflog история локальна: она говорит вам лишь о том, какие ветки в локальном репозитории были изменены и в какой момент времени.

12.3. Изучение зависших объектов

В некоторых ситуациях reflog, возможно, не сможет вам помочь.
Например, предположим, вы удалили ветвь, в истории которой содержался необходимый вам релиз. Reflog также был удалён, однако, если вы еще не очищали от мусора репозиторий, то вы по-прежнему ещё можете найти потерянный вами коммит в зависших объектах, о которых вам сообщает команда git-fsck.
См. раздел “Dangling objects” для изучения деталей.

$ git fsck
dangling commit 7281251ddd2a61e38657c827739c57015671a6b3
dangling commit 2706a059f258c6b245f298dc4ff2ccd30ec21a63
dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5
...

Вы можете изучить любой из этих зависших коммитов, например дав команду:

$ gitk 7281251ddd --not --all

которая делает буквально то, что в ней задано: это означает, что вы хотите увидеть историю описанного зависшего коммита, исключая из неё историю существующих ныне ветвей и тегов.
Таким образом, вы получаете историю от какого-то существующего коммита до потеренного. (И заметим, что эта история может содержать не один коммит: мы ведь получали информацию только о «конце линии» которая зависла, но перед ней может находиться долгоя и сложная история коммита, которая также была отброшена).

Если вы решили, что хотите вернуться эту историю, вы всегда можете создать новую ссылочную, точку указывающию на потерянный коммит, например, востановив эту ветвь как новую :

$ git branch recovered-branch 7281251ddd

Другие виды зависших объектов (blobs и trees), так-же могут существовать, и эти зависшие объекты могут возникать в разных ситуациях.