Продвинутое редактирование истории Git: исправление коммитов и интерактивный rebase
Используйте git commit --amend и интерактивный rebase для очистки локальной истории без нарушения общих веток.
Продвинутое редактирование истории Git: исправление коммитов и интерактивный rebase
Продвинутое редактирование истории Git помогает очистить коммиты до того, как они станут частью общей истории. Когда вы понимаете, как исправлять коммиты и использовать интерактивный rebase, вы можете исправлять мелкие ошибки, объединять шумные коммиты и представлять более понятную историю для ревью кода.
Ключевое правило простое: редактирование локальной истории — это нормально, но переписывание общей истории требует осторожности. Команды мощны, потому что они изменяют идентификаторы коммитов, а не только сообщения коммитов.
Что на самом деле меняет редактирование истории Git
Каждый коммит Git имеет идентификатор, основанный на его содержимом, метаданных, родительском коммите, авторе, дате и сообщении. Если вы исправляете коммит или переписываете его во время rebase, Git создает новый коммит с новым идентификатором.
Это нормально, когда коммит существует только на вашей машине. Это рискованно, когда другие люди уже получили старый коммит. Их ветки могут все еще указывать на старую историю, в то время как ваша указывает на переписанную.
Используйте редактирование истории для:
- Исправления опечатки в последнем сообщении коммита.
- Добавления забытого файла в ваш последний локальный коммит.
- Объединения нескольких рабочих коммитов перед открытием pull request.
- Переупорядочивания локальных коммитов, чтобы связанные изменения было легче ревьюить.
- Разделения одного большого локального коммита на несколько логических.
Избегайте редактирования истории на:
- Основных ветках.
- Релизных ветках.
- Общих функциональных ветках, используемых несколькими людьми.
- Любой ветке с тегами развертывания или чувствительными к аудиту коммитами.
Перед началом проверьте, на какой ветке вы находитесь и что уже отправлено:
git status
git branch --show-current
git log --oneline --decorate -5
Если вы не уверены, безопасно ли переписывание, создайте резервную ветку:
git branch backup-before-rebase
Эта ветка даст вам известную точку восстановления, если rebase пойдет не так.
Исправление последнего коммита
git commit --amend заменяет последний коммит новым. Это самый быстрый способ исправить последний коммит до того, как кто-то другой начнет от него зависеть.
Чтобы отредактировать только сообщение коммита:
git commit --amend
Git открывает ваш редактор с текущим сообщением. Сохраните исправленное сообщение, и Git создаст заменяющий коммит.
Чтобы добавить забытый файл в последний коммит:
git add path/to/file
git commit --amend --no-edit
Флаг --no-edit сохраняет существующее сообщение. Это полезно, когда сообщение коммита уже правильное, и вы только забыли добавить одно изменение.
Вот распространенный сценарий. Вы коммитите обновление Dockerfile, затем замечаете, что забыли соответствующее изменение .dockerignore:
git add Dockerfile
git commit -m "Улучшить кэширование сборки Docker"
git add .dockerignore
git commit --amend --no-edit
Теперь в ветке один чистый коммит вместо второго коммита с сообщением "забыл файл." Это упрощает ревью.
Если оригинальный коммит уже был отправлен, удаленная ветка все еще имеет старый идентификатор коммита. Отправка исправленного коммита требует принудительного обновления:
git push --force-with-lease
Предпочитайте --force-with-lease вместо --force. Он отказывается перезаписывать удаленную ветку, если кто-то другой отправил новую работу после вашего последнего fetch.
Очистка нескольких коммитов с помощью интерактивного rebase
Интерактивный rebase позволяет редактировать несколько коммитов одновременно. Вы выбираете диапазон, затем Git открывает список задач, где вы можете выбрать, переписать, объединить, исправить, переупорядочить или остановиться на коммитах.
Чтобы отредактировать последние пять коммитов:
git rebase -i HEAD~5
Вы увидите список, подобный этому:
pick a1b2c3d Добавить скрипт развертывания
pick b2c3d4e Исправить опечатку
pick c3d4e5f Добавить проверку здоровья
pick d4e5f6a Обновить документацию
pick e5f6a7b Настроить таймаут
Измените команду в начале каждой строки, чтобы контролировать, что произойдет. Часто используемые варианты:
pick: оставить коммит как есть.reword: сохранить изменения, но отредактировать сообщение.squash: объединить этот коммит с предыдущим и отредактировать объединенное сообщение.fixup: объединить этот коммит с предыдущим и отбросить сообщение этого коммита.edit: остановиться на этом коммите, чтобы вы могли изменить его содержимое.drop: удалить коммит.
Например, если "Исправить опечатку" относится к "Добавить скрипт развертывания", измените вторую строку:
pick a1b2c3d Добавить скрипт развертывания
fixup b2c3d4e Исправить опечатку
pick c3d4e5f Добавить проверку здоровья
pick d4e5f6a Обновить документацию
pick e5f6a7b Настроить таймаут
Сохраните и закройте редактор. Git воспроизводит коммиты и объединяет исправление опечатки в более ранний коммит.
Интерактивный rebase также полезен для улучшения сообщений коммитов перед pull request. Если три коммита все говорят "обновление", используйте reword и превратите их в сообщения, объясняющие реальное изменение.
Если возникает конфликт, разрешите его как обычный конфликт слияния:
git status
git add resolved-file
git rebase --continue
Если вы решите, что rebase того не стоит:
git rebase --abort
Это вернет вашу ветку в состояние до начала rebase.
Разделение коммита во время rebase
Иногда один коммит содержит два несвязанных изменения. Например, у вас может быть один коммит, который обновляет манифест Kubernetes и также исправляет раздел README. Эти изменения, вероятно, должны быть разделены.
Запустите интерактивный rebase:
git rebase -i HEAD~3
Измените pick на edit для коммита, который вы хотите разделить. Когда Git остановится на этом коммите, сбросьте его, сохранив изменения в вашем рабочем дереве:
git reset HEAD^
Теперь добавьте и закоммитьте первую логическую часть:
git add k8s/deployment.yaml
git commit -m "Обновить проверку здоровья развертывания"
Затем добавьте и закоммитьте вторую часть:
git add README.md
git commit -m "Документировать поведение проверки здоровья"
Продолжите rebase:
git rebase --continue
Это превращает один широкий коммит в два сфокусированных. Ревьюеры могут понять каждое изменение, не разделяя мысленно несвязанную работу.
Когда обращаться за помощью перед переписыванием
Обращайтесь за помощью перед переписыванием любой ветки, которую используют другие люди. Тимлид, релиз-инженер или мейнтейнер репозитория могут сказать вам, безопасно ли переписывать ветку и как команда обрабатывает принудительные отправки.
Немедленно обращайтесь за помощью, если rebase затрагивает коммиты, которые уже развернуты, помечены тегами или упоминаются в отчетах об инцидентах. В таких случаях сохранение истории часто важнее, чем сделать ее аккуратной.
Для обычных функциональных веток редактирование истории — это нормальный шаг очистки. Используйте git commit --amend для последнего коммита, интерактивный rebase для небольшой локальной серии и --force-with-lease, когда вам нужно обновить удаленную ветку, которой вы владеете.