Современные команды и возможности Git

Источник: «Modern Git Commands and Features You Should Be Using»
Все мы — используем git каждый день, однако большинство из нас, как будто задержались в 2005 году, используют только основные команды, такие как add, commit, push или pull.

С тех пор в 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 Reference: Reset, restore and revert

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-страницы на предмет множества флагов и опций, добавленных к старым добрым командам за прошедшие годы.

Дополнительные материалы

Предыдущая Статья

Обработка ошибок при работе со сторонними API

Следующая Статья

Понимание работы кэширования в GitHub Actions