Git checkout: как работать с ветками
Команда checkout
в системе контроля версий Git отвечает за переключение между отдельными ветками репозитория.
Каждое переключение обновляет файлы в рабочем каталоге на основе тех данных, которые хранятся в выбранной ветке. При этом каждый последующий коммит автоматически добавляется в активную ветку, выбранную ранее с помощью команды checkout
.
В этом руководстве будут рассмотрены возможные пути использования как команды git checkout
, так и ряда других сопутствующих команд (git branch
, git reflog
, git remote show
), которые позволяют взаимодействовать с ветками как локального, так и удаленного репозитория.
1. Создание репозитория
Сперва подготовим отдельный каталог для тестового проекта в Git:
mkdir project
После чего перейдем в него:
cd project
Наконец, выполним инициализацию репозитория Git:
git init
После этого нам станут доступны все команды по управлению как ветками, так и репозиторием в целом.
2. Создание файла и выполнение коммита
Чтобы понять, как переключение веток влияет на рабочий каталог (и репозиторий целикоv), мы подготовим импровизированный файл исходника проекта с тривиальным содержимым внутри:
sudo nano file_m
Наполнение файла будет следующим:
file in master
Давайте проверим состояние рабочего каталога:
ls
Существует только один файл:
file_m
Теперь проиндексируем изменения:
git add file_m
После чего выполним коммит:
git commit -m "First commit"
По ходу этого руководства мы будем наблюдать за тем, как работа с ветками влияет на содержимое рабочего каталога — в частности на файлы, которые мы создаем или редактируем.
3. Создание новой ветки
Предположим, мы решили внедрить в наш проект новую фичу, но пока что до конца не уверены в ее необходимости или эффективности. По сути, мы хотим проверить некую гипотезу с возможностью отката изменений до основной стабильной версии проекта.
В этих целях Git предоставляет возможность создания отдельных веток и переключения между ними. Таким образом мы всегда сможем проверить работоспособность проекта как с фичей, так и без нее.
Однако для начала давайте узнаем, на какой ветке мы находимся сейчас:
git branch
В консоли появится вывод с подсвеченной активной веткой master
:
* master
Именно в эту ветку мы совершили предыдущий коммит. Это значит, что в этой ветке находился ранее созданный файл file_m
.
Теперь мы попробуем создать отдельную ветку под внедряемую нами импровизированную фичу. Для этих целей используется та же самая команда, но с указанием имени новой ветки:
git branch feature1
Однако важно помнить, что git branch
не выполняет автоматическое переключение на созданную ветку.
Мы можем убедиться в этом, снова проверив список существующих веток:
git branch
Можно заметить, что к списку добавилась ветка feature1
, однако активной веткой (то есть помеченной с помощью зеленого цвета и символа звездочки) по прежнему является master
:
feature1
* master
Теперь у нас есть несколько веток, между которыми мы можем переключаться.
4. Переключение на существующую ветку
Для того, чтобы вручную переключиться на уже существующую ветку, необходимо выполнить команду checkout
, указав название ветки:
git checkout feature1
В консоли появится сообщение об успешном переключении:
Switched to branch 'feature1'
Снова проверим список существующих веток:
git branch
Как видно, активной веткой теперь является feature1
:
* feature1
master
Давайте снова проверим состояние рабочего каталога:
ls
В нем по-прежнему один файл, который был «унаследован» из ветки master
:
file_m
Так как отдельная ветка feature1
нужна для модификации проекта, мы создадим еще один файл:
sudo nano file_f1
Его содержимое пусть будет таким:
file in feature1
Проиндексируем изменения:
git add file_f1
И снова сделаем коммит:
git commit -m "Commit from feature1"
Снова проверим рабочий каталог:
ls
Можно убедиться, что файлов теперь несколько:
file_m file_f1
Теперь переключимся снова на главную ветку:
git checkout master
После этого в рабочем каталоге останется только один файл:
file_m
Каждый раз, когда мы переключаемся между ветками, файлы в рабочем каталоге обновляются до состояния тех коммитов, которые существуют в активной ветке.
5. Переключение на новую ветку
Предположим, мы решили добавить в наш проект еще одну фичу, а значит, хотим выделить под нее новую ветку.
Сперва убедимся, что мы находимся на master
:
git checkout master
А теперь попробуем переключиться на еще не созданную ветку feature2
:
git checkout feature2
Закономерно, мы получим ошибку:
error: pathspec 'feature2' did not match any file(s) known to git
Однако команда checkout
позволяет создавать новые ветки в момент переключения на них. Для этого необходимо добавить флаг -b
:
git checkout -b feature2
В консоли появится сообщение об успешном переключении:
Switched to a new branch 'feature2'
По сути, команда checkout
с флагом -b
эквивалентна двум вызовам:
git branch feature2
git checkout feature2
Снова проверим список существующих веток:
git branch
Теперь у нас есть ветка feature2
, которая стала активной сразу после своего создания:
feature1
* feature2
master
Новая ветка создается на основе той ветки (ее рабочего каталога и списка коммитов), которая была активной. Перед созданием ветки feature2
мы переключились на ветку master
, а значит в рабочем каталоге должен быть только файл file_m
, но не файл file_f1
.
6. Удаление ветки
Удалить ветку, которая является активной, невозможно:
git branch -d feature2
Флаг -d
указывает на запрос удаления указанной ветки. В консоли появится сообщение об ошибке:
error: Cannot delete branch 'feature2' checked out at '/root/project'
Поэтому нам сперва нужно переключиться на любую другую ветку:
git checkout master
После чего выполнить непосредственно удаление:
git branch -d feature2
На этот раз в консоли появится сообщение об успешном удалении ветки:
Deleted branch feature2 (was 24c65ff).
Список существующих веток станет таким:
feature1
* master
7. Создание ветки из другой ветки
Git позволяет явно указывать, на основе какой ветки будет создана новая ветка, без необходимости предварительного переключения.
Давайте убедимся, что мы находимся в ветке master
:
git checkout master
На данный момент специальный указатель HEAD
указывает на активную ветку master
, которая, в свою очередь, является указателем на последний коммит этой ветки.
До этого мы создавали ветку feature2
из активной ветки master
. Однако сейчас мы создадим ветку feature2
из ветки feature1
(а не из master
) без явного переключения на нее — мы по-прежнему будем в master
:
git checkout -b feature2 feature1
Теперь активной веткой является feature2
. Проверим содержимое рабочего каталога:
ls
Как видно, состояние каталога такое же, как в feature1
, а не в master
:
file_m file_f1
Мы также можем посмотреть историю коммитов:
git log
Как видно, ветка feature2
содержит как коммиты ветки master, так и коммиты ветки feature1
:
commit fb1b1616c85c258f647df4137df535df5ac17d6c (HEAD -> feature2, feature1)
Author: root <root@2450302-yn55665.twc1.net>
Date: Tue Feb 13 02:18:02 2024 +0300
Commit from feature1
commit 24c65ffab574a5e478061034137298ca2ce33c94 (master)
Author: root <root@2450302-yn55665.twc1.net>
Date: Mon Feb 12 23:30:56 2024 +0300
First commit
8. Сброс ветки до состояния другой ветки
Помимо создания ветки из другой ветки команда checkout
позволяет выполнять сброс (reset
) существующей ветки до состояния другой существующей ветки.
Например, мы можем сбросить ветку feature2
до состояние ветки master
:
git checkout -B feature2 master
Обратите внимание, что используется флаг -B
, а не -b
.
В консоли появится соответствующее сообщение:
Reset branch 'feature2'
Проверим рабочий каталог:
ls
Как видно, в нем только один файл:
file_m
При этом список «унаследованных» коммитов ветки feature2
превратиться в список коммитов ветки master
:
git log
В консольном выводе будет один единственный коммит — самый первый:
commit 24c65ffab574a5e478061034137298ca2ce33c94 (HEAD -> feature2, master)
Author: root <root@2450302-yn55665.twc1.net>
Date: Mon Feb 12 23:30:56 2024 +0300
First commit
9. Просмотр истории переключений
Переключение веток — это не просто операция чтения. Смена ветки вносит изменение в репозиторий, создавая новую запись в истории переключений.
По этой причине в Git есть специальная команда, выводящая полную хронологию переключения между ветками:
git reflog
Хронология операций выводится отображается снизу-вверх, то есть самые последние переключения находятся выше:
fb1b161 (HEAD -> feature2, feature1) HEAD@{1}: checkout: moving from master to feature2
24c65ff (master) HEAD@{2}: checkout: moving from feature1 to master
fb1b161 (HEAD -> feature2, feature1) HEAD@{3}: commit: Added the first feature
24c65ff (master) HEAD@{4}: checkout: moving from master to feature1
24c65ff (master) HEAD@{5}: checkout: moving from feature2 to master
24c65ff (master) HEAD@{6}: checkout: moving from feature1 to feature2
24c65ff (master) HEAD@{7}: checkout: moving from master to feature1
24c65ff (master) HEAD@{8}: commit (initial): First commit
10. Переключение на удаленную ветку
Добавление удаленного репозитория
Предположим, у нас есть удаленный репозиторий GitHub, с которым мы работаем через HTTPS-соединение:
git remote add repository_remote https://github.com/USER/REPOSITORY.git
Либо же мы могли получить к нему доступ через SSH:
git remote add repository_remote git@github.com:USER/REPOSITORY.git
В этом случае предварительно выполняется генерация SSH-ключа:
ssh-keygen -t rsa -b 4096 -C "GITHUB_ACCOUNT_EMAIL"
При этом публичный ключ (.pub
), появившийся в каталоге /.ssh/known_hosts/
, копируется в настройки аккаунта GitHub в раздел SSH Keys.
В нашем случае удаленным репозиторием выступит Nginx:
git remote add repository_remote https://github.com/nginx/nginx
Загрузка файлов из удаленной ветки
После добавления удаленного репозитория можно посмотреть список всех его веток:
git remote show repository_remote
Перед переключением на удаленную ветку нужно сперва извлечь подробную информацию об удаленном репозитории — ветки и теги:
git fetch repository_remote
Можно также указать сразу все удаленные репозитории:
git fetch --all
Теперь мы можем напрямую переключиться на удаленную ветку, получив ее файлы в рабочий каталог:
git checkout branches/stable-0.5
При этом в более старых версиях Git приходилось явно указывать название удаленного репозитория:
git checkout repository_remote/branches/stable-0.5
Теперь, если выполнить команду:
git branch
Можно увидеть указанную удаленную ветку в статусе активной:
* branches/stable-0.5
feature2
feature1
master
Проверим состояние рабочего каталога:
ls
Теперь в нем присутствуют следующие каталоги:
auto conf contrib docs misc src
Удаленную ветку, ровно как и локальную, можно удалить. Для этого ее сперва нужно сделать неактивной:
git checkout master
После чего выполнить само удаление:
git branch -D branches/stable-0.5
Теперь список веток проекта станет, как прежде:
feature2
feature1
* master
11. Переключение на коммит
Точно так же, как переключаться на разные ветки, вы можете переключаться на конкретные коммиты. Однако, важно отметить разницу между коммитами и ветками.
Ветки отклоняются от временной шкалы проекта, но не нарушают общую последовательность изменений. А вот коммиты больше похожи лишь на точки прогресса, содержащие определенные состояния проекта в конкретные моменты времени.
Давайте сперва переключимся на самую последнюю созданную ветку, которая содержит наибольшее число коммитов:
git checkout feature2
Чтобы переключиться на конкретный коммит, вместо имени ветки в checkout
указывается хеш (ID) коммита:
git checkout fb1b1616c85c258f647df4137df535df5ac17d6c
Чтобы узнать хэш, можно воспользоваться командой:
git log
В нашем случае история коммитов выглядит примерно так — отличаться могут только хэши:
commit fb1b1616c85c258f647df4137df535df5ac17d6c (HEAD -> feature2, feature1)
Author: root <root@2450302-yn55665.twc1.net>
Date: Tue Feb 13 02:18:02 2024 +0300
Commit from feature1
commit 24c65ffab574a5e478061034137298ca2ce33c94 (master)
Author: root <root@2450302-yn55665.twc1.net>
Date: Mon Feb 12 23:30:56 2024 +0300
First commit
После переключения на коммит можно посмотреть, какая ветка является активной:
git branch
Теперь список веток приобрел следующий вид:
* (HEAD detached at fb1b1616c)
feature2
feature1
master
Эта манипуляция приводит к состоянию отсоединенного указателя HEAD
(«detached HEAD»). Таким образом, все последующий коммиты не будут принадлежать ни одной из существующих веток.
Однако такой режим небезопасен — отсутствие конкретной ветки в указателе HEAD
может привести к потере данных.
По этой причине выбранный коммит (тот, на который указывает HEAD
) принято «оборачивать» в новую ветку, в рамках которой производятся дальнейшие модификации проекта.
Чаще всего функцию переключения на конкретный коммит используют для просмотра изменений кодовой базы, которые были внесены на определенной стадии разработки.
Отличие checkout от switch
В более поздних версиях (2.23 и выше) Git есть другая команда для работы с ветками — switch
.
Эти команды довольно похожи, однако switch
является более специализированным вариантом:
-
git switch
— новая команда, которая в большей степени ориентирована на работу с ветками. Напротив,git checkout
— старая команда, которая способна делать и другие «периферийные» задачи. Например, создавать новые в ветки в момент переключения или изменять содержимое рабочего каталога до состояние конкретного коммита. -
git checkout
имеет более универсальный (и менее стандартизированный) синтаксис, нежелиgit switch
. С другой стороны по этой причинеcheckout
может казаться более сложной и запутанной, а значит, подверженной ошибкам.
Заключение
В этом небольшом руководстве мы подробно рассказали о том, что делает команда git checkout
, основная функция которой — переключение между отдельными ветками репозитория.
Вот полный список функций, которые выполняет команда checkout
:
-
Переключение между существующими локальными ветками
-
Создание новых локальных веток
-
Создание новых локальных веток на основе других веток
-
Сброс существующих локальных веток до состояния других веток
-
Переключение между существующими удаленными ветками (и загрузкой их файлов в рабочий каталог)
-
Переключение на конкретный коммит из локальной или удаленной ветки
После переключения на другую ветку, как правило, продолжается использование таких команд, как git add
и git commit
для индексации изменений и обновления состояния репозитория конкретно в этой ветке.
При этом нужно быть всегда осторожным — переключение между ветками после внесения изменений в рабочий каталог без совершения коммита приводит к потере новых данных.
Больше информации о работе с Git можно найти в наших инструкциях и в официальной документации.