做一个有品位的程序员

简介: 参见百湖培训之前,华为的一个小伙伴发现了Git实现的一个 Bug,给我发了一个 Pull Request,让我审核以及代发到 Git 社区。不用看代码,只看 Pull Request 的说明,我相信大家就可以闻到这是一个好代码,写代码的人有品味。 参见:https://github.com/jiangxin/git/pull/25 —— 问:“能够写出正确代码的程序员就是有品味的程序员

参见百湖培训之前,华为的一个小伙伴发现了Git实现的一个 Bug,给我发了一个 Pull Request,让我审核以及代发到 Git 社区。不用看代码,只看 Pull Request 的说明,我相信大家就可以闻到这是一个好代码,写代码的人有品味。

参见:https://github.com/jiangxin/git/pull/25

—— 问:“能够写出正确代码的程序员就是有品味的程序员么?”

—— 答:“还不够。品味来自于每一个细节,有品位的程序员会把每一次提交做小、做对、做好,让人看懂,且无可挑剔,这样才够逼格,才可以称为有品位。”

熟练使用 Git,会让程序员更有品味。

提交做小

写小提交就是将问题解耦:“Do one thing and do it well”。开源项目的提交通常都很小,每个提交只修改一个到几个文件,每次只修改几行到几十行。
找一个你熟悉的开源项目,在仓库中执行下面的命令,可以很容易地统计出来每个提交的修改量。

$ git log --no-merges --pretty= --shortstat
2 files changed, 25 insertions(+), 4 deletions(-)
1 file changed, 4 insertions(+), 12 deletions(-)
2 files changed, 30 insertions(+), 1 deletion(-)
3 files changed, 15 insertions(+), 5 deletions(-)

而我看到很多公司内的项目则不是这样,一个提交动辄修改成百上千的文件,涉及成千上万行的源代码。试问这样的提交能 Show 出来给人看么?

可是在开发过程中,程序员一旦进入状态,往往才思如泉涌,不经意间就写出一个大提交。比如我又一次向 Git 贡献代码时,
提交还不算太大,就被 Git 的维护者 Junio 吐槽,要我拆分提交,便于评审:

TODO(链接寻找中。。。)

那么如何将提交拆分为若干个小提交呢?

拆分当前提交(松耦合)

先以拆分最新的提交为例,可以如下操作:

  1. 将当前提交撤销,重置到上一次提交。撤销提交的改动保留在工作区中。

        $ git reset HEAD^
    
  2. 通过补丁块拣选方式选择要提交的修改。Git 会逐一显示工作区更改,如果确认此处改动要提交,输入“y“。

        $ git add -p
    
  3. 以撤销提交的提交说明为蓝本,撰写新的提交。

        $ git commit -e -C HEAD@{1}
    
  4. 对于工作区其余的修改进行提交,完成一个提交拆分为两个的操作。

        $ git add -u
        $ git commit
    

拆分当前提交(紧耦合)

如果要拆分的提交,不同的实现逻辑耦合在一起,难以通过补丁块拣选(git add -p)的方式修改提交,怎么办?这时可以
直接编辑文件,删除要剥离出此次提交的修改,然后执行:

$ git commit --amend

然后执行下面的命令,还原原有的文件修改,然后再提交。如下:

$ git checkout HEAD@{1} -- .
$ git commit

同样完成了一个提交拆分为两个提交的操作。

拆分历史某个提交

如果要拆分的是历史提交(如提交 54321),而非当前提交,则可以执行交互式变基(git rebase -i),如下:

$ git rebase -i 54321^

Git 会自动将参与变基的提交写在一个动作文件中,还会自动打开编辑器(比如 vi 编辑器)。在编辑器中显示内容示例如下:

pick 54321 要拆分的提交
pick ...   其他参与变基的提交

将要拆分的提交 54321 前面的关键字 pick 修改为 edit,保存并退出。变基操作随即开始执行。

首先会在提交 54321 处停下来,这时要拆分的提交成为了当前提交,参照前面“拆分当前提交”的方法对提交 54321 进行拆分。拆分结束再执行
git rebase --continue 完成整个变基操作。

提交做对

“好的文章不是写出来的,而是改出来的。” 代码提交也是如此。

程序员写完代码,往往迫不及待地敲下:git commit,然后发现提交中少了一个文件,或者提交了多余的文件,或者发现提交中包含错误无法编译,或者提交说明中出现了错别字。

Git 提供了一个修改当前提交的快捷命令:git commit --amend,相信很多人都用过,不再赘述。

如果你发现错误出现在上一个提交或其他历史提交中怎么办呢?我有一个小窍门,在《Git权威指南》里我没有写到哦。

比如发现历史提交 54321 中包含错误,直接在当前工作区中针对这个错误进行修改,然后执行下面命令。

git commit --fixup 54321

你会发现使用了 --fixup 参数的提交命令,不再询问你提交说明怎么写,而是直接把错误提交 54321
的提交说明的第一行拿来,在前面增加一个前缀“fixup!”,如下:

fixup! 原提交说明

如果一次没有改对,还可以再接着改,甚至你还可以针对这个修正提交进行 fixup,产生如下格式的提交说明:

fixup! fixup! 原提交说明

当开发工作完成后,待推送/评审的提交中出现大量的包含“fixup!”前缀的提交该如何处理呢?

如果你执行过一次下面的命令,即针对错误提交 54321 及其后面所有提交执行交互式变基(注意其中的 --autosquash 参数),你就会惊叹 Git 设计的是这么巧妙:

$ git rebase -i --autosquash 54321^

交互式变基弹出的编辑器内自动对提交进行排序,将提交 54321 连同它的所有修正提交压缩为一个提交。

Tips:执行 git config --global rebase.autoSquash true 命令设置配置变量 rebase.autosquash,执行 git rebase -i 命令会自动带上 --autosquash 参数。

对于“提交做对”,很多开源项目还通过单元测试用例提供保障。对于这样的项目,在提交代码时往往要求提供相应的测试用例。
Git 项目本身就对测试用例有着很高的要求,其测试框架也非常有意思。我曾经针对Git的单元测试框架写过博客,参见:
复用 git.git 测试框架

Tips:Git 的测试框架代码经过重构,已经成为一个单独的项目:Sharness,更加方便重用了。我曾经的几个项目都使用了这个框架写用例,后面有时间专题介绍。

提交做好

仅仅做到提交做小、提交做对,往往还不够,还要通过撰写详细的提交说明让评审者信服,这样才能够让提交尽快通过评审合入项目仓库中。

例如今年7月份在华为公司内部的 Git 服务器上发现一个异常,最终将问题定位到 Git 工具本身。整个代码改动只有区区一行:

你能猜到提交说明写了多少么?写了20多行!

receive-pack: crash when checking with non-exist HEAD

If HEAD of a repository points to a conflict reference, such as:

* There exist a reference named 'refs/heads/jx/feature1', but HEAD
  points to 'refs/heads/jx', or

* There exist a reference named 'refs/heads/feature', but HEAD points
  to 'refs/heads/feature/bad'.

When we push to delete a reference for this repo, such as:

        git push /path/to/bad-head-repo.git :some/good/reference

The git-receive-pack process will crash.

This is because if HEAD points to a conflict reference, the function
`resolve_refdup("HEAD", ...)` does not return a valid reference name,
but a null buffer.  Later matching the delete reference against the null
buffer will cause git-receive-pack crash.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

Git 对于提交说明的格式有着如下约定俗成的规定:

  • 提交主题

提交说明第一行是提交主题,是整个提交的概要性描述。可以在提交主题中添加所更改的模块名称作为前缀(如:receive-pack:)。
提交主题(即提交说明的第一行)尽量保持在50字节以内(Gerrit 的commit_log检查插件似乎有着稍微宽泛一些的要求)。
这是因为对于像 Linux、Git 这样的开源项目,是以邮件列表作为代码评审的平台,提交主题要作为邮件的标题,而邮件标题本身有长度上的限制。

  • 提交主题后的空行

必须要在提交说明的第一行和后续的提交说明中间留一个空行!如果没有这个空行,很多 Git 客户端会将连续几行的提交说明合在一起作为提交描述。这样显然太糟了。

  • 提交说明主体

提交主题之外的提交说明也有长度的限制,最好以72字节为限,超过则断行。因为 GitHub 在显示提交说明时支持 Markdown 语法,
所以作为一个有品位的程序员学些 Markdown 的语法,让你的提交说明的可读性变得更强吧。

我总结过一个 Markdown 和其他文本标记语言的语法说明,可供参考:

  • 签名区

在提交说明最后是签名区。签名区可以看出这个提交的参与者、评审记录等等。

最后,让我们一起学习成为一名有品位的程序员吧。并依靠你对代码的品味,高质量严要求,守护你的项目吧。

Notes: 本文最早在 2015年底发表在我的博客上: http://www.worldhello.net/2015/12/23/taste-of-a-programmer.html

目录
相关文章
|
3月前
|
人工智能 程序员 C#
两种程序员,你是哪一种?
在这个由代码编织的世界里,程序员这个大家庭里,住着两种截然不同的 "物种" —— 一种是将编程视为日常工作的职业型,另一种则是热衷于技术探索的狂热分子,你是哪一种呢?今天,我们就来聊聊这两种程序员的 "特征"。
|
前端开发 JavaScript 关系型数据库
程序员1
程序员1
113 0
|
程序员
厉害了,天刚一冷程序员就都换上了衬衫。。
这才农历九月初,大秋天的,深圳的天气就已经降温了。更搞笑的是,朋友圈、群里都在转发下面这张图片,相信大部分人已经看过了吧
厉害了,天刚一冷程序员就都换上了衬衫。。
|
设计模式 Java 程序员
@程序员,你该如何磨快你的锯子
@程序员,你该如何磨快你的锯子
163 0
@程序员,你该如何磨快你的锯子
|
前端开发 程序员 C#
水瓶座的回顾-高贵的程序员
水瓶座的回顾-高贵的程序员
119 0
|
程序员 开发者
作为一个程序员的阴暗面
  一个全栈开发者的自白   迈克尔-米勒 6分钟阅读   你刚从8小时的工作中回家。你一整天都在接听电话和发送电子邮件,试图找到新的线索,以便你能在这个月赚到佣金。回到家,和家人一起在你辛辛苦苦维持的两居室公寓里放松一下,不过是在第二天的工作开始之前的一个单纯的假期。   你和你的伴侣赚的钱只够你们两个人每月支付所有的账单并让你们的家人吃饱。当你坐在餐桌前时,你感觉到你的手机在震动,因为有一条新的信息传来......   这是你的工作。   信息中写道:"明天不要再来了,你已经被替换了"。   当你坐在那里盯着墙壁,无法理解你刚刚读到的内容时,思想开始在你的脑海中飞驰。我们这个月
182 0
|
程序员
为什么要选择做一名程序员?
从这篇文章开始准备做一个大学生学编程系列文章,主要帮助一些还在编程路上徘徊的小伙伴,作为一个过来人的身份总结一些编程经验以及心得,在自学的过程中走了很多的弯路,在此通过这个专栏分享给需要编程的小伙伴。
2089 1
|
程序员 Ruby Java
不要再叫自己“程序员”了
程序员不要将自己限定在写代码这一单一职能上,需要认清自身商业价值的本质,需要锻炼自己的沟通能力,擅于表现自己。职业只是一种生活方式,并不能完全支配我们的幸福。我们应该为了生活而工作,而不要为了工作而生活。
2563 0
|
程序员
我是如何从煤矿工成为程序员的
译文出自:外刊IT评论
738 0
|
程序员
程序员该如何锻炼身体
为什么要锻炼 在椅子上坐了大半天,不知道是吃的饭不合适等缘故还是喝水不足的缘故,有那么一瞬间感觉到浑身内脏湿漉漉的,仿佛外面包裹了一层黄油,哇好恶心,我怎么能说出这种话,不过真的整个人感觉不舒服,后来我想了下人的生命周期中,哪个是最核心的部分,对,是血液!最后我的大脑一致认为,是血液没有很好的执行监听内脏的任务,因为它的进程好像并没有那么活跃。
1313 0

相关实验场景

更多