jekyll-last-commit
Jekyll generator plugin and liquid tag to access the last commit information for a file.
Provides performant access to page.last_modified_at for Jekyll sites with very large page counts (100s+).
Use cases for a site page, document, static file, or data file:
- accessing the last commit date
- accessing the last committer name, email, and other metadata
Inspired by the work done at gjtorikian/jekyll-last-modified-at and aimed at improved performance. Seeks to be drop-in replacement. Uses libgit2/rugged rather than spawning a process.
Important:
- ignores commits where a file has been renamed without content changes
- has not been tested on Windows
- if your git repo uses SHA-256: the version of libgit2 that rugged uses must also support SHA-256
- as of 2024 October, for the current libgit2 stable release v1.8.1, that means building libgit2 with -DEXPERIMENTAL_SHA256
Example Usage
The following is the last (most recent) commit from a repo:
main 5fde57927efdb2f440dd40c802687b60384e5d9d
Author: Kip Landergren <klandergren@users.noreply.github.com>
AuthorDate: Fri Dec 16 18:30:53 2022 -0800
Commit: Kip Landergren <klandergren@users.noreply.github.com>
CommitDate: Fri Dec 16 18:30:53 2022 -0800
add new pages to the site
Every page that was created or modified by this commit will get access to the commit’s information:
| usage | rendered |
|---|---|
{{ page.last_commit.sha }} |
5fde57927efdb2f440dd40c802687b60384e5d9d |
{{ page.last_commit.author.name }} |
Kip Landergren |
{{ page.last_commit.author.email }} |
klandergren@users.noreply.github.com |
{{ page.last_commit.author.time }} |
2022-12-16 18:30:53 -0800 |
{{ page.last_commit.committer.name }} |
Kip Landergren |
{{ page.last_commit.committer.email }} |
klandergren@users.noreply.github.com |
{{ page.last_commit.committer.time }} |
2022-12-16 18:30:53 -0800 |
{{ page.last_commit.message }} |
add new pages to the site |
{{ page.last_commit.time }} |
2022-12-16 18:30:53 -0800 |
{{ page.last_commit.time_epoch }} |
1671244253 |
{{ page.last_modified_at }} |
2022-12-16 18:30:53 -0800 |
{{ page.last_modified_at | date: '%F' }} |
2022-12-16 |
{{ page.last_modified_at_formatted }} |
December 16, 2022 |
{% last_modified_at %} |
December 16, 2022 |
{% last_modified_at %F %} |
2022-12-16 |
Additionally, last commit info is accessible for any data file via site.data.meta[data_file] with the same API:
| usage | rendered |
|---|---|
{{ site.data.meta[data_file].last_commit.sha }} |
5fde57927efdb2f440dd40c802687b60384e5d9d |
{{ site.data.meta[data_file].last_commit.author.name }} |
Kip Landergren |
{{ site.data.meta[data_file].last_commit.author.email }} |
klandergren@users.noreply.github.com |
{{ site.data.meta[data_file].last_commit.author.time }} |
2022-12-16 18:30:53 -0800 |
{{ site.data.meta[data_file].last_commit.committer.name }} |
Kip Landergren |
{{ site.data.meta[data_file].last_commit.committer.email }} |
klandergren@users.noreply.github.com |
{{ site.data.meta[data_file].last_commit.committer.time }} |
2022-12-16 18:30:53 -0800 |
{{ site.data.meta[data_file].last_commit.message }} |
add new pages to the site |
{{ site.data.meta[data_file].last_commit.time }} |
2022-12-16 18:30:53 -0800 |
{{ site.data.meta[data_file].last_commit.time_epoch }} |
1671244253 |
{{ site.data.meta[data_file].last_modified_at }} |
2022-12-16 18:30:53 -0800 |
{{ site.data.meta[data_file].last_modified_at | date: '%F' }} |
2022-12-16 |
{{ site.data.meta[data_file].last_modified_at_formatted }} |
December 16, 2022 |
Every page has their own last commit info computed during rebuild.
Installation
Add to your Gemfile:
group :jekyll_plugins do
gem "jekyll-last-commit"
endand run bundle install.
Configuration
All of the following are optional:
jekyll-last-commit:
date_format: '%F' # default: `%B %d, %Y`
# if a commit is not found `File.mtime` is used
should_fall_back_to_mtime: false # default: `true`
# enable processing of data and static files
index_data_files: true # default: false
index_static_files: true # default: false
# information about data files is stored in a separate site.data hash
data_files_key: 'meta' # default: metaThe use case for should_fall_back_to_mtime is so that rendering of a file that is not yet tracked by git looks correct (e.g. a new, uncommitted blog post).
Date Format Directives
See Time#strftime documentation for available date format directives.
Examples
| format | example output |
|---|---|
default (%B %d, %Y), via {{ page.last_modified_at_formatted }}
|
December 11, 2022 |
{{ page.last_modified_at | date: '%c' }} |
Fri Dec 16 18:30:53 2022 |
{{ page.last_modified_at | date: '%F' }} |
2022-12-16 |
{{ page.last_modified_at | date: '%D' }} |
12/16/22 |
{{ page.last_modified_at | date: '%v' }} |
16-DEC-2022 |
{{ page.last_modified_at | date: '%r' }} |
06:30:53 PM |
{{ page.last_modified_at | date: '%R' }} |
18:30 |
{{ page.last_modified_at | date: '%T' }} |
18:30:53 |
Documentation
-
page.last_commit-
page.last_commit.authorpage.last_commit.author.emailpage.last_commit.author.namepage.last_commit.author.time
-
page.last_commit.committerpage.last_commit.committer.emailpage.last_commit.committer.namepage.last_commit.committer.time
page.last_commit.messagepage.last_commit.shapage.last_commit.timepage.last_commit.time_epoch
-
page.last_modified_atpage.last_modified_at_formattedsite.data.meta[data_file].last_commitsite.data.meta[data_file].last_modified_atsite.data.meta[data_file].last_modified_at_formattedlast_modified_at
page.last_commit
Gives access to the underlying rugged commit object information.
Important: ignores commits where a file has been renamed without content changes
| field | type | usage |
|---|---|---|
| author |
Hash object |
see page.last_commit.author
|
| committer |
Hash object |
see page.last_commit.committer
|
| message | String |
{{ page.last_commit.message }} |
| sha | String |
{{ page.last_commit.sha }} |
| time |
Time object |
{{ page.last_commit.time }} |
| time_epoch | Integer |
{{ page.last_commit.time_epoch }} |
page.last_commit.author
Information about the author of the last commit for this file.
| field | type | usage |
|---|---|---|
String |
{{ page.last_commit.author.email }} |
|
| name | String |
{{ page.last_commit.author.name }} |
| time |
Time object |
{{ page.last_commit.author.time }} |
page.last_commit.committer
Information about the committer of the last commit for this file.
| field | type | usage |
|---|---|---|
String |
{{ page.last_commit.committer.email }} |
|
| name | String |
{{ page.last_commit.committer.name }} |
| time |
Time object |
{{ page.last_commit.committer.time }} |
page.last_modified_at
The Time object associated with the last commit for this file.
Example default output: 2022-12-11 19:54:26 -0800
Can be formatted using a liquid date filter:
{{ page.last_modified_at | date: '%B' }}
output:
December
page.last_modified_at_formatted
The formatted string of the Time object associated with the last commit for this file. Format controlled via _config.yml.
Default format: %B %d, %Y
Example default output: December 11, 2022
Override in _config.yml via:
jekyll-last-commit:
date_format: '%F'If you need a per-page date format, use {{ page.last_modified_at | date: '%F }}' with whatever format string you want.
site.data.meta[data_file].last_commit
-
data_fileis the name of the file within_data/, e.g.foo.json -
.yml,.yaml,.json,.tsv, and.csvsupported
See page.last_commit for further information.
site.data.meta[data_file].last_modified_at
-
data_fileis the name of the file within_data/, e.g.foo.json -
.yml,.yaml,.json,.tsv, and.csvsupported
See page.last_modified_at for further information.
site.data.meta[data_file].last_modified_at_formatted
-
data_fileis the name of the file within_data/, e.g.foo.json -
.yml,.yaml,.json,.tsv, and.csvsupported
See page.last_modified_at_formatted for further information.
last_modified_at
A liquid tag that renders the formatted date using either the passed date format string, what was specified in _config.yml, or the default %B %d, %Y:
<p>{% last_modified_at %}</p>
<p>{% last_modified_at %F %D %}</p>Added solely to be drop-in replacement with gjtorikian/jekyll-last-modified-at.
Performance
Comparison made to gjtorikian/jekyll-last-modified-at on a 2017 MacBook Pro running a 3.1 GHz Quad-Core Intel Core i7
site with ~1400 pages and ~2500 commits
generation command (note: no use of ---incremental):
$ JEKYLL_ENV=development bundle exec --gemfile=./static-site/Gemfile jekyll serve --port 4001 --source ./static-site --destination /tmp/_site_development| case | baseline | jekyll-last-modified-at | jekyll-last-commit | improvement |
|---|---|---|---|---|
| initial generation | 16.480 s | 79.601 s | 22.447 s | ~71% improvement |
| subsequent generation | 15.727 s | 78.200 s | 20.739 s | ~73% improvement |
How It Works
Walks the commit history until all documents and pages are matched to a commit. Falls back to File.mtime when no commit found. Runs fresh on each site generation.
FAQ
Why not just improve gjtorikian/jekyll-last-modified-at ?
See improving render performance via PATH_CACHE usage and bulk git log ... call #85 which includes two PRs more in line with that repository’s architecture and conventions.
Why not fork gjtorikian/jekyll-last-modified-at ?
Grabbing data via libgit2/rugged would be too big of a rewrite for what is a very popular plugin. If folks have performance issues getting page.last_modified_at they can safely compare their options using this!
Will this work with GitHub Pages?
- It does not work with the old-style GitHub Pages integration, which doesn't allow third-party plugins.
- It does not work with GitHub's https://github.com/actions/jekyll-build-pages action, which does not allow third-party gems either.
- It works with the official Jekyll GitHub Action but needs you to configure the
actions/checkoutstep withfetch-depthof 0 to ensure all Git history is fetched.
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0