While reading blog posts from other static site generator (SSG) users, I sometimes see that a post includes a link to the specific Git commit for that post’s most recent update. In this post, I’ll show you how to do it in a Hugo site, in case you’re interested in doing the same. As an additional benefit, it’ll automate something you might have been doing manually up to now.
I got the idea yesterday, when I saw a post from Aleksandr Hovhannisyan. In it, he gave a fine tutorial about displaying this data in pages built with the Eleventy SSG. Hovhannisyan’s method employed JavaScript to fetch the necessary Git data for use by his Eleventy templates.
On the other hand: with a Hugo site, things are much easier, thanks to the built-in availability of Git info variables. Once you set Hugo to fetch these variables, they’re available from within a .GitInfo
object.
In your project config file, set enableGitInfo
to true
(here, I’m showing the Hugo default of TOML, although my own config file is actually YAML):
enableGitInfo = true
As the setting’s name implies, this activates the presence of the Git info variables.
I’ll get to the part about displaying commit info shortly but, first, let’s note that making this setting may have just liberated you from a nit-picking duty involved in how you display your posts’ dates. If you’ve been using manual entries in your posts’ front matter to indicate when they were last modified, you no longer have to do that. The Git info will, by default, provide this data as .Lastmod
.1
However, in production, you will need to deploy your site using a GitHub Action (or other CI/CD), as I've been doing lately. The problem is that, although these automated .Lastmod
indications will be correct when you’re developing locally with hugo server
, they’ll all take on the current date when you deploy. Fortunately, there’s an explanation and solution, from a thread2 on the Hugo Discourse forum:
By default, the GitHub “checkout” action only fetches a single commit (for the ref/SHA that triggered the workflow). This results in the behavior you describe — i.e.[,] the current date/time is used for
.Lastmod
.If you modify the checkout action to fetch the entire history (by specifying
fetch-depth: 0
), then.GitInfo
and.Lastmod
[work] as expected[.]
[Stylistic edits and one link applied.]
This is because (a.) hosts’ Git configurations for builds typically are set to so-called shallow-clone behavior; and, apparently, (b.) no host allows altering this in either its built-in UI or any optional config files (e.g., netlify.toml
with Netlify or vercel.json
with Vercel). Shallow-clone behavior causes problems with using .GitInfo
data as described in this post, so keep this in mind if you typically deploy via your host’s built-in user interface rather than with CI/CD.
Note from the future: In testing for a later article, I found that, in fact, Netlify and DigitalOcean App Platform exhibit deep-clone behavior where Git is concerned, so you likely can use the information herein with their native UIs rather than having to deploy to them via CI/CD.
To make use of this answer in a GitHub Action, you could just add a with
section to the GitHub Action’s Checkout default branch
step:
- name: Checkout default branch
uses: actions/checkout@v3
with:
fetch-depth: 0
. . . and, indeed, that fixes the glitch.
You might also want to test for whether a post’s day of original publication and its “last-modified” day are the same --- e.g., when you fix a typo or otherwise edit something while it’s still the same day as when you first issued the post --- and, if so, show only the “original-pub” listing, to avoid duplication. However, this requires comparing the formatted dates, since full timestamps clearly can never be the same down to the nanosecond; so here’s how you could accomplish this in an applicable template:
<p>
<strong>{{ .PublishDate.Format "January 2, 2006" }}</strong><br />
{{- if ne (.PublishDate.Format "January 2, 2006") (.Lastmod.Format "January 2, 2006") }}
Last modified {{ .Lastmod.Format "January 2, 2006" }}
{{- else -}}
{{- end -}}
</p>
Within the paragraph, if the two are not equal (ne
), this shows the “Last modified” statement; otherwise, it just inserts a non-breaking space so the height of the line will be the same.
That takes care of Git info for dates; but what about the original subject of this post, namely how you can link to a page’s most recent Git commit?
Well, the .GitInfo
object also provides two variables for each commit’s hash: the full hash (.Hash
) and the more familiar abbreviated hash (.AbbreviatedHash
). Adding this within the proper templates is pretty matter-of-fact. The following example displays .AbbreviatedHash
while the link is an exampleuser
repo link plus /commit/
plus .Hash
:
<p>
{{- if $.GitInfo -}}
<strong>Latest commit</strong>: <a href="https://github.com/exampleuser/hugo_site/commit/{{ .GitInfo.Hash }}" target="_blank" rel="noopener">{{ .GitInfo.AbbreviatedHash }}</a>
{{- else -}}
{{- end -}}
</p>
(The if $.GitInfo
conditional prevents hugo server
errors during local development while you’re working on content you haven’t yet committed.3 You can thank another Hugo Discourse forum answer for that one.)
And, yes, the previous advisory still applies: i.e., you have to deploy to your host via CI/CD in order to see the correct Git commit data for each post.
So, now, you’ve automated both (a.) displaying that Lastmod
stuff and (b.) linking to the commit from which each page’s latest version originates. Yet, you needed no additional tools, and very little extra code, to do it. Not bad for a few minutes’ worth of work in Hugo, eh?
Update, 2022-10-07: If you’re interested in displaying both per-page Git info and whole-site Git info in your Hugo site, check the solution suggested by Hugo expert/contributor Joe Mooring. Thanks to Rodrigo Alcaraz de la Osa for the Q&A session that led me toward this additional information!
-
By default, Hugo will give higher priority to the Git info variable
.Lastmod
vs. other possibilities—including any manualLastmod
entries you may have already provided in your content’s front matter. ↩ -
The original question dates from December 25, 2019, but it took another 21 months before an answer, much less the answer, appeared. Jeeeez. ↩
-
The reason you don’t have to do this with the dates-display mentioned earlier is because, in that case, we’re simply using
Lastmod
(a variable Hugo already knows) rather than, specifically,.GitInfo.Lastmod
, the absence of which for a given page gives Hugo fits. ↩
Top comments (2)
Nice information Bryce 🙂, if someone is not familiar with Git or Github they can refer to this blog as well.
Here's the link 👉 Git and Github Tutorial: Beginner to Advanced
Thanks, and good luck with that series!