Когда вы открываете пулреквест, и ваш код смотрят и комментируют другие, бывает нужно что-то исправить. Обычно такие изменения мы комментируем сообщением вроде «Увеличил шрифт на 2px» или «Поменял оттенок фона в шапке». Такие маленькие изменения интересны только пока они в пулреквесте. Ревьювер (человек, который смотрит ваш код), может легко узнать, что и когда вы изменили, а не читать весь diff заново, а вы можете легко откатить коммит, если он не нужен. Но когда приходит время вливать пулреквест, эти маленькие коммиты теряют свою ценность. Поэтому лучше их склеить в один. Как — я расскажу вам сегодня.

Как подготовиться

Для некоторых операций из этой статьи гит будет открывать текстовый редактор. Во многих системах по умолчанию это Vim. Не самый дружелюбный для новичков. Чтобы вам было комфортно, установите в качестве гит-редактора ваш любимый редактор. Это делается с помощью команды:

git config --global core.editor 

Например, если у вас Mac OS, и вы хотите установить Atom в качестве редактора по умолчанию, введите:

git config --global core.editor "atom --wait"

А если у вас Windows, и вы хотите установить Notepad++ в качестве гит-редактора, ваша команда будет такая:

git config --global core.editor "'C:/Program Files/Notepad++/notepad++.exe' -multiInst -notabbar -nosession -noPlugin"

Вам нужно просто узнать команду для запуска вашего редактора и подставить её. Вот список команд для популярных редакторов.

Как склеивать коммиты

Сначала узнаем, сколько коммитов нужно склеить. Эта команда покажет, какие коммиты у вас прибавились по сравнению с веткой master:

git cherry -v master

А эта — сколько их:

git cherry -v master | wc -l
git cherry -v master
git cherry -v master

Отлично, у меня 5 коммитов. Теперь я хочу переписать историю с момента HEAD~5, т.е. с того, что было 5 коммитов назад. Для этого я делаю так:

git rebase -i HEAD~5

флаг -i — значит в интерактивном режиме. У меня открывается такой файл:

git rebase -i HEAD~5
git rebase -i HEAD~5

Тут список моих коммитов и большой комментарий о том, что я могу сделать. Из списка комманд видно, что мы можем использовать squash или fixup для того, чтобы склеить коммиты. Первый пригодится тогда, когда вы хотите изменить коммит-месседж, а второй — когда хотите использовать коммит-месседж первого. Итак, чтобы склеить все коммиты, я делаю вот так:

Сквош коммитов
Сквош коммитов

То есть я говорю гиту «используй первый коммит, а остальные приклей к нему». Потом я сохраняю файл и закрываю его. Гит склеивает коммиты и предлагает мне ввести коммит-месседж (показывает коммит-месседжи всех склеенных коммитов):

Коммит-месседжи склеенных коммитов
Коммит-месседжи склеенных коммитов

Я хочу оставить только последний:

Коммит-месседж результирующего коммита
Коммит-месседж результирующего коммита

Сохраняю файл и смотрю лог:

git log
git log

Ура, остался один коммит с нужным сообщением.

Теперь нужно вашу работу отправить на ваш гит-сервер. Обычно вы делаете это с помощью git push, но сейчас вам это не удастся:

git push
git push

Это потому, что ваша локальная ветка и ветка на сервере отличаются. Причём не просто в локальной ветке есть некоторые коммиты, которых нет на сервере, но и старых коммитов нет. Ведь у вас теперь один новый коммит вместо всех прежних. Чтобы запушить на сервер все ваши изменения, несмотря на конфликт, запустите пуш с флагом --force:

git push --force
git push --force
git push --force

Теперь всё получилось! Используйте форс только тогда, когда вы уверены в своих действиях!

Как автоматизировать ребейз

Обычно всё-таки нужно оставить коммит-месседж первого изменения, типа «Сверстал то-то», остальные коммиты — просто правки к нему. Число правок иногда доходит до 15, не очень удобно сидеть и писать везде squash. Можно сделать так, чтобы коммиты автоматически присквошивались к нужному, только для этого нужно их правильно коммитить. Предположим, вы поменяли файл и хотите закоммитить изменения как правку по ревью:

git status
git status

Сначала надо узнать хеш коммита, к которому этот коммит является правкой. Воспользуемся уже знакомым cherry:

git cherry -v master
git cherry -v master

Ага, вот он наш хеш. А теперь следующий коммит обозначим правкой к этому:

git commit --fixup <хеш нужного коммита>

Необязательно в точности копировать весь хеш, достаточно первых 7 символов. Я обычно выделяю от начала и сколько выделится и копирую:

git commit --fixup
git commit --fixup

Изменения закоммитились с сообщением fixup! Сообщение того коммита. Наша история теперь выглядит вот так:

git log
git log

Добавим ещё несколько правок таким образом. Посмотрим изменения:

git cherry -v master
git cherry -v master

Отлично. А теперь склеим все эти коммиты. Только не вручную. Гит сделает это за нас:

git rebase -i --autosquash HEAD~4

И гит сам подставит слово fixup там, где нужно:

git rebase -i --autosquash
git rebase -i --autosquash

Просто сохраняем, и гит склеивает коммиты и использует коммит-месседж первого:

Результат автосквоша
Результат автосквоша

Ура, теперь вы умеете склеивать коммиты!