Skip to main content

Яка різниця між git merge та git rebase?

git merge інтегрує гілку, створюючи новий коміт з двома батьками і зберігаючи повну записану історію обох гілок. git rebase відтворює твої коміти поверх іншої гілки, переписуючи їхні SHA і формуючи лінійну послідовність.

Теорія

TL;DR

  • Аналогія: merge це зшивання двох звітів разом, обидва шляхи залишаються видимими. Rebase це переписування своїх нотаток у кінець основного звіту так, щоб виглядало як один документ.
  • Головна різниця: merge додає один коміт з двома батьками; rebase створює нові коміти з новими SHA для кожного відтвореного.
  • Merge зберігає точки розходження і злиття гілок. Rebase робить так, наче робота йшла послідовно від початку.
  • Правило вибору: спільна або публічна гілка? Merge. Локальна feature-гілка перед PR? Rebase.
  • Після push на спільний remote: тільки merge. Rebase після push ламає клони команди.

Швидкий приклад

bash
# main має коміт B, feature має D і E git checkout main && echo "B" >> app.js && git commit -m "B" git checkout -b feature echo "D" >> app.js && git commit -m "D" echo "E" >> app.js && git commit -m "E" # Варіант 1: merge git checkout main && git merge feature # Історія: A-B-M (M має двох батьків: B і E) # Варіант 2: rebase (на feature-гілці, перед мержем) git checkout feature && git rebase main # Історія: A-B-D'-E' (D' і E' мають нові SHA)

Після merge в логу видно розгалуження і з'єднання. Після rebase пряма лінія. Код однаковий, але записана версія подій різна.

Ключова різниця

Merge знаходить спільного предка двох гілок, обчислює three-way diff і записує новий коміт з обома верхівками як батьками. Оригінальні коміти не змінюються. Rebase бере кожен коміт між точкою розгалуження і верхівкою feature-гілки, відв'язує його від оригінального батька і по черзі відтворює на новій базі через cherry-pick. Оскільки змінюється SHA батька, змінюється і SHA коміту, навіть якщо diff ідентичний. Саме тому не можна безпечно робити rebase на гілці, яку вже клонували колеги.

Коли що використовувати

  • Спільна гілка (main, develop): тільки merge. Rebase на гілці, яку вже підтягнули інші, створює дублікати комітів і змушує команду вручну виправляти локальний стан.
  • Локальна feature-гілка перед PR: зробити rebase на main спочатку. Reviewer отримає чисту послідовну історію.
  • Публічна історія вже запушена і підтягнута колегою: merge. Навіть якщо це твоя власна гілка.
  • Інтерактивний rebase (git rebase -i): підходить для squash або редагування комітів до першого push.

Таблиця порівняння

Аспектgit mergegit rebase
Форма історіїРозгалужена, показує відхиленняЛінійна, коміти послідовно
Нові комітиОдин merge commit (два батьки)Новий коміт для кожного відтвореного, нові SHA
Переписує історію?НіТак
Безпечний після push?ТакНі
Вирішення конфліктівОдин раз, в точці злиттяПо одному разу на кожен відтворюваний коміт
Коли використовуватиСпільні гілки, аудитЛокальні feature-гілки, чисті PR

Як це працює всередині

Merge обчислює three-way diff: спільний предок плюс дві верхівки гілок. Без конфліктів Git записує один коміт з двома батьками автоматично. Rebase йде по комітах між точкою розгалуження і верхівкою feature-гілки, робить cherry-pick кожного на нову базу і записує нові коміти. Оскільки батьківський SHA змінився, змінюється і SHA дочірнього коміту, навіть якщо код у ньому байт-у-байт однаковий.

Типові помилки

Rebase спільної гілки.

bash
# Неправильно git checkout main && git rebase feature # Переписує публічний main

Кожен, хто вже підтягнув main, тепер має розбіжну історію. git pull впаде. Їм потрібно git reset --hard origin/main щоб відновитись. Для гілок, якими активно користується команда, тільки git merge.

Очікування одного кроку вирішення конфліктів під час rebase.

bash
git rebase main # Конфлікт на D -> виправляємо -> git rebase --continue # Конфлікт на E -> виправляємо -> git rebase --continue

Merge видає всі конфлікти одразу. Rebase зупиняється на кожному конфліктному коміті. Ті, хто чекає одного кроку, перериваються думаючи що щось зламалось. Це нормально: git rebase --continue після кожного конфлікту, або git rebase --abort щоб повернутись до початкового стану.

Force-push після rebase коли колега вже підтягнув гілку.

bash
git push origin feature # Колега підтягує git rebase main # Нові SHA на feature git push --force-with-lease # Перезаписує remote # Локальна feature-гілка колеги тепер осиротіла

--force-with-lease перевіряє що верхівка remote збігається з останнім SHA, отриманим при fetch. Але він не захищає колегу, який підтягнув між твоїм fetch і push. На спільній гілці це завжди проблема.

Merge після rebase без розуміння fast-forward.

Після rebase feature-гілки на main, верхівка feature напряму попереду main. git merge feature зробить fast-forward автоматично і не додасть merge commit. Якщо потрібен merge commit щоб позначити межу PR, треба явно вказати --no-ff:

bash
git checkout main && git merge --no-ff feature

Реальне використання

  • React (facebook/react): контрибьютори роблять rebase feature-гілок локально перед PR. Main залишається лінійним.
  • Node.js core (nodejs/node): merge commits для стабільних гілок (v20.x), інтерактивний rebase для особистого WIP.
  • Linux kernel (torvalds/linux): суворий rebase перед злиттям у Linus. Лінійна історія потрібна щоб git bisect коректно працював на тисячах комітів.
  • Express.js: rebase + merge --no-ff при злитті PR, щоб межа PR залишалась видною в логу.

Більшість команд зупиняється десь між цими підходами. "Rebase перед PR, merge в main" вирішує переважну більшість ситуацій і тримається без зайвого контролю.

Питання для поглиблення

Q: Як перевірити що rebase створив нові коміти?
A: git reflog одразу після rebase покаже старі SHA до операції і нові після. git log --oneline --graph також показує лінійний результат.

Q: Що робить --force-with-lease і коли це безпечно?
A: Push відбудеться тільки якщо верхівка remote збігається з останнім SHA, отриманим при fetch. Безпечно коли ти єдиний на гілці і ніхто не підтягував її після твого останнього fetch.

Q: Чим вирішення конфліктів при rebase відрізняється від merge?
A: Rebase зупиняється на кожному коміті що викликає конфлікт. Виправляєш і запускаєш git rebase --continue. Merge збирає всі конфлікти і вирішуєш їх за один раз.

Q: Що таке git pull --rebase origin main і чим відрізняється від git rebase main?
A: git pull --rebase спочатку отримує нові коміти з remote, потім робить rebase локальної гілки на оновлену верхівку. git rebase main робить rebase тільки на локальний main, який може відставати від remote.

Q: Чому в команді з багатьма контрибьюторами merge масштабується краще ніж rebase?
A: Відтворення сотень комітів від багатьох розробників генерує часті конфлікти на кожен коміт окремо. Merge це один коміт на інтеграцію. До того ж merge зберігає хронологію контрибьюторів, що важливо для git blame і аудиту.

Приклади

Merge в Node.js Express додатку

bash
git checkout main echo "app.get('/', (req,res) => res.send('Home'));" > server.js git add . && git commit -m "Initial Express server" git checkout -b auth-feature echo "app.use('/admin', authMiddleware);" >> server.js git add . && git commit -m "Add auth route guard" echo "function authMiddleware(req,res,next){ next(); }" >> server.js git add . && git commit -m "Implement auth check" git checkout main git merge auth-feature # Результат: merge commit з двома батьками # git log --graph показує точку розгалуження і злиття

Обидва оригінальні коміти залишились з оригінальними SHA. В логу видно коли auth-feature відгалузилась від main і коли потрапила назад.

Rebase перед відкриттям PR

bash
# Поки ти був на auth-feature, main отримав нові коміти git checkout auth-feature git rebase main # Коміти auth-feature тепер відтворені поверх останнього main # git log --graph: лінійно - Initial -> нові коміти main -> auth коміти git push origin auth-feature git push --force-with-lease # Тільки якщо гілку вже пушили раніше

Код той самий що і після merge. Але історія читається так, наче auth-коміти написані після останніх змін в main. PR стає простіше ревьюити.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?