Jake McCrary

The usefulness of Clojure's cond->

| Comments

Clojure’s cond-> (and cond->>) is a versatile macro. It isn’t a new macro, it has been around since version 1.5, but I finally discovered and started using it sometime last year. It isn’t a workhorse macro, you won’t be using it everyday, but it comes in handy.

What is cond->?

Let’s start by looking at the docstring.

1
2
3
4
5
6
Usage: (cond-> expr & clauses)

Takes an expression and a set of test/form pairs. Threads expr (via ->)
through each form for which the corresponding test
expression is true. Note that, unlike cond branching, cond-> threading does
not short circuit after the first true test expression.

So what does the docstring mean? Let’s break it down with an example.

1
2
3
(cond-> 10
  false inc)
=> 10

In the above example 10 is the expr mentioned in the docstring and everything after it are the clauses. Each clause is a pair made up of a test and a form. In this example there is a single clause with the value false as the test the function inc as the form. Since the test evaluates to a false value the expression is not threaded into the form. As a result the original expression, 10, is returned.

Let’s look at an example with a truthy test.

1
2
3
(cond-> 10
  true (- 2)
=> 8

Once again, 10 is the starting expression. The single clause has a test that evaluates to true so the expression is threaded into the first position of the form (- 2). The result is 8 and this is returned.

Next is an example of a cond-> with multiple clauses. Explanations are inline with the code.

1
2
3
4
5
6
7
8
9
10
(cond-> 10 ; start with 10
  ;; test evaluates to true, so apply inc to 10. Current value is now 11.
  true inc

  ;; (zero? 1) evaluates to false, do not perform action. Current value stays 11.
  (zero? 1) (+ 2)

  ;; (pos? 4) evaluates to true, thread 11 into first position of form.
  (pos? 4) (- 5))
=> 6 ; The result of (- 11 5) is 6.

If you understand the above example then you have a good grasp of cond->. But when is this functionality useful?

When do I use cond->?

Looking through the codebases I work on, I almost primarily see cond-> being used with the initial expression being a hash-map. It is being used in situations where we want to selectively assoc, update, or dissoc something from a map.

If cond-> did not exist you would accomplish those selective modifications with code similar to below.

1
2
3
(if (some-pred? q)
  (assoc m :a-key :a-value)
  m)

You can rewrite the above with cond->.

1
2
(cond-> m
  (some-pred? q) (assoc :a-key :a-value))

If you’re not used to seeing cond-> the above transformation might seem like a step backwards. I know it felt that way to me when I first saw cond->. Give yourself time to get familiar with it and you’ll be glad you’re using it.

A meatier example of using cond-> is demonstrated below. Here we’re manipulating data structures designed for use with honeysql to generate SQL statements. We start with a base-query and selectively modify it based on incoming parameters.

1
2
3
4
5
6
7
8
(defn query [req-params]
  (let [and-clause (fnil conj [:and])
        base-query {:select [:name :job]
                    :from [:person]}]
    (cond-> base-query
      (:job req-params) (update :where and-clause [:= :job (:job req-params)])
      (:name req-params) (update :where and-clause [:= :name (:name req-params)])
      (:min-age req-params) (update :where and-clause [:> :age (:min-age req-params)]))))

Hopefully this gives you a taste of cond->. I’ve found it to be quite useful. It has a place in every Clojure developer’s toolbox.

Book review: Serverless Single Page Apps

| Comments

I’ve read Ben Rady’s Serverless Single Page Apps twice now. As an early technical reviewer, I was able to watch and take part in the book’s evolution. The early draft was good but the most recent near-final draft was better.

Serverless Single Page Apps walks you through building a low-cost, highly-available, serverless single page web application. It does this on top of various Amazon web services (DynamoDB, Cognito, Lambda, API Gateway, S3). If you follow along you’ll end up with a simple web application with authentication.

The book is very enjoyable. The examples are clear and the book is well written. The book uses JavaScript to implement the serverless application. For the user interface it uses plain JavaScript with a bit of jQuery and for the AWS Lambda functions you dip into some Node.js. Serverless doesn’t distract you from learning about serverless applications by forcing you to learn new JavaScript frameworks or libraries.

One of my favorite parts of the book is Ben’s use of test driven development. The examples provided give the reader a decent taste of the benefits of test-first development. Having the tests helped me when I made some silly mistakes in later parts of the book.

Overall I’d recommend this book to developers who are interested in learning what a serverless application might look like. If you follow along you’ll know how to build one by the end and will have a good starting point for diving deeper into the topic.

Reading in 2015

| Comments

At the beginning of the year I generally take the time to reflect on my reading in the previous year. I’m nearly three months but I’m finally taking a look at 2015. Here are my summaries of my 2014 and 2013 reading.

I’ve continued to keep track of my reading by using Goodreads. My profile contains the full list and reviews of books I’ve read since 2010. Here is my 2015 list.

2015 Goals

2015 did not have an easily measured goal. I set the vague goal of increasing the quality of my reading by attempting to think deeper about what I’ve read.

2015 Results

I have no idea if I achieved my goal. Some books have stuck with me and I’ve thought quite a bit about the stories. Others I’ve forgotten already.

Looking at raw numbers I read 51 books in 2015 for a total of about 21,790 pages. When compared to 2014 these numbers are lower by 19 books and about 1300 pages.

In terms of star ratings, 2015 was a better year. I had three more five star books and one more four star book. The 19 book difference between 2014 and 2015 is entirely found in two and three star books.

Recommendations

I awarded ten books a five star rating. This is more five stars than any other year. Each of the five star books I’d recommend without hesitation. Below is my list of five star books. The titles are affiliate links to Amazon and the my review text links to Goodreads.

One of the great things about writing this post is that it forces me to pause and reflect on the previous years books. Its great seeing this list of great books and remembering the stories. Of these ten books the ones I remember most fondly are_Stoner, Snow Crash, and The Pale King_.

There were also a ton of great four star books this year. One that stands out is Joseph Heller’s Something Happened (my review). Kurt Vonnegut wrote a brilliant review of this book which I encourage you to read.

Dave MacLeod’s Make or Break: Don’t Let Climbing Injuries Dictate Your Success (my review) deserves a mention. I highly recommend this book to any climber. We push our bodies hard and this book will help you prevent and recover from injuries. I’ve used it as a reference so many times over the past year. It probably deserves five stars.

Other Stats

Unsurprisingly, I’m continuing to mostly read ebooks.

1
2
3
4
5
|           | 2014 | 2015 |
|-----------+------+------|
| ebook     |   64 |   47 |
| hardcover |    1 |    1 |
| paperback |    4 |    3 |

My average rating went up.

1
2
3
4
5
6
7
| Year | Average Rating |
|------+----------------|
| 2011 |           3.84 |
| 2012 |           3.66 |
| 2013 |           3.67 |
| 2014 |           3.48 |
| 2015 |           3.86 |

Last year I had many repeat authors. This year I had fewer. Neal Stephenson and Donna Tart really stood out this year. I read multiple books from both of them and rated every book five stars.

1
2
3
4
5
6
| Author               | My Average Rating | Number of Pages | Number of books |
|----------------------+-------------------+-----------------+-----------------|
| Neal Stephenson      |                 5 |            2693 |               3 |
| Donna Tartt          |                 5 |            1427 |               2 |
| Paolo Bacigalupi     |       3.666666667 |            1113 |               3 |
| Bret Easton Ellis    |               3.5 |             590 |               2 |

2016 Goals

In 2016 I’m planning on reading one or two biographies. That isn’t a genre I typically read. It should be a pretty easy goal to hit. If you have any recommendations please leave them in a comment.

ClojureScript: Treat warnings as errors

| Comments

Recently my team deployed a new version of our ClojureScript UI and it had a minor bug. It was trivial to fix the problem, a ClojureScript build warning pointed us to the cause. As a result we started thinking it would be nice to have build warnings count as errors and fail our ClojureScript build.

We use Leiningen (version 2.5.3) and lein-cljsbuild (version 1.1.1). After some searching we found that lein-cljsbuild supports specifying custom warning handlers as the value to the :warning-handlers key. The lein-cljsbuild README even provides an example, which we took and added a (System/exit 1) to the end of it.

1
2
3
4
5
:warning-handlers [(fn [warning-type env extra]
                     (when-let [s (cljs.analyzer/error-message warning-type extra)]
                       (binding [*out* *err*]
                         (println "WARNING:" (cljs.analyzer/message env s)))
                       (System/exit 1)))]

This worked! Well, it sort of worked. Our build failed whenever there was a warning but now we were seeing spurious warnings. We saw “Use of undeclared Var” warnings when functions created in a letfn where calling each other. Definitely not a situation that warrants a warning and definitely not a build failure.

We weren’t seeing this warning before so we opened ClojureScript’s source and found the default warning handler. The default handler checks that warning-type has a truthy value in the map *cljs-warnings*. Inspired by the default handler we added the same check to the start of our warning handler.

1
2
3
4
5
6
:warning-handlers [(fn [warning-type env extra]
                     (when (warning-type cljs.analyzer/*cljs-warnings*)
                       (when-let [s (cljs.analyzer/error-message warning-type extra)]
                         (binding [*out* *err*]
                           (println "WARNING:" (cljs.analyzer/message env s)))
                         (System/exit 1))))]

Success! Now we no longer get incorrect warnings when compiling our letfn form and our build still fails if a warning occurs. Now we can build and deploy with a little more confidence.

Even quicker feedback from your Clojure tests

| Comments

I was recently inspired by a post on a mailing list to make the TDD cycle with clojure.test and lein-test-refresh even faster. lein-test-refresh is a Leiningen tool that monitors your Clojure project’s source, reloads changes, and then runs your tests. Tools like it provide some of the fastest feedback cycles possible.

To make the feedback cycle even faster I added the option to only run tests in changed namespaces. This means you’re running the minimum number of tests after a change. Version 0.12.0 of lein-test-refresh was released earlier this week with this feature.

To use it add [com.jakemccrary/lein-test-refresh 0.12.0] as a plugin to your profiles.clj or project.clj. An example project.clj can be found in the project’s GitHub repo.

Once you’re on the latest version you can toggle this feature from the command line by providing a :changes-only flag, lein test-refresh :changes-only, or by adding :changes-only true to your :test-refresh configuration section in your project.clj or profiles.clj. When the feature is on you can still run all your tests by hitting enter in the terminal running lein test-refresh.

Below is an example of the time difference between running all my tests and the tests in a single namespace.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Ran 49 tests containing 219 assertions.
0 failures, 0 errors.

Passed all tests
Finished at 14:42:41.655 (run time: 2.006s)
*********************************************
*************** Running tests ***************
:reloading (lumanu.utils-test)

Ran 1 tests containing 3 assertions.
0 failures, 0 errors.

Passed all tests
Finished at 14:43:12.648 (run time: 0.085s)

I’ve been using this feature for about a week now and am enjoying it. My whole test suite isn’t particularly slow but even still I’ve been enjoying the faster feedback.

SQL: Aggregate a set of values together

| Comments

Lately I’ve been working on projects that use Postgres as our relational database. This has allowed us to simplify some of our Clojure code by leaning on some built-in features of Postgres. One SQL function supported by Postgres which has greatly simplified our code is the array_agg aggregate function.

What is array_agg?

The array_agg function takes an argument and returns an array of the argument type. That sentence will make more sense after an example. The snippet below shows a simplified schema for a blog’s database. There is a table called blog_posts that contains details about posts, a table called categories that has labels that can be applied to blog posts, and a join table called post_categories that links the two previous tables together.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
blog=# select id, title from blog_posts;
 id |    title
----+--------------
  1 | SQL Post
  2 | Clojure Post

blog=# select * from categories;
 id |   name
----+----------
  1 | sql
  2 | emacs
  3 | clojure
  4 | postgres

blog=# select * from post_categories;
 blog_post_id | category_id
--------------+-------------
            1 |           1
            2 |           2
            1 |           4
            2 |           3

Before I learned about array_agg, if I wanted to know how each blog post had been categorized I might have written the following query.

1
2
3
4
5
6
7
8
9
10
11
12
13
select title, name as category
  from blog_posts bp
  join post_categories pc on pc.blog_post_id = bp.id
  join categories c on c.id = pc.category_id
  order by title;


    title     | category
--------------+----------
 Clojure Post | emacs
 Clojure Post | clojure
 SQL Post     | sql
 SQL Post     | postgres

The result is readable but as the number of posts and categories grow it becomes harder to read. The query also doesn’t answer the question, “How are my posts categorized?”, well. The ideal answer is a single row per post that shows the post’s categories. You can use array_agg to get that ideal answer.

1
2
3
4
5
6
7
8
9
10
select title, array_agg(name) as categories
  from blog_posts bp
  join post_categories pc on pc.blog_post_id = bp.id
  join categories c on c.id = pc.category_id
  group by title;

    title     |   categories
--------------+-----------------
 SQL Post     | {sql,postgres}
 Clojure Post | {emacs,clojure}

I find the array_agg version much nicer to read. The result answers the question in a very direct fashion and the query expresses the question well. Everything about the query expresses the question, you no longer have an extra order by clause to make the result more readable by human eyes.

How did it make my Clojure code simpler?

The above is great and it makes everything more readable for a human. Most of the time I’m not querying a SQL database so that a human can directly read the results; instead I’m using Clojure to manipulate results of a query. Fortunately, array_agg simplifies my Clojure code as well.

I’m working with a schema that has many relationships similar to the above relationship. Continuing with the example from above the snippet below shows the data shape we’d get back from clojure.java.jdbc prior to using array_agg. The data shape we actually want follows.

1
2
3
4
5
6
7
8
9
;; data shape you get from the non-array_agg query.
[{:title "Clojure Post" :category "emacs"}
 {:title "SQL Post" :category "sql"}
 {:title "Clojure Post" :category "clojure"}
 {:title "SQL Post" :category "postgres"}]

;; data shape you want
[{:title "Clojure Post" :categories ["emacs" "clojure"]}
 {:title "SQL Post" :categories ["sql" "postgres"]}]

Since we’re not getting data in our desired shape we need to write code that combines rows. One way of doing that is to use reduce and map.

1
2
3
4
(defn squash-by-title [rows]
  (->> rows
       (reduce (fn [r row] (update r (:title row) conj (:category row))) {})
       (map (fn [[title categories]] {:title title :categories categories}))))

I’ve been writing Clojure for a long time and when I see code like above it still takes me a bit of time to figure out what is happening. Not only that, but eventually your project has different squash operations depending on what data you’re pulling back from the database. They are probably mostly similar and eventually you abstract the differences and feel great. Then you come back months later and have to figure out how it all works. Luckily, if you’re using a database that supports array_agg, there is a better way.

The first step is to change your queries to use array_agg. The second step is to extend the clojure.java.jdbc/IResultSetReadColumn protocol to the type returned by your jdbc driver. For my project that looks like the following code:

1
2
3
4
5
6
;; clojure.java.jdbc has been required as jdbc

(extend-protocol jdbc/IResultSetReadColumn
  org.postgresql.jdbc4.Jdbc4Array
  (result-set-read-column [pgobj metadata i]
    (vec (.getArray pgobj))))

By changing my queries to use array_agg and adding those four lines of code I’m able to delete all of my squashing functions and get data from my database in the shape I want. I also end up with easier to understand code and more expressive queries. Awesome.

Thanks to Timothy Pratley for providing feedback on earlier versions of this post.

GitHub Code Reviews

| Comments

Last December I wrote about the effective code review process I started at Outpace. The process works well; participants say it is the most effective review process they’ve experienced. The rest of this post is a summary of the process with a bit of an enhancement around setting up the code for review. I’d recommend you read the original post for a bit more color on the process.

Steps for GitHub code review

  1. Select the code to review.
  2. About a week before the review, create a branch and delete the code you’re reviewing.
  3. Push this branch to GitHub and open a pull request. This pull request provides a location where comments can be made on every line of code.
  4. Schedule the code review meeting. Make sure participants have two to three days to asynchronously review the code in the pull request.
  5. Have the code review. Get everyone together (video chat or in person) and go through the comments on the pull request and discuss. Add action items as a comment. The leader of the code review keeps discussion moving.

It’s a lightweight process. If you’re already using GitHub it doesn’t bring in any other tools and, unlike some dedicated code review software I’ve used, the GitHub pull request interface has good performance.

One complaint about this process is that the code you’re reviewing appears as deleted in the pull request. It is a superficial complaint but seeing the entire code base as deleted can feel a bit weird.

For the most recent code review, I figured out how to have all the code appear as added. The snippet below contains the steps and example commands.

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
# cd to the repository you are reviewing.
cd blog

# Make a new branch.
git checkout -b empty-repo

# Copy all files in repo to a temporary directory.
rm -rf /tmp/repo && mkdir /tmp/repo && cp -R * /tmp/repo

# Remove all files from repository, commit, and push to GitHub.
rm -rf *
git commit -am 'remove all files'
git push origin empty-repo

# Create a new branch with the empty-repo as the parent.
git checkout -b code-review

# Copy back in the files and add the files you want to review.
# Commit and push to GitHub.
cp -R /tmp/repo/* .
git add files-to-review
git commit -m 'adding files for review'
git push origin code-review

# Now, go to project on GitHub and switch to the code-review branch.
# Open a pull request comparing the empty-repo and the code-review
# branch.

Voila, you now have a pull request with every line under review marked as added instead of deleted! It takes a little more than two times the number steps required to open a pull request with the code deleted but you might find it worth it. Seeing code as added instead of removed is a minor thing but minor things can make a process more enjoyable. It is nice to know it is possible.

If you aren’t doing code reviews or have found them useless in the past, I recommend you try out this process. This post is the abbreviated version but it gives you enough to get started. If you haven’t done one in this style before, I’d highly recommend reading the longer post as it gives some details that I’ve left out here.

My favorite clj-refactor features

| Comments

If you write Clojure using Emacs you should check out clj-refactor. It is working better than ever and makes developing Clojure more enjoyable.

I don’t use all the features in clj-refactor. There are a lot of features I haven’t had the need to use and many I just can’t remember. Below are the features I use consistently.

Favorite Features

My favorite feature of clj-refactor is the magic requires. This feature lets you type a prefix (such as (str/)) and have the namespace automatically added to your ns form (in this example [clojure.string :as str]). It is awesome. You can also add your own prefix mappings.

My other most frequently used refactorings are introduce let, expand let, and move to let. These three are very complementary and are a quick way if introducing named locals.

Add missing libspec is a recent discovery of mine. Have you ever paired with a developer who uses Intellij with Cursive and been a bit jealous of the auto-requiring? I have. This refactoring lets you do that. Type whatever symbol you want and clj-refactor tries to resolve it and then require the containing namespace with correct prefix. Recently I broke a massive namespace into a few smaller ones and this refactoring saved me a ton of time.

I used to use move form when trying to reorganize namespaces but now I pretty much just cut and paste and use add missing libspec to fix the requires. I want to use move form but I haven’t had a ton of success with it. Add missing libspec plus cut and paste is a few more steps but my success rate has been much higher.

Sort ns does exactly what it says, it sorts your ns form. Once you get used to keeping your ns forms sorted you won’t go back.

Extract function is another refactoring I recently stumbled upon. I’ve used it a few times since then and when it works it is pretty awesome. I’ve had unexpected behavior a couple of times but it was unclear if that was my fault or it not handling macros well. If you’re extracting a function you might as well give it a shot.

The final feature is the automatic insertion of namespace declarations when you create a new Clojure file. I nearly forgot to highlight this feature because it requires no action on my side and it is amazing. If I never have to type a namespace symbol again I’ll be happy.

Customization

Below is my entire clj-refactor setup from my Emacs init.el. It doesn’t take much to get it to a state I like.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(require 'clj-refactor)

;; Add custom magic requires.
(dolist (mapping '(("maps" . "outpace.util.maps")
                   ("seqs" . "outpace.util.seqs")
                   ("times" . "outpace.util.times")
                   ("repl" . "outpace.util.repl")
                   ("time" . "clj-time.core")
                   ("string" . "clojure.string")))
  (add-to-list 'cljr-magic-require-namespaces mapping t))

(setq cljr-favor-prefix-notation nil)

(add-hook 'clojure-mode-hook (lambda ()
                               (clj-refactor-mode 1)
                               (yas/minor-mode 1)
                               (cljr-add-keybindings-with-prefix "C-c C-x")))

If you use Emacs and write Clojure you should check out clj-refactor. There are enough features that consistently work and help keep you in the flow that it is worth using.

Emacs: automatically require common namespaces

| Comments

If you’re writing Clojure in Emacs you should check out clj-refactor. It provides some neat functionality. Some examples include the ability to extract functions, introduce let forms, and inline symbols. It also has a feature called “magic requires” that automatically requires common namespaces when you type their short form.

Out of the box five short forms are supported. They are io for clojure.java.io, set for clojure.set, str for clojure.string, walk for clojure.walk, and zip for clojure.zip. If you type (str/ then (:require [clojure.string :as str]) will be added to your ns form. It is pretty awesome. This feature is on by default but you can turn it off by adding (setq cljr-magic-requires nil) to your Emacs configuration.

This feature is also extensible. You can add your own mappings of short form to namespace. The following snippet of elisp adds mappings for maps, seqs, and string.

1
2
3
4
(dolist (mapping '(("maps" . "outpace.util.maps")
                   ("seqs" . "outpace.util.seqs")
                   ("string" . "clojure.string")))
  (add-to-list 'cljr-magic-require-namespaces mapping t))

It doesn’t take a lot of code but having it is awesome. If there are namespaces you frequently require I highly recommend setting this up.

Use git pre-commit hooks to stop unwanted commits

| Comments

Sometimes you’ll make a change to some code and not want to commit it. You probably add a comment to the code and hope you’ll either see the comment in the diff before committing or just remember not to check in the change. If you’ve ever done this you’ve probably also committed something you didn’t mean to commit. I know I have.

Luckily we can do better. Using git pre-commit hooks we can make git stop us from committing. Below is a git pre-commit hook that searches for the text nocommit and if found rejects the commit. With it you can stick nocommit in a comment next to the change you don’t want committed and know that it won’t be committed.

The code

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
#!/bin/sh

if git rev-parse --verify HEAD >/dev/null 2>&1
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=$(git hash-object -t tree /dev/null)
fi

patch_filename=$(mktemp -t commit_hook_changes.XXXXXX)
git diff --exit-code --binary --ignore-submodules --no-color > $patch_filename
has_unstaged_changes=$?

if [ $has_unstaged_changes -ne 0 ]; then
    echo "Stashing unstaged changes in $patch_filename."
    git checkout -- .
fi

quit() {
    if [ $has_unstaged_changes -ne 0 ]; then
        git apply $patch_filename
        if [ $? -ne 0 ]; then
            git checkout -- .
            git apply $patch_filename
        fi
    fi

    exit $1
}


# Redirect output to stderr.
exec 1>&2

files_with_nocommit=$(git diff --cached --name-only --diff-filter=ACM $against | xargs grep -i "nocommit" -l | tr '\n' ' ')

if [ "x${files_with_nocommit}x" != "xx" ]; then
    tput setaf 1
    echo "File being committed with 'nocommit' in it:"
    echo $files_with_nocommit | tr ' ' '\n'
    tput sgr0
    quit 1
fi

quit 0

Lines 3-10 figure out what revision to diff against. They can pretty much be ignored.

Lines 11-30 are all about handling unstaged changes. They create a patch with these changes and revert these changes from the repository. Then, in the function quit, the unstaged changes are reapplied to the repository. All of this is done so that nocommit in a un-committed piece of text doesn’t cause the committed changes to be rejected.

Some online guides suggest using git stash to achieve what is described above. I started out using git stash but ran into problems where I’d end up in weird states. Unfortunately I didn’t take good notes and I’m unable to describe the various bad things that happened. Trust me when I say bad things did happen and that this way (create patch, revert, apply patch) is much more successful.

Line 36 figures out what files contain nocommit. Lines 38-44 report what files contain nocommit and then rejects the commit by exiting with a non-zero exit code. The first tput changes the output of the echo commands to colored red and the second tput changes output back to default.

Using with a single repository

To enable in a single repository you need to add the above code to a .git/hooks/pre-commit file in your local repository and make that file executable. Once you’ve done that try adding nocommit to a file and then try to commit it. The commit will be rejected if the pre-commit hook is setup properly.

Using with multiple repositories

I want this pre-commit hook enabled in all of my repositories. I use git init templates to do this. git help init or a Google search can help fill in the gaps with setting this up but below are the steps I ended up taking.

  1. git config --global init.templatedir ~/.git-templates
  2. mkdir -p ~/.git-templates/hooks
  3. touch ~/.git-templates/hooks/pre-commit
  4. Copy and paste the above code into ~/.git-templates/hooks/pre-commit
  5. chmod +x ~/.git-templates/hooks/pre-commit

After following those steps any repository created by git init will contain the pre-commit hook. To add to an existing repository cd into the repo and run git init ..

Example output

If you try to commit some text with nocommit in it you’ll see something similar to the image below and the commit will be rejected.

Error message

If you ever need to commit and want to ignore pre-commit hooks (example: If you are writing a blog post that is full of the text nocommit) then you can ignore pre-commit hooks by using git commit --no-verify.

I’ve found this pre-commit hook really useful. It has saved me from committing numerous times. I’d recommend adopting it.

Errata

2015/12/23

I’m updated the code to be more portable. It was brought to my attention by a comment that the original code took advantage of some bash extensions and specific mktemp behavior found in OS X. The pre-commit code has now been tested works in OS X and Ubuntu 14.04. There may be minor changes you need to perform to get it to work on your system.