summaryrefslogtreecommitdiff
path: root/public/git-go-pre-commit.html
blob: 5c536f196107e029aab8276d01aa6ff3f098c62b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>A git pre-commit hook for automatically formatting Go code — Luke Shumaker</title>
  <link rel="stylesheet" href="assets/style.css">
  <link rel="alternate" type="application/atom+xml" href="./index.atom" name="web log entries"/>
</head>
<body>
<header><a href="/">Luke Shumaker</a> » <a href=/blog>blog</a> » git-go-pre-commit</header>
<article>
<h1 id="a-git-pre-commit-hook-for-automatically-formatting-go-code">A git pre-commit hook for automatically formatting Go code</h1>
<p>One of the (many) wonderful things about the Go programming language is the <code>gofmt</code> tool, which formats your source in a canonical way. I thought it would be nice to integrate this in my <code>git</code> workflow by adding it in a pre-commit hook to automatically format my source code when I committed it.</p>
<p>The Go distribution contains a git pre-commit hook that checks whether the source code is formatted, and aborts the commit if it isn't. I don't remember if I was aware of this at the time (or if it even existed at the time, or if it is new), but I wanted it to go ahead and format the code for me.</p>
<p>I found a few solutions online, but they were all missing something—support for partial commits. I frequently use <code>git add -p</code>/<code>git gui</code> to commit a subset of the changes I've made to a file, the existing solutions would end up adding the entire set of changes to my commit.</p>
<p>I ended up writing a solution that only formats the version of the that is staged for commit; here's my <code>.git/hooks/pre-commit</code>:</p>
<pre><code>#!/bin/bash

# This would only loop over files that are already staged for commit.
#     git diff --cached --numstat |
#     while read add del file; do
#         …
#     done

shopt -s globstar
for file in **/*.go; do
    tmp=&quot;$(mktemp &quot;$file.bak.XXXXXXXXXX&quot;)&quot;
    mv &quot;$file&quot; &quot;$tmp&quot;
    git checkout &quot;$file&quot;
    gofmt -w &quot;$file&quot;
    git add &quot;$file&quot;
    mv &quot;$tmp&quot; &quot;$file&quot;
done</code></pre>
<p>It's still not perfect. It will try to operate on every <code>*.go</code> file—which might do weird things if you have a file that hasn't been checked in at all. This also has the effect of formatting files that were checked in without being formatted, but weren't modified in this commit.</p>
<p>I don't remember why I did that—as you can see from the comment, I knew how to only select files that were staged for commit. I haven't worked on any projects in Go in a while—if I return to one of them, and remember why I did that, I will update this page.</p>

</article>
<footer>
<p>The content of this page is Copyright © 2013 <a href="mailto:lukeshu@sbcglobal.net">Luke Shumaker</a>.</p>
<p>This page is licensed under the <a href="http://www.wtfpl.net/txt/copying/">WTFPL-2</a> license.</p>
</footer>
</body>
</html>