Современные команды и возможности Git
С тех пор в Git появилось множество возможностей, использование которых может значительно облегчить жизнь. Давайте рассмотрим некоторые из недавно добавленных современных команд git, о которых следует знать.
git switch
Новинкой с 2019 года, а точнее, с версии Git 2.23, стала команда git switch
, с помощью которой можно переключать ветви:
git switch other-branch
git switch - # Переключение на предыдущую ветку, аналогично "cd -".
git switch remote-branch # Прямое переключение на удалённую ветку и начало её отслеживания
Это здорово, но мы уже давно переключаем ветви в Git с помощью git checkout, зачем нужна отдельная команда? git checkout универсальная команда — она может (среди прочего) проверять или восстанавливать определённые файлы или даже определённые коммиты, а новая команда git switch только переключает ветвь. Кроме того, git switch выполняет дополнительные проверки на корректность, которых нет у git checkout. Например, git switch прервёт операцию, если она приведёт к потере локальных изменений.
git restore
Ещё одна новая подкоманда/функция, добавленная в Git версии 2.23, — git restore
, с помощью которой можно восстановить файл до последнего коммита:
# Отмена изменений, внесённых в файл, аналогично "git reset some-file.py".
git restore --staged some-file.py
# Отмена и сброс изменений, внесённых в файл, аналогично "git checkout some-file.py".
git restore --staged --worktree some-file.py
# Возврат файла к предыдущему коммиту, как и "git reset commit -- some-file.py".
git restore --source HEAD~2 some-file.py
Комментарии в приведённом выше фрагменте объясняют работу различных вариантов git restore. В целом, git restore заменяет и упрощает некоторые случаи использования git reset и git checkout, которые уже являются перегруженными функциями. Раздел документации для сравнения revert
, restore
и reset
.
Есть три команды с похожими названиями:
git reset
,git restore
иgit revert
.
git-revert
— это создание нового коммита, отменяющего изменения, сделанные другими коммитами.git-restore
— это восстановление файлов в рабочем дереве из индекса или другого коммита. Эта команда не обновляет вашу ветку. Команда также может быть использована для восстановления файлов в индексе из другого коммита.git-reset
— это обновление вашей ветви, перемещение вершины для добавления или удаления коммитов из ветви. Эта операция изменяет историю коммитов.
git reset
также может использоваться для восстановления индекса, накладываясь наgit restore
.
git sparse-checkout
Следующая команда — git sparse-checkout
, немного странная функция, добавленная в Git 2.25, вышедший 13 января 2020 года.
Допустим, у вас большой монорепо, с микросервисами, разделёнными по отдельным директориям, и такие команды, как checkout или status, работают очень медленно из-за размера репозитория, но, возможно, вам необходимо работать только с одним поддеревом/директорией. Тогда на помощь приходит git sparse-checkout:
$ git clone --no-checkout https://github.com/derrickstolee/sparse-checkout-example
$ cd sparse-checkout-example
$ git sparse-checkout init --cone # Настройте git на соответствие файлам только в корневом каталоге
$ git checkout main # Скачивание файлов только в корневом каталоге
$ ls
bootstrap.sh LICENSE.md README.md
$ git sparse-checkout set service/common
$ ls
bootstrap.sh LICENSE.md README.md service
$ tree .
.
├── bootstrap.sh
├── LICENSE.md
├── README.md
└── service
├── common
│ ├── app.js
│ ├── Dockerfile
... ...
В приведённом примере сначала клонируем репозиторий, не загружая все файлы. Затем используем git sparse-checkout init --cone
, чтобы настроить git на загрузку файлов только в корень репозитория. Таким образом, после выполнения checkout у нас будет только 3 файла, а не целое дерево. Чтобы загрузить/проверить определённую директорию, используем git sparse-checkout set ...
.
Как уже говорилось, это может быть весьма удобно при локальной работе с огромными репозиториями, но не менее полезно и в CI/CD для повышения производительности конвейера, когда требуется собрать/развернуть только часть репозитория и нет необходимости загружать всё.
Подробно о sparse-checkout написано в статье Bring your monorepo down to size with sparse-checkout.
git worktree
Нередки случаи, когда приходится работать над несколькими функциями в одном приложении (репозитории) одновременно, или, может быть, критическая ошибка появляется, когда вы находитесь в середине работы над каким-то запросом.
В таких ситуациях приходится либо клонировать несколько версий/ветвей репозитория, либо хранить/выбрасывать всё, над чем вы работали в данный момент. Выходом из этих ситуаций является git worktree
, выпущенный 24 сентября 2018 года:
git branch
# * dev
# master
git worktree list
# /.../some-repo ews5ger [dev]
git worktree add -b hotfix ./hotfix master
# Preparing worktree (new branch 'hotfix')
# HEAD is now at 5ea9faa Signed commit.
git worktree list
# /.../test-repo ews5ger [dev]
# /.../test-repo/hotfix 5ea9faa [hotfix]
cd hotfix/ # Чистое рабочее дерево, где вы можете вносить изменения и пушить их.
Эта команда позволяет одновременно контролировать несколько ветвей одного репозитория. В примере есть две ветви dev
и master
. Допустим, мы работаем над функцией в ветви dev
, но нас попросили срочно исправить баг. Вместо того чтобы сохранять изменения и сбрасывать ветвь, мы создадим новое рабочее дерево в подкаталоге ./hotfix
из ветки master
. Затем можно перейти в эту директорию, внести изменения, опубликовать их и вернуться к исходному рабочему дереву.
Более подробную информацию можно найти в статье Experiment on your code freely with Git worktree.
git bisect
И, наконец, git bisect
, который не так уж и нов (Git 1.7.14, выпущен 13 мая 2012 года), но большинство использует только функции git примерно с 2005 года, поэтому я думаю, что его стоит показать в любом случае.
Как описано на странице документации: git-bisect — использование бинарного поиска для поиска коммита, в котором была обнаружена ошибка:
git bisect start
git bisect bad HEAD # Предоставлен нерабочий коммит
git bisect good 479420e # Предоставлен коммит, который, как вы знаете, работает
# Bisecting: 2 revisions left to test after this (roughly 1 step)
# [3258487215718444a6148439fa8476e8e7bd49c8] Refactoring.
# Протестируйте текущий коммит...
git bisect bad # Если коммит не работает
git bisect good # Если коммит работает
# Git делит на части левую или правую половину диапазона в зависимости от последней команды
# Продолжайте тестирование, пока не найдёте виновного.
git bisect reset # Сброс на исходный коммит
Мы начинаем с явного запуска сеанса биссекции с помощью git bisect start
, после чего предоставляем неработающий коммит (скорее всего, HEAD) и последний известный работающий коммит или тэг. Получив эту информацию, git проверит коммит, находящийся на полпути между "плохим" и "хорошим" коммитом. В этот момент нужно проверить, есть ли в этой версии ошибка или нет. Используем git bisect good
, чтобы сообщить git, что она работает, или git bisect bad
, что не работает. Повторяем этот процесс до тех пор, пока не останется ни одного коммита, и git сообщит, в каком коммите возникла проблема.
Рекомендую ознакомиться со страницей документации, где показано ещё несколько вариантов работы с git bisect, включая визуализацию, воспроизведение или пропуск коммитов.
Заключение
Если вы ищете какую-то проблему, связанную с git, то, скорее всего, попадёте на вопрос StackOverflow с ответом, набравшим несколько тысяч голосов. И хотя этот ответ, скорее всего, всё ещё актуален, он вполне может быть устаревшим, ведь он был написан 10 лет назад. Поэтому, возможно, существует лучший, более простой и лёгкий способ сделать это. Поэтому, когда вы сталкиваетесь с проблемой git, рекомендую обратиться к документации git за более новыми командами, которые снабжены множеством отличных примеров, или изучить man-страницы на предмет множества флагов и опций, добавленных к старым добрым командам за прошедшие годы.