[版本管理] Git 开发管理模式与规范

1. git 开发模式

1.1. GitHub Flow——以部署为中心的开发模式

❶ 令 master 分支时常保持可以部署的状态
❷ 进行新的作业时要从master分支创建新分支,新分支名称要具有 描述性
❸ 在❷新建的本地仓库分支中进行提交
❹ 在 GitHub 端仓库创建同名分支,定期 push
❺ 需要帮助或反馈时创建 Pull Request,以Pull Request进行交流
❻ 让其他开发者进行审查,确认作业完成后与 master 分支合并
❼ 与 master 分支合并后立刻部署

1.2. Git Flow——以开发为中心的开发模式

❶ 从开发版的分支(develop)创建工作分支(feature branches),进 行功能的实现或修正
❷ 工作分支(feature branches)的修改结束后,与开发版的分支 (develop)进行合并
❸ 重复上述❶和❷,不断实现功能直至可以发布
❹ 创建用于发布的分支(release branches),处理发布的各项工作
❺ 发布工作完成后与 master 分支合并,打上版本标签(Tag)进行发布
❻ 如果发布的软件出现BUG,以打了标签的版本为基础进行修正 (hotfixes)

1.2.1. 专栏:版本号的分配规则

版本控制策略规定了软件版本号的分配规则,因此制定该策 略时应当尽量简单易懂。 比如在用 x.y.z格式进行版本管理时的规则如下所示。

  1. x 在重大功能变更或新版本不向下兼容时加 1,此时 y 与 z 的数字归 0
  2. y在添加新功能或者删除已有功能时加1,此时z的数字归0
  3. z 只在进行内部修改后加 1
    下面举个具体例子:

1.0.0:最初发布的版本 …
1.0.1:修正了轻微 BUG …
1.0.2:修复漏洞 …●
1.1.0:添加新功能 …
2.0.0:更新整体 UI 并添加新功能

这便是版本号的大致分配规则。 如果团队采用了GitFlow,那么成员在交流的时候会经常用到版本号,因此版本控制策略越早制定越好

2. commit 提交指南

一份理解 commit 信息重要性以及如何写好它们的指导手册。

它可以帮你了解什么是 commit,为什么填写好的信息说明比较重要,以及提供最佳实践、计划和(重新)书写良好的 commit 历史的一些建议。

2.1. 什么是 “commit”?

简而言之,commit 就是你本地仓库中文件的一个快照。
和一些人的想法相反,git 不仅存储文件之间的差异,还存储所有文件的完整版本
对于从一次提交到另一次提交之间未发生改变的文件,git 仅存储之前已存的同一份文件的链接。

下面的图片显示了 git 随着时间变化如何存储数据,其中每个『版本』都是一个 commit:

2.2. 为什么 commit 信息很重要?

为了最大化这些好处,我们可以使用下一节描述的一些好的实践和标准。

2.3. 好的实践

这些是从我的经验、网络文章和其他指南中收集的一些实践案例。如果您有其他实践(或有不同意见),请尽管随时打开 Pull Request 并贡献您的意见。

2.3.1. 使用祈使形式

# 好示例
Use InventoryBackendPool to retrieve inventory backend
# 坏示例
Used InventoryBackendPool to retrieve inventory backend

但为什么要使用祈使形式?

一个 Commit 信息描述了提到的变化实际做了什么,它的影响,而非做的内容。

这篇来自 Chris Beams 的优秀文章 给我们一个简单的句子,可以帮助我们以祈使形式来书写更好的 commit 信息:

If applied, this commit will <commit message>

示例:

# 好示例
If applied, this commit will use InventoryBackendPool to retrieve inventory backend
# 坏示例
If applied, this commit will used InventoryBackendPool to retrieve inventory backend

2.3.2. 首字母大写

# 好示例
Add `use` method to Credit model
# 坏示例
add `use` method to Credit model

首字母需要大写的原因是遵守句子开头使用大写字母的语法规则。

这个实践的使用可能因人而异,团队间亦可能不同,甚至不同语言的人群间也会不同。
大写与否,一个重要的点是要保持标准一致并且遵守它。

2.3.3. 尝试在不必查看源代码的情况下沟通变化内容

# 好示例
Add `use` method to Credit model
# 坏示例
Add `use` method
# 好示例
Increase left padding between textbox and layout frame
# 坏示例
Adjust css

很多场景中(例子:多次提交、多次变更和重构)这都有助于帮助代码审查者理解代码提交者当时的想法。

2.3.4. 使用消息体来解释『为什么』、『是什么』、『怎么做』以及附加细节信息

# 好示例
修复了 InventoryBackend 子类的方法名

InventoryBackend 派生出的类没有
遵循基类接口

它之所以运行,是因为 cart 以错误的方式
调用了后端实现。
# 好示例
Cart 中对 credit  json 对象间做序列化和反序列化

基于两个主要原因将 Credit 实例转化成 dict

  - Pickle 依赖于类的文件路径
  如果需要重构的话我们不想破坏任何东西
  - Dict 和内建类型在默认情况下是可以通过 pickle 来序列化的
# 好示例
Add `use` method to Credit

 namedtuple 变成 class
是因为我们需要使用新的值来设置属性(in_use_amount

提交信息的主题和正文被一个空白行分割
附加的空白行被认为是提交信息正文的一部分。

类似 -*\ 的字符是用来提高可读性的元素。

2.3.5. 避免通用消息或者没有任何上下文的消息

# 坏示例
Fix this

Fix stuff

It should work now

Change stuff

Adjust css

2.3.6. 限制字符数量

推荐主题最多使用 50 个字符,消息体最多使用 72 个字符。

2.3.7. 保持语言的一致性

对于项目所有者:选择一个语言并使用该语言书写所有的 commit 信息。理想情况下,它应该匹配代码注释、默认翻译区域(对于做了本地化的应用)等等。

对于项目贡献者:基于已有 commit 历史书写同样语言的 commit 信息。

# 好示例
ababab Add `use` method to Credit model
efefef Use InventoryBackendPool to retrieve inventory backend
bebebe Fix method name of InventoryBackend child classes
# 好示例(葡萄牙语示例)
ababab Adiciona o método `use` ao model Credit
efefef Usa o InventoryBackendPool para recuperar o backend de estoque
bebebe Corrige nome de método na classe InventoryBackend
# 坏示例(混合了英语和葡萄牙语)
ababab Usa o InventoryBackendPool para recuperar o backend de estoque
efefef Add `use` method to Credit model
cdcdcd Agora vai

2.3.8. 模板

这是一个样板,由 Tim Pope 编写,出现在文章高级 Git 手册

简化变更内容到 50 字符左右或者更少

如有必要,可提供更详细的说明文字。
将它包装成大约 72 个字符左右。
在某些情况下,第一行被视为 commit 的信息主题,余下文字被认为信息正文。
将摘要和正文分离开的空白行很有必要(除非你忽略了整个正文);
不同的工具像 `log`、`shortlog`、`rebase`,
可能会变得混乱,如果你同时运行两个。

解释本次 commit 正在解决的问题。
专注于此次变更的原因,而非如何变更(代码会解释这点)。
此次变更是否有副作用或其他隐性后果?
这里就是解释它们的地方。

空白行之后有更进一步的段落。

 - 也可以用要点符号。

 - 通常使用连字符或者星号作为要点符号,
 前面有一个空格,中间有空白行,
 但是约定惯例各不相同。

如果你使用问题跟踪,在底部放置它们的引用,
像下面这样:

Resolves: #123
See also: #456, #789

2.4. Rebase 与 Merge

这节是 Atlassian 优秀教程中的一个 TL;DR“Merge 与 Rebase”

2.4.1. Rebase

TL;DR: 把你的分支中的 commit 一个接一个地应用到 base 分支,生成一个新树。

2.4.2. Merge

TL;DR: 使用两个分支间的差异,创建新的 commit,称作(适当地)merge 提交

2.4.3. 为什么有些人更倾向于 merge 而不是 rebase?

我尤其更倾向于 rebase 而不是 merge,理由包含:

2.4.4. 何时做 squash?

“Squashing” 是处理一系列 commit 并将它们压缩为一个 commit 的过程。

它在多种情况下都有用,例子:

2.4.5. 何时避免 rebase 和 squash?

避免在多人协作的公共 commit 或者共享分支中执行 rebase 和 squash。
rebase、squash 重写历史记录、覆盖已有 commit,在共享分支的 commit 中执行以上操作(例子,推送到远程仓库的 commit 或者来自其他分支的 commit)可能造成混淆,并且由于分歧的树干和冲突大家可能会丢失他们的变更(本地和远程的)。

2.5. 有用的 git 命令

2.5.1. rebase -i

使用它来压制 commit,编辑信息,重写/删除/重新排序 commit,等等。

pick 002a7cc Improve description and update document title
pick 897f66d Add contributing section
pick e9549cf Add a section of Available languages
pick ec003aa Add "What is a commit" section"
pick bbe5361 Add source referencing as a point of help wanted
pick b71115e Add a section explaining the importance of commit messages
pick 669bf2b Add "Good practices" section
pick d8340d7 Add capitalization of first letter practice
pick 925f42b Add a practice to encourage good descriptions
pick be05171 Add a section showing good uses of message body
pick d115bb8 Add generic messages and column limit sections
pick 1693840 Add a section about language consistency
pick 80c5f47 Add commit message template
pick 8827962 Fix triple "m" typo
pick 9b81c72 Add "Rebase vs Merge" section

# Rebase 9e6dc75..9b81c72 onto 9e6dc75 (15 commands)
#
# 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 the previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# 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.5.1.1. fixup

使用它轻松地清理 commit 并且无须一个更复杂的 rebase 操作。
这篇文章提供了如何以及何时这么做的很好的示例。

2.5.2. cherry-pick

它非常适用于在发布到错误分支上的 commit,无须再次编码。

示例:

$ git cherry-pick 790ab21
[master 094d820] Fix English grammar in Contributing
 Date: Sun Feb 25 23:14:23 2018 -0300
 1 file changed, 1 insertion(+), 1 deletion(-)

2.5.3. add/checkout/reset [--patch | -p]

假设我们有以下差异:

diff --git a/README.md b/README.md
index 7b45277..6b1993c 100644
--- a/README.md
+++ b/README.md
@@ -186,10 +186,13 @@ bebebe Corrige nome de método na classe InventoryBackend
 ``
 # 坏示例(混合英语和葡萄牙语)
 ababab Usa o InventoryBackendPool para recuperar o backend de estoque
-efefef Add `use` method to Credit model
 cdcdcd Agora vai
 ``

+### 样板
+
+这是一个样板,[由 Tim Pope 编写](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html),出现在文章 [**高级 Git 手册**](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project)。
+
 ## 贡献

 感谢任何形式的帮助,可以帮到我的主题示例:
@@ -202,3 +205,4 @@ 感谢任何形式的帮助,可以帮到我的主题示例:

 - [如何书写 Git 的 Commit 信息](https://chris.beams.io/posts/git-commit/)
 - [高级 Git 手册 —— Commit 指导](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines)
+- [A Note About Git Commit Messages](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)

我们可以使用 git add -p 来只添加我们需要的补丁,无须修改已经编写的代码。
将较大的变更拆分成小的 commit 或者重置/检出特殊的变更。

暂存这个区块 [y,n,q,a,d,/,j,J,g,s,e,?]? s
拆分成 2 个区块

2.5.3.1. 区块 1

@@ -186,7 +186,6 @@
 ``
 # 坏示例 (mixes English and Portuguese)
 ababab Usa o InventoryBackendPool para recuperar o backend de estoque
-efefef Add `use` method to Credit model
 cdcdcd Agora vai
 ``

暂存这个区块 [y,n,q,a,d,/,j,J,g,e,?]?

2.5.3.2. 区块 2

@@ -190,6 +189,10 @@
 ``
 cdcdcd Agora vai
 ``

+### 样板
+
+这是一个样板,[由 Tim Pope 编写](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html),出现在文章 [**高级 Git 手册**](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project)。
+
 ## 贡献

感谢任何形式的帮助,可以帮到我的主题示例:
暂存这个区块 [y,n,q,a,d,/,K,j,J,g,e,?]?

2.5.3.3. 区块 3

@@ -202,3 +205,4 @@ 感谢任何形式的帮助,可以帮到我的主题示例:

 - [如何书写 Git 的 Commit 信息](https://chris.beams.io/posts/git-commit/)
 - [高级 Git 手册 —— Commit 指导](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines)
+- [关于 Git 的 Commit 信息的注意事项](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)

2.6. 灵感、来源和进一步阅读材料


如果你觉得这篇文章对你有帮助,不妨请我喝杯咖啡,鼓励我创造更多!