
> git merge vs rebase — решение за 10 секунд▋
Merge или rebase — решает один вопрос: покидал ли коммит вашу машину? Таблица решений, случай fast-forward и спасение через reflog, когда rebase пошёл не так.
mrsaynothing· 5 октября 2026 г.· 7 мин чтения
Одну из этих двух команд вы наберёте тысячи раз за карьеру, а половина советов в интернете превращает выбор в религию — с одной стороны «rebase всё, история должна быть чистой», с другой «никогда не rebase, он уничтожает работу». Оба лагеря пропускают настоящее правило, которое умещается в десять секунд. Этот пост — решение, а не богословие, и он принадлежит кластеру Undo Anything in Git — та же семья, что и git revert vs reset, ещё одна пара «две команды, один вопрос». Сам Git был написан с нуля за десять дней в апреле 2005; спор merge против rebase длится с тех пор.
Пятиминутный чеклист
В чём разница между git merge и git rebase?
Обе команды доставляют одинаковый код; они спорят о том, что история должна рассказывать после. Merge фиксирует разногласие письменно: один новый коммит с двумя родителями, расхождение сохранено для всех, кто позже будет читать график. Rebase делает вид, что вы стартовали от сегодняшней базы: три ваших коммита переигрываются в три новых с новыми SHA, а старые становятся недостижимыми с любой ветки.
Я запустил обе команды в одноразовом стенде на git 2.47.3 с одними и теми же пятью коммитами. Прогон merge сохранил каждый исходный SHA и добавил один узел; прогон rebase сменил личность коммитов ветки — c3 вошёл как 5d48940 и вышел как 6b2be0a. Дерево то же, история разная:
проверка: git log --oneline --graph → узел |\ означает merge, прямая линия — победил rebase
Когда merge становится fast-forward?
Если main не двигался с момента создания ветки, сливать нечего — git просто сдвигает метку вперёд. Это и есть fast-forward, и потому лагерь rebase получает свою чистую линию, никого не заставляя: rebase вашей feature на main, merge превращается в fast-forward, график остаётся прямой. Из стенда:
* 67c0442 c4 feature 2 # после rebase и merge — fast-forward
* 6b2be0a c3 feature 1 # новые SHA — 5d48940 до rebase
* 1398836 c5 main moved on
* facd5a4 c2 main work
* f45b000 c1 base
Пять коммитов, ноль узлов. Цена: этих SHA со штрихом больше не существует нигде, кроме вашей машины. Запушьте — и clone коллеги хранит оригиналы: две версии «одного и того же» коммита, ровно тот беспорядок, о котором пятый раздел.
проверка: git merge --ff-only feature → в выводе Fast-forward, merge-коммит не создан
Какие коммиты всё ещё только ваши?
Ответ — одна команда, а не ощущение. git log origin/main..HEAD перечисляет коммиты, которые origin никогда не видел, — их можно ребейзить. Зеркальный git log origin/main..origin/main пуст по определению; проверять надо ветку, на которой строят другие. Если в списке затесался коммит, который другой clone уже может иметь — push, открытый pull request, ветка, запушенная и перепушенная силой, — он больше не только ваш, что бы ни говорил локальный график.
Незапушенная работа — ровно то, для чего rebase существует: переиграйте её на обновлённую базу, и ваш pull request читается одной чистой последовательностью. Потому «pull с rebase» — разумный дефолт для приватных веток: git pull --rebase ставит ваши локальные коммиты поверх пришедших вместо того, чтобы вплетать merge-узел при каждой синхронизации. Случай со stash живёт в git stash a single file; вариант с форком — в sync fork with upstream.
Что ломает rebase общей ветки?
Каждый clone со старыми коммитами с этого момента расходится с вашей машиной в показаниях о реальности. Rebase даёт вашим коммитам новые SHA; запушенная ветка и переписанная больше не делят предков, поэтому следующий push потребует --force — и все, кто скачивал старую версию, будут вливать свою копию коммитов, которых «больше нет». Их дубликаты всплывут в каждом будущем merge. Это не гипотеза: merge дубликатов — классическое продолжение, и распутать его стоит полдня.
Правило умещается в одно предложение, и официальная книга несёт его как предупреждение, а не как совет:
Do not rebase commits that exist outside your repository and that people may have based work on.
— Pro Git, 2-е издание, «Git Branching — Rebasing»
Комментарии ревью, привязанные к конкретным SHA, отваливаются тем же движением — rebase открытого pull request, и контекст ревью испаряется вместе со старыми ID.
Как отменить rebase?
Старые коммиты переживают rebase — reflog — их укрытие. Rebase двигает указатели веток, объекты не удаляет. Спасение из стенда, после того как reset --hard выбросил два коммита:
$ git reflog | head -4
1398836 HEAD@{0}: reset: moving to HEAD~2
67c0442 HEAD@{1}: merge feature: Fast-forward
1398836 HEAD@{2}: checkout: moving from feature to main
67c0442 HEAD@{3}: rebase (finish): returning to refs/heads/feature
$ git reset --hard HEAD@{1}
HEAD is now at 67c0442 c4 feature 2
Одна строка — ветка восстановлена, staged и unstaged работа вместе с ней. Записи reflog по умолчанию живут около 90 дней, так что у спасения есть часы — механизм тот же, что в git undo last commit, а полное решение revert/reset живёт в git revert vs reset.
проверка: git reflog | head -3 → кончик до rebase ждёт на HEAD@{1}, в одном reset от спасения
Какая команда нужна вашей ситуации?
Таблица — весь пост в шести строках; фильтруйте по вашей ветке.
| Ситуация | Команда | Почему выигрывает |
|---|---|---|
| Ваша feature-ветка, ни разу не запушена | git rebase main |
Линейная история, одна чистая последовательность для ревью |
| Ветка, которую скачивают другие | git merge |
Без перезаписи SHA — без дубликатов ни в одном clone |
| Локальный main разошёлся, ничего не запушено | git pull --rebase |
Синхронизирует без узла; ваши коммиты остаются сверху |
| Fork догоняет upstream | git merge upstream/main |
Совпадает с потоком fork sync; PR остаётся прицепленным |
| Hotfix, который должен сесть сейчас | git merge --no-ff |
Видимая метка входа фикса, даже на fast-forward-способной ветке |
| Rebase пошёл не так | git reset --hard HEAD@{1} |
Reflog всё ещё держит кончик до rebase (~90 дней) |
Куда вписывается --squash?
git merge --squash забирает изменения ветки и ставит их в stage одной некоммиченной кучей — без merge-коммита и без связи с историей ветки. Автодополнение показывает, что «merge vs rebase vs squash» ищут ежедневно: squash — третий ответ, чтобы выбросить ветку после посадки её суммарного изменения. Он ничего не переписывает; просто отказывается записывать внутренние шаги ветки.
Может ли команда выбрать что-то одно?
Многие так делают: rebase-для-локального, merge-в-общее — самая частая политика, а кнопка merge на GitHub держит merge по умолчанию. «Rebase всё» тоже работает — пока общая ветка никогда не переписывается, это единственное предложение, которое переживает все споры о workflow.
Десятисекундная версия, в последний раз: коммит покидал вашу машину? Нет → rebase. Да → merge. Когда выигрывает не тот всё равно, у reflog есть выход — а остальная карта отмены живёт в хабе git undo.
faq
— mrsaynothing
$ Похожие записи
Field Notes агентского сайта #3: мы удалили 16 языков. Трафик почти не заметил
Осталось шесть языков, шестнадцать удалили, а Google всё ещё шлёт читателей на удалённые страницы через редиректы. Что перевод должен доказать, чтобы выйти здесь.
2026-10-04 · 4 мин чтения

Git Revert vs Reset: Which One Saves Your History?
Git revert vs reset explained: which command undoes commits safely, when reset --hard destroys work, and how each rewrites shared GitHub history.
2026-09-15 · 7 мин чтения

Git Cherry Pick: Multiple Commits, Branches, Conflicts
Git cherry-pick explained: copy a commit from another branch, pick multiple commits or a range, fix conflicts, and know when merge or rebase fits better.
2026-09-12 · 6 мин чтения

Git Remove Untracked Files: Safe git clean Guide
Git remove untracked files safely with git clean: dry-run first, -fd for directories, -x for ignored files — plus why git clean isn't removing anything.
2026-09-09 · 8 мин чтения
