像linux kernel一样管理你的项目


坚持良好的代码管理习惯,将极大的帮助我们做出更好的项目。
本文用于记录如何有效的使用git进行代码管理,像诸多优秀的项目一样。
本文尤其适合需要向其他项目提交patch的情形。不懂这句话的请参考标题,哈哈。


1. 如何正确的对待commit ?

commit是一个项目的变更史,如何维持一个高质量的commit log对于项目的质量
至关重要。
那么什么样的commit log是合格的呢?

1.1 避免初级的错误

# git diff --check   // identifies possible whitespace errors  

1.2 坚持一个commit对应一个功能

这点在实践的过程中可能很难满足,但是git提供了其他的方式让你可以在事后
进行弥补[1]
尽管如此,这些弥补措施只能在将代码与他人共享(git push)之前使用。
尽管如此,这些弥补措施只能在将代码与他人共享(git push)之前使用。
尽管如此,这些弥补措施只能在将代码与他人共享(git push)之前使用。

// 方式1:修改最近的一次commit信息  
// 同时也会将已进行的更改添加进入修改后的commit  
// 典型应用场景:commit后才发现遗漏了某个文件  
# git commit --amend  

// 方式2:修改最近的多次commit信息  
// 正式使用该方法前,最好用一个测试分支先体验一下。  
# git rebase -i HEAD~n    // 修改最近的n个commit  
<< 将需要被修改的commit信息对应行的"pick"改为"edit"  
# git commit --amend      // 真实的修改选择的commit信息  
# git rebase --continue   // 让rebase自动完成后续(未修改)的commit  

// 方式3:将最近的多个commit替换为一个commit
# git rebase -i HEAD~n  
<< 将需要被合并的前n-1commit对应行的"pick"改为"squash"  
<< 之后会自动进入修改commit的文本编辑页面,让你编辑新的commit message  

// 方式4:使用相对人工的方式合并多个commit  
# git reset --soft HEAD~n  // 使用soft选项,仅将commit log回退n个记录  
# git commit -a // 提交正式的commit message  

1.3 使用更规范的commit message格式

在确保了一个commit对应一个功能后,最后就是确保commit message的规范性了。
首先贴一下写一个合格commit message的规则,最后贴一下Tim Pope给出来的一个
模板[2]

第一行用一句简短的话总结这个commit  

第一行最好用一个描述性的前缀开始,比如  
  "net:"表示针对net子系统的修改  
  "fix:"表示这是一个bug fix  

第二行为空行

最后可以增加一些详细的描述,用以解释commit具体干了什么,为什么这么干  

commit message格式的一个模板如下,为保持原文含义,直接贴上英文版。

Short (50 chars or less) summary of changes

More detailed explanatory text, if necessary.  Wrap it to
about 72 characters or so.  In some contexts, the first
line is treated as the subject of an email and the rest of
the text as the body.  The blank line separating the
summary from the body is critical (unless you omit the body
entirely); tools like rebase can get confused if you run
the two together.

Further paragraphs come after blank lines.

  - Bullet points are okay, too

  - Typically a hyphen or asterisk is used for the bullet,
    preceded by a single space, with blank lines in
    between, but conventions vary here

2. 如何通过commit生成patch ?

在得到合格的commit log后,则可以通过commit log直接得到想要的patch。
这里推荐使用format-patch格式的,理由自行理解。

// 生成前两个commit对应的patch,一个commit生成一个patch  
//     -s 表示添加签名
//     -n 表示patch里面会有[patch n/m]类似的标记 
# git format-patch -sn -2
// 与上一条命令含义相同
# git format-patch -sn HEAD HEAD~2..HEAD  
// 忽略掉diff里面的prefix  
# git format-patch -sn --no-prefix -1  

由于git format-patch会将每一个commit单独生成一个patch,这也就更加依赖
在上一步中是否严格控制好commit log的风格了。


3. 如何检查patch的代码风格?

坚持良好的代码风格是长久做项目的基石,所以在提交patch前最好自行检查好
代码是否符合目标项目的代码风格。
以Linux kernel为例,它提供了scripts/checkpatch.pl脚本用于检查代码风格。

# scripts/checkpatch.pl PATCH-NAME
# scripts/checkpatch.pl --terse PATCH-NAME   // 生成简单的报告  

4. 如何使用tag管理重要的发行版?

项目在进行到一定阶段后,可能需要对外进行发布。长久下来,实际作业环境下
可能同时使用着不同的发布版本。但是项目本身的git管理肯定不会同时维护多
个分支。当然要求项目的所有开发人员都凭脑力记住特定发型版本对应的commit
也不太现实。这就是我理解的tag的用途:通过tag标识诸多发行版本与git仓库
某个时期状态的关系。
git中tag分为lightweight tag和annotated tag,建议仅使用annotated tag。
以下便是tag常见的用法:

# git tag                // 显示当前所有的tag  
# git tag -l 'v1.0.*'    // 仅显示满足特定pattern的tag  

# git tag -a v1.5 -m 'my version 1.5'   // 创建一个带标签的tag  
# git tag -a v1.6        // 如果不适用-m,则会进入文本编辑器进行编辑  

# git show v1.5          // 查看v1.5的信息  

# git log --pretty=oneline      // 查看commit log  
# git tag -a v1.7 abcdef        // 为特定的commit添加tag

# git push origin v1.7   // 提交tag v1.7  
# git push origin --tags // 提交所有的tag

# git checkout -b version 2.0 v2.0   // 新建一个branch查看v2.0源码  

参考资料

A Note About Git Commit Messages
Distributed Git - Contributing to a Project
Git Tools - Rewriting History
HOWTO: Create and submit your first Linux kernel patch using GIT