Разрешение расходящихся историй: стратегии Git Merge и Rebase
Сравнение git merge и git rebase для расходящихся веток, обработка конфликтов, общая история и выбор безопасного для команды рабочего процесса.
Разрешение расходящихся историй: стратегии Git Merge и Rebase
Git merge и rebase помогают, когда ваша ветка расходится с другой веткой. Разница в том, как они сохраняют историю, и неправильный выбор может усложнить совместную работу больше, чем необходимо.
Вам не нужна идеальная история для каждого изменения. Вам нужна стратегия, которую понимает ваша команда и которая поддерживает стабильность общих веток.
Что означает расходящаяся история
Расходящаяся история возникает, когда две ветки имеют разные коммиты после общей точки начала. Например, вы создали feature/log-cleanup из main в понедельник. Во вторник кто-то влил исправление безопасности в main. Теперь ваша ветка и main имеют коммиты, которых нет у другой.
Вы можете увидеть это с помощью:
git log --oneline --graph --decorate --all
Вы также можете увидеть, как Git сообщает, что ваша ветка и удаленная ветка разошлись. Это означает, что обе стороны имеют уникальные коммиты. Git требует, чтобы вы решили, как их объединить.
Два обычных инструмента — это merge и rebase:
git merge main
или:
git rebase main
Оба могут привести к одинаковому конечному содержимому файлов. История будет выглядеть по-разному.
Как работает Git Merge
Merge объединяет истории без перезаписи существующих коммитов. Если ваша ветка и main продвинулись вперед, Git создает коммит слияния, который связывает две линии работы.
Типичный поток выглядит так:
git switch feature/log-cleanup
git fetch origin
git merge origin/main
Если конфликтов нет, Git создает коммит слияния или выполняет fast-forward, когда это возможно. Если есть конфликты, Git приостанавливается и просит вас их разрешить.
Merge хорош, когда вы хотите сохранить истинную форму совместной работы. Он показывает, что работа выполнялась параллельно и была объединена позже. Это может быть полезно для долгоживущих веток, релизных веток и общих функциональных веток.
Компромисс в том, что частые слияния могут создавать шум. Ветка со многими коммитами "merge main into feature" может быть сложнее для просмотра. Это не значит, что это неправильно, но это может сделать историю менее аккуратной.
Используйте merge, когда:
- Ветка используется совместно с другими разработчиками.
- Вы хотите избежать перезаписи истории коммитов.
- Ваша команда ценит точную запись точек интеграции.
- Вы обновляете релизную ветку или защищенную ветку.
Для ветки, которая уже была отправлена и используется другими, merge обычно является более безопасным выбором по умолчанию.
Как работает Git Rebase
Rebase перемещает коммиты вашей ветки так, чтобы они оказались поверх нового базового коммита. Вместо отображения двух расходящихся линий, соединенных коммитом слияния, история ветки становится линейной.
Типичный поток выглядит так:
git switch feature/log-cleanup
git fetch origin
git rebase origin/main
Git воспроизводит ваши коммиты один за другим поверх последнего main. Если возникает конфликт, Git останавливается на коммите, который не может быть воспроизведен. После исправления конфликта продолжите с помощью:
git add <исправленные-файлы>
git rebase --continue
Если rebase становится запутанным, вы можете отменить его:
git rebase --abort
Rebase полезен для локальных, частных веток, потому что он сохраняет историю легко читаемой. Рецензенты могут видеть ваши коммиты так, как если бы они были созданы на основе последнего main с самого начала.
Используйте rebase, когда:
- Ветка ваша и не является общей.
- Вы хотите чистую, линейную историю коммитов.
- Вы готовите ветку перед открытием pull request.
- Ваша команда явно предпочитает перебазированные функциональные ветки.
Главное правило простое: не перебазируйте публичные коммиты, если ваша команда этого не ожидает. Rebase перезаписывает хэши коммитов. Если кто-то другой основывал работу на ваших старых коммитах, ваша перезапись может создать для них путаницу.
Для получения дополнительных сведений о повседневных инструментах истории Git см. изучение истории проекта.
Обработка конфликтов во время Merge или Rebase
Конфликты возникают, когда Git не может автоматически объединить изменения. Они распространены в часто изменяемых файлах, таких как манифесты развертывания, файлы блокировки зависимостей и общие конфигурационные файлы.
Начните с проверки статуса:
git status
Откройте конфликтующие файлы и найдите маркеры конфликтов:
<<<<<<< HEAD
текущая версия ветки
=======
входящая версия
>>>>>>> имя-ветки
Ваша задача — заменить отмеченный блок на окончательное содержимое, которое вы действительно хотите. Не оставляйте маркеры.
После редактирования добавьте файл в индекс:
git add путь/к/файлу
Для merge завершите с помощью:
git commit
Для rebase продолжите с помощью:
git rebase --continue
Запустите тесты или хотя бы соответствующую команду проверки после разрешения конфликтов. Файл может быть синтаксически чистым, но логически неверным. Например, два изменения Kubernetes YAML могут объединиться без конфликта, но при этом определять дублирующиеся порты или несовпадающие метки.
Выбор командной стратегии
Лучшая стратегия — та, которая создает предсказуемую историю для вашего проекта. Многие команды используют rebase для частных функциональных веток и коммиты слияния для pull request. Другие сжимают всю функциональную работу в один коммит. Некоторые команды инфраструктуры предпочитают коммиты слияния, потому что они показывают, когда именно ветки были интегрированы.
Выберите правило и задокументируйте его. Простая политика может быть такой:
- Перебазируйте свою собственную функциональную ветку перед рецензией.
- Никогда не перебазируйте
main, релизные ветки или общие ветки. - Используйте коммиты слияния для одобренных pull request.
- Используйте squash merge для небольших исправлений одной цели.
Это предотвращает споры о стиле Git во время срочной работы. Это также упрощает автоматизацию, поскольку CI, заметки о релизе и инструменты развертывания могут полагаться на согласованную историю.
Когда обращаться за помощью
Спрашивайте перед force-push после rebase на ветке, которую могут использовать другие. Также спрашивайте, когда конфликт затрагивает настройки безопасности, миграции баз данных, файлы развертывания продакшена или сгенерированные файлы блокировки, которые вы не понимаете.
Если merge или rebase запутываются, остановитесь и проверьте состояние с помощью git status. Обычно вы можете прервать и вернуться к предыдущему состоянию с помощью git merge --abort или git rebase --abort. Это лучше, чем пробовать случайные команды, пока рабочее дерево не станет чистым.
Merge и rebase решают одну и ту же общую проблему, но рассказывают разные истории. Merge сохраняет то, как работа объединилась. Rebase создает более чистую линию коммитов. Используйте каждый там, где он подходит, и ваша история Git останется полезной, а не станет источником риска.