Skip to content

更改提交

提交记录你的工作历史记录,并且保证你所做的更改事神圣不可侵犯的,但该提交自身不是一成不变的。
Git 提供了几个工具和命令,专门用来帮你修改完善版本库中的提交。
有很多正当理由让你去修改或返工某个提交或整个提交序列:

  • 可以在某个问题变为遗留问题之前修复它
  • 可以将大而全面的变更分解为一系列小而专题的提交。相反,也可以将一些小的变更组合成一个更大的提交
  • 可以合并反馈评论和建议
  • 可以在不破坏构建需求的情况下重新排列提交序列
  • 可以将提交调整为一个更合乎逻辑的序列
  • 可以删除意外提交的调试代码

变基提交 git rebase

git rebase 命令事用来改变一串提交以什么为基础的。此命令至少需要提交将迁往的分支名。默认情况下,不在目标分支中的当前分支提交会变基

git rebase 的一个常见用途事保持你正在开发的一系列提交相对于另一个分支事最新的,拿通常是 master 分支或者来自另一个版本库的追踪分支

如图所示,有两个分支正在开发中。最初,topic 分支是从 master 分支的提交 B 处开始的。在此期间 master 分支已经进展到了提交 E

执行gitbase之前

可以改写提交让它们基于提交 E 而不是提交 B,这样提交就相对于 master 分支是最新的了。
因为 topic 分支需要成为当前分支,所以可以使用:

1
2
3
4
$ git checkout master
$ git pull origin master
$ git checkout topic
$ git rebase master

或者

1
$ git rebase master topic

在如上图所示的情况下,使用 git rebase 命令通常称为向前移植(forward porting)。在本例中,特性分支 topic 已经向前移植到了 master 分支

做一个向前或向后移植的变基没有什么神奇的,两者都可以使用 git base 实现。
解释权通常留给一个更基本的理解,关于什么功能都被认为是在另一个功能之前或之后

当从别处复制版本库之后,会经常使用 git rebase 操作来把开发分支向前移植到 origin/master 追踪分支上。

当变基操作完成后,新的提交图类如下图所示

执行gitrebase之后

git rebase 命令也可以用 --onto 选项把一条分支上的开发线整个移植到完全不同的分支上

例如,假设你已经在特性分支 feature 上开发了一个新功能,在提交 P 和 Q 中,是基于 maint 分支的,如图所示。
要把 feature 分支上的提交 P 和 Q 从 maint 分支迁移到 master 分支,发出如下命令

图10_14执行gitrebase之前

1
$ git rebase --onto master maint^ feature

提交图看起来如下图所示:

图10_15执行gitrebase迁移之后

变基操作一次只迁移一个提交,从各自原始提交位置迁移到新的提交基础。因为,每个移动的提交都可能有冲突需要解决

如果发现冲突,rebase 操作会临时挂起进程以便你可以解决冲突。
一旦所有冲突都解决了,并且索引已经更新,就可以用 git rebase --continue 命令恢复变基操作。该命令会提交解决的冲突然后处理要变基的下一个提交。
在检查变基冲突的时候,如果你决定这个提交是没有必要的,你就可以通过 git rebase --skip 来通知 git rebase 命令提交这个提交,移动到下一个提交。
这么做可能不对,特别是当后续提交依赖于这个提交引入的变更时。
在这种情况下,该问题可能会像滚雪球一样越滚越大,因此最好还是真正去解决冲突。
最后,如果你认为不应该进行变基操作,就可以用 git rebase --abort 来中止操作,并把版本库恢复到发出 git rebase 命令之前的状态

使用 git rebase -i

假设你开始写俳句(日本的一种短诗),在签入前已经完成了两行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ git init
已初始化空的 Git 仓库于 /Users/nocilantro/Desktop/test_repo/.git/
$ cat haiku
Talk about colour
No jealous behaviour here
$ git add haiku
$ git commit -m "Start my haiku"
[master(根提交) b79c17f] Start my haiku
 1 file changed, 2 insertions(+)
 create mode 100644 haiku

你继续写作,但决定不应该用英式拼写,而应该用美式拼写 color。所以,做了个提交来更改它

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ git diff
diff --git a/haiku b/haiku
index 088bea0..958aff0 100644
--- a/haiku
+++ b/haiku
@@ -1,2 +1,2 @@
-Talk about colour
+Talk about color
 No jealous behaviour here

$ git commit -a -m "Use color instead of colour"
[master ea3add6] Use color instead of colour
 1 file changed, 1 insertion(+), 1 deletion(-)

最后,完成了最后一行并提交它

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ git diff
diff --git a/haiku b/haiku
index 958aff0..cdeddf9 100644
--- a/haiku
+++ b/haiku
@@ -1,2 +1,3 @@
 Talk about color
 No jealous behaviour here
+I favour red wine

$ git commit -a -m "Finish my colour haiku"
[master b9dcf8f] Finish my colour haiku
 1 file changed, 1 insertion(+)

但是,你又有了拼写窘境并决定把所有英式拼写 "ou" 逗改成美式拼写 "o"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ git diff
diff --git a/haiku b/haiku
index cdeddf9..064c1b5 100644
--- a/haiku
+++ b/haiku
@@ -1,3 +1,3 @@
 Talk about color
-No jealous behaviour here
-I favour red wine
+No jealous behavior here
+I favor red wine
$ git commit -a -m "Use American spellings"
[master 6cedd83] Use American spellings
 1 file changed, 2 insertions(+), 2 deletions(-)

此时,积累的提交历史记录如下所示:

1
2
3
4
5
$ git show-branch --more=4
[master] Use American spellings
[master^] Finish my colour haiku
[master~2] Use color instead of colour
[master~3] Start my haiku

在看完提交序列或者接受检查反馈后,你决定想先完成俳句再改正,希望如下提交历史记录。

1
2
3
4
[master] Use American spellings
[master^] Use color instead of colour
[master~2] Finish my colour haiku
[master~3] Start my haiku

但之后你还注意到没有必要用两个相似的提交来改正单词拼写。因此,你还想把 master 和 master^ 合并成一个提交

1
2
3
[master] Use American spellings
[master^] Finish my colour haiku
[master~2] Start my haiku

重新排序、编辑、删除,把多个提交合并成一个,把一个提交分离成多个,这都可以很轻松地使用 git rebase 命令的 -i 或者 --inter-active 选项完成。
此命令允许你修改一个分支的大小,然后把它们放回原来的分支或者不同的分支

这个例子就是一个典型的应用,在原地修改同一分支。
在这种情况下,4 个提交之间有三个变更集要修改;git rebase -i 需要知道你实际上想更改哪个提交

git rebase -i master~3

你将到编辑器里编辑一个文件,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
pick ea3add6 Use color instead of colour
pick b9dcf8f Finish my colour haiku
pick 6cedd83 Use American spellings

# 变基 b79c17f..6cedd83 到 b79c17f(3 个提交)
#
# 命令:
# p, pick <提交> = 使用提交
# r, reword <提交> = 使用提交,但修改提交说明
# e, edit <提交> = 使用提交,进入 shell 以便进行提交修补
# s, squash <提交> = 使用提交,但融合到前一个提交
# f, fixup <提交> = 类似于 "squash",但丢弃提交说明日志
# x, exec <命令> = 使用 shell 运行命令(此行剩余部分)
# b, break = 在此处停止(使用 'git rebase --continue' 继续变基)
# d, drop <提交> = 删除提交
# l, label <label> = 为当前 HEAD 打上标记
# t, reset <label> = 重置 HEAD 到该标记
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       创建一个合并提交,并使用原始的合并提交说明(如果没有指定
# .       原始提交,使用注释部分的 oneline 作为提交说明)。使用
# .       -c <提交> 可以编辑提交说明。
#
# 可以对这些行重新排序,将从上至下执行。
#
# 如果您在这里删除一行,对应的提交将会丢失。
#
# 然而,如果您删除全部内容,变基操作将会终止。

前三行列出你在命令行里指定的可编辑提交范围内的提交。
提交最初是按照从最老到最新排序的,每个提交前都有动词 pick(拾取)。
如果你现在离开编辑器,每个提交将按照顺序拾起,并应用到目标分支,然后提交。
# 开头的行是一些有用的提醒和注释,会被程序所忽略

然而, 这时你可以随意对提交进行重新排序、合并。修改或删除。要按照列出来的步骤操作,只要像下面那样在编辑器里重新排列提交然后退出即可

1
2
3
pick b9dcf8f Finish my colour haiku
pick ea3add6 Use color instead of colour
pick 6cedd83 Use American spellings
1
2
3
4
5
6
7
成功变基并更新 refs/heads/master。

$ git show-branch --more=4
[master] Use American spellings
[master^] Use color instead of colour
[master~2] Finish my colour haiku
[master~3] Start my haiku

这里,提交的历史已经记录重写;两个拼写提交放在一起,两个创作提交放在一起了。

继续遵循给出的顺序,下一步是把两个拼写提交合并成一个。再一次发出 git rebase -i master~3 命令。这次,把提交列表从

1
2
3
pick b9dcf8f Finish my colour haiku
pick ea3add6 Use color instead of colour
pick 6cedd83 Use American spellings

转换成

1
2
3
pick b9dcf8f Finish my colour haiku
pick ea3add6 Use color instead of colour
squash 6cedd83 Use American spellings

第三次提交会合并到前一个提交中,新的提交信息模板会把这两个提交组合起来

在本例中,两个提交信息合在一起,显示在编辑器里

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 这是一个 2 个提交的组合。
# 这是第一个提交说明:

Use color instead of colour

# 这是提交说明 #2:

Use American spellings

# 请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交
# 说明将会终止提交。
#
# 日期:  Sun Oct 10 20:26:10 2021 +0800
#
# 交互式变基操作正在进行中;至 b79c17f
# 最后的命令已完成(3 条命令被执行):
#    pick a427823 Use color instead of colour
#    squash 6ae27cc Use
# 未剩下任何命令。
# 您在执行将分支 'master' 变基到 'b79c17f' 的操作。
#
# 要提交的变更:
#       修改:     haiku

这些消息可以编辑到只剩下:

1
Use American spellings

最后,可以看到变基序列的结果;

1
2
3
4
$ git show-branch --more=4
[master] Use American spellings
[master^] Finish my colour haiku
[master~2] Start my haiku

虽然这里演示的重新排序和合并是发生在两次调用 git rebase -i master~3 中,但是这两步可以一次完成。
在一步中合并多个连续的提交也是完全有效的。

1
git pull --rebase

变基与合并