Git 是最流行的代码版本控制系统,这一系列文章介绍了一些 Git 的高阶使用方式,从而帮助我们可以更好的利用 Git 的能力。本系列一共 8 篇文章,这是第 1 篇。原文:Creating the Perfect Commit in Git[1]
Git 中的提交可以是以下两种情况之一:
- 可以是出于多种目的所作的各种各样的更改: 用于修复 bug 的几行代码,尝试重写旧模块,或者实现全新功能的几个新文件。
- 或者,如果稍微小心一点,就可以帮助我们保持对事情的掌控。可以将提交作为一个仅存放特定类型相关更改的容器,从而使我们更容易理解发生了什么。
本文将讨论如何构建后一种类型的提交,或者换句话说: “完美的”提交。
Git 进阶系列:
- 创建完美的提交(本文)
- Git中的分支策略
- 基于Pull Request实现更好的协作
- 合并冲突
- Rebase vs Merge
- 交互式Rebase
- Git中的Cherry-pick提交
- 用Reflog恢复丢失的提交
为什么整洁、细粒度的提交很重要
真有必要以谨慎、深思熟虑的方式撰写提交吗?就不能只是把 Git 当作一个无聊的备份系统吗?我们再来回顾一下上面的例子。
如果我们只是遵循第一种选择(只要修改发生,就塞进提交里去),提交就会失去很多价值。提交和提交之间的分隔变得很随意,似乎没有理由将更改放入某一个提交而不是另一个提交中。之后再看这些提交,比如当你的同事试图搞清楚修改了什么,就像在家里翻箱倒柜找东西一样,很难找到想要的东西。
如果我们遵循第二种方式,只在提交中放入相关的内容中,需要制定更多的计划和规则。但最终我们会得到非常有价值的奖励: 干净的提交历史!这些提交可以帮助我们理解发生了什么,有助于以可理解的方式解释复杂的变化。
那怎样才能创建更好的提交呢?
创作更好的提交
在 Git 中创建更好的提交需要理解一个核心概念: 暂存区(Staging Area) 。
暂存区正是为了这个目的而创建的,从而允许开发人员以非常细粒度的方式选择应该作为下一次提交的部分更改。而且,与其他版本控制系统不同,Git 强制使用暂存区。
然而不幸的是,暂存区仍然很容易被忽略,一次简单的git add .
将接受当前所有本地更改,并标记为下一次提交。
的确,有时这是非常有用和有效的方法。但很多时候,我们最好停一下,看一下所有更改是否真的是关于同一个主题,也许选择两次或三次独立提交可能是更好的选择。
大多数情况下,保持较小的提交比保持较大的提交更有意义。专注于单独的主题(而不是两个、三个或四个),往往使提交更容易阅读。
暂存区允许我们仔细挑选每一个应该进入下次提交的更改:
$ git add file1.ext file2.ex
这会只将这两个文件标记为下一次提交,而将其他更改留给将来提交或进一步编辑。
简单停一下,有意识的选择什么应该放到下一次提交,对我们有很大的帮助。但还可以做得更精确一些,因为有时候即使是单个文件中的更改也属于多个主题。
来看一个真实的例子,可以使用“git diff”命令或者像 Tower[2]一样的 git 桌面 GUI 来查看“index.html”文件中的详细变更:
现在,我们尝试添加给git add
添加-p
选项:
$ git add -p index.html
该命令指示 Git 对文件进行“补丁”级别的检查: Git 会帮助我们检查文件中的所有更改,然后问我们,对于每个块,是否要添加到暂存区:
我们为第一个块键入 Y(表示“是”),为第二个块键入 N(表示“否”),可以在下次提交时将该文件的第一部分更改包含进来,但将其他更改留到以后或进行更多的编辑。
测试代码
既然我们谈论的是“完美的提交”,就不能忽视测试这个话题。如何准确“测试”代码当然有不同方式,但是“测试很重要”这一概念已经是老生常谈了。事实上,如果一段代码没有经过适当的测试,许多团队拒绝认为变更已经完成。
还在犹豫是否应该测试自己的代码?让我们澄清一些关于测试的误区:
- “测试被高估了”: 事实上,测试可以帮助我们更快找到 bug。最重要的是,帮助我们在产品投入生产之前找到它们,从而避免误造成最大的伤害。毫不夸张的说,尽早发现漏洞是无价的!
- “测试花费了宝贵的时间”: 一段时间之后,你会发现编写良好的测试使你编码的速度更快了。测试减少了 debug 的时间,结构良好的测试也为实际实现做好了准备。
- “测试是复杂的”: 也许几年前会有这方面的争论,但现在已经不一样了,大多数专业编程框架和语言都对配置、编写和管理测试提供了广泛的支持。
总而言之,将测试作为开发的一部分几乎可以保证能够使我们代码库更加健壮,同时能够帮助我们成为更好的开发者。
有价值的提交信息
使用 Git 进行版本控制并不是一种备份代码的特殊方式。况且,正如我们已经讨论过的,提交并不是任意更改的转储。提交的存在可以帮助我们及团队成员理解项目中发生了什么,好的提交信息可以更好的确保这一点。
但什么是好的提交信息呢?
- 有一个简明扼要的主题行(subject line)总结变更
- 有一个描述性的主体(message body),尽可能简洁的解释了最重要的事实
让我们从主题行开始,目标是获得所发生事情的简要总结。当然,简洁是一个相对概念,一般的经验法则是(理想情况下)将主题控制在 50 个字符以内。顺便说一下,如果你发现自己很难想出简短的内容,可能表明提交囊括了太多主题!可能值得再去看一看,是否可以把它分成多个单独的提交。
如果用一个换行符和一个额外的空行结束主题,Git 就会知道下面的文本是消息的“主体”。在这里,有更多空间来描述发生了什么。正文应该旨在回答这些问题:
- 提交之后,项目发生了什么变化?
- 做出这种改变的原因是什么?
- 有什么特别需要注意的吗?关于这些变化还有什么其他人应该知道的吗?
如果在编写提交消息体时牢记这些问题,就很可能对所发生的事情给出有用的描述,最终会使团队成员(一段时间后也会使我们自己)在试图理解这一提交时受益。
在上面关于提交消息内容的规则之外,许多团队还关心格式: 在字符限制、软或硬换行等方面达成一致,这些都有助于在团队中产生更好的提交。
为了更容易遵守这些规则,我们最近在 Git 桌面 GUI Tower 中添加了一些功能: 例如,可以根据自己的喜好配置字符数或自动换行。
优秀的代码库包含大量优秀的提交
任何开发人员都想要让自己的代码库越来越好,但只有一种方法可以实现这个崇高目标: 不断做出优秀的提交!希望我能够证明(a)追求这个目标绝对值得,(b)实现它并不难。
如果想更深入了解高级 Git 工具,可以免费查看“Advanced Git Kit[3]”: 这是关于分支策略、交互式 Rebase、Reflog、子模块等主题的短视频集合。
References:
[1] Creating the Perfect Commit in Git: https://css-tricks.com/creating-the-perfect-commit-in-git/
[2] Git Tower: https://www.git-tower.com
[3] Advanced Git Kit: https://www.git-tower.com/learn/git/advanced-git-kit