Перейти к содержанию
Инструменты

Git rebase или merge: сравните историю в небольшом репозитории

Merge и rebase позволяют учесть изменения другой ветки, но оставляют разную историю. В отдельном временном репозитории проследите исходные коммиты, их повторное применение и восстановление, а затем выбирайте подход для общей ветки.

Автор: Опубликовано

5 мин чтенияРедакционный разборОбновлено
  • Git
  • Rebase
  • Merge
  • История коммитов
Главная мысль

Merge сохраняет существующие коммиты и при расхождении веток фиксирует объединение. Rebase повторно применяет изменения на новой базе и может создать другие ID. Выбор зависит от владельцев ветки и правил команды.

Выясните, кто уже использует историю ветки

Для собственных неопубликованных коммитов rebase на main может сделать следующее ревью понятнее. Если другие разработчики уже построили работу на этой ветке, merge сохраняет опорные коммиты и не заставляет их разбирать переписанную историю. Перед публикацией изменения истории проверьте принятый в команде способ интеграции.

Merge не всегда создаёт merge-коммит. Если текущий коммит является предком входящего, возможен fast-forward: Git передвинет указатель ветки. --no-ff требует merge-коммит там, где иначе возможен fast-forward. Поэтому линейный граф сам по себе не доказывает, что кто-то использовал rebase.

Выбор зависит от владения и требований к истории
СитуацияОбычный выборПричина
Личная веткаRebaseРевью на текущей базе
Общая feature-веткаMergeСохранить коммиты
Нужна граница интеграцииMerge с --no-ffОставить merge-коммит
Защищённая mainПравила репозиторияРазрешённый процесс

Создайте одинаковое расхождение для двух опытов

Выполните команды в оболочке с Git, поддерживающим switch и init -b. Они создают новый временный каталог, задают автора только в этом репозитории и не обращаются к remote. Сохраните каталог для изучения: упражнение меняет только новый репозиторий. Разные файлы выбраны специально, чтобы избежать конфликтов содержимого.

A — общий исходный коммит. B добавляет feature.txt в feature; C добавляет main.txt в main. Ветки merge-demo и rebase-demo начинают с одного B. Так можно сравнить два преобразования одинаковой исходной работы. В реальном проекте после интеграции нужны проверки: отсутствие текстового конфликта ещё не означает совместимость поведения.

DEMO_DIR=$(mktemp -d)
cd "$DEMO_DIR"
git init -b main
git config user.name "History Demo"
git config user.email "demo@example.invalid"
printf '%s\n' 'base' > base.txt
git add base.txt
git commit -m "A: base"
git switch -c feature
printf '%s\n' 'feature' > feature.txt
git add feature.txt
git commit -m "B: feature"
git switch main
printf '%s\n' 'main update' > main.txt
git add main.txt
git commit -m "C: main update"
git switch -c merge-demo feature
git merge --no-ff main -m "M: merge main into feature"
git log --graph --oneline --all
git switch -c rebase-demo feature
git rebase main
git log --graph --oneline main rebase-demo

Читайте родителей коммита, а не порядок строк

В merge-demo коммит M имеет двух родителей: B и C. Исходный ID коммита B остаётся в истории. Граф фиксирует включение main в feature-ветку, а рабочее дерево содержит оба добавленных файла.

В rebase-demo повторно применённый feature-коммит идёт после C. Назовём его B-прим: родитель теперь C вместо A, поэтому меняется ID, хотя изменение feature.txt в упражнении осталось тем же. Исходная feature всё ещё указывает на B: rebase передвинул только rebase-demo. Сравните git show feature и git show rebase-demo, затем изучите родителей через git rev-list --parents -n 1 для каждого результата.

Продолжайте именно ту операцию, которая остановилась

При rebase Git может останавливаться на каждом конфликтующем коммите. Изучите git status, исправьте файл, добавьте его в индекс и продолжите. --skip пропускает применение текущего коммита; используйте его только если намеренно исключаете это изменение. --abort возвращает ветку к состоянию до rebase, пока операция ещё выполняется.

Ниже приведены альтернативы, а не последовательный скрипт. Замените пути и placeholder проверенного коммита. При конфликте merge нужны git merge --continue или git merge --abort. Начинайте операцию с чистым рабочим деревом либо заранее сохраните изменения: merge abort не всегда способен восстановить посторонние незакоммиченные правки.

git status
git add path/to/resolved-file
git rebase --continue
# Отмена незавершённого rebase вместо продолжения:
git rebase --abort
# После завершения сначала найдите и проверьте старый коммит:
git reflog show feature
OLD_TIP=replace-with-verified-commit
git branch rescue-feature "$OLD_TIP"

Сначала сохраните найденный старый коммит

После завершённого rebase --abort уже не действует. Найдите прежнюю вершину нужной ветки в её reflog, проверьте содержимое коммита и создайте на нём rescue-ветку. Это сохраняет доступ без немедленного reset текущей ветки. В нашем упражнении менялась rebase-demo, поэтому смотрите её reflog, а не историю feature.

Reflog — локальная история указателей, а не бессрочная удалённая резервная копия. Записи и недостижимые объекты могут удаляться, а в другом клоне их может не быть. Перед крупным переписыванием проще создать именованную backup-ветку. Не запускайте скопированный reset --hard, пока не сохранили текущие незакоммиченные изменения.

Публикацию новой истории обсуждайте отдельно

Обычный push после rebase может быть отклонён, поскольку удалённая вершина больше не является предком локальной. Это полезный сигнал. Прежде чем заменять опубликованную историю, проверьте владение веткой и договорённости команды. Rebase собственной ветки не означает разрешение переписать main или чужую ветку.

При согласованном переписывании явный --force-with-lease=<ref>:<expected-commit> проверяет, что remote всё ещё содержит подтверждённую вершину. Он не устраняет последствия для коллег. Краткая форма опирается на remote-tracking данные и может потерять защиту, если фоновый fetch их обновил. Следуйте принятой процедуре публикации и проверьте результат после неё.

Коротко

Частые вопросы

Меняет ли rebase сам код?

Он повторно применяет выбранные изменения на другой базе. В итоговом коде есть изменения этой базы, а разрешение конфликтов может поменять результат. В нашем простом опыте feature-изменение сохранилось, но родитель и ID коммита другие.

Всегда ли merge создаёт дополнительный коммит?

Нет. При fast-forward Git передвигает указатель. Merge-коммит появляется при расхождении истории или когда его требуют выбранные параметры.

Можно ли отменить завершённый rebase через --abort?

Нет. Abort относится к текущей незавершённой операции. После завершения изучите reflog, проверьте старую вершину и создайте rescue-ветку перед восстановлением.

Безопасен ли force-with-lease для общей ветки?

Он проверяет условие обновления remote, но всё равно переписывает опубликованную историю и требует договорённости. Явная ожидаемая вершина не зависит только от remote-tracking данных, которые может обновить фоновый fetch.

Источники

Источники и редакционная политика

Сведения проверены 4 октября 2026 г. Ссылки рядом с разделами указывают источники фактов и технических объяснений. Выводы, учебные сценарии и рекомендации по подготовке — редакционная работа RecallDeck.

От чтения к воспроизведению

Отрепетируйте полный цикл интервью.

RecallDeck возвращает сложные темы по расписанию и помогает удерживать в памяти язык, SQL, архитектуру и поведенческие истории.

Начать подготовку

Продолжить подготовку