Схлопываем git-коммиты в один

444 прочитали

Простая и чистая git-история может помочь при исследовании проблемы с кодом, в моей практике случается, что рассматривая строку кода обычно полезно посмотреть, а что ещё поменялось в других местах и, возможно, сделать выводы почему сделали именно так. Если коммит один, то легко увидеть все затронутые файлы и строки кода, а если задача решалась в несколько коммитов, то это сделать не получится. Кроме этого один коммит проще ребейзить или перемещать в другие ветки. Так вот, как же это сделать?

Первое - нужно понять сколько коммитов мы хотим "схлопнуть". Для этого лучше проверить историю ветки:

git log

3 последних коммита в истории ветки
3 последних коммита в истории ветки

Вот так может выглядеть история разработке в ветки фичи, несколько коммитов могут появится в процессе code-review, ещё несколько при исправлении ошибок, в итоге можно их превратить в один коммит. Набираем команду:

git rebase -i HEAD~3

В общем случае git rebase меняет историю ветки и мы указали количество коммитов которые хотим исправить - посмотрите на HEAD~3, именно столько коммитов мы хотим изменить. Параметр -i означает интерактивность, в этом случае git откроет нам редактор и предложит определиться с изменениями.

Пример внешнего вида редактора при ребейзе коммитов
Пример внешнего вида редактора при ребейзе коммитов

Git подсказывает нам команды, мы будем использовать squash - команда объединения коммита с предыдущим. Команды работают так: нужно просто поменять её имя (крайнее левое слово, в данном случае pick, у каждого коммита) на подходящее для нашей цели. В нашем случае нужно заменить слово pick у всех коммитов (кроме первого, то есть верхнего) на слово squash. Или, что еще проще, заменить на одну букву s, не обязательно писать все слово squash полностью. Можно вообще ничего не трогать и просто выйти, тогда git сообщит что все сделал фразой "Successfully rebased and updated refs/heads/master." и всё останется по-старому.

Если коммитов получилось много, можно ли ускорить процесс замены команды? Если вы используете vim, то легко, попробуйте набрать:

:2,3s/pick/s/

Это обычная замена подстроки редактора vim, тут написано "от 2-й до 3-й строки нужно заменить (символ s) слово pick на слово s. Или вообще заменить все глобально:

:%s/pick/s/g

Заменит все pick на s во всем файле (ключ g - global), правда после этого придется исправить самый первый коммит назад, на pick. В итоге должна получится такая картина

Результат редактирования при ребейзе
Результат редактирования при ребейзе

2-й и 3-й коммиты объединяться с первым и станут единым целым. Таких объединений можно делать несколько, это нормально, можно даже сделать сразу несколько групп: 2-й и 3-й коммиты объединяем с первым, а какие-нибудь 9-й и 8-й с 7-м коммитом и все это за один раз. Так же коммиты можно поменять местами. Теперь сохраним файл, git снова откроет для нас редактор для того, чтобы проверить и изменить сообщение коммита, если хотите

Изменение комментария для коммита в процессе ребейза
Изменение комментария для коммита в процессе ребейза

Можно просто оставить как есть, сохранить и выйти, а можно отредактировать, я обычно удаляю лишние незакомментированные строки (это сообщения других коммитов) и оставляю лишь одно. Готово! Смотрим историю

git log

Последний "схлопнутый" коммит
Последний "схлопнутый" коммит

В конце стоит упомянуть, что история в вашей ветке теперь не такая как на сервере, вы ведь пушили эту ветку раньше и постоянно добавляли коммиты туда, а теперь у вас совсем другая ветка со старым именем, в ней ведь всего лишь одно изменение. Для этого вам понадобится ключ force

git push --force

Ключ force запушит ветку несмотря на разногласия в истории, фактически уничтожив старую ветку на сервере, заменив её вашей, новой. Будьте осторожны, ведь кто-то другой мог добавить в ветку "своих" коммитов, вы их удалите этой командой.