Git commit history 规范标准(好的习惯,终身受益)
开篇
多数情况我们使用 Git,都是以最简单快速的方式将代码同步到远端,如:git commit -> git pull -> git push,这没有任何技巧,多人同步协作提交下的 commit history 会变得混乱不堪。
一个优秀的项目,它的 commit history 需要足够清晰、足够规范,才能提现出团队的专业性。
接下来我们将从以下几个角度,来营造一个清晰、简洁的提交历史环境。
- commit message
- 多人协同开发
- 合并多个提交
- 补充提交
- 转移提交
一、commit message
一个规范的 commit message 可以很直观的描述代码变更的类型、影响范围及变更原因。例如:
feat(view): 新增用户详情页面
提交由三部分组成:
type(必需),用于说明 commit 类型。feat 代表新功能,fix 代表修复bug,style 代表格式的变动。scope(非必需),用于说明 commit 影响范围。如数据层、视图层,亦或者是更改组件 components。subject(必需),用于描述本次 commit 提交的目的。
二、多人协同开发
现有一个 dev 开发分支,大家都在这个分支上进行开发。在你提交本地代码到远端前,其他同学早于你先将他的 commit 提交到远端,这时你就需要先执行 git pull 更新远端代码到本地,才可以再进行本地代码的提交。
但这时你查看提交历史会发现,执行 git pull 命令会自动生成一条 merge commit(Merge branch 'dev' of https://gitxxxxx),且 commit history 历史线出现了分叉。当多人进行这样的操作时,commit history 就会变得不清晰。

这里,我们需要了解一种新的代码合并方式:rebase 变基。与 merge 功能相似,它会将你本地的所有提交,接到主分支的末端,从而形成一条直线线路,干净明了。
假设本地 dev 和远端 dev 都在 B 节点上,这时在远端 dev 上有了一个新的提交 E,本地 dev 有了两个新的提交 C 和 D,图示如下:

其中,远端 dev 可以看作是基分支(目标分支),本地 dev 则为待变基分支。
当执行 rebase 操作时,git 会从两个分支的共同祖先(本例中的 B 节点)开始提取「待变基分支」上的修改(C 和 D),将这些修改应用到「基分支」的最新提交(E 节点)的后面。图示如下:

说白了就是可以提取我们在 A 分支上的改动,然后应用在 B 分支的最新提交后面。
注意,生成的 新C 和 新 D,内容与原来的 C D 相同,但 commitId 为新生成的;另外你会发现,commit history 将不会按照提交时间进行排序,它会将本地 dev 的提交全部应用到远端 dev 最新提交之后。
回到主题,若想要 commit history 能够清晰明朗的显示每一条有意义的提交记录,可以使用 git pull --rebase(git fetch + git rebase)代替 git pull(git fetch + git merge)。
git pull --rebase origin dev
执行后的效果如下:

若执行命令后遇到冲突,可以选择:
// 1、放弃合并,回到 git pull --rebase 之前
git rebase --abort
// 2、手动解决冲突,并 git add 冲突文件,然后执行下面命令继续变基
git rebase --continue // 解决完冲突,继续变基
当然,git rebase 还可用于分支与分支之间的合并操作,但这里通常不建议使用它,分支分支之间的合并还是推荐使用 git merge --no-ff 来完成,这会生成一条 "Merge branch xx to xx" 可以清楚的记录分支的合并操作。
三、合并多个提交
比如今天实现一个页面,早上提交了视图代码 view,中午提交了类型定义 type,下午提交了逻辑代码 logic。
下班前我们要把 commit 推送到远端,由于它们同属于一个功能,为了使记录更简洁明了,git rebase -i [startpoint] [endpoint] 可以将三个 commit 合并为一个 commit 进行提交。
我们通过 git log 查看这三个 commit id,假设看到信息如下:
commit 44444xxxxx
feat: 提交 logic
commit 33333xxxxx
feat: 提交 type
commit 22222xxxxx
feat: 提交 view
commit 11111xxxxx
feat: first commit
我们可以运行以下命令:
git rebase -i 11111xxxxx 44444xxxxx
// or
git rebase -i HEAD~3
-i 会弹出交互式的界面让用户编辑完成合并操作,[startpoint] [endpoint] 则指定了一个编辑区间(前开后闭的区间),如果不指定 [endpoint],则该区间的终点默认是当前分支 HEAD 所指向的 commit。
此时你所看到的编辑界面如下:

每个 commit id 前面代表了要使用的指令(默认使 pick),将三个 commit 合并为一个 commit,可以按键 i 编辑改写如下:
pick d489f04 feat: 提交 view
s dfafc83 feat: 提交 type
s 7679b52 feat: 提交 logic
按键 ESC 退出编辑,按键 :wq 退出编辑界面,重新执行 git log 看到 commit history 如下:

可以看到,此命令会将多个 commit 合并,并生成一个全新的 commitId 进行提交。如果这几个提交都是你操作的,你不用担心会出现合并冲突,它会自动应用最新代码。
四、补充提交
当我们将变更代码作为一次 commit 提交后(本地提交),但是发现遗漏了一个要提交的文件。大部分同学会选择再提交一次 commit 作为补充提交,可是这次提交并没有实质意义。
git commit --amend 命令可以实现撤销上一个提交动作,并基于这次提交重新编辑 commit message 或要提交的变更文件。
操作演示:
我们初始化 git 仓库,新建了两个文件:a.js 和 b.js,将 a.js 添加到暂存区并提交 commit:
git init
touch a.js b.js
git add a.js
git commit -m 'feat: first commit'
这时发现 b.js 忘记提交了,接下来可以将 b.js 添加到暂存区,执行 git commit --amend:
git add b.js
git commit --amend
此时你会看到编辑面板,展示了 commit message 及提交的变更文件,按下 i 可以进入编辑状态,如果不需要更改 message,输入 :wq 退出即可。

注意,不建议在 commit 被提交到远端仓库后做此操作,仅在本地提交使用。
五、转移提交
在对分支代码进行合并时通常会有两种情况:
- 一是将一个分支上的所有 commit,合并到目标分支,采用 git merge 命令;
- 另一个是将指定的一个过多个 commit,转移到目标分支,采用 git cherry-pick 命令。
命令使用如下:
// 1、将其他分支上的指定 commit 转移到当前分支
git cherry-pick <commitId>
// 2、将指定分支上的最近一个 commit 转移到当前分支
git cherry-pick branchName
// 3、转移连续的多个 commit
git cherry-pick A^..B // git cherry-pick e73e93^..aa5a2a
若转移过程中遇到冲突,可选择:
// 放弃转移
git cherry-pick --abort
// 手动解决冲突,并 git add 冲突文件,然后执行下面命令继续转移
git cherry-pick --continue
注意,cherry-pick 后的 commit 会追加到分支最新一次提交的顶部,也会造成 commit history 并未按照提交时间进行排序。
最后
感谢阅读,如有不足之处,欢迎评论指出。
参考: 1. Commit message 使用指南 2. 互联网大厂,代码提交规范和技巧! 3. git rebase详解(图解+最简单示例,一次就懂)
转载自:https://juejin.cn/post/7147669559061774344