Интерактивное перебазирование (Interactive rebasing)
Интерактивное перебазирование является одной из самых мощных и потенциально сложных функций Git. Оно позволяет гибко переписывать историю коммитов. Вы можете изменить порядок коммитов, объединить (сжать, squash) их, отредактировать сообщения коммитов или даже полностью удалить коммиты. Это как детальный редактор для вашей истории коммитов.
Предупреждение: Интерактивное перебазирование, особенно на общих ветках, может быть рискованным, так как оно переписывает историю. Никогда не перебазируйте коммиты, которые уже были отправлены в общий репозиторий, если вы не полностью понимаете последствия и не согласовали это с командой. Перебазирование обычно безопаснее и более уместно для очистки ваших локальных коммитов перед их публикацией.
Давайте создадим серию коммитов, чтобы посмотреть, как работает интерактивное перебазирование:
echo "First change" >> hello.txt
git commit -am "First change"
echo "Second change" >> hello.txt
git commit -am "Second change"
echo "Third change" >> hello.txt
git commit -am "Third change"
Эти команды создают три новых коммита последовательно, каждый из которых добавляет строку в наш файл "hello.txt". Сокращение -am
в git commit -am
объединяет добавление всех измененных и удаленных файлов в область подготовки (-a
) с созданием коммита (-m
). Это удобный ярлык, когда вы уже отслеживаете файлы и просто хотите закоммитить изменения в них.
Теперь предположим, что мы хотим очистить эти три коммита. Возможно, "First change" и "Second change" на самом деле были частью одного логического изменения, и мы хотим объединить их в один коммит. И, возможно, мы хотим улучшить сообщение коммита для "Third change". Интерактивное перебазирование идеально подходит для этого.
Начните интерактивную сессию перебазирования с помощью:
git rebase -i HEAD~3
Понять эту команду можно так:
git rebase -i
: git rebase
- это команда для перебазирования. Флаг -i
означает "интерактивный", который сообщает Git, что мы хотим выполнить интерактивное перебазирование.
HEAD~3
: Это указывает диапазон коммитов, которые мы хотим перебазировать. HEAD
ссылается на текущий коммит. ~3
означает "вернуться на три коммита назад от HEAD". Таким образом, HEAD~3
выбирает последние три коммита. Вы также можете указать хэш коммита вместо HEAD~3
, чтобы начать перебазирование с определенной точки в истории.
Когда вы запустите эту команду, Git откроет ваш текстовый редактор по умолчанию со списком последних трех коммитов. В интерфейсе редактора вы укажете Git, как изменить вашу историю. Вы увидите что-то вроде этого (хэши коммитов будут разными):
1 pick 63c95db First change
2 pick 68e7909 Second change
3 pick 5371424 Third change
4
5 ## Rebase 3bf348d..5371424 onto 3bf348d (3 commands)
6 #
7 ## Commands:
8 ## p, pick <commit> = use commit
9 ## r, reword <commit> = use commit, but edit the commit message
10 ## e, edit <commit> = use commit, but stop for amending
11 ## s, squash <commit> = use commit, but meld into previous commit
12 ## f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
13 ## commit's log message, unless -C is used, in which case
14 ## keep only this commit's message; -c is same as -C but
15 ## opens the editor
16 ## x, exec <command> = run command (the rest of the line) using shell
17 ## b, break = stop here (continue rebase later with 'git rebase --continue')
18 ## d, drop <commit> = remove commit
19 ## l, label <label> = label current HEAD with a name
20 ## t, reset <label> = reset HEAD to a label
21 ## m, merge [-C <commit> | -c <commit>] <label> [## <oneline>]
22 #. create a merge commit using the original merge commit's
23 #. message (or the oneline, if no original merge commit was
24 #. specified); use -c <commit> to reword the commit message
25 #
26 ## These lines can be re-ordered; they are executed from top to bottom.
27 #
28 ## If you remove a line here THAT COMMIT WILL BE LOST.
29 #
30 ## However, if you remove everything, the rebase will be aborted.
Понимание интерактивного редактора перебазирования:
- Список коммитов: Верхняя часть файла содержит список коммитов, которые входят в операцию перебазирования. По умолчанию они перечислены в обратном хронологическом порядке (самый старый сверху, самый новый снизу).
- Команды: Под списком коммитов Git предоставляет список доступных команд, которые вы можете использовать для изменения каждого коммита. Самые распространенные из них:
pick
(или p
): Использовать коммит как есть. Это значение по умолчанию.
reword
(или r
): Использовать коммит, но позволить изменить сообщение коммита.
edit
(или e
): Использовать коммит, но остановить процесс перебазирования на этом коммите, чтобы вы могли внести дополнительные изменения (например, исправить файлы, добавить новые изменения).
squash
(или s
): Объединить этот коммит с предыдущим коммитом в списке. Сообщение коммита этого коммита будет добавлено к сообщению предыдущего коммита (позже вы сможете отредактировать объединенное сообщение).
fixup
(или f
): Аналогично squash
, но отбрасывает сообщение коммита этого коммита и использует только сообщение предыдущего коммита.
drop
(или d
): Полностью удалить этот коммит из истории.
Наша план интерактивного перебазирования:
Мы хотим:
- Объединить "Second change" с "First change".
- Изменить сообщение для "Third change" на более информативное.
Для этого измените файл в редакторе так, чтобы он выглядел следующим образом:
pick abc1234 First change
squash def5678 Second change
reword ghi9101 Third change
Не изменяйте хэши коммитов в файле редактора.
Инструкции по редактированию в Vim (если вы используете этот редактор):
- Войти в режим вставки: Нажмите клавишу
i
. Это позволит вам вводить текст и редактировать файл.
- Внести изменения: Используйте стрелки на клавиатуре, чтобы перейти к нужным строкам и изменить "pick" на "squash" и "reword", как показано выше.
- Выйти из режима вставки: Нажмите клавишу
Esc
.
- Сохранить и выйти: Введите
:wq
и нажмите Enter
.
После сохранения и закрытия файла редактора Git начнет выполнять операции перебазирования в соответствии с вашими инструкциями.
Сначала, объединение коммитов:
Git сначала обработает команду "squash". Он объединит изменения из "Second change" в "First change". Затем он снова откроет ваш текстовый редактор, на этот раз, чтобы вы могли отредактировать объединенное сообщение коммита для объединенного коммита. Вы увидите что-то вроде этого в редакторе:
## This is a combination of 2 commits.
## This is the 1st commit message:
First change
## This is the 2nd commit message:
Second change
## Please enter the commit message for your changes. Lines starting
## with '#' will be ignored, and an empty message aborts the commit.
#
## Date: Tue Oct 24 10:30:00 2023 +0000
#
## interactive rebase in progress; onto <base_commit_hash>
#
## Last commands done (2 commands done):
## pick abc1234 First change
## squash def5678 Second change
## Next command to do (1 remaining command):
## reword ghi9101 Third change
## You are currently rebasing branch 'master' on '<base_commit_hash>'.
В этом редакторе показаны исходные сообщения коммитов, которые объединяются. Теперь вы можете отредактировать этот текст, чтобы создать единое, связанное сообщение коммита для объединенного коммита. Например, вы можете изменить его на:
Combined first and second changes: Initial setup of hello.txt
После редактирования сообщения сохраните и закройте редактор.
Далее, изменение сообщения коммита:
Затем Git перейдёт к команде "reword" для "Third change". Он снова откроет редактор, на этот раз, чтобы вы могли отредактировать сообщение коммита для "Third change". Вы увидите исходное сообщение:
Third change
## Please enter the commit message for your changes. Lines starting
## with '#' will be ignored, and an empty message aborts the commit.
#
## Date: Tue Oct 24 10:35:00 2023 +0000
#
## interactive rebase in progress; onto <base_commit_hash>
#
## Last commands done (3 commands done):
## pick abc1234 First change
## squash def5678 Second change
## reword ghi9101 Third change
## Next command to do (0 remaining commands):
## (finish)
## You are currently rebasing branch 'master' on '<base_commit_hash>'.
Измените сообщение на более описательное, например:
Improved third change: Added a more descriptive line to hello.txt
Сохраните и закройте редактор.
Завершение перебазирования:
После обработки всех команд в ваших инструкциях по интерактивному перебазированию Git завершит процесс перебазирования. Обычно вы увидите сообщение вроде "Successfully rebased and updated refs/heads/master."
Проверка перебазированной истории:
Теперь снова проверьте журнал коммитов:
git log --oneline
Вы должны увидеть историю, которая выглядит примерно так (хэши коммитов будут разными):
<commit_hash_third_revised> Improved third change: Added a more descriptive line to hello.txt
<commit_hash_combined> Combined first and second changes: Initial setup of hello.txt
<commit_hash_cherry_pick> Add new feature
<commit_hash_revert> Revert "Add line to be reverted"
<commit_hash_original_reverted> Add line to be reverted
<commit_hash_initial_amended> Initial commit with important note
Обратите внимание, что:
- Теперь у вас меньше коммитов, чем раньше (мы объединили два коммита в один).
- Сообщение коммита для "First change" и "Second change" теперь объединено и более информативно.
- Сообщение коммита для "Third change" было изменено.
- Хэши коммитов перебазированных коммитов изменились, так как мы переписали историю.
Важные напоминания об интерактивном перебазировании:
- Инструмент для локальной истории: Интерактивное перебазирование в первую очередь является инструментом для очистки вашей локальной истории коммитов.
- Избегайте перебазирования общих веток: Не перебазируйте ветки, которые были отправлены в общий репозиторий, если вы не абсолютно уверены в своих действиях и не согласовали это с командой. Переписывание общей истории может привести к серьезным проблемам для коллег по проекту.
git rebase --abort
: Если вы совершили ошибку во время интерактивного перебазирования, или если вы запутались или столкнулись с конфликтами, которые не можете разрешить, вы всегда можете использовать git rebase --abort
, чтобы отменить операцию перебазирования и вернуть вашу ветку в состояние, в котором она была до начала перебазирования. Это безопасный способ выйти из перебазирования, если что-то пойдет не так.