Смертельный бой: Git Rebase против Merge
Вечный вопрос...
Смертельный бой: Git Rebase против (Squash) Merge!
Делать Rebase? Или Squash Merge?
- Это вопрос личных предпочтений?
- Ответ: Не когда задействована одна или несколько команд! Любой выбор повлияет на удобство использования другого подхода!
Почему эта тема вызывает религиозный фанатизм?
Некоторые инженеры используют знание git (и терминала) как сигнал своего относительного уровня мастерства. А любая практика, связанная с нашей идентичностью или эго, становится практически невозможной для беспристрастного анализа — не говоря уже об изменении.
Другие факторы включают знакомство с инструментом и ошибку выжившего, которые ещё больше затрудняют нашу собственную оценку и предположения.
Ключевой вопрос: Какова цель git-коммита?
- Вы коммитите часто и помногу? С мышлением «чекпоинта» или резервной копии?
- Когда записывается всё, включая ложные старты и эксперименты? (например,
git commit -am "Updated deps" && git push, повторяется регулярно) - Возможно, сообщения коммитов для вас менее важны, чем сам код?
- Когда записывается всё, включая ложные старты и эксперименты? (например,
- Или ваши коммиты — это тщательно продуманное, выверенное произведение искусства?
- Может, каждый коммит — это самодостаточная, атомарная единица работы? (например,
git add package.json && git commit -m "Updated deps") - Или вы просто не выносите «грязные» логи коммитов?
- Проходят ли ваши PR-ревью часто коммит за коммитом?
- Может, каждый коммит — это самодостаточная, атомарная единица работы? (например,
| 💡 Какие ещё ментальные модели определяют ваше отношение к коммитам? Дайте знать @justsml!
Думаете ли вы о git так, чтобы приносить максимальную пользу себе, своей команде и своей организации?
Учитывая совершенно разные подходы к стратегии коммитов, неудивительно, что вокруг «правильного» способа использования git столько путаницы.
Сценарий: Создание исправленного тега релиза
Сравним процесс создания тега релиза с исключением некоторых последних коммитов на main.
Путь Rebase
Ментальная модель: «Я хочу создать альтернативную версию существующей истории. (Например, я накосячил 16 слияний назад и мне может понадобиться тонкий контроль для исправления. Также есть риск увязнуть в бесконечном цикле конфликтов и --continue.)»
- Получить последнее:
git checkout main&&git pull - Создать новую ветку:
git checkout -b release/hot-newness-and-stuff - Запустить интерактивный rebase и указать git-ссылку на точку, куда хотите вернуться во времени.
git rebase -i HEAD~6(Примечание:HEAD~6— это сокращение для «6 коммитов назад») - Удалить нужные коммиты, изменив их префикс на
drop. Сохранить и закрыть редактор. - Разрешить конфликты слияния,
git add .&&git rebase --continue(НЕ делайтеgit commit). - Повторять предыдущий шаг до завершения.
- Создать тег и отправить. Пример:
git tag -a v1.2.3 -m 'Release v1.2.3'&&git push --tags
Плюсы
- 🔌 Абсолютная власть. Вы можете менять историю.
Минусы
- 😰 Абсолютная власть. Вы можете менять историю. (Ладно, это и плюс, и минус…)
- 🔂 Можно увязнуть в бесконечном цикле конфликтов и
—-continue. (Иногда даже сgit rerere) - 🙀 Ломает ключевые возможности для коллаборации: потерянные/осиротевшие комментарии к PR. Некрасиво.
- 🖇️ Пермалинки могут перестать быть такими уж постоянными.
Путь (Squash) Merge
Ментальная модель: «Я хочу кастомный релиз, начиная с определённой точки и включая нужные ветки.»
- Получить последнее:
git checkout main&&git pull - Создать новую ветку:
git checkout -b release/hot-newness-and-stuff - Слить нужные ветки и/или коммиты:
git merge --no-ff feature/hot-newness bug/fix-123(используйте флаг--no-ff, где возможно.) - Разрешить конфликт слияния (если возникнет).
- Создать тег и отправить. Пример:
git tag -a v1.2.3 -m 'Release v1.2.3'&&git push --tags
Плюсы
- 💪 Меньше церемоний, меньше конфликтов в целом, используются уже знакомые команды git.
- 🚀 Позволяет мыслить на уровне PR/веток, игнорируя детализацию отдельных коммитов (если она не нужна.)
- 🦺 Неразрушающий подход. Можно вернуться и/или создать новые ветки в любой момент.
- 🎥 Сохраняет существующие коммиты и сообщения целиком, что приводит к меньшему «шуму» в blame.
Минусы
- 🔏 Сложнее менять сообщения коммитов.
- 🤐 Сложнее скрыть свою работу.
Заключение
В конце дня более простой процесс с меньшим риском должен победить.
Хотя у Rebase-пользователей действительно есть способы решить (или избежать) свои проблемы, факт остаётся фактом: вам понадобится чёрный пояс по git. (Например, даже простой git push сталкивается с дополнительной сложностью: был ли это git push --force или git push --force-with-lease? Зачем вообще с этим связываться?)
Есть ещё одна причина, по которой rebasing для создания изменённой истории всегда будет в невыгодном положении по сравнению с git merge .... Команда git merge позволяет CLI git применять продвинутые алгоритмы для избежания конфликтов, анализируя HEAD каждой ветки.
Это может быть умнее, потому что каждое слияние заботится только о последнем состоянии каждой нужной ветки, тогда как rebasing должен воспроизводить (или удалять) историю коммитов в указанной последовательности. Это ограничивает способность git оптимизировать слияние, поскольку он сравнивает только 2 коммита за раз.
В итоге rebasing означает, что вы периодически будете заново переживать нерелевантные старые коммиты и конфликты — даже если знаете, что они уже были удалены или разрешены.
Итого
- 💃 Ответ: SQUASH MERGE ваши PR-ы в
main.- История вашей ветки останется на месте, если понадобится, а
mainбудет относительно «чистым.»
- История вашей ветки останется на месте, если понадобится, а
- 🔤 Всегда коммитьте!
- В более чем 95% корпоративных проектов мышление «резервной копии» предпочтительнее мышления «вылепленного произведения искусства». Со временем смысл сообщений ваших коммитов потускнеет гораздо быстрее, чем код, логика и тесты которого сохранят свою значимость.