Пошук уроків, статей та іншого контенту
Порівняєте fast-forward та three-way merge і дізнаєтеся, коли Git створює merge-коміт.
git merge переносить зміни з однієї гілки в поточну. Git може зробити це двома основними способами:
fast-forward merge — просто пересунути вказівник гілки вперед;
three-way merge — порівняти три коміти й створити новий merge-коміт.
Спосіб залежить від структури історії комітів.
Fast-forward можливий, коли поточна гілка не має власних нових комітів після розгалуження.
Наприклад, історія має вигляд:
A---B main
\
C---D featureЯкщо перейти на main і виконати:
git merge featureGit може просто пересунути вказівник main із коміту B на коміт D:
A---B---C---D main, featureНовий коміт не створюється. Історія залишається лінійною.
# Створюємо репозиторій і початкову гілку
mkdir merge-example
cd merge-example
git init
git branch -M main
git config user.name "Developer"
git config user.email "developer@example.com"
printf "Початковий файл\n" > README.md
git add README.md
git commit -m "Додати README"
# Створюємо гілку для нової функції
git switch -c feature
printf "Нова функція\n" > feature.txt
git add feature.txt
git commit -m "Додати нову функцію"
# Повертаємося в main і об'єднуємо feature
git switch main
git merge feature
# Переглядаємо історію
git log --oneline --graph --all --decorateУ цьому прикладі після створення feature у main не було нових комітів. Тому Git виконає fast-forward.
Типовий результат команди git merge feature:
Updating ...
Fast-forward
feature.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 feature.txtВажливо: fast-forward — це не окремий тип коміту. Це спосіб перемістити вказівник гілки без створення нового коміту.
Three-way merge потрібен, коли обидві гілки після розгалуження отримали власні коміти.
Початкова історія:
C---D feature
/
A---B
\
E---F mainЩоб об’єднати feature у main, Git використовує три коміти:
базовий коміт — спільний предок гілок, у прикладі B;
поточний коміт — вершина гілки main, у прикладі F;
коміт іншої гілки — вершина feature, у прикладі D.
Git порівнює:
зміни від B до F;
зміни від B до D;
після чого об’єднує їх у результат.
Оскільки після розгалуження обидві гілки змінилися, Git створює новий merge-коміт:
C---D feature
/ \
A---B---E---M mainКоміт M має двох батьків:
попередній коміт main;
попередній коміт feature.
Продовжимо попередній приклад:
# У main додаємо окремий коміт
git switch main
printf "Зміна в main\n" > main.txt
git add main.txt
git commit -m "Додати зміни в main"
# У feature додаємо інший коміт
git switch feature
printf "Ще одна частина функції\n" >> feature.txt
git add feature.txt
git commit -m "Розширити функцію"
# Об'єднуємо feature у main
git switch main
git merge --no-edit feature
# Переглядаємо історію з батьківськими комітами
git log --oneline --graph --all --decorateТепер main і feature мають різні коміти після спільного предка. Git створить merge-коміт.
Очікувана структура історії буде приблизно такою:
* abc1234 (HEAD -> main) Merge branch 'feature'
|\
| * def5678 (feature) Розширити функцію
* | 9876543 Додати зміни в main
|/
* 1234567 Додати READMEПараметр --no-edit використовують, щоб Git не відкривав редактор для підтвердження стандартного повідомлення merge-коміту.
За замовчуванням Git діє так:
якщо можливий fast-forward — виконує fast-forward;
якщо гілки розійшлися — створює merge-коміт;
якщо поточна гілка вже містить усі коміти іншої гілки — не змінює історію.
Наприклад, якщо main уже містить feature:
A---B---C main, featureкоманда:
git switch main
git merge featureне створить нового коміту, тому що об’єднувати вже нічого.
--no-ffІноді merge-коміт потрібен навіть тоді, коли Git міг би виконати fast-forward. Для цього використовують --no-ff:
git switch main
git merge --no-ff --no-edit featureУ такому випадку Git створить окремий merge-коміт:
C---D feature
/ \
A---B-------M mainЦе дає змогу зберегти в історії факт об’єднання гілки. Такий підхід може бути корисним, якщо кожну функцію розробляють в окремій гілці й хочуть бачити її як окрему групу комітів.
Без --no-ff у лінійному випадку історія виглядала б так:
A---B---C---D mainЗ --no-ff у ній буде явно видно операцію merge:
C---D feature
/ \
A---B-------M main--ff-onlyПараметр --ff-only дозволяє виконати merge лише тоді, коли він може бути fast-forward:
git merge --ff-only featureЯкщо гілки розійшлися, команда завершиться помилкою і не створить merge-коміт.
Це корисно, коли потрібно гарантувати лінійну історію та не створювати merge-коміти автоматично.
Three-way merge може виявити конфлікт, якщо обидві гілки змінили ті самі рядки або несумісні частини файлу.
У такому випадку Git:
зупиняє merge;
позначає конфліктні файли;
очікує, що розробник вручну виправить конфлікти;
після додавання виправлених файлів створює merge-коміт.
Типова послідовність після виправлення конфлікту:
# Перевіряємо стан репозиторію
git status
# Після ручового виправлення конфліктів додаємо файли
git add path/to/file
# Завершуємо merge
git commitЯкщо потрібно скасувати незавершений merge:
git merge --abortне створює нового коміту;
зберігає лінійну історію;
можливий лише без нових комітів у поточній гілці;
змінює лише вказівник поточної гілки.
порівнює базовий, поточний і цільовий коміти;
потрібен, коли гілки розійшлися;
зазвичай створює merge-коміт;
може призвести до конфліктів.
git mergeКоманда:
git merge featureне гарантує створення merge-коміту. Якщо можливий fast-forward, Git використає його.
Якщо merge-коміт потрібен завжди, використовуйте:
git merge --no-ff featureКоманди merge об’єднують зазначену гілку в поточну. Перед merge перевіряйте поточну гілку:
git branch --show-currentНаприклад, щоб додати зміни feature у main, потрібно виконати:
git switch main
git merge feature--ff-only і --no-ffЦі параметри мають протилежні цілі:
--ff-only забороняє merge-коміт і завершується помилкою, якщо fast-forward неможливий;
--no-ff забороняє fast-forward і змушує створити merge-коміт.
Fast-forward merge пересуває вказівник гілки вперед і не створює нового коміту.
Fast-forward можливий, коли поточна гілка не має власних комітів після розгалуження.
Three-way merge використовує спільного предка та кінцеві коміти обох гілок.
Якщо гілки розійшлися, Git зазвичай створює merge-коміт із двома батьками.
--no-ff примусово створює merge-коміт навіть для лінійної історії.
--ff-only дозволяє merge лише за умови fast-forward.