交互式变基(Interactive Rebasing)
交互式变基是 Git 最强大但也可能最复杂的功能之一。它允许你以非常灵活的方式重写提交历史。你可以重新排序提交、合并(压缩)提交、编辑提交消息,甚至完全删除提交。这就像为你的提交历史提供了一个细粒度的编辑器。
警告: 交互式变基,尤其是在共享分支上,可能会带来风险,因为它会重写历史。除非你完全理解其影响并与团队协调好,否则不要对已经推送到共享仓库的提交进行变基。 变基通常更适合在分享之前清理你的 本地 提交。
让我们创建一系列提交,以便我们可以实际操作交互式变基:
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" 文件中添加了一行。git commit -am
中的 -am
是 -a
(暂存所有修改和删除的文件)和 -m
(提交)的组合。当你已经在跟踪文件并希望提交更改时,这是一个方便的快捷方式。
现在,假设我们想要清理这三个提交。也许 "First change" 和 "Second change" 实际上是同一逻辑更改的一部分,我们希望将它们合并为一个提交。也许我们还想改进 "Third change" 的提交消息。交互式变基非常适合这种情况。
使用以下命令启动交互式变基会话:
git rebase -i HEAD~3
让我们理解这个命令:
git rebase -i
:git rebase
是变基的命令。-i
标志代表 "interactive"(交互式),告诉 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 将继续处理 "Third change" 的 "reword" 命令。它将再次打开编辑器,这次是让你编辑 "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
取消变基操作,并将分支恢复到变基之前的状态。这是在变基出错时安全退出的方法。