交互式变基
交互式变基(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」压缩(squash)到「First change」中。
- 重写(reword)「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 来取消变基操作,并将分支恢复到开始变基之前的状态。如果事情进展不顺利,这是退出变基的一种安全方式。