Продвинутый Git: interactive rebase, cherry-pick и bisect
Продвинутое3 минРедакция Innovialabs
Коротко
Interactive rebase переписывает ваши локальные коммиты (склеить, переименовать, переставить), cherry-pick копирует отдельный коммит в другую ветку, а bisect бинарным поиском находит коммит, который сломал код.
Три инструмента и когда они нужны
git rebase -i— навести порядок в своих коммитах до того, как их увидят коллеги: склеить «fix typo» с основным коммитом, переписать сообщение, поменять порядок.git cherry-pick— взять один конкретный коммит из другой ветки, например hotfix изdevelopвrelease, без слияния всей ветки.git bisect— найти коммит, после которого что-то сломалось, проверяя историю бинарным поиском вместо просмотра каждого коммита.
Главное правило: rebase и любое переписывание истории — только для коммитов, которые ещё не попали в общую ветку. Если коммиты уже запушены и на них работают другие, переписанная история создаст конфликты у всей команды.
Interactive rebase: чистим историю
Чтобы отредактировать последние 4 коммита:
git rebase -i HEAD~4
Git откроет редактор со списком коммитов (сверху — самый старый) и командами:
| Команда | Что делает |
|---|---|
pick | оставить коммит как есть |
reword | оставить изменения, но поменять сообщение |
edit | остановиться на коммите, чтобы изменить его содержимое |
squash | склеить с предыдущим, объединив сообщения |
fixup | склеить с предыдущим, отбросив сообщение |
drop | удалить коммит |
Порядок строк можно менять — коммиты применятся в новом порядке.
Полезный приём: при исправлении делайте git commit --fixup <hash>, а затем git rebase -i --autosquash <base> — Git сам расставит fixup рядом с нужными коммитами.
Если что-то пошло не так во время rebase:
git rebase --continue— после разрешения конфликта;git rebase --abort— вернуть всё как было до начала;git reflog— найти состояние ветки до rebase, если уже поздно для abort.
После rebase уже запушенной личной ветки нужен force push. Используйте безопасный вариант git push --force-with-lease: он откажется перезаписывать, если на сервере появились чужие коммиты.
Cherry-pick: переносим исправление
git switch release/2.0
git cherry-pick a1b2c3d
Git создаст новый коммит с теми же изменениями, но другим хешем. Полезные флаги:
-x— добавить в сообщение строку «cherry picked from commit …», чтобы было видно происхождение;A..B— перенести диапазон коммитов (без самого A);--no-commit— применить изменения, но не коммитить сразу.
Когда cherry-pick — плохая идея: если вы регулярно переносите много коммитов между ветками, это признак проблем в процессе ветвления. Дубликаты коммитов потом усложняют слияния. Лучше сделать исправление в общей базовой ветке и слить её туда, где оно нужно.
Bisect: ищем коммит с багом
Допустим, сейчас тесты падают, а в теге v1.4 всё работало:
git bisect start
git bisect bad # текущий коммит сломан
git bisect good v1.4 # здесь всё работало
Git переключит вас на коммит посередине. Проверьте и отметьте git bisect good или git bisect bad. С каждым шагом диапазон сокращается вдвое, поэтому даже среди сотен коммитов нужно лишь несколько проверок. В конце Git покажет первый плохой коммит. Завершите сессию командой git bisect reset.
Автоматизация: если проверку можно выразить скриптом, который возвращает 0 при успехе и ненулевой код при ошибке, Git пройдёт поиск сам:
git bisect run npm test
Если какой-то коммит невозможно проверить (например, он не собирается), используйте git bisect skip.
Частые ошибки
- Rebase общей ветки (
main,develop) — ломает историю коллегам. - Обычный
--forceвместо--force-with-lease. - Bisect с нестабильным тестом: «плавающий» результат уводит поиск не туда.
- Огромные коммиты: bisect найдёт виновника, но внутри будет 40 файлов. Маленькие осмысленные коммиты делают все три инструмента полезнее.