<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tony Bai &#187; pre-commit-hook</title>
	<atom:link href="http://tonybai.com/tag/pre-commit-hook/feed/" rel="self" type="application/rss+xml" />
	<link>https://tonybai.com</link>
	<description>一个程序员的心路历程</description>
	<lastBuildDate>Mon, 08 Jun 2026 23:32:23 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>让reviewdog支持gitlab-push-commit，守住代码质量下限</title>
		<link>https://tonybai.com/2022/09/08/make-reviewdog-support-gitlab-push-commit-to-preserve-the-code-quality-floor/</link>
		<comments>https://tonybai.com/2022/09/08/make-reviewdog-support-gitlab-push-commit-to-preserve-the-code-quality-floor/#comments</comments>
		<pubDate>Thu, 08 Sep 2022 13:37:30 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[.gitlab-ci.yml]]></category>
		<category><![CDATA[.reviewdog.yml]]></category>
		<category><![CDATA[Codereview]]></category>
		<category><![CDATA[Compiler]]></category>
		<category><![CDATA[diff]]></category>
		<category><![CDATA[fortran]]></category>
		<category><![CDATA[fuzzing]]></category>
		<category><![CDATA[gerrit]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[git-commit]]></category>
		<category><![CDATA[git-diff]]></category>
		<category><![CDATA[git-push]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[gitlab]]></category>
		<category><![CDATA[gitlab-runner]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[golangci-lint]]></category>
		<category><![CDATA[govet]]></category>
		<category><![CDATA[Make]]></category>
		<category><![CDATA[pre-commit-hook]]></category>
		<category><![CDATA[reviewdog]]></category>
		<category><![CDATA[Test]]></category>
		<category><![CDATA[token]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[代码评审]]></category>
		<category><![CDATA[单元测试]]></category>
		<category><![CDATA[同行评审]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[程序设计语言]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[编译器]]></category>
		<category><![CDATA[集成测试]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3654</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2022/09/08/make-reviewdog-support-gitlab-push-commit-to-preserve-the-code-quality-floor 一. 代码质量保证的手段 从世界上首款计算机高级程序设计语言Fortran自上世纪50年代诞生以来，编程这个行当已经走过了近70年。虽然年头已不少，但不可否认的一点是：软件生产依然无法像硬件那样标准化，同一个小功能，N个程序员的有N种实现方法。 那么如何保证生产出的软件的质量符合我们的要求呢？不同领域的程序员都在进行着努力，比如：做编译器的让编译器更加严格，努力将内存安全问题彻底消除(如Rust)；做工具链的为程序员提供了内置于语言的各种单测、集成测试、接口测试、fuzzing test等工具(如Go工具链)，让程序员可以更容易地对自己所写的代码进行全方位的测试，以期找出更多的代码中的潜在问题&#8230; 当然，还有一种主观的代码质量保证方法目前依旧是主流，它就是是同行的代码评审(code review, cr)。 代码评审的方法主要有两种，一种是大家坐到一个会议室中，对某个人的某段代码“发表大论”；另外一种则是利用像gerrit这样的工具，在线对其他人的某次提交的代码或某PR的代码进行“评头论足”。 不过无论哪种，最初的时候大家都会细无巨细地从语法层面看到代码结构设计，再到业务逻辑层面，但这样做的弊端也是很显而易见，那就是效率低下，不聚焦(focus)。 于是人们想到了：能否利用工具来尽可能地发现语法层面的问题，这样代码评审时，人类专家便可以聚焦代码结构设计与业务逻辑层面的问题，分工明确后，效率自然提升(如下图)： 注：目前绝大多数工具链仅能自动帮助程序员解决语法层面的问题。将来，随着工具的日益强大，工具可以不断升级关注层次，逐渐进化到具备发现代码结构设计问题，甚至可以发现业务层面逻辑问题的能力。 于是就有了reviewdog这样的可以调用各种linter工具对代码进行自动扫描并将问题以comment的形式自动提交的代码仓库的工具。 到这里很多朋友会问，即便让工具来关注语法层面的问题，为何要用reviewdog这样的工具，git的pre-commit hook、git server hooks、利用Make等工具做开发阶段检查等手段也能检查代码中的语法问题，它们不再香了吗？ 下面简单看看这些方法的“问题”(我们假设大家都已经在使用git作为代码版本管理工具)： git pre-commit-hook git pre-commit hook是一个客户端的git hook，它是放在开发人员本地代码copy中的.git/hooks目录下的钩子，当开发人员在本地执行git commit时会被唤起执行。pre-commot hook的问题就在于我们没法在中心代码仓库对pre-commit hook的脚本内容做统一管理和维护。这个更适合开发人员根据自己的喜好、代码素养在自己的开发环境下部署。 此外，有些代码并不一定是在开发者自己的开发机上提交的，换环境后，pre-commit hook就不在生效。 利用Make等工具做本地检查 利用make工具，我们可以在本地build代码之前对代码做lint等各种静态检查，但和pre-commit-hook一样，虽然Makefile可以提交代码仓库，但真正用于检查代码的工具依旧是在开发人员本地，难于对工具版本，设定的检查规则进行统一管理维护，可能导致不同开发人员环境有不一致的情况。另外同样的情况，有些代码并不一定是在开发者自己的开发机上提交的，换环境后，Make工具依赖的代码检查工具可能并不存在，检查环节就无法有效实施。 git server hooks git支持server hooks，gitlab自12.8版本也开始支持server hooks(替换之前的custom hooks)。 Git server支持以下钩子： pre-receive post-receive update 我倒是没有深研究过这些server hooks是否能满足我们的功能要求，但就git server hooks的部署特点就决定了，它不适合，因为它要在gitlab的server上执行，这就意味着我们需要的所有静态代码检查工具都要部署和配置在与gitlab server同一个环境中，这耦合性太强，根本不便于我们对这些静态代码检查工具的管理与日常维护。 而像reviewdog这样的工具将与ci工具(比如gitlab-ci)集成，运行在slave/worker/runner的机器上，而这些机器上的环境便很容易统一的定制与管理。 好了，下面进入reviewdog时间！ 注：我们以代码仓库为gitlab为例，我曾做过小调查，目前企业内部基本都在使用gitlab搭建私有git仓库，除了那些自实现code仓库平台的大厂。 二. [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/make-reviewdog-support-gitlab-push-commit-to-preserve-the-code-quality-floor-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2022/09/08/make-reviewdog-support-gitlab-push-commit-to-preserve-the-code-quality-floor">本文永久链接</a> &#8211; https://tonybai.com/2022/09/08/make-reviewdog-support-gitlab-push-commit-to-preserve-the-code-quality-floor</p>
<h3>一. 代码质量保证的手段</h3>
<p>从世界上首款计算机高级程序设计语言<a href="https://fortran-lang.org/en/">Fortran</a>自上世纪50年代诞生以来，编程这个行当已经走过了近70年。虽然年头已不少，但不可否认的一点是：<strong>软件生产依然无法像硬件那样标准化，同一个小功能，N个程序员的有N种实现方法</strong>。</p>
<p>那么如何保证生产出的软件的质量符合我们的要求呢？不同领域的程序员都在进行着努力，比如：做编译器的让编译器更加严格，努力将内存安全问题彻底消除(如<a href="https://tonybai.com/2021/03/15/rust-vs-go-why-they-are-better-together">Rust</a>)；做工具链的为程序员提供了内置于语言的各种单测、集成测试、接口测试、fuzzing test等工具(如Go工具链)，让程序员可以更容易地对自己所写的代码进行全方位的测试，以期找出更多的代码中的潜在问题&#8230;</p>
<p>当然，还有一种主观的代码质量保证方法目前依旧是主流，它就是是<a href="https://tonybai.com/2013/07/08/code-review-from-rule-of-man-to-rule-of-law/"><strong>同行的代码评审(code review, cr)</strong></a>。</p>
<p>代码评审的方法主要有两种，一种是大家坐到一个会议室中，对某个人的某段代码“发表大论”；另外一种则是利用像<a href="https://www.gerritcodereview.com">gerrit</a>这样的工具，在线对其他人的某次提交的代码或某PR的代码进行“评头论足”。</p>
<p>不过无论哪种，最初的时候大家都会细无巨细地从语法层面看到代码结构设计，再到业务逻辑层面，但这样做的弊端也是很显而易见，那就是<strong>效率低下，不聚焦(focus)</strong>。</p>
<p>于是人们想到了：能否利用工具来尽可能地发现语法层面的问题，这样代码评审时，人类专家便可以聚焦代码结构设计与业务逻辑层面的问题，分工明确后，效率自然提升(如下图)：</p>
<p><img src="https://tonybai.com/wp-content/uploads/make-reviewdog-support-gitlab-push-commit-to-preserve-the-code-quality-floor-2.png" alt="" /></p>
<blockquote>
<p>注：目前绝大多数工具链仅能自动帮助程序员解决语法层面的问题。将来，随着工具的日益强大，工具可以不断升级关注层次，逐渐进化到具备发现代码结构设计问题，甚至可以发现业务层面逻辑问题的能力。</p>
</blockquote>
<p>于是就有了<a href="https://github.com/reviewdog/reviewdog">reviewdog</a>这样的可以调用各种linter工具对代码进行自动扫描并将问题以comment的形式自动提交的代码仓库的工具。</p>
<p>到这里很多朋友会问，即便让工具来关注语法层面的问题，为何要用reviewdog这样的工具，git的pre-commit hook、git server hooks、利用Make等工具做开发阶段检查等手段也能检查代码中的语法问题，它们不再香了吗？</p>
<p>下面简单看看这些方法的“问题”(我们假设大家都已经在使用git作为代码版本管理工具)：</p>
<ul>
<li>git pre-commit-hook  </li>
</ul>
<p>git pre-commit hook是一个客户端的git hook，它是放在开发人员本地代码copy中的.git/hooks目录下的钩子，当开发人员在本地执行git commit时会被唤起执行。pre-commot hook的问题就在于我们没法在中心代码仓库对pre-commit hook的脚本内容做统一管理和维护。这个更适合开发人员根据自己的喜好、代码素养在自己的开发环境下部署。</p>
<p>此外，有些代码并不一定是在开发者自己的开发机上提交的，换环境后，pre-commit hook就不在生效。</p>
<ul>
<li>利用Make等工具做本地检查</li>
</ul>
<p>利用make工具，我们可以在本地build代码之前对代码做lint等各种静态检查，但和pre-commit-hook一样，虽然Makefile可以提交代码仓库，但真正用于检查代码的工具依旧是在开发人员本地，难于对工具版本，设定的检查规则进行统一管理维护，可能导致不同开发人员环境有不一致的情况。另外同样的情况，有些代码并不一定是在开发者自己的开发机上提交的，换环境后，Make工具依赖的代码检查工具可能并不存在，检查环节就无法有效实施。</p>
<ul>
<li>git server hooks</li>
</ul>
<p>git支持<a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_server_side_hooks">server hooks</a>，<a href="https://docs.gitlab.com/ee/administration/server_hooks.html">gitlab自12.8版本也开始支持server hooks</a>(替换之前的custom hooks)。</p>
<p>Git server支持以下钩子：</p>
<ul>
<li>pre-receive</li>
<li>post-receive</li>
<li>update</li>
</ul>
<p>我倒是没有深研究过这些server hooks是否能满足我们的功能要求，但就git server hooks的部署特点就决定了，它不适合，因为它要在gitlab的server上执行，这就意味着我们需要的所有静态代码检查工具都要部署和配置在与gitlab server同一个环境中，这耦合性太强，根本不便于我们对这些静态代码检查工具的管理与日常维护。</p>
<p>而像reviewdog这样的工具将与ci工具(比如gitlab-ci)集成，运行在slave/worker/runner的机器上，而这些机器上的环境便很容易统一的定制与管理。</p>
<p>好了，下面进入reviewdog时间！</p>
<blockquote>
<p>注：我们以代码仓库为gitlab为例，我曾做过小调查，目前企业内部基本都在使用gitlab搭建私有git仓库，除了那些自实现code仓库平台的大厂。</p>
</blockquote>
<h3>二. reviewdog是什么</h3>
<p>reviewdog是一个什么样的工具呢？我们来看看下面这幅示意图：</p>
<p><img src="https://tonybai.com/wp-content/uploads/make-reviewdog-support-gitlab-push-commit-to-preserve-the-code-quality-floor-3.png" alt="" /></p>
<p>我们看到，这是一幅基于gitlab的ci执行流程图，在这个流程中，reviewdog运行在gitlab-runner节点，也就是负责真正执行ci job的节点上。每当开发人员执行一次git push，将commit同步到代码仓库，一次ci job将被触发，在承载该ci job的gitlab-runner节点上，reviewdog被唤起，它做了三件事：</p>
<ul>
<li>调用静态代码检查工具对最新pull下来的代码进行检查；</li>
<li>将代码检查结果(第几行有问题)与commit diff的结果进行比对，得到交集(即commit diff中变更(add和update)的代码行与代码检查结果的行一致的，放入交集中)；</li>
<li>将交集中代码检查结果信息以gitlab commit comment的形式post到gitlab仓库中</li>
</ul>
<p>这样开发人员就可以通过commit页面看到这些comments，并应对这些comment，必要情况下，会修复这些问题。</p>
<p>我们看到reviewdog和其他工具相比，最大的不同就是可以找出commit diff与lint结果中的交集，并与代码仓库交互，将这些交集中的结果以comments的形式放入commit页面，<strong>就像同行代码评审时，同行直接在你的commit页面添加comment一样</strong>。</p>
<p>然而当前版本的reviewdog还不支持直接在gitlab-push-commit上做检查与提交comment，可能是这样的场景较为少见，因为目前开源项目更多采用基于pr(pull request)的工作流，所以reviewdog内置了诸如github-pr-check、github-pr-review、gitlab-mr-commit等工作流的代码review。而像我们使用的基于gitlab-push-commit可能并不多见（当然我们内部使用这种也是有特定上下文的）。</p>
<p>那么如何让reviewdog支持gitlab-push-commit，即对push动作中的commit进行静态代码检查并将结果以comment的形式放入commit页面呢？我们只能<a href="https://github.com/bigwhite/reviewdog">fork reviewdog项目</a>，并在<a href="https://github.com/bigwhite/reviewdog">fork后的项目</a>中自行添加对gitlab-push-commit模式的支持。</p>
<h3>三. 改造reviewdog以支持gitlab-push-commit模式</h3>
<p>reviewdog就是一个命令行工具，通常就是一次性执行，因此它的代码结构较为清晰。我们可以简单围绕它支持的几种reporter模式来搞清楚如何增加对gitlab-push-commit模式的支持。</p>
<p>这里说明一下gitlab-push-commit模式的含义，首先该模式适用于开发人员通过git push推送代码到gitlab时触发的ci job。在该ci job中，reviewdog会运行配置的静态代码分析工具(比如golangci-lint等)对最新的代码进行扫描，并得到问题集合；然后获取最新的commit的sha值(CI_COMMIT_SHA)以及push之前的latest commit的sha值(CI_COMMIT_BEFORE_SHA)，并比较这两个版本间的diff。最后通过文件名与行号将问题集合与diff集合中的“交集”找出来，并将结果以comment形式通过gitlab client api提交到的此次push的最新的那个commit的页面。</p>
<p>目前该模式尚存在一个“瑕疵”，那就是如果一个push中有多个commit，那么gitlab-push-commit模式不会针对每个commit做diff和comment，而只是会用push中的latest commit与push之前的最新commit做比较。</p>
<p>定义清除gitlab-push-commit模式含义后，我们就可以“照葫芦画瓢”的为reviewdog增加该模式的支持了！</p>
<p>在main.go中，我们主要是在run函数中增加一个reporter case分支：</p>
<pre><code>// https://github.com/bigwhite/reviewdog/blob/master/cmd/reviewdog/main.go
func run(r io.Reader, w io.Writer, opt *option) error {
... ...

case "gitlab-push-commit":
    build, cli, err := gitlabBuildWithClient(opt.reporter)
    if err != nil {
        return err
    }
    log.Printf("reviewdog: [gitlab-push-commit-report] gitlabBuildWithClient ok\n")

    gc, err := gitlabservice.NewGitLabPushCommitsCommenter(cli, build.Owner, build.Repo, build.SHA)
    if err != nil {
        return err
    }
    log.Printf("reviewdog: [gitlab-push-commit-report] NewGitLabPushCommitsCommenter ok\n")

    cs = reviewdog.MultiCommentService(gc, cs)
    ds, err = gitlabservice.NewGitLabPushCommitsDiff(cli, build.Owner, build.Repo, build.SHA, build.BeforeSHA)
    if err != nil {
        return err
    }
    log.Printf("reviewdog: [gitlab-push-commit-report] NewGitLabPushCommitsDiff ok\n")
... ...

}
</code></pre>
<p>在这个case中，我们主要是为后面的project.Run或reviewdog.Run方法准备gitlab client对象、PushCommitsCommenter对象(位于service/gitlab/gitlab_push_commits.go中)、PushCommitsDiff对象(位于service/gitlab/gitlab_push_commits_diff.go中)等。</p>
<p>gitlab_push_commits.go和gitlab_push_commits_diff.go是新增的两个go源文件，也是参考了同目录下的gitlab_mr_commit.go和gitlab_mr_diff.go改写而成的。具体代码这里就不列出来了，大家有兴趣可以自行阅读。</p>
<h3>四. 部署gitlab-runner验证新版reviewdog</h3>
<p>下面我们就来验证一下上述改造后的reviewdog。</p>
<h4>1. 安装gitlab-runner</h4>
<p>我们先在gitlab上建立一个实验项目，然后为该项目配置ci。如果你的gitlab还没有注册gitlab-runner，可以按下面步骤安装和注册runner节点(可以在顶层group下面建立，这样runner可以在group内共享：settings => CI/CD => Runners => Show runner installation instructions 有部署runner的详细命令说明)：</p>
<pre><code>//假设我们有一个ubuntu 20.04的主机，我们可以按下面命令安装和注册一个gitlab-runner：

sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64

# Give it permissions to execute
sudo chmod +x /usr/local/bin/gitlab-runner

# Create a GitLab CI user
sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash

# Install and run as service
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start

# 注册该runner
sudo gitlab-runner register --url http://{gitlab-server-ip-addr}/ --registration-token {registration token}
</code></pre>
<p>上面命令会在/etc/gitlab-runner下面建立一个runner自用配置文件：config.toml：</p>
<pre><code>//  /etc/gitlab-runner/config.toml

concurrent = 1
check_interval = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "runner for ard group"
  url = "http://gitlab_ip_addr/"
  id = 1
  token = "{registration token}"
  token_obtained_at = 2022-09-01T11:03:43Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "shell"
  shell = "bash"
  environment = ["PATH=/home/tonybai/.bin/go1.18/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin"]
  [runners.custom_build_dir]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]
</code></pre>
<p>这里我选择了shell executor，即基于主机shell执行ci job中的命令。runners下的environment可以设置shell的环境变量，这里的设置将覆盖对应账号(比如gitlab-runner)下的环境变量值。</p>
<p>gitlab-runner部署成功后，我们在group的runners下面便可以看到下面的available runners：</p>
<p><img src="https://tonybai.com/wp-content/uploads/make-reviewdog-support-gitlab-push-commit-to-preserve-the-code-quality-floor-4.png" alt="" /></p>
<blockquote>
<p>注：在创建runner时，我为该runner设置了两个tag：ard和ci。</p>
<p>注：确保runner执行的命令在主机的PATH下面可以找到。</p>
</blockquote>
<h4>2. 创建personal access token</h4>
<p>reviewdog需要通过gitlab client API访问gitlab仓库获取信息并提交comments，这就需要我们为runner执行的命令提供access token。</p>
<p>gitlab有多种access token，比如：personal access token、project access token等。我们创建personal access token，我也测试过project access token，使用<a href="https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html">project access token</a>可以成功提交comment，但是notify mail十有八九无法发送出来。</p>
<p>access token要保存好，因为它只显示一次。</p>
<p>我们将personal access token配置到实验项目的variable中(Settings => CI/CD => variables)，variable的key为REVIEWDOG_GITLAB_API_TOKEN，值为刚刚创建的token。</p>
<p>后续每次CI job执行，该variable会作为预定义的环境变量对job生效。我们的reviewdog便可以使用该token访问gitlab。</p>
<h4>3. 配置实验项目的ci pipeline</h4>
<p>我们可以通过代码的形式配置实验项目的ci pipeline，我们在项目根目录下建立.gitlab-ci.yml文件，其内容如下：</p>
<pre><code>// .gitlab-ci.yml

build-job:
  tags:
      - ard
  stage: build
  script:
    - export CI_REPO_OWNER=ard/incubators
    - export CI_REPO_NAME=learn-gitlab
    - reviewdog -reporter=gitlab-push-commit
  only:
    - master
    - pushes
</code></pre>
<p>.gitlab-ci.yml的具体字段含义可以参考gitlab文档。在这个配置中，值得注意的有几点：</p>
<ul>
<li>使用tags关联runner(这里用ard这个tag)；</li>
<li>script部分是job具体执行的命令列表，这里先设置CI_REPO_OWNER和CI_REPO_NAME两个环境变量，供reviewdog使用；然后执行reviewdog；</li>
<li>only部分描述仅针对master分支的push事件触发ci job。</li>
</ul>
<h4>4. 配置.reviewdog.yml</h4>
<p>最后，我们来配置一下适合实验项目的reviewdog的配置文件。我们同样在项目根目录下建立.reviewdog.yml文件，其内容如下：</p>
<pre><code>runner:
  golangci:
    cmd: golangci-lint run --max-same-issues=0 --out-format=line-number ./...
    errorformat:
      - '%E%f:%l:%c: %m'
      - '%E%f:%l: %m'
      - '%C%.%#'
    level: warning
</code></pre>
<p>在这里我们看到，我们使用golangci-lint这个静态检查工具对实验项目的代码进行检查。这里的&#8211;max-same-issues=0的含义是不限制相同错误的数量。至于.reviewdog.yml的具体格式，reviewdog项目自身的<a href="https://github.com/bigwhite/reviewdog/blob/master/.reviewdog.yml">.reviewdog.yml</a>很具参考价值，大家需要时可以仔细研究。</p>
<h4>5. 推送代码并验证reviewdog的执行结果</h4>
<p>我们可以故意在代码中写下有问题的一些代码，这些问题要保证可以被golangci-lint工具扫描出来，比如：</p>
<pre><code>package main

type Foo struct {
    A int
    B string
    C bool
}

func Demo1() error {
    return nil
}

func Demo2() error {
    return nil
}

func Demo3() error {
    return nil
}

func main() {
    f := &amp;Foo{1, "tony", false}
    _ = f
    Demo2()
    Demo1()
    Demo3()
}
</code></pre>
<p>这里并没有对Demo函数调用进行错误处理，golangci-lint中的errcheck可以检测出这个问题。提交并push这些代码到仓库，稍等片刻，我们便可收到notify mail，打开commit页面，便会看到下面这样的commit comments：</p>
<p><img src="https://tonybai.com/wp-content/uploads/make-reviewdog-support-gitlab-push-commit-to-preserve-the-code-quality-floor-5.png" alt="" /></p>
<p>看到这样的结果，说明reviewdog按预期工作了！</p>
<h3>五. 小结</h3>
<p>本文介绍了如何基于reviewdog对push提交的commit进行静态代码检查并像一个“同行”一样在commit中提交评论的方法。</p>
<p>这样做的目的就是希望通过工具提升代码评审的效率，同时也守住代码质量的下限。</p>
<p>就像本文开始所说的那样，随着检查工具能力的增强，这样的基于reviewdog自动检查代码的方案在保证代码质量方面还可以继续提升。</p>
<p>Go开源了go/ast等工具链，有能力的童鞋可以基于go/ast自行开发具有“特定目的”的检查工具并集成到reviewdog中，这将使得检查更有针对性和有效性。</p>
<p>本文涉及源码在<a href="https://github.com/bigwhite/reviewdog">这里</a>下载 &#8211; https://github.com/bigwhite/reviewdog/</p>
<hr />
<p><a href="https://wx.zsxq.com/dweb2/index/group/51284458844544">“Gopher部落”知识星球</a>旨在打造一个精品Go学习和进阶社群！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！2022年，Gopher部落全面改版，将持续分享Go语言与Go应用领域的知识、技巧与实践，并增加诸多互动形式。欢迎大家加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/。smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。2020年4月8日，中国三大电信运营商联合发布《5G消息白皮书》，51短信平台也会全新升级到“51商用消息平台”，全面支持5G RCS消息。</p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博：https://weibo.com/bigwhite20xx</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2022, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2022/09/08/make-reviewdog-support-gitlab-push-commit-to-preserve-the-code-quality-floor/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>代码评审，由人治过渡到“法治”</title>
		<link>https://tonybai.com/2013/07/08/code-review-from-rule-of-man-to-rule-of-law/</link>
		<comments>https://tonybai.com/2013/07/08/code-review-from-rule-of-man-to-rule-of-law/#comments</comments>
		<pubDate>Mon, 08 Jul 2013 08:23:27 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[APR]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Blogger]]></category>
		<category><![CDATA[Code-Review]]></category>
		<category><![CDATA[pre-commit-hook]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[ReviewBoard]]></category>
		<category><![CDATA[Subversion]]></category>
		<category><![CDATA[代码评审]]></category>
		<category><![CDATA[代码走查]]></category>
		<category><![CDATA[博客]]></category>
		<category><![CDATA[学习]]></category>
		<category><![CDATA[工作]]></category>
		<category><![CDATA[思考]]></category>
		<category><![CDATA[感悟]]></category>
		<category><![CDATA[法治]]></category>
		<category><![CDATA[程序员]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=1294</guid>
		<description><![CDATA[事实证明：有效的代码评审(Code Review，也有叫代码审查的），对保证代码质量具有十分重要的作用。因此这两年来我一直尝试着在这块不断改进和完善，以期望能形成一套合理、规范、有 效且高效的代码评审流程，这包括引入在线代码评审系统、走查和在线评审结合、规范评审Request的规模与有效性、设立评审专员等，用心不可谓不良苦 ^_^。大家也的确形成了及时提交Code Review Request或组织进行代码走查的良好习惯。不过我还是发现了一些问题。 * 有些组（我对其影响力不足的^_^）依旧没有严格执行代码评审环节，代码屡屡出现低级错误； * 走查形式的会议评审缺乏全面性，效果好坏与参与者的&#8220;状态&#8221;直接相关； * 在线评审环节缺乏&#8220;责任制&#8221;，常出现的一种情况是：请求大家评审，结果可能却是大家都没有评审。出现&#34;Request Review Miss&#34;的现象。 这让我陷入思考：长期以来我们在代码评审这块过于依赖人的自觉性，理想地认为每个人都能认识到代码评审的重要性，并认真地执行代码评审的流程或充满激情地 参与到其他人发起的代码评审过程中去，但结果事与愿违。这就像党员如何保持纯洁性一样，如果仅仅依靠个人道德/职业水平约束，这事往往是不成的。事实证明 人治在中国社会是会造成各种社会问题的。我们的代码评审环节也是一样，我们不能再期望所有人都能和我站在一条认知和激情水平线上，于是我打算尝试向&#8220;法 治&#8221;过渡。 &#34;法&#34;，规则制度也，是团队一致认同的可以提升产品质量的规则制度。以此为前提，我要做的就是设立&#8220;检查和预防&#8221;机构，即以很低的Cost，检查大家是否按&#8220;法&#8221;完成了代码评审环节，提醒大家要按&#8220;法&#8221;进行。我采取了几个措施： 【规范Commit Log 】 这是一个前提工作。实现规范的Commit Log便于后续的检查和监督，同时细化规范的Commit Log信息对代码维护是大有裨益的。在Commit Log中还增加了一些关联信息，方便维护者了解该Commit的背景。初期的模板是这么来确定的： 模板结构： TITLE BODY RELATIONSHIPS 展开后如下： [Category] Title content Body content [BUGID] QC#733 &#124; JIRA#766 [REVIEWID] RB#767 [REVIEWED BY] xx, yy, zz [SIGNOFF BY] xx TITLE Category： &#160;&#160; &#8211; BUGFIX [...]]]></description>
			<content:encoded><![CDATA[<p>事实证明：有效的<a href="http://tonybai.com/2011/02/22/code-reviews/">代码评审</a>(Code Review，也有叫代码审查的），对保证代码质量具有十分重要的作用。因此这两年来我一直尝试着在这块不断改进和完善，以期望能形成一套合理、规范、有 效且高效的代码评审流程，这包括引入<a href="http://tonybai.com/2010/12/18/thoughts-on-online-coding-review/">在线代码评审系统</a>、走查和在线评审结合、规范评审Request的规模与有效性、设立评审专员等，用心不可谓不良苦 ^_^。大家也的确形成了及时提交Code Review Request或组织进行代码走查的良好习惯。不过我还是发现了一些问题。</p>
<p>* 有些组（我对其影响力不足的^_^）依旧没有严格执行代码评审环节，代码屡屡出现低级错误；<br />
	* 走查形式的会议评审缺乏全面性，效果好坏与参与者的&ldquo;状态&rdquo;直接相关；<br />
	* 在线评审环节缺乏&ldquo;责任制&rdquo;，常出现的一种情况是：请求大家评审，结果可能却是大家都没有评审。出现&quot;Request Review Miss&quot;的现象。</p>
<p>这让我陷入思考：长期以来我们在代码评审这块过于依赖人的自觉性，理想地认为每个人都能认识到代码评审的重要性，并认真地执行代码评审的流程或充满激情地 参与到其他人发起的代码评审过程中去，但结果事与愿违。这就像党员如何保持纯洁性一样，如果仅仅依靠个人道德/职业水平约束，这事往往是不成的。事实证明 人治在中国社会是会造成各种社会问题的。我们的代码评审环节也是一样，我们不能再期望所有人都能和我站在一条认知和激情水平线上，于是我打算尝试向&ldquo;法 治&rdquo;过渡。</p>
<p>&quot;法&quot;，规则制度也，是团队一致认同的可以提升产品质量的规则制度。以此为前提，我要做的就是设立&ldquo;检查和预防&rdquo;机构，即以很低的Cost，检查大家是否按&ldquo;法&rdquo;完成了代码评审环节，提醒大家要按&ldquo;法&rdquo;进行。我采取了几个措施：</p>
<p><b>【规范Commit Log </b><b>】</b></p>
<p>这是一个前提工作。实现规范的<a href="http://tonybai.com/2013/05/09/also-talk-about-commit-log/">Commit Log</a>便于后续的检查和监督，同时细化规范的Commit Log信息对代码维护是大有裨益的。在Commit Log中还增加了一些关联信息，方便维护者了解该Commit的背景。初期的模板是这么来确定的：</p>
<p>模板结构：</p>
<p><font face="Courier New"><i>TITLE</i><br />
	<i>BODY</i><br />
	<i>RELATIONSHIPS</i></font></p>
<p>展开后如下：</p>
<p><font face="Courier New"><i>[Category] Title content</i></font></p>
<p><font face="Courier New"><i>Body content</i></font></p>
<p><font face="Courier New"><i>[BUGID] QC#733 | JIRA#766</i><br />
	<i>[REVIEWID] RB#767</i><br />
	<i>[REVIEWED BY] xx, yy, zz</i><br />
	<i>[SIGNOFF BY] xx</i></font></p>
<p>TITLE Category：<br />
	&nbsp;&nbsp; &#8211; BUGFIX 代码修复<br />
	&nbsp;&nbsp; &#8211; FEATURE 新功能特性添加<br />
	&nbsp;&nbsp; &#8211; TASK 诸如代码美化、调整版本号等<br />
	&nbsp;&nbsp; &#8211; URGENT 紧急提交，对此类commit，可不做review和拦截</p>
<p>BODY Content：<br />
	&nbsp;&nbsp; 有关此次修改的详细信息说明</p>
<p>RELATIONSHIPS：<br />
	&nbsp;&nbsp; &#8211; [BUGID] 一般用Bug跟踪系统的ID号<br />
	&nbsp;&nbsp; &#8211; [REVIEWID] reviewboard上的ID号<br />
	&nbsp;&nbsp; &#8211; [REVIEWED BY] xx, yy, zz<br />
	&nbsp;&nbsp; &#8211; [SIGNOFF BY] xx</p>
<p><b>【&quot;全覆盖&quot;原则</b>】</p>
<p>所有变更代码都要发起在线&ldquo;Code Review Request&rdquo;，即便是会议走查的代码，会后也要补提&ldquo;Review Request&rdquo;。</p>
<p><b>【&ldquo;低保&rdquo;原则</b>】</p>
<p>每个Review Request至少选择两名评审负责人，填到&quot;Request&quot;中，这两个人必须对此Request给出评审意见，这是一个评审的<b>最低保障</b>了，这总比没有人评审要好。当然了其他人也都可以参与评审。只有这两名评审负责人明确提交&quot;ship it&quot; Comment后，该代码才算是通过评审。</p>
<p><b>【关键路径拦截】</b></p>
<p>&quot;对不起，若不符合规定，你的工作将无法进行下去&quot;。有了统一的Commit Log模板，我们就可以对大家的代码Commit环节做检查和<a href="http://tonybai.com/2010/08/07/use-svn-pre-commit-hook/">拦截</a>了。如果代码没有进行评审，无法填写模板中的字段内容，那代码将无法提交到代码库中。如 果虚构Commit log内容，这将是极大的错误，在抽查中一旦发现，后果将是很严重的^_^。</p>
<p>当然这一过程中还有很多细节需要考虑，比如Reviewer的选择不能集中在一个人身上，否则会造成热点；再比如紧急提交代码应该如何处理等等。&ldquo;法治&rdquo; 是与一定的&ldquo;国情&rdquo;相匹配的，并不是所有的组织都需要进行这么严格且略有死板&ldquo;法治&rdquo;手段，依团队内组员的专业能力和认知水平而定。</p>
<p>有些公司开发了自己的统一开发平台，将一系列流程都在一套系统中规范了起来，这当然是更好的&ldquo;法治&rdquo;了。但在没有这样的平台的前提下，初步使用上述的几个手段，还是会收获一些改进的。</p>
<p style='text-align:left'>&copy; 2013, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2013/07/08/code-review-from-rule-of-man-to-rule-of-law/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>也谈Commit log</title>
		<link>https://tonybai.com/2013/05/09/also-talk-about-commit-log/</link>
		<comments>https://tonybai.com/2013/05/09/also-talk-about-commit-log/#comments</comments>
		<pubDate>Thu, 09 May 2013 09:18:55 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Blogger]]></category>
		<category><![CDATA[Bug]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Chinglish]]></category>
		<category><![CDATA[Commit]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[Kernel]]></category>
		<category><![CDATA[Mercurial]]></category>
		<category><![CDATA[Opensource]]></category>
		<category><![CDATA[pre-commit-hook]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[Solaris]]></category>
		<category><![CDATA[Subversion]]></category>
		<category><![CDATA[svn]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[内核]]></category>
		<category><![CDATA[博客]]></category>
		<category><![CDATA[学习]]></category>
		<category><![CDATA[工作]]></category>
		<category><![CDATA[开源]]></category>
		<category><![CDATA[思考]]></category>
		<category><![CDATA[感悟]]></category>
		<category><![CDATA[模板]]></category>
		<category><![CDATA[版本控制]]></category>
		<category><![CDATA[程序员]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=1264</guid>
		<description><![CDATA[在版本控制工具大行其道的今天，作为程序员，势必要每天与各种版本控制系统（比如Subversion、Git、Mercurial等）打交道， 每天不commit几次代码都不好意思说自己是专业程序员^_^。不过commit代码可不止敲入commit命令这么简单，对于一个专业程序员 来说，我们还要关注每次commit所携带的背景信息，这里暂且称之为&#8220;commit context&#8221;。在每次commit时，这些上下文信息只能通过commit log来体现。 一、Commit Context 今日的软件复杂度日益增加，软件开发模式也早已从单打独斗的英雄模式变成了团队协作模式了，而在团队模式下，版本控制系统发挥着至关重要的作用， 它让开发过程变得有序，将冲突解决的成本尽可能地降低到最低。但版本控制系统毕竟不是智能的，它只是机械地记录着每次提交前后的内容的raw差 异，至于这个差异究竟代表了什么，版本管理系统是不得而知的，这就需要我们开发者们来提供，这就算是产生commit context的动机吧。即便是一个人开发维护的项目，个人的记忆也是有时效性的，时间久了，以前的代码变更context势必也就淡忘了，良好且规范的 commit context有助于更好的维护项目，追踪历史思路和行为，甚至在查找bug时也是能帮得上大忙的，比如确认bug引入的时段边界、代码范围等。 前面说了，commit context最终是以commit log形式提供的，这才是我在这篇文章中真正要说的内容^_^。评价一个项目的好坏，无论是商业项目，还是开源项目，代码本身质量是一个重要的方面，代码 维护的规范性则是另外不可忽略的一个重要因素，而在代码维护规范性方面，commit log的规范是一项重要内容。做了这么多年Coding工作，到目前为止部门内部还没有哪一个项目在commit log规范方面是让我满意和欣赏的。另外本人在亲为commit log方面也是不能让自己满意的，这也是促使我思考commit log这块内容的一个初衷。 commit log承载着每次commit动作的context。一般来说context中至少要有一项内容，那就是此次代码变更的summary，这是最基本的要 求。如果你的commit log还是空着的，那你真该反思反思了，那是对自己和他人的不负责任。但无论是商业公司内部开发还是开源项目，commit context涉及到的因素往往不止一个，很多情况下commit context还与项目过程、质量保证流程以及项目使用的一些工具系统有 关联。我们来看两个知名开源项目的commit log样例吧。 [example1 - Linux Kernel] audit: catch possible NULL audit buffers It&#39;s possible for audit_log_start() to return NULL.&#160; Handle it in the various callers. Signed-off-by: Kees Cook [...]]]></description>
			<content:encoded><![CDATA[<p>在<a href="http://tonybai.com/2011/02/18/put-everything-under-version-control/">版本控制工具</a>大行其道的今天，作为程序员，势必要每天与各种版本控制系统（比如<a href="http://subversion.apache.org">Subversion</a>、<a href="http://git-scm.com">Git</a>、<a href="http://mercurial.selenic.com/‎">Mercurial</a>等）打交道， 每天不commit几次代码都不好意思说自己是专业程序员^_^。不过commit代码可不止敲入commit命令这么简单，对于一个专业程序员 来说，我们还要关注每次commit所携带的背景信息，这里暂且称之为&ldquo;commit context&rdquo;。在每次commit时，这些上下文信息只能通过commit log来体现。</p>
<p><b>一、Commit Context</b></p>
<p>今日的软件复杂度日益增加，软件开发模式也早已从单打独斗的英雄模式变成了团队协作模式了，而在团队模式下，版本控制系统发挥着至关重要的作用， 它让开发过程变得有序，将冲突解决的成本尽可能地降低到最低。但版本控制系统毕竟不是智能的，它只是机械地记录着每次提交前后的内容的raw差 异，至于这个差异究竟代表了什么，版本管理系统是不得而知的，这就需要我们开发者们来提供，这就算是产生commit context的动机吧。即便是一个人开发维护的项目，个人的记忆也是有时效性的，时间久了，以前的代码变更context势必也就淡忘了，良好且规范的 commit context有助于更好的维护项目，追踪历史思路和行为，甚至在查找bug时也是能帮得上大忙的，比如确认bug引入的时段边界、代码范围等。</p>
<p>前面说了，commit context最终是以commit log形式提供的，这才是我在这篇文章中真正要说的内容^_^。评价一个项目的好坏，无论是商业项目，还是开源项目，代码本身质量是一个重要的方面，代码 维护的规范性则是另外不可忽略的一个重要因素，而在代码维护规范性方面，commit log的规范是一项重要内容。做了这么多年Coding工作，到目前为止部门内部还没有哪一个项目在commit log规范方面是让我满意和欣赏的。另外本人在亲为commit log方面也是不能让自己满意的，这也是促使我思考commit log这块内容的一个初衷。</p>
<p>commit log承载着每次commit动作的context。一般来说context中至少要有一项内容，那就是此次代码变更的summary，这是最基本的要 求。如果你的commit log还是空着的，那你真该反思反思了，那是对自己和他人的不负责任。但无论是商业公司内部开发还是开源项目，commit context涉及到的因素往往不止一个，很多情况下commit context还与<b>项目过程、质量保证流程以及项目使用的一些工具系统</b>有 关联。我们来看两个知名开源项目的commit log样例吧。</p>
<p><i>[example1 - Linux Kernel]</i></p>
<p><font face="Courier New">audit: catch possible NULL audit buffers<br />
	It&#39;s possible for audit_log_start() to return NULL.&nbsp; Handle it in the<br />
	various callers.</font></p>
<p><font face="Courier New">Signed-off-by: Kees Cook <a class="moz-txt-link-rfc2396E" href="mailto:keescook@chromium.org">&lt;keescook@chromium.org&gt;</a><br />
	Cc: Al Viro <a class="moz-txt-link-rfc2396E" href="mailto:viro@zeniv.linux.org.uk">&lt;viro@zeniv.linux.org.uk&gt;</a><br />
	Cc: Eric Paris <a class="moz-txt-link-rfc2396E" href="mailto:eparis@redhat.com">&lt;eparis@redhat.com&gt;</a><br />
	Cc: Jeff Layton <a class="moz-txt-link-rfc2396E" href="mailto:jlayton@redhat.com">&lt;jlayton@redhat.com&gt;</a><br />
	Cc: &quot;Eric W. Biederman&quot; <a class="moz-txt-link-rfc2396E" href="mailto:ebiederm@xmission.com">&lt;ebiederm@xmission.com&gt;</a><br />
	Cc: Julien Tinnes <a class="moz-txt-link-rfc2396E" href="mailto:jln@google.com">&lt;jln@google.com&gt;</a><br />
	Cc: Will Drewry <a class="moz-txt-link-rfc2396E" href="mailto:wad@google.com">&lt;wad@google.com&gt;</a><br />
	Cc: Steve Grubb <a class="moz-txt-link-rfc2396E" href="mailto:sgrubb@redhat.com">&lt;sgrubb@redhat.com&gt;</a><br />
	Cc: Andrea Arcangeli <a class="moz-txt-link-rfc2396E" href="mailto:aarcange@redhat.com">&lt;aarcange@redhat.com&gt;</a><br />
	Signed-off-by: Andrew Morton <a class="moz-txt-link-rfc2396E" href="mailto:akpm@linux-foundation.org">&lt;akpm@linux-foundation.org&gt;</a><br />
	Signed-off-by: Linus Torvalds <a class="moz-txt-link-rfc2396E" href="mailto:torvalds@linux-foundation.org">&lt;torvalds@linux-foundation.org&gt;</a></font></p>
<p>这是<a href="https://www.kernel.org">Linux Kernel</a>项目的一个commit log的内容。从这个log携带的context信息来看，我们能够清楚地了解如下一些内容：</p>
<p>- 修改的内核模块范围audit<br />
	- 修改的原因summary: to catch possible NULL audit buffers<br />
	- 这个patch从诞生到被merge到trunk过程中涉及到的相关的人员列表<br />
	- 这个patch由Who sign-off的。</p>
<p>将mail list放入到commit log中，这是Linux Kernel开发过程规范所要求的，同样也是质量保证的一个方法。在《<a href="http://tonybai.com/2012/03/27/how-to-participate-linux-community-section-1/">如何加入Linux内核开发社区</a>》系列文章中你可以了解到一些有关Linux Kernel开发过程的内容。从这个例子中我们主要可以看出commit context与Project过程、质量保证链条方面的相关性。</p>
<p><i>[example2 - Apache Subversion]</i></p>
<p><font face="Courier New">Fix issue #3498 &#8211; Subversion password stores freeze Eclipse</font></p>
<p><font face="Courier New">* subversion/libsvn_auth_gnome_keyring/gnome_keyring.c<br />
	&nbsp; (simple_gnome_keyring_first_creds, simple_gnome_keyring_save_creds,<br />
	&nbsp;&nbsp; ssl_client_cert_pw_gnome_keyring_first_creds,<br />
	&nbsp;&nbsp; ssl_client_cert_pw_gnome_keyring_save_creds): If the keyring is locked<br />
	&nbsp;&nbsp;&nbsp; and we are in interactive mode but have no unlock prompt function, don&#39;t<br />
	&nbsp;&nbsp;&nbsp; throw a &quot;GNOME Keyring is locked and we are non-interactive&quot; error;<br />
	&nbsp;&nbsp;&nbsp; instead, continue without unlocking it, so that the unlocking may be<br />
	&nbsp;&nbsp;&nbsp; handled by the default GNOME Keyring unlock dialog box.</font></p>
<p>这是Apache Subversion项目的一个commit log的内容。同样从这个log携带的context信息来看，我们能够清楚地了解如下一些内容：</p>
<p>- 修改的代码范围subversion/libsvn_auth_gnome_keyring/gnome_keyring.c，包括括号中的函数名列表， 这个显然更为细致。<br />
	- 修改的原因summary: <font face="Courier New">Fix issue #3498 &#8211; Subversion password stores freeze Eclipse</font><br />
	- 这个patch与问题跟踪系统的关联性 -<font face="Courier New">issue #3498</font>。</p>
<p>通过这个commit log，我们可以快速找到此patch对应的问题跟踪系统中的条目#3498，这样可以查看到一些更为细致的context信息。从这个例子我们主要能够 看出commit context与项目所使用的一些工具系统的关联。</p>
<p>综合以上可以看出良好的commit log是可以清楚全面反映commit context的。这里的&ldquo;全面&rdquo;是project-dependent的，是需要能够体现出涉及project的一切必要信息的：过程的、质量的、工具 的。</p>
<p><b>二、Commit log格式</b></p>
<p>Commit log没有放之四海而皆准的统一格式，而是project-dependent的。就我个人而言，我会在下面的几个问题上有纠结。</p>
<p><b><i>* 语言</i></b></p>
<p>不得不承认在创造编程语言方面，西方文化占了主导，语言中的关键字也多取自英语。虽然目前主流的语言以及新兴的语言都号称源码原生支持utf8或 unicode其他字符集格式，但却是很少见到在源文件中使用非英语命名变量或函数的，这也影响了我在commit log中对语言的选择 &#8211; 我基本上都是用英文编写commit log的。目前主流的版本控制工具都是支持unicode字符集的，你用中文提交也是没有任何问题的，尤其是在国内商业项目中，使用中文描述起来，理解上快且歧义少。我是不反对用中文写commit log的，但反感的是中英文混合写commit log（有些人用中文，有些人用英文）。每当批量看commit log时，中英文混在一起，一点美感都没有了。</p>
<p>commit log不是给最终用户看的，而是给开发维护人员看的。因此选择语言种类时要看这种语言是否能给开发维护人员的工作带来便利，精确全面地传达context。即便 应用是要发布给非洲人民，但若开发人员都是中国人，一样可以用中文编写commit log。</p>
<p><b><i>* 地道</i></b></p>
<p>说到&ldquo;地道&rdquo;，主要是针对你选择<b>外语</b>（大多数情况是英语）作为你commit log的承载语言时。就像生活在国外要用外国人熟悉的语言习惯与人交流似的，我们在用英语编写commit log时也要学会选用&ldquo;地道&rdquo;的词汇，远离<a href="http://en.wikipedia.org/wiki/Chinglish">Chinglish</a>。当然想立即做到&ldquo;地道&rdquo;也不是那么容易，毕竟我们一直以来就按照Chinglish的思维去学 习英语的，一个比较好的方式就是多看看知名开源项目（比如linux kernel）的commit log，看看人家是如何选择词汇和组织句子的。其实Commit log中用到的词汇和句型很少，看多了也就找猫画虎的学会了。</p>
<p><b><i>* 规范</i></b></p>
<p>&ldquo;没有规矩，不成方圆&rdquo;，无论是商业软件项目，还是大型开源项目，莫不如此。如果要想很好的传达commit context，一个设计规范，内容全面的commit log格式是必不可少的。我们无需从头做起，很多开源项目在这方面都已经有一些良好的实践，比如上面提到的linux kernel的commit log convention，再比如这里有Apache Subversion的<a href="http://subversion.apache.org/docs/community-guide/conventions.html#coding-style">Commit log要求</a>。TYPO3和FLOW3也有自己详细的<a href="http://wiki.typo3.org/CommitMessage_Format_(Git)">Commit log说明</a>。</p>
<p>制定规范时总体来说，注意以下几点：<br />
	&#8211; 格式简明扼要，只保留必要的项；<br />
	&#8211; 注意与项目过程、质量保证流程的结合，以及与第三方工具的关联（注意序号或ID的唯一性）；<br />
	&#8211; 对于规模较大的系统，可以考虑在log中体现影响的涉及的&ldquo;子模块&rdquo;或&ldquo;子目录&rdquo;名字或者逻辑功能的名字（比如前面linux kernel例子中的audit），这样便于快速定位本地commit的影响范畴。</p>
<p><b>三、Commit模板</b></p>
<p>如果像linux kernel或subversion那样涉及到过程、质量控制以及第三方工具的集成（比如问题跟踪系统、代码评审系统等）时，建议设置Commit log template(模板)以简化开发者commit log编写的工作。</p>
<p><b><i>* Subversion命令行客户端支持commit log模板</i></b></p>
<p>Subversion在命令行客户端侧暂无对模板的支持。不过可以通过一些trick模拟实现这个功能：</p>
<p>- 创建commit log模板log.tmpl，放在特定目录下，本例中放在用户的$HOME目录下<br />
	- 添加并导出环境变量SVN_EDITOR<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face="Courier New">export SVN_EDITOR=&quot;rm svn-commit.tmp &amp;&amp; cp ~/log.tmpl svn-commit.tmp &amp;&amp; vi &quot;</font></p>
<p>svn commit时，svn客户端会在当前路径下会执行类似$SVN_EDITOR svn-commit.tmp的命令，而svn-commit.tmp文件已经被替换为我们的模板文件，开发者只需按模板填写内容，并保存退出即可。如果 commit成功，svn客户端会删除当前目录下的svn-commit.tmp，否则svn-commit.tmp不会被删除，这将导致下次再提交 时，svn客户端检测到svn-commit.tmp的存在，从而新建立一个svn-commit.2.tmp的新文件，导致模板失效，这也是这个方法的 一个瑕疵。</p>
<p><b><i>* Git命令行支持commit log模板</i></b></p>
<p>Git是目前very hot的分布式版本管理工具，起步晚，但起点高，因此已经内置了对模板的支持，只需将模板文件配置一下即可。<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font face="Courier New"> git config &#8211;global commit.template ~/log.tmpl</font></p>
<p><b>四、良好格式commit log</b><b>的实施</b></p>
<p>即便有了良好格式的commit log的模板定义，但就我经验而言，实施起来也还会遇到诸多问题。commit行为是客户端发起的，要让所有开发者都能很好的使用模板并主动按模板提交需 要一些流程以及工具支持。比如在server段部署<a href="http://tonybai.com/2010/08/07/use-svn-pre-commit-hook/">pre-commit hook</a>，对提交的log格式进行检查，不符合模板格式的予以拒绝等。</p>
<p>对于与问题跟踪系统有关联的log格式，还要注意保持问题跟踪系统id或序号的唯一性，这显然是管理和过程方面的工作。</p>
<p>对于开源项目，一般merge到trunk需要owner的检查，所以反倒实施起来容易了些，只要有一篇内容丰富的 developer/community guide或convention之类的文档即可，多数知名的opensource project(比如linux kernel、subversion、apache httpd server、python等)都是有这类文档的，为这些project提交patch前是要好好阅读这些文档的，不能坏了规矩^_^。&nbsp; &nbsp; &nbsp;<br />
	&nbsp;</p>
<p style='text-align:left'>&copy; 2013, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2013/05/09/also-talk-about-commit-log/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
