Команда checkout
в системе контроля версий Git отвечает за переключение между отдельными ветками репозитория.
Каждое переключение обновляет файлы в рабочем каталоге на основе тех данных, которые хранятся в выбранной ветке. При этом каждый последующий коммит автоматически добавляется в активную ветку, выбранную ранее с помощью команды checkout
.
В этом руководстве будут рассмотрены возможные пути использования как команды git checkout
, так и ряда других сопутствующих команд (git branch
, git reflog
, git remote show
), которые позволяют взаимодействовать с ветками как локального, так и удаленного репозитория.
cloud
Сперва подготовим отдельный каталог для тестового проекта в Git:
mkdir project
После чего перейдем в него:
cd project
Наконец, выполним инициализацию репозитория Git:
git init
После этого нам станут доступны все команды по управлению как ветками, так и репозиторием в целом.
Чтобы понять, как переключение веток влияет на рабочий каталог (и репозиторий целикоv), мы подготовим импровизированный файл исходника проекта с тривиальным содержимым внутри:
sudo nano file_m
Наполнение файла будет следующим:
file in master
Давайте проверим состояние рабочего каталога:
ls
Существует только один файл:
file_m
Теперь проиндексируем изменения:
git add file_m
После чего выполним коммит:
git commit -m "First commit"
По ходу этого руководства мы будем наблюдать за тем, как работа с ветками влияет на содержимое рабочего каталога — в частности на файлы, которые мы создаем или редактируем.
Предположим, мы решили внедрить в наш проект новую фичу, но пока что до конца не уверены в ее необходимости или эффективности. По сути, мы хотим проверить некую гипотезу с возможностью отката изменений до основной стабильной версии проекта.
В этих целях Git предоставляет возможность создания отдельных веток и переключения между ними. Таким образом мы всегда сможем проверить работоспособность проекта как с фичей, так и без нее.
Однако для начала давайте узнаем, на какой ветке мы находимся сейчас:
git branch
В консоли появится вывод с подсвеченной активной веткой master
:
* master
Именно в эту ветку мы совершили предыдущий коммит. Это значит, что в этой ветке находился ранее созданный файл file_m
.
Теперь мы попробуем создать отдельную ветку под внедряемую нами импровизированную фичу. Для этих целей используется та же самая команда, но с указанием имени новой ветки:
git branch feature1
Однако важно помнить, что git branch
не выполняет автоматическое переключение на созданную ветку.
Мы можем убедиться в этом, снова проверив список существующих веток:
git branch
Можно заметить, что к списку добавилась ветка feature1
, однако активной веткой (то есть помеченной с помощью зеленого цвета и символа звездочки) по прежнему является master
:
feature1
* master
Теперь у нас есть несколько веток, между которыми мы можем переключаться.
Для того, чтобы вручную переключиться на уже существующую ветку, необходимо выполнить команду 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
Каждый раз, когда мы переключаемся между ветками, файлы в рабочем каталоге обновляются до состояния тех коммитов, которые существуют в активной ветке.
Предположим, мы решили добавить в наш проект еще одну фичу, а значит, хотим выделить под нее новую ветку.
Сперва убедимся, что мы находимся на 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
.
Удалить ветку, которая является активной, невозможно:
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
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
Помимо создания ветки из другой ветки команда 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
Переключение веток — это не просто операция чтения. Смена ветки вносит изменение в репозиторий, создавая новую запись в истории переключений.
По этой причине в 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
Предположим, у нас есть удаленный репозиторий 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
Точно так же, как переключаться на разные ветки, вы можете переключаться на конкретные коммиты. Однако, важно отметить разницу между коммитами и ветками.
Ветки отклоняются от временной шкалы проекта, но не нарушают общую последовательность изменений. А вот коммиты больше похожи лишь на точки прогресса, содержащие определенные состояния проекта в конкретные моменты времени.
Давайте сперва переключимся на самую последнюю созданную ветку, которая содержит наибольшее число коммитов:
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
) принято «оборачивать» в новую ветку, в рамках которой производятся дальнейшие модификации проекта.
Чаще всего функцию переключения на конкретный коммит используют для просмотра изменений кодовой базы, которые были внесены на определенной стадии разработки.
В более поздних версиях (2.23 и выше) Git есть другая команда для работы с ветками — switch
.
Эти команды довольно похожи, однако switch
является более специализированным вариантом:
git switch
— новая команда, которая в большей степени ориентирована на работу с ветками. Напротив, git checkout
— старая команда, которая способна делать и другие «периферийные» задачи. Например, создавать новые в ветки в момент переключения или изменять содержимое рабочего каталога до состояние конкретного коммита.
git checkout
имеет более универсальный (и менее стандартизированный) синтаксис, нежели git switch
. С другой стороны по этой причине checkout
может казаться более сложной и запутанной, а значит, подверженной ошибкам.
Подготовили для вас выгодные тарифы на облачные серверы
В этом небольшом руководстве мы подробно рассказали о том, что делает команда git checkout
, основная функция которой — переключение между отдельными ветками репозитория.
Вот полный список функций, которые выполняет команда checkout
:
Переключение между существующими локальными ветками
Создание новых локальных веток
Создание новых локальных веток на основе других веток
Сброс существующих локальных веток до состояния других веток
Переключение между существующими удаленными ветками (и загрузкой их файлов в рабочий каталог)
Переключение на конкретный коммит из локальной или удаленной ветки
После переключения на другую ветку, как правило, продолжается использование таких команд, как git add
и git commit
для индексации изменений и обновления состояния репозитория конкретно в этой ветке.
При этом нужно быть всегда осторожным — переключение между ветками после внесения изменений в рабочий каталог без совершения коммита приводит к потере новых данных.
Больше информации о работе с Git можно найти в наших инструкциях и в официальной документации.