↓Перейти к содержанию
  1. Git: руководства и команды/

git merge vs rebase: в чём разница и когда что использовать

·7 минут·

Одна из самых важных дискуссий в сообществе Git разработчиков вращается вокруг выбора между git merge и git rebase. Обе команды решают одну задачу: интегрировать изменения из одной ветки в другую. Однако способы, которыми они это делают, радикально отличаются, и выбор между ними влияет на историю вашего проекта, читаемость логов и возможность отката изменений.

В этой статье мы разберем, как работает каждая команда, сравним их по ключевым параметрам и выясним, в каких ситуациях использовать одну или другую. Эти знания помогут вам и вашей команде работать с Git более эффективно и без конфликтов в процессе разработки.

Как работает git merge #

Команда git merge объединяет две ветки, создав специальный коммит слияния, который имеет двух родителей: последний коммит текущей ветки и последний коммит объединяемой ветки.

Представьте себе, что вы работаете на ветке feature, а в ветке main тем временем появились новые коммиты. Вот визуальное представление ситуации:

main:     A -- B -- C -- D
                   /
feature:           E -- F -- G

После выполнения команды:

git checkout main
git merge feature

История будет выглядеть так:

main:     A -- B -- C -- D -- M (merge commit)
                   /          /
feature:           E -- F -- G

Коммит M (merge commit) имеет двух родителей: D и G. Он содержит все изменения из обеих веток. Вот пример просмотра истории с визуализацией:

git log --oneline --graph --all
* commit-hash-M Merge branch 'feature' into 'main'
|\
| * commit-hash-G Feature: add new functionality
| * commit-hash-F Refactor code
| * commit-hash-E Initial feature implementation
* commit-hash-D Fix bug in main
* commit-hash-C Add documentation
* commit-hash-B Update dependencies
* commit-hash-A Initial commit

Это сохраняет полную историю того, что каждая ветка делала независимо друг от друга. При слиянии с main могут возникнуть конфликты, если обе ветки изменили одни и те же строки. Разрешение конфликтов также создается как отдельный коммит слияния.

Преимущества git merge #

  • Полная история ветвлений сохраняется
  • Безопасна для публичных веток
  • Просто добавляет коммит, не переписывая историю
  • Очевидно, где произошло объединение (коммит слияния)

Недостатки git merge #

  • История может быть запутанной с множеством веток
  • Много коммитов слияния в активных проектах
  • Сложнее найти конкретное изменение в длинной истории

Как работает git rebase #

Команда git rebase работает иначе. Вместо создания коммита слияния, она перемещает (перепишет) все коммиты из одной ветки на основу другой ветки.

Вернемся к нашему примеру:

main:     A -- B -- C -- D
                   /
feature:           E -- F -- G

После выполнения:

git checkout feature
git rebase main

История становится линейной:

main:     A -- B -- C -- D
                           \
feature:                     E' -- F' -- G'

Коммиты E, F, G переписаны и получили новые хеши (E’, F’, G’). Эти новые коммиты содержат те же изменения (diff), но построены поверх последнего коммита main (D).

Вот как это выглядит в лог:

git log --oneline --graph
* commit-hash-G' Feature: add new functionality
* commit-hash-F' Refactor code
* commit-hash-E' Initial feature implementation
* commit-hash-D Fix bug in main
* commit-hash-C Add documentation
* commit-hash-B Update dependencies
* commit-hash-A Initial commit

История полностью линейная, без веток. Это делает логи намного чище и понятнее.

Преимущества git rebase #

  • Чистая, линейная история
  • Легче найти конкретное изменение (git bisect работает лучше)
  • Логи читаются как одна связная история
  • Нет “шумных” коммитов слияния

Недостатки git rebase #

  • Переписывает историю — хеши коммитов меняются
  • Опасна для публичных веток
  • Может привести к потере работы, если что-то пошло не так
  • Конфликты нужно разрешать для каждого переписанного коммита

Сравнение: главные различия #

Вот таблица, которая наглядно показывает различия между merge и rebase:

Параметрgit mergegit rebase
ИсторияСохраняет ветвления и слиянияЛинейная история
Коммит слиянияСоздается специальный merge commitНет merge commit
Хеши коммитовНе меняютсяПереписываются новыми хешами
БезопасностьБезопасна всегдаОпасна для публичных веток
Читаемость логовМожет быть запутаннойОчень читаема и понятна
Разрешение конфликтовОдин раз в merge commitДля каждого конфликтного коммита
ОткатПросто git reset или git revertСложнее, может потребоваться reflog
Для публичных ветокРекомендуетсяНИКОГДА

Золотое правило rebase: НИКОГДА не rebase публичные ветки #

Это правило невозможно переоценить. Если вы выполните rebase на ветке, которая уже push’ена в удаленный репозиторий и другие люди на ней работают, вы создадите серьезные проблемы.

Правильно (безопасно):

# Вы работаете на локальной feature ветке
git checkout my-feature
git rebase main
# История переписана локально, это безопасно
git push origin my-feature  # первый push

# Или безопасный rebase в PR
git push origin my-feature -u

Неправильно (опасно):

# Ветка уже в удаленном репозитории, на ней работают другие
git push origin main
git rebase origin/develop  # ОПАСНО!
git push origin main --force  # ОЧЕНЬ ОПАСНО!

# Другие разработчики получат конфликты и потеряют историю

Когда вы делаете git push --force после rebase публичной ветки, все остальные разработчики, которые использовали старые коммиты, получат конфликты слияния и потеряют свою работу. История становится несогласованной между локальными копиями и сервером.

Когда использовать git merge #

  • Pull request в основную ветку — используйте merge, чтобы сохранить историю интеграции
  • Слияние в main/master — merge показывает, когда и какие feature были добавлены
  • Коллаборативные ветки — если над веткой работают несколько человек, merge безопаснее
  • Историческая ценность — когда важно сохранить полную историю ветвлений проекта
  • Откат целой feature — merge commit легче откатить одной командой git revert <merge-commit>

Когда использовать git rebase #

  • Локальные feature ветки — перед отправкой в PR, rebase на main для чистоты
  • Синхронизация с главной веткой — git rebase main перед push в PR
  • Очистка неопубликованных коммитов — переоформатирование набросков перед push
  • Interactive rebase — для укрупнения, переписания или удаления коммитов
  • Личный форк проекта — если вы работаете в форке, rebase main безопасен
  • Контроль точки ветвления — убедиться, что ваши коммиты отталкиваются от последнего main

Interactive rebase (git rebase -i) #

Interactive rebase — это мощный инструмент для редактирования истории своих коммитов. С помощью -i вы можете:

  • Объединять коммиты (squash) в один
  • Переописывать сообщения коммитов (reword)
  • Переупорядочивать коммиты (reorder)
  • Удалять коммиты (drop)
  • Разделять коммиты (edit)
# Переписать последние 3 коммита
git rebase -i HEAD~3

# Откроется редактор со списком:
# pick abc1234 Fix bug in login
# pick def5678 Add test for login
# pick ghi9012 Update documentation

# Измените pick на squash для объединения или reword для переписания
# pick abc1234 Fix bug in login
# squash def5678 Add test for login
# reword ghi9012 Update documentation

# Сохраните, и Git проведет вас через процесс

После интерактивного rebase ваши коммиты будут переписаны в одно или несколько более логичных изменений.

git pull –rebase #

По умолчанию git pull выполняет git fetch и затем git merge. Если вы предпочитаете rebase, используйте флаг:

# Один раз
git pull --rebase

# Для текущей ветки
git config branch.<branch-name>.rebase true

# Глобально для всех веток (осторожнее!)
git config --global pull.rebase true

Это полезно, если вы хотите сохранять линейную историю в своих локальных ветках, не загромождая логи коммитами слияния из pull.

FAQ #

В: Как выбрать стратегию слияния для pull request на GitHub? О: GitHub предлагает три опции при закрытии PR:

  • Create a merge commit — обычный merge
  • Squash and merge — объединяет все коммиты в один и затем merge
  • Rebase and merge — rebase на основу и fast-forward merge

Для большинства проектов рекомендуется “Create a merge commit” или “Squash and merge”, чтобы сохранить историю и оставить main/master чистыми.

В: Я случайно сделал rebase публичной ветки. Что делать? О: Если вы еще не push’или, просто не push’ьте. Если уже push’или, используйте:

git reflog  # найти старый коммит
git reset --hard <старый-hash>
git push --force  # только если вы уверены

В: Почему merge конфликты другие, чем rebase конфликты? О: Merge создает конфликт в один момент времени (при merge). При rebase каждый переписанный коммит может иметь свой конфликт, потому что код меняется поэтапно.

В: Как я могу практиковать rebase без риска? О: Создайте временную ветку:

git checkout -b test-rebase
git rebase main  # экспериментируйте здесь
git reset --hard origin/my-feature  # если что-то пошло не так

В: Можно ли настроить Git на использование rebase по умолчанию? О: Да:

git config --global pull.rebase true  # глобально
git config pull.rebase true  # для проекта

Заключение и практические рекомендации #

Лучшей практикой считается гибридный подход:

  1. На локальной feature ветке используйте git rebase main перед отправкой PR, чтобы убедиться, что ваши коммиты отталкиваются от последнего состояния main.

  2. При закрытии PR используйте “Create a merge commit” или “Squash and merge”, чтобы сохранить историю интеграции в main.

  3. Для interactive rebase используйте только на локальных ветках до первого push.

  4. НИКОГДА не делайте rebase публичных веток.

Это обеспечивает:

  • Чистые локальные истории (благодаря rebase перед отправкой)
  • Ясную историю интеграции в main (благодаря merge коммитам)
  • Безопасность для команды (благодаря соблюдению правила)

Читайте также:

  • /ru/git/git-merge/ — более детально о merge
  • /ru/git/git-rebase/ — полный справочник по rebase
  • /ru/git/konflikty-git-merge/ — как разрешать конфликты

Помните: выбор между merge и rebase — это вопрос соглашения в команде. Обсудите со своей командой, какой подход вы будете использовать, и придерживайтесь этого. Последовательность намного важнее идеального выбора.