Git最佳实践:原子性提交(atomic commits)
什么是原子性提交?
作为版本控制的最佳实践之一,不管你是用什么工具,都应该保持原子性提交。在百科中对原子性的定义是:
原子性:在一个大型系统中,形成一个不可分割的最简单元或组件。
当代码变动时你想创建提交时,这个提交应该尽可能的小量,并且包含一个不可分割的特性(feature)、修复(fix)或优化(improved)。以下是创建一个简单的联系人表单的git log样例:
- Create HTML for form
- Style form
- Add HTML5 validation
- Fix unrelated JS bug
- Add ajax submit to form with mock server results from PHP
- Add JS validation to form
- Send form results via email
- Log form results to database
- Style form validation and success/error messages
每个提交都是添加了一个最小基本的特性、修复活着对特性或代码的小改善。另外提交日志输入时使用『语义化日志风格』(semantic commit message style) 会帮助我们的提交更加原子化。
如果你打算基于多次提交做pull request的时候,相当于把多个小特性合并成一个大特性,比如:Create working contact form"。关于pull request的操作实践可以参考这篇文章 Creating good pull requests
为什么要原子性提交
Code reviews会更简单
当你像这样保持小量提交时,别人review你代码或检查每次递增的变动时会更容易。
更容易回滚
有时候你开发了一个特性,但是却跟不相关的bug修复操作混在一起提交了。然后之后发现这个特性不够好决定回滚的时候,这就会出问题了。所以提交特性如果只是包含这个特性的话,回滚时候会更容易处理。但是如果它跟bug修复混在一起再回滚,就会非常麻烦,尤其是如果处理这个回滚的人是其他人而不是你的时候。
提交变动文件部分
在尝试实践原子性提交的时候,最大的问题是经常在当前任务的时候必须做一些不相关的代码变动。有个比较好的解决办法是,在需要变动的地方写下简单的Todo,等当前任务完成并且提交之后,再来实现这部分的代码。
提交当前任务的时候,如果不想把其他变动的文件混在这次提交中,你可以先使用git add
命令把相关文件添加到stage暂存再提交。
例如:
$ git st
M contact.html
M contact.php
$ git add contact.html
$ git st
A contact.html
M contact.php
$ git ci -m "Add required attribute to form fields"
如果跟当前任务不相干的变动在同一个文件时候,你可以使用git add --patch
或者 git add -p
来实现文件局部区块的提交。
在使用git add
命令的时候加上patch
参数,它会显示文件中的第一个变动,并且会询问是否要暂存到stage区。
$ git add -p
diff --git a/coffee/forms.coffee b/coffee/forms.coffee
index a9f4d6d..40a8ed8 100644
--- a/coffee/forms.coffee
+++ b/coffee/forms.coffee
@@ -15,6 +15,7 @@ window.xhrForms =
$this = $(@)
$this.mask $this.data("mask")
+ # Submit handler for our ajax forms
handleSubmit: (form) ->
$form = $(form)
url = $form.attr "action"
Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ?
y - stage this hunk
n - do not stage this hunk
a - stage this and all the remaining hunks in the file
d - do not stage this hunk nor any of the remaining hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? n
然后会跳到下一个变动区块,然后我们可以选择yes。
@@ -40,7 +41,7 @@ window.xhrForms =
$msg = $(".form-notice", $form)
if !$msg.length
$msg = $("<div/>").addClass("form-notice").prependTo($form)
- $msg.removeClass "form-success"
+ $msg.removeClass "form-success form-error"
if data.status
$msg.addClass "form-success"
else
Stage this hunk [y,n,q,a,d,/,K,g,e,?]? y
如果我们再来检查状态,会发现我们的文件被局部暂存了。
最后我们可以提交我们刚才暂存的代码变动了。
$git commit -m "Remove form-error class too"
## 不要找借口
除了一些比较相互影响比较复杂的变动,一般来说,原子性提交是非常简单的。所以也没有理由再把bug修复混在特性开发中了。持续保持原子性提交的习惯,会对你的工作流很有好处,并且后续的开发者也会非常感谢你的。