Файл: Система управления версиями git и российский сервис хранения исходного кода gitflic.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 06.12.2023
Просмотров: 216
Скачиваний: 7
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
59
Рисунок 2.10 – Тестовый репозиторий
Выполним для этого репозитория команду git log –pretty = full.
Git начал формировать список коммитов с того из них, на который указывает содержимое файла HEAD через файл с именем New_Branch. В список коммитов попали все коммиты, составляющие дерево репозитория.
Два из них принадлежат ветке New_Branch и один – ветке Main_branch. git log --pretty=full commit 09a543aaa4b8adaced3ea2e4b98b9973dfcfc002 (HEAD -> New_Branch)
Author: Git_Book git_book@home.com
Commit: Git_Book
Третий коммит. commit 905b38d0d881bb229252127c0ee1c988ab84acd3
Author: Git_Book git_book@home.com
Commit: Git_Book
Проверка перехода между ветками commit 79a9f3a521f77bed2c94115cbe7a6a7316abd276 (Main_branch)
Author: Git_Book
Commit: Git_Book
Test commit 1
В первой строке результата выполнения команды в скобках записано, что текущая ветка New_Branch (на неё указывает HEAD). Следующий ком- мит принадлежит текущей ветке (отсутствует информация, заключённая в скобки). Последний коммит принадлежит ветке Main_branch (название ветки в скобках).
Если необходимо вывести список коммитов только для одной ветки, то для ветки Main_branch это сделать легко: git log Main_branch commit 79a9f3a521f77bed2c94115cbe7a6a7316abd276 (Main_branch)
Author: Git_Book
Date: Thu Jul 21 21:12:24 2022 +0300
Test commit 1
Для ветки New_Branch команда будет выглядеть сложнее:
60
$ git log Main_Branch..New_Branch commit 09a543aaa4b8adaced3ea2e4b98b9973dfcfc002 (HEAD -> New_Branch)
Author: gitflic-muiv-gmbulan
Date: Sun Jul 24 21:52:43 2022 +0300
Третий коммит. commit 905b38d0d881bb229252127c0ee1c988ab84acd3
Author: gitflic-muiv-gmbulan
Date: Sun Jul 24 19:36:06 2022 +0300
Проверка перехода между ветками
В команде git log Main_Branch..New_Branch указано, что нужно выве- сти сведения о коммитах, расположенных в ветке New_Branch от её начала до того места на дереве репозитория, где эта ветка примыкает к ветке
Main_Branch.
2.15. Сравнение версий файлов, команды git diff, git show
При анализе истории изменений файлов в репозитории нередко бы- вает необходимо сравнить версии одного и того же файла. Для выполнения этой операции используется команда git diff.
Формат команды git diff:
Git diff <ключ> <файл1> <файл2>
Возможные значения ключей:
--diff-filter=<признак>; признак может иметь одно из значений: А – сравнение для файлов, добавленных в индекс; D – сравнение для удалённых файлов; М – сравнение для изменённых файлов;
--word-diff=color – выделять удалённые слова красным цветом, а до- бавленные – зелёным. Этот ключ действует по умолчанию.
Файл1, файл2 – имена файлов и/или коммитов, для которых будет производиться сравнение. <ключ> <файл1> <файл2> являются необязатель- ными атрибутами команды git diff.
Рассмотрим на примере, как задаются файлы, для которых будет выпол- няться операция сравнения, и как «расшифровывать» результаты сравнения.
В рабочую директорию был добавлен файл с именем Learn_diff.txt.
Вначале этот файл содержал следующие строчки (версия 1):
Файл Изменения, добавленный в коммит.
Неизменяемая строка.
61
Изменяемая строка: коммит.
Этот файл был добавлен в коммит:
/c/Learn_git
(Main_branch)
$ git add Learn_diff.txt
/c/Learn_git
(Main_branch)
$ git commit -m 'Изучение команды git diff'
[Main_branch b5fd14d] Изучение команды git diff
2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 Learn_diff.txt
Содержимое файла Learn_diff.txt было изменено (версия 2):
Файл Изменения, добавленный в индекс.
Неизменяемая строка.
Изменяемая строка: индекс.
Изменённый файл был добавлен в индекс:
/c/Learn_git
(Main_branch)
$ git add Learn_diff.txt
Файл Learn_diff.txt был изменён ещё раз (версия 3):
Файл Изменения, не отслеживаемый.
Неизменяемая строка.
Изменяемая строка: рабочая директория.
После выполнения этих действий существуют три версии файла
Learn_diff.txt: первая хранится в последнем коммите, вторая хранится в ин- дексе, третья хранится в рабочей директории.
Выполним команду git diff без ключей и указания имён файлов:
1 2
3 4
5 6
7 8
9 10 11 12 13 14
/c/Learn_git
(Main_branch)
$ git diff diff --git a/Learn_diff.txt b/Learn_diff.txt index c0901fb..d687242 100644
--- a/Learn_diff.txt
+++ b/Learn_diff.txt
@@ -1,3 +1,3 @@
-Файл Изменения, добавленный в индекс.
+Файл Изменения, не отслеживаемый.
Неизменяемая строка.
-Изменяемая строка: индекс.
\ No newline at end of file
+Изменяемая строка: рабочая директория.
\ No newline at end of file
Результат выполнения команды расшифровывается следующим образом.
Строка 3. Git сообщает, что будут сравниваться два файла a/Learn_diff.txt и b/Learn_diff.txt.
Строка 4. Git выводит хеш-значения сравниваемых файлов.
Замечание!
62
Не обязательно указывать в команде 40 символов хеш-значения, достаточно ука-
зать несколько первых символов (не менее 5).
Строки 5, 6. Git сообщает, что для обозначения изменений в файле a/Learn_diff.txt будет использоваться знак минус, а для обозначения измене- ний в файле b/Learn_diff.txt – знак плюс.
Строка 7. Заголовок блока описания изменений. Заголовок с двух сто- рон ограничен символами @@ и указывает, что из файла а в блок сравнения будут добавлены 3 строки, начиная с первой, и из файла b – три строки, начиная с первой.
Строки с 8 по 14. Git описывает, какие изменения были выполнены в файле а, чтобы получить файл b. Красным цветом выделены удалённые строки, зелёным цветом – добавленные строки и чёрным цветом – неизме- нившиеся строки. Текст «\ No newline at end of file» говорит о том, что строка, указанная перед ним, последняя в файле.
Таким образом, из файла a были удалены строки:
− Файл Изменения, добавленный в индекс.
− Изменяемая строка: индекс.
В файл b были добавлены строки:
− Файл Изменения, не отслеживаемый.
− Изменяемая строка: рабочая директория.
Строка «Неизменяемая строка.» присутствует в обоих файлах.
Сравнивая приведённые выше версии файлов, понимаем, что сравни- вались файлы, сохранённые в индексе и в рабочей директории.
Делаем вывод, что команда git diff без указания ключа и имён файлов сравнивает изменения в файлах, хранящихся в индексе и в рабочей дирек- тории. Если существуют несколько файлов, в которых произошли измене- ния, описания изменений будут для всех файлов.
Выполним команду
1 2 3 4 5 6 7 8 9 ... 12
git diff HEAD:
/c/Learn_git
(Main_branch)
$ git diff HEAD diff --git a/Learn_diff.txt b/Learn_diff.txt index 0f83f71..d687242 100644
--- a/Learn_diff.txt
+++ b/Learn_diff.txt
@@ -1,3 +1,3 @@
-Файл Изменения, добавленный в коммит.
+Файл Изменения, не отслеживаемый.
Неизменяемая строка.
63
-Изменяемая строка: коммит.
\ No newline at end of file
+Изменяемая строка: рабочая директория.
\ No newline at end of file
Проведя анализ результата сравнения, понимаем, что команда git diff
HEAD сравнивает файлы, сохранённые в последнем коммите и в рабочей директории.
Выполним команду
git diff –stagged:
/c/Learn_git
(Main_branch)
$ git diff --staged diff --git a/Learn_diff.txt b/Learn_diff.txt index 0f83f71..c0901fb 100644
--- a/Learn_diff.txt
+++ b/Learn_diff.txt
@@ -1,3 +1,3 @@
-Файл Изменения, добавленный в коммит.
+Файл Изменения, добавленный в индекс.
Неизменяемая строка.
-Изменяемая строка: коммит.
\ No newline at end of file
+Изменяемая строка: индекс.
\ No newline at end of file
Анализ результатов сравнения показывает, что команда git diff –staged сравнивает файлы, хранящиеся в последнем коммите и в индексе.
Приведённые три варианта команды git diff позволяют проводить сравнение файлов в пределах последнего коммита. Рассмотрим другие варианты:
− git diff HEAD^ – cравнение файлов из предпоследнего коммита и из рабочей директории;
− git diff <имя ветки1> <имя ветки 2> – сравнение файлов из коммитов, расположенных в головных коммитах указанных веток;
− git diff <имя ветки> – сравнение файлов из головного коммита текущей ветки и головного коммита ветки, указанной в команде;
− Git diff <хеш-значение 1> <хеш-значение 2> – сравнение файлов из коммитов, заданных их хеш-значениями.
Указанные команды сравнивают ВСЕ файлы, расположенные в областях, указанных в команде git diff. Если необходимо сравнить только один файл, то его имя нужно добавить в конец соответствующей команды:
− git diff <имя файла>
− git diff HEAD < имя файла >
− git diff –staged < имя файла >
64
− git diff HEAD^ < имя файла >
− git diff < имя ветки1> < имя ветки 2> < имя файла >
− git diff < имя ветки> < имя файла >
− git diff < хеш-значение 1> < хеш-значение 2> < имя файла >
Команда git show показывает содержимое указанного объекта.
Формат команды git show:
git show <объект>
В зависимости от типа объекта будет показано:
− для blob-объекта – текст сохранённого файла;
− для дерева – список объектов, входящих в дерево;
− для коммита – информация о коммите и различия между файлами из заданного в команде коммита и файлами из предшествующего коммита
(для формирования списка различий используется команда git diff).
Пример выполнения команды git show:
/c/Learn_git
(Main_branch)
$ git show commit b5fd14da49b6c21759266118fd34ea78ff51d2d2 (
HEAD ->
Main_branch
)
Author: Git_Book
Date: Mon Jul 25 21:55:20 2022 +0300
Изучение клманды git diff diff --git a/Learn_diff.txt b/Learn_diff.txt new file mode 100644 index 0000000..0f83f71
--- /dev/null
+++ b/Learn_diff.txt
@@ -0,0 +1,3 @@
+Файл Изменения, добавленный в коммит.
+Неизменяемая строка.
+Изменяемая строка: коммит.
\ No newline at end of file
В приведённом примере в команде git show не указан объект. В этом слу- чае в качестве объекта используется коммит HEAD. Файлы, содержащиеся в головном коммите, будут сравниваться с файлами из предыдущего коммита.
Поскольку в предыдущем коммите файла а не было (его хеш-значение равно
0), в качестве разницы выведено содержимое файла b (нет удалённых и неизменных строк, есть только добавленные строки).
65
2.16. Удаление файлов, команда git rm
Результат удаления файла из рабочей директории, с точки зрения Git, будет зависеть от того, в каком состоянии файл находился перед удалением.
Рассмотрим варианты.
1. Удаляемый файл находится в состоянии не отслеживаемый. Уда- ление такого файла никак не повлияет на состояние Git, поскольку Git ни- чего не «знает» об этом файле.
2. Удаляемый файл находится в состоянии отслеживаемый, не изме- нённый. При выполнении ближайшего коммита файл будет исключён из де- рева рабочей директории, но его копия будет храниться в предыдущих ком- митах. Потери информации не произойдёт.
3. Удаляемый файл находится в состоянии отслеживаемый, изменён- ный, не добавленный в индекс. Случай аналогичен рассмотренному в п. 2, за исключением того, что изменения, сделанные в файле, будут потеряны без возможности восстановления.
4. Удаляемый файл находится в состоянии отслеживаемый, изменён- ный, добавленный в индекс. Копия удалённого файла хранится в индексе и будет добавлена в ближайший коммит. Удаление файла из рабочей дирек- тории не произойдёт.
Удаление файла в ситуациях, описанных в первых трёх пунктах, не требует использования команд Git. Но необходимо быть внимательным, чтобы избежать возможных негативных последствий. Для удаления файла в четвёртой ситуации требуется специальная команда, которая позволит уда- лить файл из индекса и из рабочей директории. Последствия выполнения такой команды аналогичны последствиям действий, описанных в п. 3. Ко- манда, позволяющая удалить файл в ситуации п. 4, называется git rm.
Формат команды git rm:
git rm <ключ> <имя удаляемого файла>
Возможные значения ключа:
--forced – удаление с игнорированием всех предупреждений;
--cached – удаление файла только из индекса. Файл становится не от- слеживаемым.
66
2.17. Восстановление файлов, команда git restore
Git не оправдывал бы своё существование, если бы не позволял вос- станавливать случайно удалённые или некорректно изменённые файлы. Ра- зумеется, Git позволяет выполнять восстановление. Для этого предназна- чена команда git restore.
Формат команды git restore:
Git restore <ключ> <имя восстанавливаемого файла>
Возможные значения ключа:
--source=<коммит> – задаёт коммит, из которого будет взят восстанав- ливаемый файл;
--worktree – записать восстановленную копию в рабочую директорию
(действует как значение по умолчанию);
--staged – записывает восстановленную копию в индекс и отменяет за- пись в рабочую директорию. Для записи в индекс и в рабочую директорию необходимо указать оба ключа: –worktree и –staged.
Рассмотрим пример восстановления файла. В рабочей директории, в индексе и в последнем коммите хранится файл с именем File1.txt, содержа- щий текст «Example1_V1».
В рабочей директории изменим текст в файле File1.txt на «Text for re- store» и выполним команду git add File1.txt. После этого удалим файл
File1.txt из рабочей директории и восстановим его двумя способами.
Первый способ: восстановление с помощью команды git restore
File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File1.txt File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ cat File1.txt
Text for restore
/c/Learn_git
(Main_branch)
$ rm File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ git restore File1.txt
/c/Learn_git
(Main_branch)
$ cat File1.txt
Example1_for restore
Файл File1.txt был восстановлен из индекса.
67
Второй способ: восстановление с помощью команды: git restore –
source=HEAD File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File1.txt File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ cat File1.txt
Example1_for restore
/c/Learn_git
(Main_branch)
$ rm File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ git restore --source=HEAD File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File1.txt File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ cat File1.txt
Example1_V1
Файл File1.txt был восстановлен из коммита.
2.18. Перемещение указателя HEAD, команда git checkout
(продолжение)
Git даёт возможность не только восстанавливать файлы, но и возвра- щаться к прежним состояниям рабочей директории. Возврат может потре- боваться в следующих случаях:
1. Необходимо просмотреть файлы проекта в том состоянии, в кото- ром проект был некоторое время назад.
2. Необходимо вернуться к предыдущему состоянию проекта, со- здать ветку и проверить альтернативное решение.
Для возврата к предыдущему коммиту необходимо изменить значе- ние, записанное в файл HEAD, так как этот файл ссылается на последний коммит. Изменение содержимого файла HEAD называют перемещением указателя. Файл HEAD может содержать или имя ветки или хеш-значение.
В первом случае HEAD указывает на последний коммит в ветке, во втором случае HEAD указывает на любой коммит. На рисунке 2.11 показано состо- яние указателей при перемещении HEAD на произвольный коммит.
68
Рисунок 2.11 – Состояние “detached head”
Файл с именем ветки в директории .git/refs/heads по-прежнему содер- жит хеш-значение последнего коммита в ветке (указывает на ветку). Файл
HEAD содержит хеш-значение произвольного коммита. Такое состояние называется «Свободный указатель» (Detached head). В таком состоянии ко- манда git log выведет список не всех коммитов ветки, а только тех, которые расположены до коммита, на который указывает HEAD. Посмотреть все коммиты в ветке можно с помощью команды git log –all. Пример выполне- ния этой команды приведён ниже.
/c/Learn_git
((b5fd14d...))
$ git log --all --pretty=oneline
6aa60021e322f099c14f8008d3ea8ce3f34dc5ef (
Main_branch
)
Перемешение HEAD b5fd14da49b6c21759266118fd34ea78ff51d2d2 (
HEAD
)
Изучение клманды git diff d240a8b99799c7921a99cb059b93be6c0c6d6917
Изучение команды git diff d58ea9721672b176fdeda4a6550030a43f982d61
Test commit 2 00edc80c522d4b79c517c7f377e99a3249e7b24b
Try commit
Из примера видно, что в ветке есть два указателя: Main_branch, указы- вающий на коммит 6aa600 и HEAD, указывающий на коммит b5fd1.
Для того чтобы выйти из состояния «Свободный указатель», доста- точно выполнить команду git checkout ‘Main_branch’.
/c/Learn_git
((b5fd14d...))
$ git checkout 'Main_branch'
Previous HEAD position was b5fd14d Изучение клманды git diff
Switched to branch 'Main_branch'
Andrey@DESKTOP-1RNVJK6
MINGW64
/c/Learn_git
(Main_branch)
$ git log --all --pretty=oneline
6aa60021e322f099c14f8008d3ea8ce3f34dc5ef (
HEAD ->
Main_branch
)
Перемешение HEAD b5fd14da49b6c21759266118fd34ea78ff51d2d2
Изучение клманды git diff d240a8b99799c7921a99cb059b93be6c0c6d6917
Изучение команды git diff d58ea9721672b176fdeda4a6550030a43f982d61
Test commit 2 00edc80c522d4b79c517c7f377e99a3249e7b24b
Try commit
Теперь HEAD указывает на файл с именем ветки, который содержит хеш-значение последнего коммита в ветке (всё, как мы привыкли видеть).
Указатель HEAD можно перемещать в пределах одной ветки. Для его перемещения используется команда checkout, рассмотренная в разделе 2.12.
Для создания состояния «Свободный указатель» в этой команде вместо
69 имени ветки необходимо указать коммит. Сделать это можно либо с помо- щью прямой адресации, указав хеш-значение коммита, либо с помощью косвенной адресации, указав смещение относительно текущего значения
HTAD:
− HEAD^ – предыдущий коммит;
− HEAD – смещение назад по ветке на n коммитов.
− Рассмотрим пример выполнения команды git checkout HEAD^.
/c/Learn_git
(Main_branch)
$ git checkout HEAD^
Note: switching to 'HEAD^'.
You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command. Example: git switch -c
Or undo this operation with: git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at b5fd14d Изучение команды git diff
В результате выполнения команды был не только перемещён указа- тель HEAD, но и получены подробные рекомендации по поводу возможных действий в состоянии ‘detached HEAD’. Рассмотрим эти рекомендации.
1. Можно изучить состояние рабочей директории (look aroubd). Дело в том, что при изменении положения указателя HEAD рабочая директория восстанавливается из того коммита, на который в данный момент указывает
HEAD.
2. Можно поэкспериментировать (make experimental changes). Все изменения, внесённые в рабочую директорию, будут потеряны после вы- хода из состояния ‘detached hed’.
3. Можно сохранить результаты экспериментов, создав новую ветку,
«растущую» из коммита, на который указывает HEAD. Ветку можно создать как с помощью команды git switch –c ‘имя новой ветки’, так и с помощью команды git checkout –b ‘имя новой ветки’. Новую ветку можно создать как до начала серии коммитов, так и после окончания серии коммитов, перед выходом из состояния ‘detached head’.
70
2.19. Удаление и объединение коммитов, команда git reset
Для того чтобы обозначить коммит, с которым в данный момент ра- ботает пользователь, в Git существуют два указателя: HEAD и файл с име- нем ветки. В предыдущем разделе была рассмотрена ситуация, когда изме- няется содержимое указателя HEAD. В настоящем разделе будет рассмот- рена ситуация, когда изменяется содержимое файла с именем ветки. Для та- кого изменения используется команда git reset.
На рисунке 2.12 показано, как изменяется положение указателей по- сле применения команды git reset.
Рисунок 2.12 – Положение указателей при выполнении команды git reset
До выполнения команды git reset указатель HEAD ссылался на файл с именем ветки, а файл с именем ветки ссылался на хеш-значение последнего коммита в ветке. После выполнения команды git reset укзатель HEAD не из- менился, а файл с именем ветки стал указывать на хеш-значение коммита, который был указан в команде. Кроме того, появился ещё один указатель
ORIG_HEAD, содержащий копию хеш-значения файла с именем ветки до его изменения. Команда git reset может выполняться в двух режимах:
71
− после перемещения указателя содержимое рабочей директории не изменяется;
− после перемещения указателя рабочая директория восстанавлива- ется из коммита, на который указывает файл с именем ветки.
От того, в каком режиме выполняется команда git reset, зависит алго- ритм её применения. Рассмотрим всё по порядку.
Формат команды git reset:
git reset <ключи> <адрес коммита>
Возможные значения ключей:
--soft – команда не изменяет ни рабочую директорию, ни индекс. Все файлы останутся неизменными, но указатель будет передвинут;
--hard – будут удалены все файлы рабочей директории и индекса, по- сле чего состояние рабочей директории и индекса будет восстановлено из коммита, указанного в команде.
Адрес коммита – абсолютный или относительный адрес коммита, на который необходимо передвинуть указатель.
2.19.1. Редактирование последнего коммита
Бывают ситуации, когда после выполнения коммита обнаруживается, что в него необходимо внести изменения. Это возможно сделать с исполь- зованием команды git reset. Команда git reset –soft HEAD^ переместит указа- тель на предпоследний коммит. Восстановления состояния рабочей дирек- тории из предпоследнего коммита не произойдёт. В результате состояние архива будет таким, каким оно было непосредственно перед выполнением последнего коммита. Можно внести исправления в подготавливаемый ком- мит (отредактировать файлы, добавить/удалить файлы в/из индекса, …).
После того, как исправления будут завершены, необходимо выпол- нить команду:
git commit –c ORIG_HEAD.
Ключ –c говорит о том, что комментарий к коммиту должен быть вве- дён заново. Ключ –С позволит скопировать комментарий из исправляемого коммита.
Возникающая в результате описанных действий ситуация показана на рисунке 2.13.
72
Рисунок 2.13 – Редактирование последнего коммита
Коммит, который был исправлен, остался в архиве, на него возможно переключиться по указателю ORIG_HEAD до тех пор, пока ORIG_HEAD не поменяет своё значение после выполнения новой команды git reset. Но даже после этого к исправленному коммиту можно обратиться, если знать его хеш-значение.
Существует другой путь внесения изменений в последний коммит.
Вносятся необходимые изменения в рабочую директорию и индекс, после чего выполняется команда:
git commit –amend.
Эта команда эквивалентна последовательности команд:
git reset –soft HEAD^
git commit -C ORIG_HEAD
Если необходимо ввести новый комментарий для исправленного ком- мита, то команда будет выглядеть так:
git commit –amend –m <новый комментарий>
2.19.2. Удаление последних коммитов
Для удаления последнего коммита и возврата к предпоследнему ис- пользуется команда:
git reset –hard HEAD^
73
Произойдёт восстановление рабочей директории и индекса из предпо- следнего коммита. После внесения изменений и очередного коммита ситуа- ция будет аналогичной, изображённой на рисунке 2.13. «Удалённый» ком- мит по-прежнему доступен по ссылке ORIG_HEAD.
Для удаления нескольких коммитов используется команда:
git reset –hard HEADn
Здесь n – количество коммитов, которые необходимо удалить. Обра- титься к цепочке «удалённых» коммитов возможно по указателю
ORIG_HEAD (до тех пор, пока он не изменится).
2.19.3. Объединение коммитов
Для объединения нескольких коммитов используется команда:
git reset –soft HEADn
Здесь n – количество коммитов, которые необходимо изменить.
После выполнения команды произойдёт перемещение указателя на n коммитов назад. Состояние рабочей директории и индекса изменено не бу- дет. И рабочая директория, и индекс будут включать все изменения, кото- рые были зафиксированы в последних n коммитах, выполненных после того, на который произошло переключение.
Команда git commit –c ORIG_HEAD зафиксирует все изменения, как бы собрав последние коммиты вместе. Ключ –С обычно не применяется, так как для объединённого коммита нужен новый комментарий.
На цепочку объединённых коммитов можно переключиться с исполь- зованием указателя ORG_HEAD.
1 2 3 4 5 6 7 8 9 ... 12
git diff HEAD:
/c/Learn_git
(Main_branch)
$ git diff HEAD diff --git a/Learn_diff.txt b/Learn_diff.txt index 0f83f71..d687242 100644
--- a/Learn_diff.txt
+++ b/Learn_diff.txt
@@ -1,3 +1,3 @@
-Файл Изменения, добавленный в коммит.
+Файл Изменения, не отслеживаемый.
Неизменяемая строка.
63
-Изменяемая строка: коммит.
\ No newline at end of file
+Изменяемая строка: рабочая директория.
\ No newline at end of file
Проведя анализ результата сравнения, понимаем, что команда git diff
HEAD сравнивает файлы, сохранённые в последнем коммите и в рабочей директории.
Выполним команду
git diff –stagged:
/c/Learn_git
(Main_branch)
$ git diff --staged diff --git a/Learn_diff.txt b/Learn_diff.txt index 0f83f71..c0901fb 100644
--- a/Learn_diff.txt
+++ b/Learn_diff.txt
@@ -1,3 +1,3 @@
-Файл Изменения, добавленный в коммит.
+Файл Изменения, добавленный в индекс.
Неизменяемая строка.
-Изменяемая строка: коммит.
\ No newline at end of file
+Изменяемая строка: индекс.
\ No newline at end of file
Анализ результатов сравнения показывает, что команда git diff –staged сравнивает файлы, хранящиеся в последнем коммите и в индексе.
Приведённые три варианта команды git diff позволяют проводить сравнение файлов в пределах последнего коммита. Рассмотрим другие варианты:
− git diff HEAD^ – cравнение файлов из предпоследнего коммита и из рабочей директории;
− git diff <имя ветки1> <имя ветки 2> – сравнение файлов из коммитов, расположенных в головных коммитах указанных веток;
− git diff <имя ветки> – сравнение файлов из головного коммита текущей ветки и головного коммита ветки, указанной в команде;
− Git diff <хеш-значение 1> <хеш-значение 2> – сравнение файлов из коммитов, заданных их хеш-значениями.
Указанные команды сравнивают ВСЕ файлы, расположенные в областях, указанных в команде git diff. Если необходимо сравнить только один файл, то его имя нужно добавить в конец соответствующей команды:
− git diff <имя файла>
− git diff HEAD < имя файла >
− git diff –staged < имя файла >
64
− git diff HEAD^ < имя файла >
− git diff < имя ветки1> < имя ветки 2> < имя файла >
− git diff < имя ветки> < имя файла >
− git diff < хеш-значение 1> < хеш-значение 2> < имя файла >
Команда git show показывает содержимое указанного объекта.
Формат команды git show:
git show <объект>
В зависимости от типа объекта будет показано:
− для blob-объекта – текст сохранённого файла;
− для дерева – список объектов, входящих в дерево;
− для коммита – информация о коммите и различия между файлами из заданного в команде коммита и файлами из предшествующего коммита
(для формирования списка различий используется команда git diff).
Пример выполнения команды git show:
/c/Learn_git
(Main_branch)
$ git show commit b5fd14da49b6c21759266118fd34ea78ff51d2d2 (
HEAD ->
Main_branch
)
Author: Git_Book
Date: Mon Jul 25 21:55:20 2022 +0300
Изучение клманды git diff diff --git a/Learn_diff.txt b/Learn_diff.txt new file mode 100644 index 0000000..0f83f71
--- /dev/null
+++ b/Learn_diff.txt
@@ -0,0 +1,3 @@
+Файл Изменения, добавленный в коммит.
+Неизменяемая строка.
+Изменяемая строка: коммит.
\ No newline at end of file
В приведённом примере в команде git show не указан объект. В этом слу- чае в качестве объекта используется коммит HEAD. Файлы, содержащиеся в головном коммите, будут сравниваться с файлами из предыдущего коммита.
Поскольку в предыдущем коммите файла а не было (его хеш-значение равно
0), в качестве разницы выведено содержимое файла b (нет удалённых и неизменных строк, есть только добавленные строки).
65
2.16. Удаление файлов, команда git rm
Результат удаления файла из рабочей директории, с точки зрения Git, будет зависеть от того, в каком состоянии файл находился перед удалением.
Рассмотрим варианты.
1. Удаляемый файл находится в состоянии не отслеживаемый. Уда- ление такого файла никак не повлияет на состояние Git, поскольку Git ни- чего не «знает» об этом файле.
2. Удаляемый файл находится в состоянии отслеживаемый, не изме- нённый. При выполнении ближайшего коммита файл будет исключён из де- рева рабочей директории, но его копия будет храниться в предыдущих ком- митах. Потери информации не произойдёт.
3. Удаляемый файл находится в состоянии отслеживаемый, изменён- ный, не добавленный в индекс. Случай аналогичен рассмотренному в п. 2, за исключением того, что изменения, сделанные в файле, будут потеряны без возможности восстановления.
4. Удаляемый файл находится в состоянии отслеживаемый, изменён- ный, добавленный в индекс. Копия удалённого файла хранится в индексе и будет добавлена в ближайший коммит. Удаление файла из рабочей дирек- тории не произойдёт.
Удаление файла в ситуациях, описанных в первых трёх пунктах, не требует использования команд Git. Но необходимо быть внимательным, чтобы избежать возможных негативных последствий. Для удаления файла в четвёртой ситуации требуется специальная команда, которая позволит уда- лить файл из индекса и из рабочей директории. Последствия выполнения такой команды аналогичны последствиям действий, описанных в п. 3. Ко- манда, позволяющая удалить файл в ситуации п. 4, называется git rm.
Формат команды git rm:
git rm <ключ> <имя удаляемого файла>
Возможные значения ключа:
--forced – удаление с игнорированием всех предупреждений;
--cached – удаление файла только из индекса. Файл становится не от- слеживаемым.
66
2.17. Восстановление файлов, команда git restore
Git не оправдывал бы своё существование, если бы не позволял вос- станавливать случайно удалённые или некорректно изменённые файлы. Ра- зумеется, Git позволяет выполнять восстановление. Для этого предназна- чена команда git restore.
Формат команды git restore:
Git restore <ключ> <имя восстанавливаемого файла>
Возможные значения ключа:
--source=<коммит> – задаёт коммит, из которого будет взят восстанав- ливаемый файл;
--worktree – записать восстановленную копию в рабочую директорию
(действует как значение по умолчанию);
--staged – записывает восстановленную копию в индекс и отменяет за- пись в рабочую директорию. Для записи в индекс и в рабочую директорию необходимо указать оба ключа: –worktree и –staged.
Рассмотрим пример восстановления файла. В рабочей директории, в индексе и в последнем коммите хранится файл с именем File1.txt, содержа- щий текст «Example1_V1».
В рабочей директории изменим текст в файле File1.txt на «Text for re- store» и выполним команду git add File1.txt. После этого удалим файл
File1.txt из рабочей директории и восстановим его двумя способами.
Первый способ: восстановление с помощью команды git restore
File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File1.txt File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ cat File1.txt
Text for restore
/c/Learn_git
(Main_branch)
$ rm File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ git restore File1.txt
/c/Learn_git
(Main_branch)
$ cat File1.txt
Example1_for restore
Файл File1.txt был восстановлен из индекса.
67
Второй способ: восстановление с помощью команды: git restore –
source=HEAD File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File1.txt File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ cat File1.txt
Example1_for restore
/c/Learn_git
(Main_branch)
$ rm File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ git restore --source=HEAD File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File1.txt File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ cat File1.txt
Example1_V1
Файл File1.txt был восстановлен из коммита.
2.18. Перемещение указателя HEAD, команда git checkout
(продолжение)
Git даёт возможность не только восстанавливать файлы, но и возвра- щаться к прежним состояниям рабочей директории. Возврат может потре- боваться в следующих случаях:
1. Необходимо просмотреть файлы проекта в том состоянии, в кото- ром проект был некоторое время назад.
2. Необходимо вернуться к предыдущему состоянию проекта, со- здать ветку и проверить альтернативное решение.
Для возврата к предыдущему коммиту необходимо изменить значе- ние, записанное в файл HEAD, так как этот файл ссылается на последний коммит. Изменение содержимого файла HEAD называют перемещением указателя. Файл HEAD может содержать или имя ветки или хеш-значение.
В первом случае HEAD указывает на последний коммит в ветке, во втором случае HEAD указывает на любой коммит. На рисунке 2.11 показано состо- яние указателей при перемещении HEAD на произвольный коммит.
68
Рисунок 2.11 – Состояние “detached head”
Файл с именем ветки в директории .git/refs/heads по-прежнему содер- жит хеш-значение последнего коммита в ветке (указывает на ветку). Файл
HEAD содержит хеш-значение произвольного коммита. Такое состояние называется «Свободный указатель» (Detached head). В таком состоянии ко- манда git log выведет список не всех коммитов ветки, а только тех, которые расположены до коммита, на который указывает HEAD. Посмотреть все коммиты в ветке можно с помощью команды git log –all. Пример выполне- ния этой команды приведён ниже.
/c/Learn_git
((b5fd14d...))
$ git log --all --pretty=oneline
6aa60021e322f099c14f8008d3ea8ce3f34dc5ef (
Main_branch
)
Перемешение HEAD b5fd14da49b6c21759266118fd34ea78ff51d2d2 (
HEAD
)
Изучение клманды git diff d240a8b99799c7921a99cb059b93be6c0c6d6917
Изучение команды git diff d58ea9721672b176fdeda4a6550030a43f982d61
Test commit 2 00edc80c522d4b79c517c7f377e99a3249e7b24b
Try commit
Из примера видно, что в ветке есть два указателя: Main_branch, указы- вающий на коммит 6aa600 и HEAD, указывающий на коммит b5fd1.
Для того чтобы выйти из состояния «Свободный указатель», доста- точно выполнить команду git checkout ‘Main_branch’.
/c/Learn_git
((b5fd14d...))
$ git checkout 'Main_branch'
Previous HEAD position was b5fd14d Изучение клманды git diff
Switched to branch 'Main_branch'
Andrey@DESKTOP-1RNVJK6
MINGW64
/c/Learn_git
(Main_branch)
$ git log --all --pretty=oneline
6aa60021e322f099c14f8008d3ea8ce3f34dc5ef (
HEAD ->
Main_branch
)
Перемешение HEAD b5fd14da49b6c21759266118fd34ea78ff51d2d2
Изучение клманды git diff d240a8b99799c7921a99cb059b93be6c0c6d6917
Изучение команды git diff d58ea9721672b176fdeda4a6550030a43f982d61
Test commit 2 00edc80c522d4b79c517c7f377e99a3249e7b24b
Try commit
Теперь HEAD указывает на файл с именем ветки, который содержит хеш-значение последнего коммита в ветке (всё, как мы привыкли видеть).
Указатель HEAD можно перемещать в пределах одной ветки. Для его перемещения используется команда checkout, рассмотренная в разделе 2.12.
Для создания состояния «Свободный указатель» в этой команде вместо
69 имени ветки необходимо указать коммит. Сделать это можно либо с помо- щью прямой адресации, указав хеш-значение коммита, либо с помощью косвенной адресации, указав смещение относительно текущего значения
HTAD:
− HEAD^ – предыдущий коммит;
− HEAD – смещение назад по ветке на n коммитов.
− Рассмотрим пример выполнения команды git checkout HEAD^.
/c/Learn_git
(Main_branch)
$ git checkout HEAD^
Note: switching to 'HEAD^'.
You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command. Example: git switch -c
Or undo this operation with: git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at b5fd14d Изучение команды git diff
В результате выполнения команды был не только перемещён указа- тель HEAD, но и получены подробные рекомендации по поводу возможных действий в состоянии ‘detached HEAD’. Рассмотрим эти рекомендации.
1. Можно изучить состояние рабочей директории (look aroubd). Дело в том, что при изменении положения указателя HEAD рабочая директория восстанавливается из того коммита, на который в данный момент указывает
HEAD.
2. Можно поэкспериментировать (make experimental changes). Все изменения, внесённые в рабочую директорию, будут потеряны после вы- хода из состояния ‘detached hed’.
3. Можно сохранить результаты экспериментов, создав новую ветку,
«растущую» из коммита, на который указывает HEAD. Ветку можно создать как с помощью команды git switch –c ‘имя новой ветки’, так и с помощью команды git checkout –b ‘имя новой ветки’. Новую ветку можно создать как до начала серии коммитов, так и после окончания серии коммитов, перед выходом из состояния ‘detached head’.
70
2.19. Удаление и объединение коммитов, команда git reset
Для того чтобы обозначить коммит, с которым в данный момент ра- ботает пользователь, в Git существуют два указателя: HEAD и файл с име- нем ветки. В предыдущем разделе была рассмотрена ситуация, когда изме- няется содержимое указателя HEAD. В настоящем разделе будет рассмот- рена ситуация, когда изменяется содержимое файла с именем ветки. Для та- кого изменения используется команда git reset.
На рисунке 2.12 показано, как изменяется положение указателей по- сле применения команды git reset.
Рисунок 2.12 – Положение указателей при выполнении команды git reset
До выполнения команды git reset указатель HEAD ссылался на файл с именем ветки, а файл с именем ветки ссылался на хеш-значение последнего коммита в ветке. После выполнения команды git reset укзатель HEAD не из- менился, а файл с именем ветки стал указывать на хеш-значение коммита, который был указан в команде. Кроме того, появился ещё один указатель
ORIG_HEAD, содержащий копию хеш-значения файла с именем ветки до его изменения. Команда git reset может выполняться в двух режимах:
71
− после перемещения указателя содержимое рабочей директории не изменяется;
− после перемещения указателя рабочая директория восстанавлива- ется из коммита, на который указывает файл с именем ветки.
От того, в каком режиме выполняется команда git reset, зависит алго- ритм её применения. Рассмотрим всё по порядку.
Формат команды git reset:
git reset <ключи> <адрес коммита>
Возможные значения ключей:
--soft – команда не изменяет ни рабочую директорию, ни индекс. Все файлы останутся неизменными, но указатель будет передвинут;
--hard – будут удалены все файлы рабочей директории и индекса, по- сле чего состояние рабочей директории и индекса будет восстановлено из коммита, указанного в команде.
Адрес коммита – абсолютный или относительный адрес коммита, на который необходимо передвинуть указатель.
2.19.1. Редактирование последнего коммита
Бывают ситуации, когда после выполнения коммита обнаруживается, что в него необходимо внести изменения. Это возможно сделать с исполь- зованием команды git reset. Команда git reset –soft HEAD^ переместит указа- тель на предпоследний коммит. Восстановления состояния рабочей дирек- тории из предпоследнего коммита не произойдёт. В результате состояние архива будет таким, каким оно было непосредственно перед выполнением последнего коммита. Можно внести исправления в подготавливаемый ком- мит (отредактировать файлы, добавить/удалить файлы в/из индекса, …).
После того, как исправления будут завершены, необходимо выпол- нить команду:
git commit –c ORIG_HEAD.
Ключ –c говорит о том, что комментарий к коммиту должен быть вве- дён заново. Ключ –С позволит скопировать комментарий из исправляемого коммита.
Возникающая в результате описанных действий ситуация показана на рисунке 2.13.
72
Рисунок 2.13 – Редактирование последнего коммита
Коммит, который был исправлен, остался в архиве, на него возможно переключиться по указателю ORIG_HEAD до тех пор, пока ORIG_HEAD не поменяет своё значение после выполнения новой команды git reset. Но даже после этого к исправленному коммиту можно обратиться, если знать его хеш-значение.
Существует другой путь внесения изменений в последний коммит.
Вносятся необходимые изменения в рабочую директорию и индекс, после чего выполняется команда:
git commit –amend.
Эта команда эквивалентна последовательности команд:
git reset –soft HEAD^
git commit -C ORIG_HEAD
Если необходимо ввести новый комментарий для исправленного ком- мита, то команда будет выглядеть так:
git commit –amend –m <новый комментарий>
2.19.2. Удаление последних коммитов
Для удаления последнего коммита и возврата к предпоследнему ис- пользуется команда:
git reset –hard HEAD^
73
Произойдёт восстановление рабочей директории и индекса из предпо- следнего коммита. После внесения изменений и очередного коммита ситуа- ция будет аналогичной, изображённой на рисунке 2.13. «Удалённый» ком- мит по-прежнему доступен по ссылке ORIG_HEAD.
Для удаления нескольких коммитов используется команда:
git reset –hard HEADn
Здесь n – количество коммитов, которые необходимо удалить. Обра- титься к цепочке «удалённых» коммитов возможно по указателю
ORIG_HEAD (до тех пор, пока он не изменится).
2.19.3. Объединение коммитов
Для объединения нескольких коммитов используется команда:
git reset –soft HEADn
Здесь n – количество коммитов, которые необходимо изменить.
После выполнения команды произойдёт перемещение указателя на n коммитов назад. Состояние рабочей директории и индекса изменено не бу- дет. И рабочая директория, и индекс будут включать все изменения, кото- рые были зафиксированы в последних n коммитах, выполненных после того, на который произошло переключение.
Команда git commit –c ORIG_HEAD зафиксирует все изменения, как бы собрав последние коммиты вместе. Ключ –С обычно не применяется, так как для объединённого коммита нужен новый комментарий.
На цепочку объединённых коммитов можно переключиться с исполь- зованием указателя ORG_HEAD.
1 2 3 4 5 6 7 8 9 ... 12
63
-Изменяемая строка: коммит.
\ No newline at end of file
+Изменяемая строка: рабочая директория.
\ No newline at end of file
Проведя анализ результата сравнения, понимаем, что команда git diff
HEAD сравнивает файлы, сохранённые в последнем коммите и в рабочей директории.
Выполним команду
git diff –stagged:
/c/Learn_git
(Main_branch)
$ git diff --staged diff --git a/Learn_diff.txt b/Learn_diff.txt index 0f83f71..c0901fb 100644
--- a/Learn_diff.txt
+++ b/Learn_diff.txt
@@ -1,3 +1,3 @@
-Файл Изменения, добавленный в коммит.
+Файл Изменения, добавленный в индекс.
Неизменяемая строка.
-Изменяемая строка: коммит.
\ No newline at end of file
+Изменяемая строка: индекс.
\ No newline at end of file
Анализ результатов сравнения показывает, что команда git diff –staged сравнивает файлы, хранящиеся в последнем коммите и в индексе.
Приведённые три варианта команды git diff позволяют проводить сравнение файлов в пределах последнего коммита. Рассмотрим другие варианты:
− git diff HEAD^ – cравнение файлов из предпоследнего коммита и из рабочей директории;
− git diff <имя ветки1> <имя ветки 2> – сравнение файлов из коммитов, расположенных в головных коммитах указанных веток;
− git diff <имя ветки> – сравнение файлов из головного коммита текущей ветки и головного коммита ветки, указанной в команде;
− Git diff <хеш-значение 1> <хеш-значение 2> – сравнение файлов из коммитов, заданных их хеш-значениями.
Указанные команды сравнивают ВСЕ файлы, расположенные в областях, указанных в команде git diff. Если необходимо сравнить только один файл, то его имя нужно добавить в конец соответствующей команды:
− git diff <имя файла>
− git diff HEAD < имя файла >
− git diff –staged < имя файла >
64
− git diff HEAD^ < имя файла >
− git diff < имя ветки1> < имя ветки 2> < имя файла >
− git diff < имя ветки> < имя файла >
− git diff < хеш-значение 1> < хеш-значение 2> < имя файла >
Команда git show показывает содержимое указанного объекта.
Формат команды git show:
git show <объект>
В зависимости от типа объекта будет показано:
− для blob-объекта – текст сохранённого файла;
− для дерева – список объектов, входящих в дерево;
− для коммита – информация о коммите и различия между файлами из заданного в команде коммита и файлами из предшествующего коммита
(для формирования списка различий используется команда git diff).
Пример выполнения команды git show:
/c/Learn_git
(Main_branch)
$ git show commit b5fd14da49b6c21759266118fd34ea78ff51d2d2 (
HEAD ->
Main_branch
)
Author: Git_Book
Date: Mon Jul 25 21:55:20 2022 +0300
Изучение клманды git diff diff --git a/Learn_diff.txt b/Learn_diff.txt new file mode 100644 index 0000000..0f83f71
--- /dev/null
+++ b/Learn_diff.txt
@@ -0,0 +1,3 @@
+Файл Изменения, добавленный в коммит.
+Неизменяемая строка.
+Изменяемая строка: коммит.
\ No newline at end of file
В приведённом примере в команде git show не указан объект. В этом слу- чае в качестве объекта используется коммит HEAD. Файлы, содержащиеся в головном коммите, будут сравниваться с файлами из предыдущего коммита.
Поскольку в предыдущем коммите файла а не было (его хеш-значение равно
0), в качестве разницы выведено содержимое файла b (нет удалённых и неизменных строк, есть только добавленные строки).
65
2.16. Удаление файлов, команда git rm
Результат удаления файла из рабочей директории, с точки зрения Git, будет зависеть от того, в каком состоянии файл находился перед удалением.
Рассмотрим варианты.
1. Удаляемый файл находится в состоянии не отслеживаемый. Уда- ление такого файла никак не повлияет на состояние Git, поскольку Git ни- чего не «знает» об этом файле.
2. Удаляемый файл находится в состоянии отслеживаемый, не изме- нённый. При выполнении ближайшего коммита файл будет исключён из де- рева рабочей директории, но его копия будет храниться в предыдущих ком- митах. Потери информации не произойдёт.
3. Удаляемый файл находится в состоянии отслеживаемый, изменён- ный, не добавленный в индекс. Случай аналогичен рассмотренному в п. 2, за исключением того, что изменения, сделанные в файле, будут потеряны без возможности восстановления.
4. Удаляемый файл находится в состоянии отслеживаемый, изменён- ный, добавленный в индекс. Копия удалённого файла хранится в индексе и будет добавлена в ближайший коммит. Удаление файла из рабочей дирек- тории не произойдёт.
Удаление файла в ситуациях, описанных в первых трёх пунктах, не требует использования команд Git. Но необходимо быть внимательным, чтобы избежать возможных негативных последствий. Для удаления файла в четвёртой ситуации требуется специальная команда, которая позволит уда- лить файл из индекса и из рабочей директории. Последствия выполнения такой команды аналогичны последствиям действий, описанных в п. 3. Ко- манда, позволяющая удалить файл в ситуации п. 4, называется git rm.
Формат команды git rm:
git rm <ключ> <имя удаляемого файла>
Возможные значения ключа:
--forced – удаление с игнорированием всех предупреждений;
--cached – удаление файла только из индекса. Файл становится не от- слеживаемым.
66
2.17. Восстановление файлов, команда git restore
Git не оправдывал бы своё существование, если бы не позволял вос- станавливать случайно удалённые или некорректно изменённые файлы. Ра- зумеется, Git позволяет выполнять восстановление. Для этого предназна- чена команда git restore.
Формат команды git restore:
Git restore <ключ> <имя восстанавливаемого файла>
Возможные значения ключа:
--source=<коммит> – задаёт коммит, из которого будет взят восстанав- ливаемый файл;
--worktree – записать восстановленную копию в рабочую директорию
(действует как значение по умолчанию);
--staged – записывает восстановленную копию в индекс и отменяет за- пись в рабочую директорию. Для записи в индекс и в рабочую директорию необходимо указать оба ключа: –worktree и –staged.
Рассмотрим пример восстановления файла. В рабочей директории, в индексе и в последнем коммите хранится файл с именем File1.txt, содержа- щий текст «Example1_V1».
В рабочей директории изменим текст в файле File1.txt на «Text for re- store» и выполним команду git add File1.txt. После этого удалим файл
File1.txt из рабочей директории и восстановим его двумя способами.
Первый способ: восстановление с помощью команды git restore
File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File1.txt File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ cat File1.txt
Text for restore
/c/Learn_git
(Main_branch)
$ rm File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ git restore File1.txt
/c/Learn_git
(Main_branch)
$ cat File1.txt
Example1_for restore
Файл File1.txt был восстановлен из индекса.
67
Второй способ: восстановление с помощью команды: git restore –
source=HEAD File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File1.txt File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ cat File1.txt
Example1_for restore
/c/Learn_git
(Main_branch)
$ rm File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ git restore --source=HEAD File1.txt
/c/Learn_git
(Main_branch)
$ dir
Dir1 File1.txt File2.txt Learn_diff.txt
/c/Learn_git
(Main_branch)
$ cat File1.txt
Example1_V1
Файл File1.txt был восстановлен из коммита.
2.18. Перемещение указателя HEAD, команда git checkout
(продолжение)
Git даёт возможность не только восстанавливать файлы, но и возвра- щаться к прежним состояниям рабочей директории. Возврат может потре- боваться в следующих случаях:
1. Необходимо просмотреть файлы проекта в том состоянии, в кото- ром проект был некоторое время назад.
2. Необходимо вернуться к предыдущему состоянию проекта, со- здать ветку и проверить альтернативное решение.
Для возврата к предыдущему коммиту необходимо изменить значе- ние, записанное в файл HEAD, так как этот файл ссылается на последний коммит. Изменение содержимого файла HEAD называют перемещением указателя. Файл HEAD может содержать или имя ветки или хеш-значение.
В первом случае HEAD указывает на последний коммит в ветке, во втором случае HEAD указывает на любой коммит. На рисунке 2.11 показано состо- яние указателей при перемещении HEAD на произвольный коммит.
68
Рисунок 2.11 – Состояние “detached head”
Файл с именем ветки в директории .git/refs/heads по-прежнему содер- жит хеш-значение последнего коммита в ветке (указывает на ветку). Файл
HEAD содержит хеш-значение произвольного коммита. Такое состояние называется «Свободный указатель» (Detached head). В таком состоянии ко- манда git log выведет список не всех коммитов ветки, а только тех, которые расположены до коммита, на который указывает HEAD. Посмотреть все коммиты в ветке можно с помощью команды git log –all. Пример выполне- ния этой команды приведён ниже.
/c/Learn_git
((b5fd14d...))
$ git log --all --pretty=oneline
6aa60021e322f099c14f8008d3ea8ce3f34dc5ef (
Main_branch
)
Перемешение HEAD b5fd14da49b6c21759266118fd34ea78ff51d2d2 (
HEAD
)
Изучение клманды git diff d240a8b99799c7921a99cb059b93be6c0c6d6917
Изучение команды git diff d58ea9721672b176fdeda4a6550030a43f982d61
Test commit 2 00edc80c522d4b79c517c7f377e99a3249e7b24b
Try commit
Из примера видно, что в ветке есть два указателя: Main_branch, указы- вающий на коммит 6aa600 и HEAD, указывающий на коммит b5fd1.
Для того чтобы выйти из состояния «Свободный указатель», доста- точно выполнить команду git checkout ‘Main_branch’.
/c/Learn_git
((b5fd14d...))
$ git checkout 'Main_branch'
Previous HEAD position was b5fd14d Изучение клманды git diff
Switched to branch 'Main_branch'
Andrey@DESKTOP-1RNVJK6
MINGW64
/c/Learn_git
(Main_branch)
$ git log --all --pretty=oneline
6aa60021e322f099c14f8008d3ea8ce3f34dc5ef (
HEAD ->
Main_branch
)
Перемешение HEAD b5fd14da49b6c21759266118fd34ea78ff51d2d2
Изучение клманды git diff d240a8b99799c7921a99cb059b93be6c0c6d6917
Изучение команды git diff d58ea9721672b176fdeda4a6550030a43f982d61
Test commit 2 00edc80c522d4b79c517c7f377e99a3249e7b24b
Try commit
Теперь HEAD указывает на файл с именем ветки, который содержит хеш-значение последнего коммита в ветке (всё, как мы привыкли видеть).
Указатель HEAD можно перемещать в пределах одной ветки. Для его перемещения используется команда checkout, рассмотренная в разделе 2.12.
Для создания состояния «Свободный указатель» в этой команде вместо
69 имени ветки необходимо указать коммит. Сделать это можно либо с помо- щью прямой адресации, указав хеш-значение коммита, либо с помощью косвенной адресации, указав смещение относительно текущего значения
HTAD:
− HEAD^ – предыдущий коммит;
− HEAD
− Рассмотрим пример выполнения команды git checkout HEAD^.
/c/Learn_git
(Main_branch)
$ git checkout HEAD^
Note: switching to 'HEAD^'.
You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command. Example: git switch -c
Or undo this operation with: git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at b5fd14d Изучение команды git diff
В результате выполнения команды был не только перемещён указа- тель HEAD, но и получены подробные рекомендации по поводу возможных действий в состоянии ‘detached HEAD’. Рассмотрим эти рекомендации.
1. Можно изучить состояние рабочей директории (look aroubd). Дело в том, что при изменении положения указателя HEAD рабочая директория восстанавливается из того коммита, на который в данный момент указывает
HEAD.
2. Можно поэкспериментировать (make experimental changes). Все изменения, внесённые в рабочую директорию, будут потеряны после вы- хода из состояния ‘detached hed’.
3. Можно сохранить результаты экспериментов, создав новую ветку,
«растущую» из коммита, на который указывает HEAD. Ветку можно создать как с помощью команды git switch –c ‘имя новой ветки’, так и с помощью команды git checkout –b ‘имя новой ветки’. Новую ветку можно создать как до начала серии коммитов, так и после окончания серии коммитов, перед выходом из состояния ‘detached head’.
70
2.19. Удаление и объединение коммитов, команда git reset
Для того чтобы обозначить коммит, с которым в данный момент ра- ботает пользователь, в Git существуют два указателя: HEAD и файл с име- нем ветки. В предыдущем разделе была рассмотрена ситуация, когда изме- няется содержимое указателя HEAD. В настоящем разделе будет рассмот- рена ситуация, когда изменяется содержимое файла с именем ветки. Для та- кого изменения используется команда git reset.
На рисунке 2.12 показано, как изменяется положение указателей по- сле применения команды git reset.
Рисунок 2.12 – Положение указателей при выполнении команды git reset
До выполнения команды git reset указатель HEAD ссылался на файл с именем ветки, а файл с именем ветки ссылался на хеш-значение последнего коммита в ветке. После выполнения команды git reset укзатель HEAD не из- менился, а файл с именем ветки стал указывать на хеш-значение коммита, который был указан в команде. Кроме того, появился ещё один указатель
ORIG_HEAD, содержащий копию хеш-значения файла с именем ветки до его изменения. Команда git reset может выполняться в двух режимах:
71
− после перемещения указателя содержимое рабочей директории не изменяется;
− после перемещения указателя рабочая директория восстанавлива- ется из коммита, на который указывает файл с именем ветки.
От того, в каком режиме выполняется команда git reset, зависит алго- ритм её применения. Рассмотрим всё по порядку.
Формат команды git reset:
git reset <ключи> <адрес коммита>
Возможные значения ключей:
--soft – команда не изменяет ни рабочую директорию, ни индекс. Все файлы останутся неизменными, но указатель будет передвинут;
--hard – будут удалены все файлы рабочей директории и индекса, по- сле чего состояние рабочей директории и индекса будет восстановлено из коммита, указанного в команде.
Адрес коммита – абсолютный или относительный адрес коммита, на который необходимо передвинуть указатель.
2.19.1. Редактирование последнего коммита
Бывают ситуации, когда после выполнения коммита обнаруживается, что в него необходимо внести изменения. Это возможно сделать с исполь- зованием команды git reset. Команда git reset –soft HEAD^ переместит указа- тель на предпоследний коммит. Восстановления состояния рабочей дирек- тории из предпоследнего коммита не произойдёт. В результате состояние архива будет таким, каким оно было непосредственно перед выполнением последнего коммита. Можно внести исправления в подготавливаемый ком- мит (отредактировать файлы, добавить/удалить файлы в/из индекса, …).
После того, как исправления будут завершены, необходимо выпол- нить команду:
git commit –c ORIG_HEAD.
Ключ –c говорит о том, что комментарий к коммиту должен быть вве- дён заново. Ключ –С позволит скопировать комментарий из исправляемого коммита.
Возникающая в результате описанных действий ситуация показана на рисунке 2.13.
72
Рисунок 2.13 – Редактирование последнего коммита
Коммит, который был исправлен, остался в архиве, на него возможно переключиться по указателю ORIG_HEAD до тех пор, пока ORIG_HEAD не поменяет своё значение после выполнения новой команды git reset. Но даже после этого к исправленному коммиту можно обратиться, если знать его хеш-значение.
Существует другой путь внесения изменений в последний коммит.
Вносятся необходимые изменения в рабочую директорию и индекс, после чего выполняется команда:
git commit –amend.
Эта команда эквивалентна последовательности команд:
git reset –soft HEAD^
git commit -C ORIG_HEAD
Если необходимо ввести новый комментарий для исправленного ком- мита, то команда будет выглядеть так:
git commit –amend –m <новый комментарий>
2.19.2. Удаление последних коммитов
Для удаления последнего коммита и возврата к предпоследнему ис- пользуется команда:
git reset –hard HEAD^
73
Произойдёт восстановление рабочей директории и индекса из предпо- следнего коммита. После внесения изменений и очередного коммита ситуа- ция будет аналогичной, изображённой на рисунке 2.13. «Удалённый» ком- мит по-прежнему доступен по ссылке ORIG_HEAD.
Для удаления нескольких коммитов используется команда:
git reset –hard HEADn
Здесь n – количество коммитов, которые необходимо удалить. Обра- титься к цепочке «удалённых» коммитов возможно по указателю
ORIG_HEAD (до тех пор, пока он не изменится).
2.19.3. Объединение коммитов
Для объединения нескольких коммитов используется команда:
git reset –soft HEADn
Здесь n – количество коммитов, которые необходимо изменить.
После выполнения команды произойдёт перемещение указателя на n коммитов назад. Состояние рабочей директории и индекса изменено не бу- дет. И рабочая директория, и индекс будут включать все изменения, кото- рые были зафиксированы в последних n коммитах, выполненных после того, на который произошло переключение.
Команда git commit –c ORIG_HEAD зафиксирует все изменения, как бы собрав последние коммиты вместе. Ключ –С обычно не применяется, так как для объединённого коммита нужен новый комментарий.
На цепочку объединённых коммитов можно переключиться с исполь- зованием указателя ORG_HEAD.
1 2 3 4 5 6 7 8 9 ... 12
2.19.4. Отмена действия команды git reset
Если по какой-то причине была выполнена нежелательная команда git reset, то все изменения можно отменить, выполнив команду: git reset ORIG_HEAD
Отмена возможна, пока не будет выполнена ещё одна команда git re- set, которая изменит значение указателя ORIG_HEAD.
2.20. Удаление веток
74
Ветки – это, как правило, «черновики». Они нужны, чтобы что-то про- верить, разобраться в сложной ситуации, создать временную «заплатку» и т.п. После завершения работы с веткой её, как правило, удаляют. Бывают и исключения. Например, ветка, в которой готовится новый релиз, имеет шанс не только остаться в репозитории, но со временем стать основной. Но всё- таки ветки часто удаляют.
Для удаления ветки используется одна из двух команд:
− git branch –d <имя ветки>;
− git branch –D <имя ветки>.
Команда с ключом –d удалит ветку только в том случае, если она была слита с другой (про слияние см. следующий раздел). Если слияние не вы- полнялось, то Git остановит операцию удаления ветки.
Команда с ключом –D удалит ветку без проверки, была ли она слита с другой или нет.
В Git ничего не теряется и не удаляется безвозвратно. Единожды попав в директорию .git/objects, объект остаётся в ней на всё время существования ре- позитория. Для обращения к объекту необходимо знать несколько первых цифр его хеш-значения. Ветка представляет собой цепочку коммитов, каждый из ко- торых ссылается на предыдущий. Попасть на «удалённую» ветку можно, зная хеш-значение её последнего коммита и указав его в команде git checkout <хеш-
значение>. После выполнения такой команды возникнет состояние «Свобод- ный указатель» (detached head), разобранное в разделе 2.18.
Необходимо помнить, что все изменения, не добавленные в коммит, при удалении ветки будут потеряны, так как они не попадут ни в один из объектов в директории .git/objects.
2.21. Слияние веток, принципы выполнения операции
При создании репозитория в нём существует одна ветка, которой можно присвоить имя в ходе выполнения команды git init. Это главная ветка проекта.
Исходя из практики использования Git, в главной ветке сохраняется только про- веренный код. Выполнение этого кода не должно приводить к проблемам, ко- торые необходимо решать в срочном порядке (наличие некритичных проблем, в некоторых случаях, допускается). При такой технологии работы над проек- том, для разработки кода каждого модуля создаётся отдельная ветка (назовём её
75 рабочей веткой модуля). В процессе написания кода модуля все коммиты со- храняются только в рабочей ветке модуля. После того, как модуль разработан и протестирован, финальная версия кода (финальный коммит) добавляется в глав- ную ветку. Структура репозитория, соответствующая описанной технологии работы над проектом, показана на рисунке 2.14.
Рисунок 2.14 – Структура проекта при использовании веток
На рисунке 2.14 термин «рабочая ветка» заменён на термин «целевая ветка». В рабочей ветке модуля могут быть дополнительные ответвления, например, для разработки кода функции. Принцип остаётся неизменным.
Код создаётся в рабочей ветке функции, тестируется и сливается с рабочей веткой модуля. В общем случае ветка, в которой хранится финальный код, называется целевой. Последней целевой веткой в этой иерархической струк- туре является главная ветка.
В ситуации (рисунки 2.14а и 2.14б) слияние будет происходить по- разному. В Git существуют два термина для обозначения слияния.
1. Явное слияние выполняется в случае, когда в целевой ветке были коммиты после создания рабочей ветки (рисунок 2.14б).
2. Неявное слияние выполняется в случае, когда в целевой ветке не
76 было коммитов после создания рабочей ветки (рисунок 2.14а).
При выполнении неявного слияния указатель целевой ветки перено- сится на последний коммит рабочей ветки (рисунок 2.15а). Новый коммит не создаётся.
Рисунок 2.15 – Явное и неявное слияние
При выполнении явного слияния создаётся новый коммит, у которого два родителя (рисунок 2.15б). Такой коммит обозначается термином merge- commit. Сделать возврат из merge-commit сложно, поэтому его нужно созда- вать, когда вероятность наличия ошибок в коде рабочей ветки практически исключена.
Возможность выполнения явного и неявного слияния ограничена:
− неявное слияние возможно создать, только если в целевой ветке не выполнялись коммиты, после присоединения к ней рабочей ветки. Это огра- ничение снять невозможно;
− явное слияние возможно выполнить, если отсутствуют конфликты.
77
Под конфликтом понимается ситуация, когда в рабочей и целевой ветке при- сутствуют файлы с одинаковым именем и различным содержанием. Кон- фликт возможно устранить, поэтому явное слияние, в отличие от неявного, можно выполнить всегда (приложив дополнительные усилия, если требу- ется устранить конфликт).
2.22. Слияние веток, команда git merge
Команда git merge выполняет слияние веток.
Формат команды: git merge <ключи> <рабочая ветка>
При слиянии текущей веткой должна быть целевая. По логике Git из- менения, сделанные в рабочей ветке, «вливаются» в целевую.
Возможные значения ключей:
--ff – выполнить неявное слияние, если возможно. Другое название яв- ного слияния fast-foreard;
--no-ff – отключить возможность неявного слияния, выполнять явное слияние;
--ff-only – выполнять только неявное слияние, если это невозможно, то остановить выполнение команды git merge.
--strategy – стратегия выполнения явного слияния. Git поддерживает следующие стратегии слияния:
− resolve;
− recursive;
− ours;
− octopus;
− subtree.
Неявное слияние не вызывает никаких сложностей, оно или выполня- ется, или не выполняется. С явным слиянием не так просто. Сложности та- ятся в стратегии выполнения слияния. По умолчанию используется страте- гия resolve, и большинство пользователей Git это устраивает. Рассмотрим эту стратегию, а с остальными читатель сможет познакомиться самостоя- тельно (например, в статье А. Прокопюк, 2013).