summaryrefslogtreecommitdiff
path: root/public/git-go-pre-commit.md
blob: b1563d985362a73e9eac8c7ceac4a01fb6080ec9 (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
44
45
46
47
48
49
50
51
52
53
54
55
56
A git pre-commit hook for automatically formatting Go code
==========================================================
---
date: "2013-10-12"
license: WTFPL-2
---

One of the (many) wonderful things about the Go programming language
is the `gofmt` tool, which formats your source in a canonical way.  I
thought it would be nice to integrate this in my `git` workflow by
adding it in a pre-commit hook to automatically format my source code
when I committed it.

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.

I found a few solutions online, but they were all missing
something—support for partial commits.  I frequently use `git add
-p`/`git gui` 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.

I ended up writing a solution that only formats the version of the
that is staged for commit; here's my `.git/hooks/pre-commit`:

	#!/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="$(mktemp "$file.bak.XXXXXXXXXX")"
		mv "$file" "$tmp"
		git checkout "$file"
		gofmt -w "$file"
		git add "$file"
		mv "$tmp" "$file"
	done

It's still not perfect.  It will try to operate on every `*.go`
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.

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.