Когда вы открываете пулреквест и ваш код смотрят и комментируют другие, бывает нужно что-то исправить. Обычно такие изменения мы комментируем сообщением вроде «Увеличил шрифт на 2px
» или «Поменял оттенок фона в шапке». Такие маленькие изменения интересны, только пока они в пулреквесте. Ревьювер (человек, который смотрит ваш код), может легко узнать, что и когда вы изменили, а не читать весь diff
заново, а вы можете легко откатить коммит, если он не нужен. Но когда приходит время вливать пулреквест, эти маленькие коммиты теряют свою ценность. Поэтому лучше их склеить в один.
Как подготовиться
Для некоторых операций из этой статьи Git будет открывать текстовый редактор. Во многих системах по умолчанию это Vim. Не самый дружелюбный для новичков. Чтобы вам было комфортно, установите в качестве Git-редактора ваш любимый редактор. Это делается с помощью команды:
git config --global core.editor
Если у вас Windows и вы хотите установить VS Code в качестве Git-редактора, ваша команда будет такой:
git config --global core.editor "code --wait"
А если у вас Mac OS и вы хотите установить Atom в качестве редактора по умолчанию, введите:
git config --global core.editor "atom --wait"
С другими редакторами нужно просто посмотреть команду и подставить её.
Как склеивать коммиты
Сначала узнаем, сколько коммитов нужно склеить. Эта команда покажет, какие коммиты у вас прибавились по сравнению с веткой master
:
git cherry -v master
А эта — сколько их:
git cherry -v master | wc -l
Отлично, у нас пять коммитов. Теперь перепишем историю с момента HEAD~5
, то есть с того, что было пять коммитов назад. Для этого делаем:
git rebase -i HEAD~5
Флаг -i
— значит в интерактивном режиме. Открывается файл:
Тут список коммитов и большой комментарий о том, что можно сделать. Из списка команд видно, что мы можем использовать squash
или fixup
для склейки коммитов. Первый пригодится, когда нужно изменить коммит-месседж, а второй — когда хотите использовать коммит-месседж первого.
Итак, чтобы склеить все коммиты, делаем так:
То есть говорим «используй первый коммит, а остальные приклей к нему». Потом сохраняем файл и закрываем его. Git склеивает коммиты и предлагает ввести коммит-месседж (показывает коммит-месседжи всех склеенных коммитов):
Оставляем только последний:
Сохраняем файл и смотрим лог:
Ура, остался один коммит с нужным сообщением.
Теперь нужно работу отправить на ваш Git-сервер. Обычно это делается с помощью git push
, но сейчас это не удастся:
Это потому, что ваша локальная ветка и ветка на сервере отличаются. Причём не просто в локальной ветке есть некоторые коммиты, которых нет на сервере, но и старых коммитов нет. Ведь теперь один новый коммит вместо всех прежних. Чтобы запушить на сервер все изменения, несмотря на конфликт, запустим пуш с флагом --force
:
git push --force
Теперь всё получилось.
⭐ Используйте --force
только тогда, когда вы уверены в своих действиях.
Как автоматизировать ребейз
Обычно всё-таки нужно оставить коммит-месседж первого изменения, типа «Сверстал то-то», остальные коммиты — просто правки к нему. Число правок иногда доходит до 15 — это не очень удобно, ведь нужно писать везде squash
. Можно сделать так, чтобы коммиты автоматически присквошивались к нужному, для этого нужно их правильно коммитить. Предположим, мы поменяли файл и захотели закоммитить изменения как правку по ревью:
Сначала нужно узнать хеш коммита, к которому этот коммит является правкой. Воспользуемся уже знакомым cherry
:
Вот наш хеш. А теперь следующий коммит обозначим правкой к этому:
git commit --fixup <хеш нужного коммита>
Необязательно в точности копировать весь хеш, достаточно первых 7 символов. Я обычно выделяю от начала и сколько выделится и копирую:
Изменения закоммитились с сообщением fixup! Сообщение того коммита
. История теперь выглядит nак:
Добавим ещё несколько правок таким образом. Посмотрим изменения:
Отлично. А теперь склеим все эти коммиты. Только не вручную. Git сделает это за нас:
git rebase -i --autosquash HEAD~4
И Git сам подставит слово fixup
там, где нужно:
Просто сохраняем, и Git склеивает коммиты и использует коммит-месседж первого: