分支命名规范

  • feature/xxx:新功能开发
  • bugfix/xxx:缺陷修复
  • hotfix/xxx:紧急修复
  • chore/xxx:日常维护任务
  • experiment/xxx:试验性开发
  • master:生产环境
  • preview:预生产环境
  • develop:开发环境
  • release:测试环境

工作流

  1. 从master分支创建feature/fix分支,开发完成后合并到develop和release分支
  2. 功能测试完成后,如果有预生产环境,合并到preview分支
  3. 预生产环境测试完成后,合并到master分支
  4. 新建tag,版本发布

整个流程中确保feature/fix单一分支的代码保持纯净独立,同步进行多个需求开发或者bug修复时,优先选择建立多个分支,计划有变时相对更为灵活。

分支合并

Git有两种合并:一种是”直进式合并”(fast forward),不生成单独的合并节点;另一种是”非直进式合并”(none fast-forword),会生成单独节点。建议总是采用后者(即使用–no-ff参数)。只要发生合并,就会有一个单独的合并节点。方便后续日志查看。

直进式合并

1
2
3
4
5
6
7
8
9
10
11
$ git merge feature/v1
Updating fec76a7..4a8eefd
Fast-forward
frontend/src/main.js | 2 ++
1 file changed, 2 insertions(+)

$ git log
commit 4a8eefd64af0b3a69da7dde22354cfefd6eae7bd (HEAD -> feature/main, origin/feature/v1, feature/v1)
Author: fuyunfeng <771085570@qq.com>
Date: Fri May 16 16:16:33 2025 +0800
feat: v1_c1

非直进式合并

1
2
3
4
5
6
7
8
9
10
11
$ git merge feature/v1 --no-ff
Merge made by the 'ort' strategy.
frontend/src/main.js | 2 ++
1 file changed, 2 insertions(+)

$ git log
commit e351c5b7b1b751abb5d11e2d01a4c91b2c86e782 (HEAD -> feature/main)
Merge: fec76a7 4a8eefd
Author: fuyunfeng <771085570@qq.com>
Date: Fri May 16 16:17:58 2025 +0800
Merge branch 'feature/v1' into feature/main

squash和rebase

实际开发过程中,分支的提交可能有多次,为了保持主分支的日志相对整洁,在发起Pull Request之前,应该先将多个commit合并成一个,如果同一版本有多个分支并行,建议先将多个分支合并到一个分支,再用合并后的分支发起Pull Request。

squash合并多个commit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git merge feature/v1 --squash
Updating fec76a7..e51663d
Fast-forward
Squash commit -- not updating HEAD
frontend/src/main.js | 3 +++
1 file changed, 3 insertions(+)

$ git status
On branch feature/main
Your branch is up to date with 'origin/feature/main'.

Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: frontend/src/main.js

使用–squash选项执行merge时,Git不会像在正常合并中那样在目标分支中创建合并提交,而是会将分支上多个提交变更直接作用到工作区中作为本地更改,然后将目标分支的指针指向这个新的提交,你只需要手动正常的提交这些更改,就好像你在当前分支上做了一次所有差异化变更一样。

rebase合并多个commit

1
2
3
4
5
6
7
8
9
10
## 指定节点数量
$ git rebase -i HEAD~3
pick 4a8eefd feat: v1_c1
pick e51663d feat: v1_c2
pick ba44513 feat: v1_v3

## 指定具体节点范围 [startpoint] [endpoint] (包含结束节点 不包括开始节点本身 end不指定默认取最新节点)
$ git rebase -i 4a8eefd
pick e51663d feat: v1_c2
pick ba44513 feat: v1_v3

使用rebase合并时,-i的意思是–interactive,即弹出交互式的界面让用户编辑完成合并操作,[startpoint] [endpoint]则指定了一个编辑区间,如果不指定[endpoint],则该区间的终点默认是当前分支HEAD所指向的commit。(注:该区间指定的是一个前开后闭的区间)。

每一个commit id 前面的pick表示指令类型,git 为我们提供了以下几个命令:

1
2
3
4
5
6
7
pick:保留该commit(缩写:p)
reword:保留该commit,但我需要修改该commit的注释(缩写:r)
edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e)
squash:将该commit和前一个commit合并(缩写:s)
fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f)
exec:执行shell命令(缩写:x)
drop:我要丢弃该commit(缩写:d)

i 进入编辑状态,以下操作会将v1分支上历史4次commmit合并成一个,并重新修改注释信息:

1
2
3
4
r 4a8eefd feat: v1_c1
f e51663d feat: v1_c2
f ba44513 feat: v1_v3
f 781983f feat: v1_c4

wq 保存退出后进入注释修改界面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
feat: v1_c1
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Fri May 16 16:16:33 2025 +0800
#
# interactive rebase in progress; onto fec76a7
# Last command done (1 command done):
# reword 4a8eefd feat: v1_c1
# Next commands to do (3 remaining commands):
# fixup e51663d feat: v1_c2
# fixup ba44513 feat: v1_v3
# You are currently editing a commit while rebasing branch 'feature/v1' on 'fec76a7'.
#
# Changes to be committed:
# modified: frontend/src/main.js

i 进入编辑状态修改注释,wq保存退出就完成了合并操作。

1
2
3
4
5
$ git rebase -i HEAD~4
[detached HEAD 7657c95] feat: v1需求rebase_squash
Date: Fri May 16 16:16:33 2025 +0800
1 file changed, 2 insertions(+)
Successfully rebased and updated refs/heads/feature/v1.

非常规情况下不建议通过rebase对任何已经提交到公共仓库中的commit进行修改。


参考链接

[分支命名规范] https://mp.weixin.qq.com/s/b0IBRcYiYuBcgggInlN92g
[Git工作流] https://www.ruanyifeng.com/blog/2015/12/git-workflow.html
[Git merge] https://zhuanlan.zhihu.com/p/462530860
[Git rebase] https://juejin.cn/post/6844903600976576519