初见Git

Git分支规范(新):Git并行工作流程规范

2020-01-30  本文已影响0人  科研者
分支流转规范图

Git是一个十分优秀的版本控制工具,但仅仅依靠版本控制工具,还不能保证在多人协作的情况让项目的版本流转有条不紊,版本演进清晰整洁;还需要 Git工作流程规范 的约束;目前广泛使用的工作流程是 Git Flow,但我个人认为它有很多不足之处,于是我就制定了一个新的规范,取名为 Git并行工作流程规范 ,为了清楚完整地描述 Git并行工作流程规范 ,我花了很多精力设计、撰写、绘图 和 制作动画,详见下文 和 相关文章!关于 Git并行工作流程规范 的设计思路请看 Git并行工作流程规范设计记录

目录

内容

1. GitFlow的问题

目前广泛使用的工作流程是 Git Flow,如下图所示:

GitFlow分支工作流程图

它可以使得版本库的演进保持简洁,主干清晰,各个分支各司其职、井井有条;

但,我个人认为,它有许多不足之外,如下:

  1. 它需要维护两个相近的长期分支:Master 和 Develop;前者用于正式发布,后者用于日常开发;
  2. 每次完成 预发布分支 和 修复分支 时,都需要 分别合并到 Master 和 Develop 这两个长期分支中;
    GitFlow修复分支合并流程图
  3. 任务不独立;新任务的 功能分支 中 有可能包含 上个还未发布的 任务 的提交;
    比如:按照 Git Flow,假设 功能A 已经开发完成,并且已经被合并到了 Develop 分支;
    这时又有 功能B 需要开发,则会从 Develop 新建 功能B 分支 用于开发 功能B ,则此时 功能B 分支 会包含 功能A 分支的所有提交;
  4. 不支持多任务并行开发;
    比如:按照 Git Flow,假设 功能A 已经开发完成,并且已经走到了 预发布 阶段,则此时 功能A 分支已经被合并到了 Develop 分支;
    这时又有 功能B 需要开发,则会从 Develop 新建 功能B 分支 用于开发 功能B ,则此时 功能B 分支 会包含 功能A 分支的所有提交;
    如果这个时候发现 功能A 有问题 或者 因种种原因,需要暂停 功能A 的发布,但需要发布 功能B ,这时就不好办了,因为 功能B 分支中 已经包含了 功能A 的所有提交;

2. Git并行工作流程的设计宗旨

3. 新概念的定义

为了方便 下方 或 今后 的表达,我需要定义以下概念:

临时性分支 终将会被合并到 长期分支中(除非弃用),如,开发完功能后,因转测,需要把 功能分支 合并到 测试分支 中,测试完后,测试分支 又会被合并到 预发布 分支中,最后,预发布分支 又会被合并到 发布分支 中;在这个过程中,功能分支 依次被合并到了 测试分支、预发布分支、发布分支;

像上面这样,分支 在被其它分支合并的过程 称为 分支 的流转

分支A 被合并到 分支B ,也称为 分支A 流转到了 分支B

分支A 流转到了 分支B,分支B 又流转到了 分支C ,像这样的过程称为 分支A 的 连续流转,也称为 分支A 连续流转到了 分支C

连续流转过程中的每一个分支 都称为该连续流转的一个流转环节,也称为该连续流转的一个 流转分支

连续流转的所有 流转环节 的有序组合 称为 流转链

流转链中 最初的 流转环节(流转分支) 又称为该流转链的 原始分支初始环节

流转链中 最终的 流转环节(流转分支) 又称为该流转链的 终点分支终点环节

流转链中 原始分支 和 终点分支 之间的 流转分支 称为 中间分支中间环节

如果 分支A 是基于 分支B 创建的,即: 分支A 是从 分支B 创建的,则称 分支B 是 分支A 的 母分支

4. 分支的分类

4.1. 按照生命周期来分

4.2. 按照作用来分

5. 按照流转环节来分

5.1. 包含关系

按照作用来划分的类别 与 本规范中按照 生命周期来划分的类别的包含关系如下:

即:

6. 分支的流转规范

根据上述的《Git并行工作流程的设计宗旨》,我为各类分支的流转设计了如下规范:

注意: 本规范(分支的流转规范)中所述的合并操作 可以是 一般的 分支 合并 操作,也可以是 Pull requests (Merge Reques),这个取决于仓库的管理策略(本人建议使用 Pull requests (Merge Reques) 的方式,因为 Pull requests (Merge Reques) 方便权限管理 和 操作确认);所以,本规范也适用于那些 用多个仓库管理同一个项目的 Git仓库管理策略;

分支流转规范图
高清图详见:分支流转规范图-高清分支流转规范图-高清透明
该图的设计思路详见:Git并行工作流程规范设计记录

7. 分支合并规范

为了使分支各司其职,演进清晰简洁,分支的变更单元可用提交节点表示,我们在合并分支时需要按照一定的规范来操作,具体如下:

8. 提交规范

8.1. 提交操作规范

为了保持分支提交历史的清晰、独立,在提交更改时,我们应做到:

多个提交合并成一个的各种方法请看Git中合并多个提交的各种方法

8.2. 提交说明规范

Git 每次提交代码,都必须要写 提交说明; Git 对 提交说明 的格式是没有限制的,你想怎么写就怎么写,如下:

杂乱的提交说明

但是,类似这种没有格式的提交说明有以下缺点:

为了 方便 查看、过滤 提交说明,我需要将提交说明格式化、规范化;目前,有多种 提交说明 的写法规范。但我推荐 Angular提交说明规范,这是目前使用最广的写法,比较合理和系统化,并且有配套的工具。

关于 Angular提交说明规范 的详细文章请见:

下面是我对 Angular提交说明规范 一个汇总描述;

8.2.1. Angular提交说明规范

Angular提交说明的格式如下

<Type>[Scope]:<Subject>
<空一行>
[Body]
<空一行>
<Footer>

9. 相关技巧

规范有了,但在实施规范的过程中会遇到各种技术难题,为了完整性,我又研究并提供了实施规范过种中各种问题的解决方案,并把这些方案撰写成文 Git技巧和问题解决方案,下面仅是本文涉及到的相关技巧:

9.1. 禁止快进式合并

git在执行合并操作时(无论是通过 merge 命令,还是通过 pull 命令),比如 将 分支B 合并进 分支A 中,如果 分支A 完全包含在 分支B 的历史中(如下图),


快进合并前

那么,git 默认会以快进的方式进行合并,合并后的效果如下:


快进式合并

非快进式合并的效果如下:


非快进式合并

如果想禁用快进式合并,可以给命令传递 --no-ff 选项, ff 是 快进的英文 fast farward 缩写;如下:

merge命令:

git merge  --no-ff  <分支B>

pull命令:

git pull --no-ff

merge 和 pull 命令中 与快进式合并相关的选项如下:

9.2. 创建空分支(独立分支、孤儿分支)

我们可以通过以下命令创建新分支:

这种方式创建的新分支 都是 基于当前的 HEAD 来创建分支的,新的分支会和当前 HEAD 拥有共同的 提交历史;
例如,假设当前 HEAD 在 分支A 上,如下图所示,


创建独立分支前

通过 git branch 分支Bgit checkout -b 分支B 来创建 分支B 后,分支图如下所示:

创建非独立分支

新建的 分支B 和 分支A 会有相同提交历史;

如果我们想创建一个没有任何历史的分支,我们可以用 带 --orphan 选项的 checkout 命令,语法如下:

git checkout --orphan <新分支名> [开始点]

--orphan 选项指定创建一个 孤儿分支 ,即:独立的分支、没有任何提交历史的分支;并且会切换到这个新的分支;
此时,你通过 branch 命令列出的分支列表里是没有这个分支的,因为该分支里没有任何提交,分支没有可被引用的提交对象;
并且,此时,新分支的 暂存区 中存放的是 开始点 处目录树中的所有文件,如果没有指定 开始点 参数,则默认会把 当前的 HEAD 作为开始点;
如果不想要暂存区的任何东西,可以执行 git rm -rf . 命令清空暂存区;
如果想将 开始点 处的整个目录树作为新分支的第一个版本进行提交,直接执行 commit 命令 git commit -m "提交信息" 即可;

如:当前在HEAD分支A,执行如下命令:

git checkout --orphan 分支B
git commit -m "提交信息"

分支图如下所示:


创建独立分支

合并独立分支的方法请看合并不相关的分支(没有共同历史的分支)

9.3. 合并不相关的分支(没有共同历史的分支)

Git默认的合并操作只会对有共同提交历史的分支进行合并;


有共同历史的分支

不过,对没有共同提交历史的分支进行合并的情况也是存在的,比如:

不相关的分支

若想对没有共同历史的分支进行合并,只需给 mergepull 命令(pull命令也会有合并的操作)添加 --allow-unrelated-histories 选项,语法如下:
merge语法:

git merge  --allow-unrelated-histories  <分支名>

pull语法:

git pull  --allow-unrelated-histories
不相关分支的合并

9.4. 只合分支并保持历史的线性

假设有 分支A 和 分支B ,当前在 分支A,如下图:


squash合并前

当我们进行合并分支时,通常会将指定分支的变更合并到当前分支中,并产生一个提交,该提交会有两个父提交的引用;如下图:


非squash合并

如果我们想合并其它分支的变更,但又想保持当前分支的线性,使分支的提交历史上没有分支交汇的情况,即:每一个提交对象都有一个父提交的对象,而非多个父提交对象;那该怎么操作呢?

我们可以给 mergepull 命令(pull命令也会有合并的操作)添加 --squash 选项,语法如下:
merge语法:

git merge  --squash  <分支名>

pull语法:

git pull  --squash

注意: 执行完 --squash 的合并操作后,git默认不会生成合并的变更提交,只是把合并的变更放在暂存区中,并附有默认的提交信息,你需要再手动进行提交下;

示例:

git merge  --squash  分支B
git commit
squash合并

9.5. 用变基的方式pull

语法:

git pull -r [<远程仓库> [[远程分支][:<本地分支>]]
git pull --rebase [<远程仓库> [[远程分支][:<本地分支>]]

说明:
远程仓库 获取指定的 远程分支 的更新到相应的远程跟踪分支,并以变基 rebase 的方式合并到 本地分支,并将本地分支中新的提交拼接到上游分支的后面;
如果 本地分支 不存在,则会创建该本地分支;

示例:

git pull -r

10. 相关文章

上一篇 下一篇

猜你喜欢

热点阅读