|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "做一个有品位的程序员" |
| 4 | +--- |
| 5 | + |
| 6 | +——“能够写出漂亮代码的程序员就是有品味的程序员么?” |
| 7 | + |
| 8 | +——“还不够。品味来自于每一个细节,有品位的程序员会把每一次提交做小、做对、做好,尽量做到整个开发的过程的无可挑剔,这样才够逼格,才可以称为有品位。” |
| 9 | + |
| 10 | +熟练使用 Git,会让程序员更有品味。 |
| 11 | + |
| 12 | +## 提交做小 |
| 13 | + |
| 14 | +写小提交就是将问题解耦:“Do one thing and do it well”。开源项目的提交通常都很小,每个提交只修改一个到几个文件,每次只修改几行到几十行。 |
| 15 | +找一个你熟悉的开源项目,在仓库中执行下面的命令,可以很容易地统计出来每个提交的修改量。 |
| 16 | + |
| 17 | + $ git log --no-merges --pretty="" --shortstat |
| 18 | + 2 files changed, 25 insertions(+), 4 deletions(-) |
| 19 | + 1 file changed, 4 insertions(+), 12 deletions(-) |
| 20 | + 2 files changed, 30 insertions(+), 1 deletion(-) |
| 21 | + 3 files changed, 15 insertions(+), 5 deletions(-) |
| 22 | + |
| 23 | +而我看到的很多公司内外的项目则不是这样,动辄成百上千的文件修改,一次改动成千上万行源代码。试问这样的提交能拿来给人看么? |
| 24 | + |
| 25 | +可是在开发过程中,程序员一旦进入状态,往往才思如泉涌,写出一个大提交。比如我又一次向 Git 贡献代码时, |
| 26 | +提交还不算太大,就被 Git 的维护者 Junio 吐槽,要我拆分提交,便于评审: |
| 27 | + |
| 28 | + TODO |
| 29 | + |
| 30 | +那么如何将提交拆分为若干个小提交呢? |
| 31 | + |
| 32 | +### 拆分当前提交(松耦合) |
| 33 | + |
| 34 | +先以拆分最新的提交为例,可以如下操作: |
| 35 | + |
| 36 | +1. 将当前提交撤销,重置到上一次提交。撤销提交的改动保留在工作区中。 |
| 37 | + |
| 38 | + $ git reset HEAD^ |
| 39 | + |
| 40 | +2. 通过补丁块拣选方式选择要提交的修改。Git 会逐一显示工作区更改,如果确认此处改动要提交,输入“y“。 |
| 41 | + |
| 42 | + $ git add -p |
| 43 | + |
| 44 | +3. 以撤销提交的提交说明为蓝本,撰写新的提交。 |
| 45 | + |
| 46 | + $ git commit -e -C HEAD@{1} |
| 47 | + |
| 48 | +4. 对于工作区其余的修改进行提交,完成一个提交拆分为两个的操作。 |
| 49 | + |
| 50 | + $ git add -u |
| 51 | + $ git commit |
| 52 | + |
| 53 | +### 拆分当前提交(紧耦合) |
| 54 | + |
| 55 | +如果要拆分的提交,不同的实现逻辑耦合在一起,难以通过补丁块拣选(`git add -p`)的方式修改提交,怎么办?这时可以 |
| 56 | +直接编辑文件,删除要剥离出此次提交的修改,然后执行: |
| 57 | + |
| 58 | + $ git commit --amend |
| 59 | + |
| 60 | +然后执行下面的命令,还原原有的文件修改,然后再提交。如下: |
| 61 | + |
| 62 | + $ git checkout HEAD@{1} -- . |
| 63 | + $ git commit |
| 64 | + |
| 65 | +同样完成了一个提交拆分为两个提交的操作。 |
| 66 | + |
| 67 | +### 拆分历史某个提交 |
| 68 | + |
| 69 | +如果要拆分的是历史提交(如提交 54321),而非当前提交,则可以执行交互式变基(`git rebase -i`),如下: |
| 70 | + |
| 71 | + $ git rebase -i 54321^ |
| 72 | + |
| 73 | +Git 会自动将参与变基的提交写在一个动作文件中,还会自动打开编辑器(比如 vi 编辑器)。在编辑器中显示内容示例如下: |
| 74 | + |
| 75 | + pick 54321 要拆分的提交 |
| 76 | + pick ... 其他参与变基的提交 |
| 77 | + |
| 78 | +将要拆分的提交 54321 前面的关键字 `pick` 修改为 `edit`,保存并退出。变基操作随即开始执行。 |
| 79 | + |
| 80 | +首先会在提交 54321 处停下来,这时要拆分的提交成为了当前提交,参照前面“拆分当前提交”的方法对提交 54321 进行拆分。拆分结束再执行 |
| 81 | +`git rebase --continue` 完成整个变基操作。 |
| 82 | + |
| 83 | +## 提交做对 |
| 84 | + |
| 85 | +“好的文章不是写出来的,而是改出来的。” 代码提交也是如此。 |
| 86 | + |
| 87 | +程序员写完代码,往往迫不及待地敲下:`git commit`,然后发现提交中少了一个文件,或者提交了多余的文件,或者发现提交中包含错误无法编译,或者提交说明中出现了错别字。 |
| 88 | + |
| 89 | +Git 提供了一个修改当前提交的快捷命令:`git commit --amend`,相信很多人都用过,不再赘述。 |
| 90 | + |
| 91 | +如果你发现错误出现在上一个提交或其他历史提交中怎么办呢?我有一个小窍门,在《Git权威指南》里我没有写到哦。 |
| 92 | + |
| 93 | +比如发现历史提交 `54321` 中包含错误,直接在当前工作区中针对这个错误进行修改,然后执行下面命令。 |
| 94 | + |
| 95 | + git commit --fixup 54321 |
| 96 | + |
| 97 | +你会发现使用了 `--fixup` 参数的提交命令,不再询问你提交说明怎么写,而是直接把错误提交 `54321` |
| 98 | +的提交说明的第一行拿来,在前面增加一个前缀“fixup!”,如下: |
| 99 | + |
| 100 | + fixup! 原提交说明 |
| 101 | + |
| 102 | +如果一次没有改对,还可以再接着改,甚至你还可以针对这个修正提交进行 fixup,产生如下格式的提交说明: |
| 103 | + |
| 104 | + fixup! fixup! 原提交说明 |
| 105 | + |
| 106 | +当开发工作完成后,待推送/评审的提交中出现大量的包含“fixup!”前缀的提交该如何处理呢? |
| 107 | + |
| 108 | +如果你执行过一次下面的命令,即针对错误提交 54321 及其后面所有提交执行交互式变基(注意其中的 `--autosquash` 参数),你就会惊叹 Git 设计的是这么巧妙: |
| 109 | + |
| 110 | + $ git rebase -i --autosquash 54321^ |
| 111 | + |
| 112 | +交互式变基弹出的编辑器内自动对提交进行排序,将提交 54321 连同它的所有修正提交压缩为一个提交。 |
| 113 | + |
| 114 | +对于“提交做对”,很多开源项目还通过单元测试用例提供保障。对于这样的项目,在提交代码时往往要求提供相应的测试用例。 |
| 115 | +Git 项目本身就对测试用例有着很高的要求,其测试框架也非常有意思。我曾经针对Git的单元测试框架写过博客,参见: |
| 116 | +[复用 git.git 测试框架](http://www.worldhello.net/2013/10/26/test-gistore-using-git-test-framework.html)。 |
| 117 | + |
| 118 | +## 提交做好 |
| 119 | + |
| 120 | +仅仅做到提交做小、提交做对,往往还不够,还要通过撰写详细的提交说明让评审者信服,这样才能够让提交尽快通过评审合入项目仓库中。 |
| 121 | + |
| 122 | +例如今年7月份在华为公司内部的 Git 服务器上发现一个异常,最终将问题定位到 Git 工具本身。整个代码改动只有区区一行: |
| 123 | + |
| 124 | +* 提交:[receive-pack: crash when checking with non-exist HEAD](https://github.com/git/git/commit/b112b14d7869bf3c000abb84cd22e57dd811d031) |
| 125 | + |
| 126 | +你能猜到提交说明写了多少么?写了20多行! |
| 127 | + |
| 128 | + receive-pack: crash when checking with non-exist HEAD |
| 129 | + |
| 130 | + If HEAD of a repository points to a conflict reference, such as: |
| 131 | + |
| 132 | + * There exist a reference named 'refs/heads/jx/feature1', but HEAD |
| 133 | + points to 'refs/heads/jx', or |
| 134 | + |
| 135 | + * There exist a reference named 'refs/heads/feature', but HEAD points |
| 136 | + to 'refs/heads/feature/bad'. |
| 137 | + |
| 138 | + When we push to delete a reference for this repo, such as: |
| 139 | + |
| 140 | + git push /path/to/bad-head-repo.git :some/good/reference |
| 141 | + |
| 142 | + The git-receive-pack process will crash. |
| 143 | + |
| 144 | + This is because if HEAD points to a conflict reference, the function |
| 145 | + `resolve_refdup("HEAD", ...)` does not return a valid reference name, |
| 146 | + but a null buffer. Later matching the delete reference against the null |
| 147 | + buffer will cause git-receive-pack crash. |
| 148 | + |
| 149 | + Signed-off-by: Jiang Xin <worldhello.net@gmail.com> |
| 150 | + Signed-off-by: Junio C Hamano <gitster@pobox.com> |
| 151 | + |
| 152 | +Git 对于提交说明的格式有着如下约定俗成的规定: |
| 153 | + |
| 154 | +* 提交主题 |
| 155 | + |
| 156 | + 提交说明第一行是提交主题,是整个提交的概要性描述。可以在提交主题中添加所更改的模块名称作为前缀(如:receive-pack:)。 |
| 157 | + 提交主题(即提交说明的第一行)尽量保持在50字节以内(Gerrit 的commit_log检查插件似乎有着稍微宽泛一些的要求)。 |
| 158 | + 这是因为对于像 Linux、Git 这样的开源项目,是以邮件列表作为代码评审的平台,提交主题要作为邮件的标题,而邮件标题本身有长度上的限制。 |
| 159 | + |
| 160 | +* 提交主题后的空行 |
| 161 | + |
| 162 | + 必须要在提交说明的第一行和后续的提交说明中间留一个空行!如果没有这个空行,很多 Git 客户端会将连续几行的提交说明合在一起作为提交描述。这样显然太糟了。 |
| 163 | + |
| 164 | +* 提交说明主体 |
| 165 | + |
| 166 | + 提交主题之外的提交说明也有长度的限制,最好以72字节为限,超过则断行。因为 GitHub 在显示提交说明时支持 Markdown 语法, |
| 167 | + 所以作为一个有品位的程序员学些 Markdown 的语法,让你的提交说明的可读性变得更强吧。 |
| 168 | + |
| 169 | + 我总结过一个 Markdown 和其他文本标记语言的语法说明,可供参考: |
| 170 | + |
| 171 | + - [轻量级标记语言语法参考](http://www.worldhello.net/gotgithub/appendix/markups.html) |
| 172 | + |
| 173 | +* 签名区 |
| 174 | + |
| 175 | +在提交说明最后是签名区。签名区可以看出这个提交的参与者、评审记录等等。 |
| 176 | + |
| 177 | +最后,让我们一起学习成为一名有品位的程序员吧。并依靠你对代码的品味,高质量严要求,守护你的项目吧。 |
0 commit comments