DanLevy.net

Смертельный бой: Git Rebase против Merge

Вечный вопрос...

Hero image for Смертельный бой: Git Rebase против Merge

Смертельный бой: Git Rebase против (Squash) Merge!

Делать Rebase? Или Squash Merge?

Почему эта тема вызывает религиозный фанатизм?

Некоторые инженеры используют знание git (и терминала) как сигнал своего относительного уровня мастерства. А любая практика, связанная с нашей идентичностью или эго, становится практически невозможной для беспристрастного анализа — не говоря уже об изменении.

Другие факторы включают знакомство с инструментом и ошибку выжившего, которые ещё больше затрудняют нашу собственную оценку и предположения.

Ключевой вопрос: Какова цель git-коммита?

  1. Вы коммитите часто и помногу? С мышлением «чекпоинта» или резервной копии?
    • Когда записывается всё, включая ложные старты и эксперименты? (например, git commit -am "Updated deps" && git push, повторяется регулярно)
    • Возможно, сообщения коммитов для вас менее важны, чем сам код?
  2. Или ваши коммиты — это тщательно продуманное, выверенное произведение искусства?
    • Может, каждый коммит — это самодостаточная, атомарная единица работы? (например, git add package.json && git commit -m "Updated deps")
    • Или вы просто не выносите «грязные» логи коммитов?
    • Проходят ли ваши PR-ревью часто коммит за коммитом?

| 💡 Какие ещё ментальные модели определяют ваше отношение к коммитам? Дайте знать @justsml!

Думаете ли вы о git так, чтобы приносить максимальную пользу себе, своей команде и своей организации?

Учитывая совершенно разные подходы к стратегии коммитов, неудивительно, что вокруг «правильного» способа использования git столько путаницы.

Сценарий: Создание исправленного тега релиза

Сравним процесс создания тега релиза с исключением некоторых последних коммитов на main.

Ветвление Git с релизом из main и двумя feature-ветками

Путь Rebase

Ментальная модель: «Я хочу создать альтернативную версию существующей истории. (Например, я накосячил 16 слияний назад и мне может понадобиться тонкий контроль для исправления. Также есть риск увязнуть в бесконечном цикле конфликтов и --continue.)»

  1. Получить последнее: git checkout main && git pull
  2. Создать новую ветку: git checkout -b release/hot-newness-and-stuff
  3. Запустить интерактивный rebase и указать git-ссылку на точку, куда хотите вернуться во времени. git rebase -i HEAD~6 (Примечание: HEAD~6 — это сокращение для «6 коммитов назад»)
  4. Удалить нужные коммиты, изменив их префикс на drop. Сохранить и закрыть редактор.
  5. Разрешить конфликты слияния, git add . && git rebase --continue (НЕ делайте git commit).
  6. Повторять предыдущий шаг до завершения.
  7. Создать тег и отправить. Пример: git tag -a v1.2.3 -m 'Release v1.2.3' && git push --tags

Плюсы

Минусы

Путь (Squash) Merge

Ментальная модель: «Я хочу кастомный релиз, начиная с определённой точки и включая нужные ветки.»

  1. Получить последнее: git checkout main && git pull
  2. Создать новую ветку: git checkout -b release/hot-newness-and-stuff
  3. Слить нужные ветки и/или коммиты: git merge --no-ff feature/hot-newness bug/fix-123 (используйте флаг --no-ff, где возможно.)
  4. Разрешить конфликт слияния (если возникнет).
  5. Создать тег и отправить. Пример: git tag -a v1.2.3 -m 'Release v1.2.3' && git push --tags

Плюсы

Минусы

Заключение

В конце дня более простой процесс с меньшим риском должен победить.

Хотя у Rebase-пользователей действительно есть способы решить (или избежать) свои проблемы, факт остаётся фактом: вам понадобится чёрный пояс по git. (Например, даже простой git push сталкивается с дополнительной сложностью: был ли это git push --force или git push --force-with-lease? Зачем вообще с этим связываться?)

Есть ещё одна причина, по которой rebasing для создания изменённой истории всегда будет в невыгодном положении по сравнению с git merge .... Команда git merge позволяет CLI git применять продвинутые алгоритмы для избежания конфликтов, анализируя HEAD каждой ветки.

Это может быть умнее, потому что каждое слияние заботится только о последнем состоянии каждой нужной ветки, тогда как rebasing должен воспроизводить (или удалять) историю коммитов в указанной последовательности. Это ограничивает способность git оптимизировать слияние, поскольку он сравнивает только 2 коммита за раз.

В итоге rebasing означает, что вы периодически будете заново переживать нерелевантные старые коммиты и конфликты — даже если знаете, что они уже были удалены или разрешены.

Итого