Пошук уроків, статей та іншого контенту
Навчитеся об’єднувати коміти та прибирати зайві повідомлення за допомогою squash і fixup.
Під час розробки одна зміна часто складається з кількох комітів:
основна реалізація;
виправлення помилки;
форматування коду;
оновлення тестів;
виправлення повідомлення коміту.
Для локальної історії це нормально. Але перед злиттям гілки в основну часто зручно об’єднати такі коміти в один логічний коміт.
Інтерактивний rebase дає змогу змінити останні коміти:
змінити їхній порядок;
об’єднати кілька комітів;
відредагувати повідомлення;
видалити зайві коміти;
виправити структуру локальної історії.
Інтерактивний rebase переписує історію. Не використовуйте його без узгодження для комітів, які вже активно використовують інші розробники.
Щоб переглянути останні три коміти, виконайте:
git rebase -i HEAD~3HEAD~3 означає: відкрити для редагування три коміти перед поточним HEAD.
Git відкриє файл із приблизно таким вмістом:
pick 1a2b3c4 Add user profile
pick 5d6e7f8 Add profile validation
pick 9a0b1c2 Fix validation message
# Rebase 0f1e2d3..9a0b1c2 onto 0f1e2d3
#
# Команди:
# p, pick = використати коміт
# r, reword = використати коміт, але змінити повідомлення
# e, edit = використати коміт і зупинитися для редагування
# s, squash = об'єднати з попереднім комітом і змінити повідомлення
# f, fixup = об'єднати з попереднім комітом і відкинути це повідомлення
# d, drop = видалити комітПорядок комітів у цьому файлі йде від найстарішого до найновішого.
Після збереження файлу Git виконає вказані операції.
squash: об’єднання зі збереженням повідомленьКоманда squash об’єднує коміт із попереднім комітом. Після цього Git відкриває редактор для створення спільного повідомлення.
Початковий список:
pick 1a2b3c4 Add user profile
pick 5d6e7f8 Add profile validation
pick 9a0b1c2 Fix validation messageЗмінимо його так:
pick 1a2b3c4 Add user profile
squash 5d6e7f8 Add profile validation
squash 9a0b1c2 Fix validation messageАбо скорочено:
pick 1a2b3c4 Add user profile
s 5d6e7f8 Add profile validation
s 9a0b1c2 Fix validation messageGit об’єднає всі три коміти. У наступному редакторі можна буде побачити їхні повідомлення:
# This is a combination of 3 commits.
# The first commit's message is:
Add user profile
# The commit message #2:
Add profile validation
# The commit message #3:
Fix validation messageЗалиште, наприклад, одне зрозуміле повідомлення:
Add user profile with validationУ результаті замість трьох комітів буде один.
squashsquash зручно використовувати, коли:
повідомлення всіх комітів містять корисний контекст;
потрібно переглянути й об’єднати ці повідомлення;
частини комітів мають бути відображені в підсумковому повідомленні.
fixup: об’єднання без збереження повідомленняfixup також об’єднує коміт із попереднім, але автоматично відкидає повідомлення fixup-коміту.
Початковий список:
pick 1a2b3c4 Add user profile
pick 5d6e7f8 Add profile validation
pick 9a0b1c2 Fix validation messageЗмінимо його так:
pick 1a2b3c4 Add user profile
squash 5d6e7f8 Add profile validation
fixup 9a0b1c2 Fix validation messageУ цьому випадку:
другий коміт об’єднається з першим;
Git запропонує відредагувати спільне повідомлення для перших двох комітів;
третій коміт також об’єднається з попереднім;
повідомлення Fix validation message буде автоматично відкинуте.
Скорочений варіант:
pick 1a2b3c4 Add user profile
s 5d6e7f8 Add profile validation
f 9a0b1c2 Fix validation messageПісля завершення залишиться один коміт із повідомленням, сформованим під час squash.
fixupfixup підходить для технічних або проміжних комітів:
Fix typo;
Fix tests;
Apply formatting;
Address review comment.
Такі повідомлення зазвичай не мають залишатися в підсумковій історії.
Нижче створюється невеликий репозиторій із трьома комітами. Потім два останні коміти об’єднуються з першим.
#!/usr/bin/env bash
set -e
rm -rf git-rebase-example
mkdir git-rebase-example
cd git-rebase-example
git init
git config user.name "Demo Developer"
git config user.email "demo@example.com"
printf "# User profile\n" > profile.md
git add profile.md
git commit -m "Add user profile"
printf "\n- Name validation\n" >> profile.md
git add profile.md
git commit -m "Add profile validation"
printf "\n- Error message\n" >> profile.md
git add profile.md
git commit -m "Fix validation message"
echo "Початкова історія:"
git log --oneline --reverse
git branch backup-before-rebase
git rebase -i HEAD~3В останньому кроці відкриється редактор. Замініть його вміст команд на:
pick <перший-коміт> Add user profile
squash <другий-коміт> Add profile validation
fixup <третій-коміт> Fix validation messageХеші потрібно залишити тими, які Git покаже у вашому репозиторії. Після збереження Git відкриє редактор повідомлення. Введіть:
Add user profile with validationПісля завершення перевірте історію:
git log --oneline --reverseЗамість трьох робочих комітів має залишитися один новий коміт із повідомленням:
Add user profile with validationФайли в робочому дереві при цьому збережуть усі зміни з початкових комітів.
squash і fixupОбидві команди об’єднують коміт із попереднім:
squash = об’єднати та відредагувати повідомлення
fixup = об’єднати й відкинути повідомлення поточного комітуПриклад:
pick A Add search
squash B Add search inputGit запропонує створити нове повідомлення на основі повідомлень A і B.
pick A Add search
fixup B Fix search typoGit збереже повідомлення коміту A, а повідомлення коміту B відкине.
Щоб об’єднати останні п’ять комітів, запустіть:
git rebase -i HEAD~5Для одного підсумкового коміту перший рядок залиште з pick, а всі наступні змініть на squash або fixup:
pick aaaaaaa Start feature
squash bbbbbbb Add component
fixup ccccccc Fix typo
fixup ddddddd Update tests
fixup eeeeeee Fix formattingУ результаті буде створено один коміт. Його повідомлення можна буде відредагувати під час обробки squash.
Об’єднання комітів може спричинити конфлікт. У такому разі Git зупинить rebase.
Перевірте стан репозиторію:
git statusВідкрийте конфліктні файли, виправте їх і додайте виправлені файли:
git add path/to/fileПісля цього продовжте rebase:
git rebase --continueЯкщо Git знову відкриє редактор повідомлення, підтвердьте або змініть його.
Якщо потрібно повністю скасувати операцію:
git rebase --abortGit спробує повернути гілку до стану, який був до початку rebase.
Після завершення інтерактивного rebase корисно перевірити:
git status
git log --oneline --decorate -n 5Переконайтеся, що:
rebase завершився без помилок;
робоче дерево чисте;
потрібні зміни не зникли;
повідомлення комітів зрозумілі;
кількість комітів стала очікуваною.
Для перевірки самих змін можна також виконати тести проєкту.
Команда:
git rebase -i HEAD~3редагує три останні коміти, але не включає коміт, на який вказує HEAD~3.
Якщо потрібно обробити чотири останні коміти, використовуйте:
git rebase -i HEAD~4squash для першого рядка squash і fixup потребують попереднього коміту. Тому перший коміт у вибраному діапазоні має залишатися pick.
Неправильно:
squash aaaaaaa First commit
pick bbbbbbb Second commitПравильно:
pick aaaaaaa First commit
squash bbbbbbb Second commitУ списку інтерактивного rebase коміти розташовані від найстарішого до найновішого. Команда squash або fixup об’єднує рядок із рядком безпосередньо вище.
pick A
fixup Bозначає: об’єднати B у A.
Поки rebase не завершено, не запускайте новий rebase поверх нього. Спочатку:
виправте конфлікт і виконайте git rebase --continue;
або скасуйте операцію через git rebase --abort.
Після rebase коміти отримують нові ідентифікатори. Якщо гілка вже була передана на віддалений репозиторій, звичайний push може бути відхилений.
Не переписуйте спільну гілку без домовленості з командою. Для власної feature-гілки після узгодження зазвичай використовують:
git push --force-with-lease--force-with-lease безпечніший за безумовний --force, оскільки Git перевіряє, чи не з’явилися на віддаленій гілці чужі зміни.
git rebase -i HEAD~N відкриває останні N комітів для редагування.
pick залишає коміт без змін.
squash об’єднує коміт із попереднім і дає змогу змінити спільне повідомлення.
fixup об’єднує коміт із попереднім і відкидає його повідомлення.
Перший коміт у вибраному діапазоні не може бути squash або fixup.
У разі конфлікту використовуйте git rebase --continue або git rebase --abort.
Перевіряйте результат через git status і git log.
Переписуйте історію лише у гілках, де це безпечно для інших учасників.