Мы, разработчики ПО, пользуемся git
каждый день, однако большинство из нас применяет только самые основные команды, например add
, commit
, push
и pull
, как будто на дворе по-прежнему 2005 год.
С тех пор в Git появилось множество фич, пользование которыми может сильно упросить вашу жизнь. Так давайте исследуем некоторые из недавно добавленных современных команд git
, о которых вам стоит знать.
Switch
git switch
появившаяся в 2019 году, или, точнее, в версии Git 2.23, позволяет переключать ветви:
git switch other-branch
git switch - # Переключение обратно на предыдудую ветвь, аналогично "cd -"
git switch remote-branch # Непосредственное переключение на удалённую ветвь и её отслеживание
Это здорово, но мы уже давно можем переключать ветви в Git при помощи git checkout
, зачем нужна отдельная команда? git checkout
— универсальная команда, она способна (среди прочего) выполнять проверку, восстанавливать конкретные файлы или даже конкретные коммиты, а новая git switch
только переключает ветви. Кроме того, switch
выполняет дополнительные проверки работоспособности, не выполняемые checkout
, например, switch
прерывает операцию, если она приведёт к потере локальных изменений.
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
в этом разделе документации.
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 # Checkout только файлов в корневой папке
$ 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 у нас будет только три файла, а не всё дерево. Чтобы затем скачать/выполнить checkout конкретной папки, мы используем git sparse-checkout set ...
.
Как уже говорилось, это может быть очень удобно при локальной работе с огромными репозиториями, но столь же полезно в CI/CD для повышения скорости работы конвейера, когда нам нужно только собрать/развернуть часть монорепозитория и нет необходимости проверять всё.
Подробное описание sparse-checkout
представлено в статье.
Worktree
Часто бывает так, что один человек одновременно работает над несколькими фичами одного приложения (репозитория); а иногда в процессе работы над запросом новой фичи внезапно возникает критичный баг.
В таких случаях приходится или иметь несколько склонированных версий/ветвей репозитория, или приостанавливать/отменять работу над тем, что вы делаете в данный момент. Для решения таких проблем создали git worktree
, выпущенную 24 сентября 2018 года:
git branch
# * dev
# master
git worktree list
# /.../some-repo ews5ger [dev]
git worktree add -b hotfix ./hotfix master
# Подготовка рабочего дерева (нового "хотфикса" ветви)
# HEAD теперь находится на коммите, подписанном 5ea9faa.
git worktree list
# /.../test-repo ews5ger [dev]
# /.../test-repo/hotfix 5ea9faa [hotfix]
cd hotfix/ # Чистое рабочее дерево, в котором можно вносить изменения и пушить их
Это позволяет нам одновременно выполнять checkout нескольких ветвей в одном репозитории. В примере выше у нас есть две ветви, dev
и master
. Допустим, мы работаем с фичей в ветви dev
, но нас попросили срочно устранить баг. Вместо того, чтобы откладывать изменения и сбрасывать ветвь до исходной версии, мы создаём новое рабочее дерево в подпапке ./hotfix
из ветви master
. Затем можно перейти в эту папку, внести изменения, запушить их и вернуться к исходному рабочему дереву.
Более подробное описание можно прочитать в статье.
Bisect
git bisect
, — не особо новая фича (Git 1.7.14, выпущенный 13 мая 2012 года), но большинство людей пользуется только фичами git
, появившимися примерно в 2005 году, так что, думаю, её всё равно стоит показать.
На странице документации она описывается так: git-bisect
— использование двоичного поиска для поиска коммита, внёсшего баг:
git bisect start
git bisect bad HEAD # Указываем поломанный коммит
git bisect good 479420e # Указываем точно работающий коммит
# Деление пополам: после этого осталось протестировать 2 ревизии (приблизительно 1 этап)
# [3258487215718444a6148439fa8476e8e7bd49c8] Рефакторинг.
# Проверяем текущий коммит...
git bisect bad # Если коммит не работает
git bisect good # Если коммит работает
# В зависимости от последней команды Git делит пополам левую или правую половину
# Продолжаем тестирование, пока не найдём виновника
git bisect reset # Сброс до исходного коммита
Мы начинаем с запуска сессии деления пополам при помощи команды git bisect start
, после которой мы указываем неработающий коммит (вероятнее всего, это HEAD
) и последний работавший коммит или тэг. Имея эту информацию, git
выполняет check out
коммита посередине между коммитами bad и good. В этой точке нам нужно протестировать, имеет ли эта версия баг; если версия работает, то мы должны использовать git bisect good
, чтобы сказатьgit
, что она работает, или git bisect bad
, если нет. Мы продолжаем процесс, пока больше не останется коммитов и git
не сообщит нам, в каком коммите возникла проблема.
Рекомендую изучить страницу документации, на которой есть ещё пара опций git bisect
, в том числе визуализация, воспроизведение или пропуск коммитов.
Заключение
В поисках проблем, связанных с git
, вы с наибольшей вероятностью наткнётесь на вопрос StackOverflow с ответом, имеющим пару тысяч голосов. Хотя этот ответ, вероятно, по-прежнему будет правильным, он вполне может оказаться устаревшим, потому что был написан десять лет назад. Уже может существовать более качественное, простое и удобное решение. Поэтому, если вы столкнётесь с проблемой с git
, я рекомендую вам почитать в документации git о более новых командах, все они снабжены отличными примерами; или же изучить страницы man
различных флагов и опций, добавленных за долгие годы разработки к старым добрым командам.
Автор:
PatientZero