Коллеги, приветствую. Сегодня мы немного поговорим о работе с коммитами в системе контроля версиями — Git.

Целью этой статьи является обзор популярных сценариев проблем и методов их решения. Мы узнаем о том, как можно решить проблемы связанные с человеческими ошибками при создании коммитов в Git, какой способ устранения ошибок лучше применять в конкретных ситуациях.

Возникающие проблемы

В ходе разработки каких-либо проектов в Git, мы можем столкнуться с различными ситуациями, такими как:

  • Неправильное сообщение коммита
  • Забыли добавить некоторые файлы
  • Забыли удалить некоторые файлы
  • Необходимость удалить коммит
  • Необходимость объединить коммиты
  • и т.д

Безопасные изменения для локального репозитория

Изменить последний коммит

Давайте предположим, что мы работаем над каким-либо проектом локально.

Вдруг мы понимаем, что в момент создания сообщения для последнего коммита мы допустили ошибку, либо сообщение вовсе не подходит. В таком случае выполним команду:

git commit --amend

Перед нами откроется текстовый редактор с предложением изменить сообщение. Также мы можем увидеть список файлов которые будут зафиксированы.

Изменим сообщение коммита, выйдем из текстового редактора и проверим историю. Для этого введем команду:

git log --oneline

Но что делать, если мы забыли добавить или изменить какие-то файлы в наш последний коммит?

Для этого импортируем нужные файлы в наш проект или изменяем уже существующие и добавляем их в индекс.

Для теста я добавлю файл forgotten file.txt в проект. После этого еще раз выполним команду:

git commit --amend

Git сообщит нам, что вновь добавленный файл в индекс будет зафиксирован. Также мы можем переименовать сообщение нашего коммита, как мы делали это ранее. Воспользуемся этой возможностью и изменим сообщение.

Проверяем историю.

В случае необходимости удаления ненужного файла из последнего коммита, мы можем удалить его из проекта, добавить изменения в индекс и воспользоваться все той же командой:

git commit --amend

Удалить коммит

Для того, чтобы удалить один или несколько коммитов, мы можем воспользоваться командой git reset. Существует два типа выполнения reset:

  • Soft — удаляет коммит из истории и переводит файлы из него в состояние рабочего каталога.
  • Hard — удаляет коммит из истории вместе с файлами.

Сначала разберем soft reset. Возвращаемся к нашему проекту.

В один момент мы осознали, что допустили ошибку и хотели бы переделать последний коммит.

Для этого выполним команду:

git reset HEAD~1

Проверим историю коммитов. Мы можем убедится, что последний коммит был удален.

Файлы, которые были созданы или изменены в удаленном коммите, перешли в состояние рабочего каталога. Мы можем продолжить работу и, например, создать новый коммит на основе этих файлов.

Подобные действия мы можем выполнять для нескольких коммитов сразу. Для примера введем команду:

git reset HEAD~3

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

Проверим состояние файлов, которых коснулась процедура удаления коммитов.

Мы можем полностью удалить коммиты без сохранения связанных с ними файлов. Для этого выполним процедуру hard reset:

git reset --hard HEAD~3 

И проверим состояние.

Массовое изменение коммитов

Вернемся к нашему проекту и предположим, что настала пора закачать на удаленный репозиторий наши изменения. Безусловно, мы можем выполнять эту процедуру хоть сейчас, однако, мы бы хотели провести небольшую оптимизацию. Например, сократить количество коммитов, исправить ошибки в сообщениях коммитов и т.д.

Для этой задачи хорошо подходит выполнение команды git rebase. Ее запуск в интерактивном режиме позволит нам последовательно сообщить Git’у как бы мы хотели выполнить оптимизацию.

Мы считаем, что 4 последних коммита можно смело объединить в один.

Для этого выполним команду:

git rebase -i HEAD~4

Перед нами откроется текстовый редактор по умолчанию в котором мы будем выполнять изменения. Используем коммит с хэшем aab958a как начальный, путем указания команды pick напротив него и укажем напротив трех остальных команду squash. Как мы можем видеть ниже в описании, команда suqash объединит коммит с предыдущим в списке.

После выполнения — выходим из текстового редактора.

Теперь Git попросит от нас ввести сообщения коммитов. Просто для наглядности мы немного переименуем наш объединенный коммит. Также Git сообщит о том, какие файлы подвергнуться изменениям.

Снова выходим из текстового редактора.

Похоже, что наше объединение нескольких коммитов прошло успешно. Давайте проверим это командой:

git log --oneline

Мы уже были готовы к публикации наших изменений, однако, посчитали, что необходимо объединить другие 3 коммита в 1 и изменить его сообщение. При этом, мы бы не хотели затрагивать уже объединенный.

Снова вводим команду:

git rebase HEAD~4

В этот раз в качестве начального коммита будет использоваться с коммит хэшем 4c17043. Для его переименования укажем напротив команду reword. Затем объединяем с ним два последующих коммита командой squash. Последний коммит мы никак не изменяем.

После переименования выходим из текстового редактора и проверяем историю.

Похоже, что нам удалось провести историю коммитов в порядок и теперь нет причин откладывать загрузку нашего проекта на удаленный репозиторий. 🙂

Безопасные изменения для удаленных репозиториев

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

Однако, что делать, если мы совместно работаем над проектом и только спустя какое-то время понимаем, что один из коммитов — лишний? Если мы воспользуемся вышеописанными методами, это может спровоцировать ряд проблем в работе над проектом у наших коллег.

В подобных случаях имеет смысл откатывать коммит. Подобная процедура создает еще один коммит, который отменит изменения ненужного. Таким образом мы не порушим историю проекта и избавим наших коллег от лишних проблем.

Снова вовзращаемся к исходному состоянию проекта.

Предположим, что изменения в коммите с хэшем и 5140d80 — лишние. Выполним команду:

git revert HEAD~2

Git сообщит нам об изменениях, после отката.

Проверим историю. Как мы можем убедиться, был создан новый коммит, «отменяющий» изменения ненужного.

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

Итоги

В этой статье мы узнали о четырех способах изменять коммиты:

  • git commit —amend — позволяет изменять последний коммит.
  • git reset — позволяет удалять коммиты.
  • git rebase — позволяет производить массовое изменение коммитов, приводить в порядок историю.
  • git revert — позволяет откатывать изменения коммитов.

Первые три команды — подойдут для использования в условиях локальной разработки.

Последняя команда — лучший способ, не создавать лишних проблем коллегам при совместной разработке.