Git 合并Commit

前言

团队项目开发中,遵循一个合理、清晰的 Git 使用流程,是非常重要的。之前写了一篇文章《Git 分支管理》,说到 Git 分支管理是一门艺术,但其实想要合理规范 Git 的使用,可以讨论的地方还有很多。前阵子跟我们公司的后台同事讨论到 commit 合并的问题。由 fio 说到的 rebase,以及我使用 GUI - GitHub Desktop 的时候发现的 Undo,在此做一篇关于合并提交的总结。

合并 Commit

为什么需要合并 Commit

其实这种需求很常见,假设你刚刚添加了一个新功能,commit 了,还没 Push。此时,你发现了一个小 Bug,然后修复了它,又commit了。过了一会,你又发现另外一个 Bug,于是你又修复了它,然后又 commit了。此时,在你Push之前,已经有3个 commit了。

如果你想要让远程仓库的项目网络“好看”,即简洁明了,每个 commit 都有它的意义。那么就应该把这3个 commit 合并后,再 Push。否则如果这种随便 commit 然后就 Push 的现象越来越多,会导致项目网络上面的提交信息可读性很差,还会让项目经理 Get 不到点。像我们团队的项目网络经常会看到像这样的提交信息:“小修复”、“改了一点小东西” …

方式一、rebase

1
$ git rebase -i origin/master

git rebase 命令的 i 参数表示互动(interactive),这时 git 会打开一个互动界面,进行下一步操作。

下面采用 Tute Costa 的例子,来解释怎么合并 commit。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pick 07c5abd Introduce OpenPGP and teach basic usage
pick de9b1eb Fix PostChecker::Post#urls
pick 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend
# Rebase 8db7e8b..fa20af3 onto 8db7e8b
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT commit WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

上面的互动界面,先列出当前分支最新的4个 commit(越下面越新)。每个 commit 前面有一个操作命令,默认是 pick,表示该行 commit 被选中,要进行 rebase 操作。

4个 commit 的下面是一大堆注释,列出可以使用的命令。

命令 说明
pick 正常选中
reword 选中,并且修改提交信息
edit 选中,rebase时会暂停,允许你修改这个commit(参考这里
squash 选中,会将当前commit与上一个commit合并
fixup 与squash相同,但不会保存当前commit的提交信息
exec 执行其他shell命令

上面这6个命令当中,squash 和 fixup 可以用来合并 commit。先把需要合并的 commit 前面的动词,改成 squash(或者s)。

1
2
3
4
pick 07c5abd Introduce OpenPGP and teach basic usage
s de9b1eb Fix PostChecker::Post#urls
s 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend

这样一改,执行后,当前分支只会剩下两个 commit。第二行和第三行的 commit,都会合并到第一行的 commit。提交信息会同时包含,这三个 commit 的提交信息。

1
2
3
4
5
6
7
8
9
# This is a combination of 3 commits.
# The first commit's message is:
Introduce OpenPGP and teach basic usage
# This is the 2nd commit message:
Fix PostChecker::Post#urls
# This is the 3rd commit message:
Hey kids, stop all the highlighting

如果将第三行的 squash 命令改成 fixup 命令。

1
2
3
4
pick 07c5abd Introduce OpenPGP and teach basic usage
s de9b1eb Fix PostChecker::Post#urls
f 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend

运行结果相同,还是会生成两个 commit,第二行和第三行的 commit,都合并到第一行的 commit。但是,新的提交信息里面,第三行 commit 的提交信息,会被注释掉。

1
2
3
4
5
6
7
8
9
# This is a combination of 3 commits.
# The first commit's message is:
Introduce OpenPGP and teach basic usage
# This is the 2nd commit message:
Fix PostChecker::Post#urls
# This is the 3rd commit message:
# Hey kids, stop all the highlighting

这也是就是 fio 所提出的问题:如果我不想让别人看到我合并的 commit 的提交信息怎么办?

回答:用 fixup (f)

方式二、“Undo” - reset

在 GitHub Desktop 这个客户端上,有个明显的 Undo 按钮,作用是撤销该 commit。平时在上述情景的时候,我都会使用 Undo,然后再重新 commit。那么原理是什么?

另外一种合并 commit 的简便方法,就是先撤销之前的 commit,然后再建一个新的 commit。

1
$ git reset HEAD~n

n 是撤销的 commit 的个数,假如上述情景要撤销 5 个commit,eg.

1
2
3
4
$ git reset HEAD~5
$ git add .
$ git commit -am "Here's the bug fix that closes #28"
$ git push --force

而 GitHub Desktop 的 Undo,其实就是每点一下执行一个reset,仅此而已。

1
$ git reset HEAD~1

补充

squash 和 fixup 命令,还可以当作命令行参数使用,自动合并 commit。

1
2
$ git commit --fixup
$ git rebase -i --autosquash

参考


GIT tip : Keep your branch clean with fixup and autosquash

  • 本文作者: Bingo
  • 本文链接: https://blog.bingo.ren/8.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!