← Назад к вопросам

Что предпочитаешь для обновлении ветки в git - git merge или git rebase?

2.0 Middle🔥 141 комментариев
#Git и VCS

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Git merge vs git rebase: выбор для обновления ветки

Это один из самых спорных вопросов в Git сообществе. Оба метода работают, но имеют разные последствия для истории проекта, отладки и совместной работы. Я подробно разберу оба подхода.

Git merge — сохранить историю

merge создаёт новый коммит, объединяющий две ветки:

# На ветке feature
git merge main
# или
git merge origin/main

Визуально:

До merge:

main:    A--B--C
          \
feature:  D--E

После merge:

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

Код в merge коммите:

# M объединяет изменения из C и E
# Git автоматически разрешает конфликты (если может)

Git rebase — переписать историю

rebase перемещает коммиты ветки на вершину main:

# На ветке feature
git rebase main
# или
git rebase origin/main

Визуально:

До rebase:

main:    A--B--C
          \
feature:  D--E

После rebase:

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

Что произошло:

  • D переместился на вершину C и стал D'
  • E переместился на вершину D' и стал E'
  • Истории разных коммитов остались, но основание изменилось

Ключевые отличия

Аспектmergerebase
ИсторияСохраняется полностьюПереписывается (выглядит линейно)
КонфликтыОдин раз в merge коммитМожет быть несколько раз для каждого коммита
Читаемость историиВидна структура разработкиЛинейная, чистая история
БезопасностьОчень безопасна, ничего не теряетсяОпасна если неправильно использовать
Совместная разработкаOK для shared ветокЗАПРЕЩЕНА для published веток
Отладка (git bisect)Проще прыгать между векамиМожет быть путаница с переписанными коммитами

Практический пример: merge

# Состояние
main:    A--B--C--D
          \
feature:  E--F

# Команда
$ git checkout feature
$ git merge main

# Результат
main:    A--B--C--D
          \ \
feature:  E--F--M (merge commit)

# История коммитов feature:
# M - Merge branch 'main' into 'feature'
# F - Feature работа
# E - Feature работа
# D - Main работа
# C - Main работа

Практический пример: rebase

# Состояние
main:    A--B--C--D
          \
feature:  E--F

# Команда
$ git checkout feature
$ git rebase main

# Результат
main:    A--B--C--D
              \
feature:       E'--F'

# История коммитов feature:
# F' - Feature работа (переписано)
# E' - Feature работа (переписано)
# D - Main работа
# C - Main работа

Мой выбор и рекомендации

Я предпочитаю комбинированный подход:

  1. Для локальных веток (не опубликованных):
    • Используй rebase для чистой истории
    • Более удобно работать с локальной веткой
# На локальной feature ветке
$ git fetch origin
$ git rebase origin/main  # Чистая история
  1. Для shared веток (main, develop):
    • Используй merge для безопасности
    • Сохраняет информацию о том, когда произошло объединение
# Из feature в main (через PR/MR)
$ git checkout main
$ git pull origin main
$ git merge --no-ff feature  # Создание merge коммита
  1. Для PR/MR в GitHub/GitLab:
    • Позволить инструменту выбрать (часто "Squash and merge")
    • Или используй rebase, если история локальной ветки чистая

Обработка конфликтов

При merge:

$ git merge main
# Конфликт!

# Редактируем файлы вручную
# <<<<<<< HEAD
# код из feature
# =======
# код из main  
# >>>>>>> main

# Сохраняем как хотим, потом
$ git add .
$ git commit -m "Merge branch 'main' into 'feature'"

При rebase:

$ git rebase main
# Конфликт на коммите E!

# Исправляем файлы
$ git add .
$ git rebase --continue  # Продолжить для следующего коммита

# Если конфликт снова
# Исправляем и опять --continue

# Или отменить
$ git rebase --abort

Когда rebase опасна

НИКОГДА не rebase публичные ветки!

# ❌ НЕПРАВИЛЬНО
$ git checkout main
$ git rebase develop  # ОПАСНО!
$ git push --force    # Ломает историю для других!

# ✅ ПРАВИЛЬНО
$ git checkout main
$ git merge develop   # Безопасно, сохраняет историю

Почему опасно:

  • Переписывает историю main
  • Другие разработчики имеют старые коммиты
  • Git будет просить force push
  • Можно потерять данные при неправильном разрешении конфликтов

Реальный рабочий процесс

# 1. Создаёшь локальную ветку
$ git checkout -b feature/new-api main

# 2. Работаешь несколько дней
$ git commit -m "API endpoint 1"
$ git commit -m "API endpoint 2"

# 3. main получил новые коммиты
# Обновляешь свою ветку (rebase, т.к. она локальная)
$ git fetch origin
$ git rebase origin/main

# 4. Отправляешь в origin
$ git push origin feature/new-api

# 5. Создаёшь PR/MR

# 6. Code review, потом merge (не rebase!)
# GitHub/GitLab выполнит merge в main

Конфигурация для удобства

# Автоматически создавать merge коммиты для shared веток
$ git config --global merge.ff false  # Всегда merge коммит

# Для fast-forward только на локальных веткахе
$ git config --local merge.ff true

Правила команды

Рекомендуемые правила для команды:

  1. main и develop — только merge (--no-ff)
  2. feature ветки — можно rebase перед PR
  3. Hotfix ветки — merge в main
  4. Release ветки — merge в main и develop
# .git/hooks/pre-push (пример)
#!/bin/bash
if [ "$(git rev-parse --abbrev-ref HEAD)" = "main" ]; then
    echo "Ошибка: push в main запрещён, используй PR!"
    exit 1
fi

Итоговая рекомендация

Используй rebase для:

  • Локальных веток перед PR
  • Очистки истории перед публикацией
  • Когда нужна линейная история

Используй merge для:

  • Объединения в shared ветки (main, develop)
  • Когда нужно сохранить информацию об интеграции
  • В командной разработке для безопасности

Золотое правило: "Никогда не rebase публичные ветки. Используй merge для shared веток, rebase для своих веток."

Выбор между ними зависит от культуры команды и требований проекта. Главное — консистентность и безопасность.