Styling Hugo Diffs

Showing just what you changed

Published January 31, 2020 #howto #hugo

I often want to show small changes I’m making to a file and it would be nice for hugo to support styling patches directly. Lets see what we can do to make this process easier.

Lets take the example of create node package.json file and add the following scripts worflow. How can we say this different than “copy this into your package.json file”?

Create sample steps

Lets first create the file using npm init -y and then immediately cp package.json package.json.base. In this case, we want to add some script attributes, like so:

  "scripts": {
    "build:eleventy": "eleventy",
    "serve:eleventy": "eleventy --serve",
    "debug:eleventy": "DEBUG=* eleventy"

So we can now look at the differences of the files using the diff command. I’m going to have it user the unified format showing 1 line of code on either side.

diff -U 1 package.json.base package.json

Which outputs:

--- package.json.base	2020-01-30 14:38:18.933332377 -0600
+++ package.json	2020-01-30 15:43:37.174332377 -0600
@@ -6,3 +6,5 @@
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "build:eleventy": "eleventy",
+    "serve:eleventy": "eleventy --serve",
+    "debug:eleventy": "DEBUG=* eleventy"

This seems a little better, but how do we keep it up to date?

Bash script

We are going to write a bash script to parse though a files. First lets do that and make sure that we can pull out the base name of the file.


for i in *.base do echo $i is the base file echo ${i%.base} is final file done

That seems to work, now lets loop over the files that match the base name, skipping the files that we don’t want. Once we get to the .base file, we know that we are at the end of our patching, and we need to compare the last stage with the original file instead, so we’ll check for that and update the variables so that the stage file is actually the current one.

@@ -4,4 +4,22 @@
-    echo $i is the base file
-    echo ${i%.base} is final file
+    last_file="$i"
+    final_file="${i%.base}"
+    for stage in ${final_file}.*
+    do
+	# Strip out emacs junk
+	if [[ $stage == *~ ]]; then
+	    continue
+	fi
+	# We are at the end!
+	if [[ $stage == *.base ]]; then
+	    stage=$final_file
+	fi
+	echo Need to patch $last_file to make $stage
+	last_file=$stage
+    done
+    echo

The logic seems to work, so lets add the actual diff creation. But the diff files match our pattern, so we’ll need to add a check to skip over them when we loop over the glob!

@@ -19,3 +19,13 @@
-	echo Need to patch $last_file to make $stage
+	# Skip parsing of diff files
+	if [[ $stage == *.diff ]]; then
+	    continue
+	fi
+	echo Creating $stage.diff
+	# -Z Ignores space
+	# -U 1 sets to the unified patch format with one surrounding lines
+	# Setting --label directly lets use avoid timestamps
+	diff -Z -U 1 --label $last_file --label $stage $last_file $stage > $stage.diff


The idea here is that you are working on an explainable chunk of work. You start by getting a base file that everyone can work off of. For example, run npm init -y and then copy that file to package.json.base. Then make your changes and run to generate the patch, which will initially just compare package.json.base and package.json

Later you want to make another change to the file. Before you edit package.json make a copy to package.json.1, and make your changes again. Then run which will regenerate all the diffs for each file. Then write about it, and repeat the process as necessary.

Displaying the patch files easily

What I want to do in while writing markdown is to do something like

If we want to show the base file:

{{ diff “package.json” }}

text here

{{ diff “package.json” “1” }}

text here

{{ diff “package.json” “final” }}

in my content file, and which will figure out the correct diff to inline it.

Here is a hugo short code that you can drop in your /layouts/shortcodes/ folder that will make that possible.

Embedding shortcodes as an example inside of hugo is a pain, so I’ll walk you through the logic but we wont’ be looking at the code.

  1. Pull out the parameters to make it easier to work with.
  2. $diff is going to contain the file contents, initialize it to blank in a scope that everyone can see.
  3. If $stage is set and is final, we append “.diff” to a file – which we assume is in the same directory as the Page that is calling the short code – and readFile.
  4. If $stage is set but not final, append “.$stage.diff” to the filename and load.
  5. If $stage is blank load “$filename.diff”. This is to avoid expanding to ..diff which has an extra ..
  6. Markdownify a link to the file directly and pass to markdownify
  7. Markdownify the actual file, formatting is as a diff

What it looks like in action

Lets first start out by creating an empty project:

  1. mkdir projectname && cd projectname
  2. npm init -y
  3. Add the following script entries to the generated package.json


--- package.json.base
+++ package.json.1
@@ -6,3 +6,5 @@
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "build:eleventy": "eleventy",
+    "serve:eleventy": "eleventy --serve",
+    "debug:eleventy": "DEBUG=* eleventy"

Oh, we forgot to install eleventy! Let’s do that now with npm install --save-dev @11ty/eleventy which should modify your package.json like so:


--- package.json.1
+++ package.json
@@ -12,3 +12,6 @@
   "author": "",
-  "license": "ISC"
+  "license": "ISC",
+  "devDependencies": {
+    "@11ty/eleventy": "^0.10.0",
+  }

Read next

See also

Splitting Git Repos and Work Directories

all the fun things git can do

I found a tutorial on how to manage your dotfiles, that works by splitting up the git repository (normally the .git directory) from the work directory. Since I have a lot of code that I put in my tutorials, I adapted the technique to have individual article directories mirrored in their own github repository. Repositories and Work Directories The normal usage of git is to type git clone <remote> to get a copy of the local directory, mess with stuff, and then add and commit your changes.

Read more

Easy scraping with httpie and jq

Pulling my GitHub starred repositories into Hugo

I recently saw a tweet mentioning the combination of using HTTPie (a command line HTTP client), jq (a lightweight and flexible command-line JSON processor) and Gron (Make JSON greppable!) was “all you needed to build a scraper.” Lets see if that’s true.

Read more

Automating hugo builds using CircleCI

Let someone else run your build server

Here's a simple CircleCI configuration to pull down the latest version of your hugo site on GitHub commits, build it, and then push it to github pages.

Read more