git checkout, switch и restore: что когда использовать
git checkout умеет две разные вещи: переключать ветки и восстанавливать файлы, поэтому с Git 2.23 его задачи разделены между git switch (ветки) и git restore (файлы). Для переключения используйте git switch имя, для отмены незакоммиченных изменений в файле — git restore файл, а checkout оставьте для старых скриптов и привычных сценариев.
Почему у git checkout две роли
git checkout исторически делает две принципиально разные операции. С именем ветки он переключает HEAD и обновляет рабочую папку. С путём к файлу он берёт версию файла из индекса или коммита и молча перезаписывает ваши изменения. Одна и та же команда то безопасно переходит между ветками, то необратимо выбрасывает несохранённую работу.
Из-за этого в Git 2.23 появились две отдельные команды: git switch — для веток, git restore — для файлов. Checkout никто не удалял, он полностью рабочий, но в новом коде, документации и подсказках самого Git используются switch и restore. Если вы только учитесь, начинайте с них; checkout знать всё равно нужно — он встречается в инструкциях, скриптах и на собеседованиях.
Соответствие команд
| Задача | git checkout | Новая команда |
|---|---|---|
| Переключиться на ветку | git checkout main | git switch main |
| Создать ветку и перейти | git checkout -b feature | git switch -c feature |
| Создать или сбросить ветку | git checkout -B feature | git switch -C feature |
| Вернуться на прошлую ветку | git checkout - | git switch - |
| Перейти на коммит (detached HEAD) | git checkout 3f2a1c9 | git switch --detach 3f2a1c9 |
| Отменить изменения в файле | git checkout -- app.js | git restore app.js |
| Убрать файл из индекса | git reset HEAD app.js | git restore --staged app.js |
| Взять файл из другой ветки или коммита | git checkout main -- app.js | git restore --source=main --staged --worktree app.js |
| Отменить все изменения в папке | git checkout -- . | git restore . |
Обратите внимание на -- в командах checkout. Это разделитель «дальше идут пути». Он защищает от двусмысленности, когда файл и ветка называются одинаково. В restore такой проблемы нет, потому что команда работает только с файлами.
git checkout для веток
git checkout feature/login # переключиться
git checkout -b feature/login # создать от текущего коммита и переключиться
git checkout -b hotfix origin/main # создать от удалённой ветки
git checkout -t origin/feature/x # локальная ветка, отслеживающая удалённую
Если локальной ветки нет, но на сервере есть ветка с таким именем, git checkout feature/x (как и git switch feature/x) сам создаст локальную ветку и настроит отслеживание. Перед этим нужен git fetch, иначе Git не знает о новой удалённой ветке.
Переключение не теряет незакоммиченные изменения: они переносятся в другую ветку, если не конфликтуют с ней. При конфликте Git откажет и предложит закоммитить или спрятать изменения через git stash. Флаг -f (--force) переключит ветку, выбросив локальные правки, — это уже опасная операция.
git switch: только ветки
Switch повторяет ветковую часть checkout, но с более строгими правилами. Главное отличие: он не переходит на коммит или тег без явного --detach. Команда git switch v1.0 выдаст ошибку вместо незаметного перехода в detached HEAD, поэтому случайно оказаться «вне ветки» труднее.
git switch main
git switch -c feature/cart
git switch -c feature/cart origin/main
git switch --detach v1.0
git switch - # предыдущая ветка
git switch -m main # перенести правки с трёхсторонним слиянием
git restore: кнопка отмены для файлов
Restore возвращает файлы к сохранённому состоянию и никак не трогает историю коммитов. Чтобы пользоваться им уверенно, нужно помнить три области Git: рабочую папку (что вы видите в редакторе), индекс (что подготовлено через git add) и коммиты.
Как отменить изменения в файле до git add
git restore app.js # вернуть версию из индекса
git restore src/ # все файлы в папке
git restore . # все изменённые файлы проекта
Без флагов restore восстанавливает рабочую папку из индекса. Если файл не добавлялся в индекс после последнего коммита, это то же самое, что версия из последнего коммита. Изменения исчезают без следа, в корзину они не попадают, поэтому перед git restore . взгляните на git diff.
Как убрать файл из индекса после git add
git restore --staged app.js
Файл перестаёт быть подготовленным к коммиту, но ваши правки в нём остаются. Это самая безопасная форма restore: ничего не теряется. Именно эту команду подсказывает git status в разделе «Changes to be committed».
Как отменить всё: и в индексе, и в файле
git restore --staged --worktree app.js
# короткая форма
git restore -SW app.js
Как вернуть файл из другого коммита или ветки
git restore --source=HEAD~2 app.js # версия двухкоммитной давности
git restore --source=main config.yml # версия из ветки main
git restore --source=3f2a1c9 -- docs/ # целая папка из коммита
По умолчанию с --source меняется только рабочая папка. Добавьте --staged, если хотите сразу подготовить изменение к коммиту. Старый вариант git checkout main -- app.js всегда меняет и индекс, и рабочую папку.
Как восстановить удалённый файл
git restore deleted.txt # удалён, но ещё не закоммичено
git restore --source=HEAD~1 deleted.txt # удаление уже закоммичено
Если неизвестно, в каком коммите файл удалили, найдите его: git log --oneline -- deleted.txt. Последний коммит в списке — тот, где файл удалён, восстанавливать нужно из его родителя: git restore --source=<хеш>~1 deleted.txt.
restore, reset и revert: не путать
Все три команды «отменяют», но на разной глубине:
- restore — работает с файлами в рабочей папке и индексе, историю не меняет. Безопасен для команды.
- reset — двигает указатель ветки назад, переписывая историю.
--softоставляет изменения в индексе,--mixed(по умолчанию) — в рабочей папке,--hardудаляет их. Для уже отправленных коммитов в общих ветках опасен. - revert — создаёт новый коммит, обратный указанному. История не переписывается, поэтому это правильный способ отменить коммит, который уже на сервере.
Простое правило: изменения ещё не закоммичены — restore; коммит только локальный — reset; коммит уже у коллег — revert.
Detached HEAD после checkout
Выполнив git checkout 3f2a1c9 или git checkout v1.0, вы увидите длинное предупреждение «You are in 'detached HEAD' state». HEAD теперь указывает на коммит, а не на ветку. Это удобно, чтобы посмотреть старую версию или запустить тесты на ней.
Коммитить в этом состоянии можно, но новые коммиты ни на одну ветку не попадут. Если вы что-то изменили и хотите сохранить:
git switch -c fix-for-v1
Если просто смотрели — вернитесь: git switch - или git switch main. Если ушли, забыв создать ветку, коммиты всё ещё доступны через git reflog: найдите хеш и выполните git branch rescue <хеш>.
Типичные ошибки
error: pathspec 'feature' did not match any file(s) known to git
Git не нашёл ни ветки, ни файла с таким именем. Обычно ветка есть только на сервере, а fetch не делался. Выполните git fetch и повторите. Проверьте и написание: имена веток чувствительны к регистру.
Случайно выбросил изменения через checkout -- или restore
Незакоммиченные правки, которых не было в индексе, Git не хранит, и вернуть их средствами Git нельзя. Остаётся локальная история редактора или IDE. Если же файл был добавлен через git add, его содержимое лежит в базе как «висящий» объект и находится через git fsck --lost-found.
Файл и ветка с одинаковым именем
git checkout test при наличии ветки test и папки test выдаст предупреждение о двусмысленности. Используйте git switch test для ветки и git restore test (или git checkout -- test) для файлов.
Рабочие сценарии на каждый день
Несколько ситуаций, которые встречаются постоянно, и правильная команда для каждой:
- Начали править файл и поняли, что пошли не туда, —
git restore файл. - Добавили в индекс лишний файл перед коммитом —
git restore --staged файл. - Нужно срочно переключиться на другую задачу посреди работы —
git stash, затемgit switch ветка; вернувшись —git switch -иgit stash pop. - Хотите взять из соседней ветки один файл, не сливая ветку целиком, —
git restore --source=feature/x --staged --worktree путь. - Нужно проверить, работал ли баг в прошлой версии, —
git switch --detach v1.0, проверка, затемgit switch -. - Хотите откатить только часть изменений в файле —
git restore -p файл: Git покажет каждый фрагмент и спросит, отменять ли его.
Интерактивный режим -p (--patch) особенно полезен: он есть и у checkout, и у restore, и позволяет выбросить отладочный вывод, сохранив полезные правки в том же файле.
Как проверить результат перед отменой
Restore и checkout с путями выбрасывают изменения без подтверждения, поэтому полезно сначала посмотреть, что именно пропадёт. git diff покажет правки в рабочей папке относительно индекса, git diff --staged — подготовленное к коммиту, git diff main -- app.js — чем файл отличается от версии в другой ветке. Если сомневаетесь, сохраните текущее состояние через git stash: из него изменения всегда можно вернуть, в отличие от restore.
Итог
git checkout совмещает переключение веток и восстановление файлов, и именно это делает его опасным. Для веток используйте git switch, для файлов — git restore: одна команда меняет только положение HEAD, другая — только содержимое файлов. Ветки, restore, reset и revert с хорошими и плохими сценариями разобраны на практике в курсе по Git.
Частые вопросы
Устарел ли git checkout?
Нет, он не объявлен устаревшим и продолжит работать. Просто для повседневной работы рекомендуют более узкие и безопасные git switch и git restore.
С какой версии Git есть switch и restore?
Обе команды появились в Git 2.23 (2019 год). Проверить версию можно командой git --version.
Можно ли отменить git restore?
Если восстанавливалась рабочая папка, выброшенные правки Git не сохранил, и отменить нельзя. git restore --staged безопасен: правки остаются в файле, их достаточно снова добавить через git add.
Как переключиться на ветку коллеги?
Выполните git fetch, затем git switch имя-ветки: Git создаст локальную ветку, связанную с origin/имя-ветки.
Чем git checkout . отличается от git reset --hard?
git checkout . (или git restore .) возвращает изменённые файлы к версии из индекса, не трогая индекс и историю. git reset --hard сбрасывает и индекс, и рабочую папку к коммиту, а с аргументом-коммитом ещё и двигает ветку.
Эта тема подробно разобрана с практикой в курсе «Git с нуля до уверенного разработчика».
- Ветки: параллельная реальность вашего кода
- Отменяем ошибки: restore, reset, revert — правильно и опасно