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
$ git rebase -i origin/master
git rebase 命令的 i 参数表示互动(interactive),这时 git 会打开一个互动界面,进行下一步操作。
下面采用 Tute Costa 的例子,来解释怎么合并 commit。
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
上面的互动界面,先列出当前分支最新的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)。
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
2
3
4
这样一改,执行后,当前分支只会剩下两个 commit。第二行和第三行的 commit,都会合并到第一行的 commit。提交信息会同时包含,这三个 commit 的提交信息。
# 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
2
3
4
5
6
7
8
9
如果将第三行的 squash 命令改成 fixup 命令。
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
2
3
4
运行结果相同,还是会生成两个 commit,第二行和第三行的 commit,都合并到第一行的 commit。但是,新的提交信息里面,第三行 commit 的提交信息,会被注释掉。
# 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
2
3
4
5
6
7
8
9
这也是就是 fio 所提出的问题:如果我不想让别人看到我合并的 commit 的提交信息怎么办?
回答:用 fixup (f)
# 方式二、“Undo” - reset
在 GitHub Desktop 这个客户端上,有个明显的 Undo 按钮,作用是撤销该 commit。平时在上述情景的时候,我都会使用 Undo,然后再重新 commit。那么原理是什么?
另外一种合并 commit 的简便方法,就是先撤销之前的 commit,然后再建一个新的 commit。
$ git reset HEAD~n
n 是撤销的 commit 的个数,假如上述情景要撤销 5 个commit,eg.
$ git reset HEAD~5
$ git add .
$ git commit -am "Here's the bug fix that closes #28"
$ git push --force
2
3
4
而 GitHub Desktop 的 Undo,其实就是每点一下执行一个reset,仅此而已。
$ git reset HEAD~1
# 补充
squash 和 fixup 命令,还可以当作命令行参数使用,自动合并 commit。
$ git commit --fixup
$ git rebase -i --autosquash
2