Put latest git info in an HTML comment

It was not really an urgent need, but I thought it would be nice to get the git info for a given content .md file, and put that into an HTML comment, inside the <head> section. More or less for better troubleshooting. Here’s how to do it.

Rejected solution

There’s one method I tried, which I abandoned, because of dependency on OS environment variables. This is the bash script snippet for that, which creates a bunch of environment variables:

for f in /dev/path/to/content/post/.md; do export $(basename $f .md|tr - |tr -d 0-9|sed 's///')="$(git log --pretty="%h %s" -1 $f)"; done

It loops through the markdown files, gets the name, manipulates it, and puts that and the latest git info into an environment variable. I put the date in my filenames, because I want them to sort on that so I can find them. So this bash script needed to allow for my filename structure, and convert the filename into something that can be used as a variable. My intention was to use these in a template, using the getenv function. It was a bit problematic to test, so, I decided to try a different tack.

Working Solution

Anyway, I gave up on the env vars, and switched to another method, the gist of which was to create a manifest in a yaml file in data, and use index against that to grab the latest info. It’s more portable anyway, assuming someone can do the below with some windows batch.

The following bash shell script needs to be run before hugo server is run, if you want the latest info. Therefore I imagine the best time to run it is right before you deploy.

This is the working script:

:\>| /dev/path/to/data/postgitinfo.yaml && for f in /dev/path/to/content/post/*.md; do echo "$(cat $f|grep slug|sed 's/slug: //'): $(git log --pretty="%h %s" -1 $f)" \>\> /dev/path/to/data/postgitinfo.yaml; done

Some points about it. It’s conveniently crammed all on one line. :wink:

  • The :\>| is a relatively portable way (on *nix) to clear the content of a file, in this case the manifest yaml. Just pipe the :\> to the file.
  • The next bit is a for in do done which loops through all your markdown files. It’s fiddly about placement of ;s.
  • The do clause uses $() because it’s safer to use than backticks (i.e. `somecmd`). It just means execute what’s between the parentheses.
  • The do echoes a couple commands to the manifest with a colon between, so it is yaml format. Specifically, the echo has two sub commands: it cat’s the file to grep, grep looks for the line with slug in it, then sed removes the leading "slug: " from that. Then the git log command finds the latest sha (%h is the short form, %H is the long form) and subject of the commit message (i.e. the top line).
  • All that is appended (each iteration of the loop) to the manifest.

The assumptions are:

  • you’re using a system that speaks bash (this works on zsh as well)
  • you’re using git, obviously
  • your posts are files with .md extensions, which have yaml frontmatter.
  • yaml frontmatter has slug defined.
  • slug is not defined twice (like in a code example or something)

When you run this it refreshes the yaml file. I’m going to put it in an alias.

Access the data from a template

Now that the manifest is created, you can access it from a template. I put mine in a partial I am calling from my head template.

{{ "<!-- ENTERING partial headend.html -->" | safeHTML }}
{{ $postgitinfo := (index $.Site.Data.postgitinfo .Slug ) }}
{{ (print "<!-- LATEST GIT UPDATE: " $postgitinfo " -->") | safeHTML }}
{{ "<!-- LEAVING partial headend.html -->" | safeHTML }}

A couple of points about this:

  • the first and last lines are simply putting HTML comments at the beginning and the end of the partial. You don’t have to do this but I find it is helpful for troubleshooting, to see what content is coming from what template.
  • Line two is using index to parse the yaml manifest (I just put it in the root of data). It’s using the same .Slug that the batch script grabbed, so, it should match up. It sticks that in a variable.
  • Line three uses print to prepend and append the HTML comment start and end tags, around the variable created in the line before. Simple string concatenation.

Then when we look at the HTML source of the page, this information is in there, et voilà.

<!-- ENTERING partial headend.html -->
<!-- LATEST GIT UPDATE: de9911e Update post, correct typo, add images -->
<!-- LEAVING partial headend.html -->

It’s nifty, so I thought someone might like to try it. If you figure out how to do it on Windows or find any glitches, add that in the comments, by all means.


Thank you. While this approach is probably no longer needed for content files, it works for grabbing git info from other files - in my case, images

see Display GitInfo for Files

I ran into a couple of glitches, I guess due to slight differences across 'nix. version

Using Ubuntu 17.04 I used:

  • > instead of :\>| to clear the manifest file
  • >> instead of \>\> to append output to the manifest file

Getting this to work for me, I also learned

  • the 2 ;s allow the for … in … do … done loop to be written on one line
  • that instead of finding slug within files, I used echo ${f##*/} to output the filename without the path
  • to add git date || author || commit message I used git log --date=format:"%e %b, %Y" --pretty="%ad || %an || %s" -1 $f ( when generating the page in hugo I replace the || with <br> )

This is what I am using in one line.
cd /dev/path/to/ && > data/imagesgitinfo.yaml && for f in /dev/path/to/static/img/*.*; do echo ${f##*/}: "$( git log --date=format:"%e %b, %Y" --pretty="%ad || %an || %s" -1 $f)" >> data/imagesgitinfo.yaml; done

Thank you so much

1 Like

Thanks for feeding back. I’m not sure why I escaped the ‘>>’ .

@Giles I was trying to apply your one-liner but I ran into trouble making it work recursively… eventually I was able to achieve that with for in $(find /dir -name *.adoc)

But the real killer is getting this to work with spaces in filenames… after much bashing and stackoverflowing, I’m still unable to do it.

Do any of you know how? Thanks in advance…

You can now run with --enableGitInfo (or enable it in site config), and you will have a .GitInfo object per page.

1 Like

Ah, the joy of working with Hugo. :smile:

This follows the Second Law of Hugo perfectly:

If it should be there, it is.

Obviously, in this case it is an option that needs to be turned on explicitly, so that it doesn’t break the First Law of Hugo:

If it shouldn’t be there, it isn’t, because it might be slow Hugo down.

Very :cool: stuff, thanks!

1 Like

Yea, in this case it is a big difference in build time with and without for bigger projects/Git histories.

Hugo keeps improving. Now this is supported within Hugo it should be much easier, more reliable, and portable. Relying on a bash script felt fragile.

Personally I ensure that filenames are lowercase with simple alphanumeric characters and “-” so I can easily move them between operating systems. So I never ran into that problem.


I have a challenge for the people in this thread - can we get a link to the “diff” between the latest edits?

GitHub does it, you see it all the time when looking at PR’s. But I’m not sure GitHub provides a way to link to a diff, based on the file name + initial commit + final commit.

It would be nice to show a list of edited content, and then let people see exactly what those edits changed.