Найти в Дзене
ZDG

Git на практике #2. Ветки

Предыдущая часть: В предыдущей части мы остановились на вопросе, как вернуться в один из предыдущих коммитов. Для более чёткого понимания нужно ознакомиться с концепцией головы (HEAD). Когда дерево растёт, его ствол удлиняется. На вершине ствола есть точка роста, из которой появляется новый побег. Эта точка роста называется "головой". Голова это состояние после самого последнего коммита в ветке. Обратите внимание, что вы находитесь не в коммите №3, а после него. Коммит уже завершён, его состояние зафиксировано. Все новые изменения накапливаются в голове и затем становятся новым коммитом. Таким образом, ветка постоянно растёт из головы. Номера коммитов Рассмотрим ситуации, когда требуется вернуться назад: Во всех случаях нам понадобится номер коммита, чтобы обратиться к нему. Список коммитов можно получить командой git log. Она простая, но по умолчанию выдаёт подробный список с датами и авторами коммитов, и чтобы сделать список более компактным, я напишу: git log --oneline В этом списке
Оглавление

Предыдущая часть:

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

Для более чёткого понимания нужно ознакомиться с концепцией головы (HEAD).

Когда дерево растёт, его ствол удлиняется. На вершине ствола есть точка роста, из которой появляется новый побег. Эта точка роста называется "головой".

Голова это состояние после самого последнего коммита в ветке.

-2

Обратите внимание, что вы находитесь не в коммите №3, а после него. Коммит уже завершён, его состояние зафиксировано. Все новые изменения накапливаются в голове и затем становятся новым коммитом. Таким образом, ветка постоянно растёт из головы.

Номера коммитов

Рассмотрим ситуации, когда требуется вернуться назад:

  • Оставить проект как есть, но вернуть один из старых файлов
  • Откатить весь проект назад и продолжить работу оттуда

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

Список коммитов можно получить командой git log. Она простая, но по умолчанию выдаёт подробный список с датами и авторами коммитов, и чтобы сделать список более компактным, я напишу:

git log --oneline

-3

В этом списке присутствуют все коммиты, сделанные в текущей ветке. У каждого из них есть комментарий и номер в виде 16-ричного числа. На самом деле номер гораздо длиннее, но сокращённой формы достаточно.

Основываясь на комментариях, мы выбираем один из коммитов и копируем его номер.

Обратите внимание, что я делал два коммита с одинаковым комментарием "new changes", и теперь не знаю, что в каком коммите было. Именно поэтому комментарии нужно писать содержательные.

Допустим, я выбрал коммит "Initial commit" с номером c857444.

Восстановление файлов

Предположим, в текущем проекте я запорол файл models/NoteModel.php, и откатить изменения обычным способом не могу, потому что они были сделаны много коммитов назад. Я хочу вернуть файлу то состояние, которое было в коммите c857444.

Если файл изменён в голове, восстанавливать его нельзя, потому что изменения пропадут, чего Git допустить не может. Поэтому сначала нужно или закоммитить изменения, или отменить их.

После этого файл можно восстанавливать:

git checkout c857 models/NoteModel.php

-4

В результате текущий файл models/NoteModel.php заменился файлом из коммита c857444.

Номер коммита не обязательно писать полностью. Достаточно написать первые несколько цифр.

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

git checkout c857 .

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

-5

Краткий итог: Да, файлы восстановлены. Задача решена.

Переход в другой коммит

Теперь рассмотрим случай, когда мы восстанавливаем файлы путём перехода в старый коммит. Для этого нужна та же самая команда, но вообще без указаний на файлы:

git checkout c857

Разница с предыдущим случаем в том, что мы не тянем файлы из коммита к себе, а сами переходим в тот коммит. Теперь наше состояние выглядит так:

-6

Для чего нужна такая операция?

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

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

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

-7

You are in 'detached HEAD' state – вы находитесь в состоянии "отвалившейся головы". Вы можете делать изменения и коммитить их, а также отменить все изменения, переключившись назад в ветку.

Что случилось с головой? Она была на вершине ветки, но после того как вы перешли в старый коммит, она перешла туда тоже. То есть "отвалилась"
с вершины.

Об этом сообщает Git:

-8

Фактически наше состояние выглядит так:

-9

Если внести какие-то изменения и сделать коммит, то он попадёт в эту голову, а не в вершину ветки.

-10

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

Для понимания: нет никакой разницы между ветками. Главная считается главной только потому, что надо было с чего-то начинать.

Переключение между ветками

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

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

git switch main

также можно писать в старом стиле:

git checkout main

И вот голова снова на вершине главной ветки.

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

Если мы попробуем снова переключиться в коммит c857 "Initial commit", то обнаружим, что создали ещё одну ветку с отвалившейся головой, в которой нет коммита "test commit".

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

Но на самом деле Git знает про эту ситуацию, и когда мы переключались в ветку main, он написал:

-11

У коммита test commit есть номер ce984ce, и если мы хотим создать нормальную ветку на основе этого коммита, и назвать её, допустим, test, то можем дать команду

git branch test ce984ce

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

Я пока не буду это делать, а просто переключусь в коммит ce984ce и посмотрю, что будет.

-12

Голова перешла на выбранный коммит. Но интереснее другое – это опять новая ветка, или всё-таки та, которую мы создали ранее? Посмотрим git log:

-13

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

Поэтому, конечно, гораздо проще сделать именованную ветку. И сделать её сразу.

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

-14

Команда git branch test создаёт ветку test от текущей позиции в дереве, а git switch test переключается в эту ветку. Всё, дальше мы как обычно работаем в ветке – вносим изменения и коммитим их.

В следующий раз обсудим слияние веток.

Читайте дальше:

Git на практике #3. Слияние веток