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"
end
and 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: meta
The 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.author
page.last_commit.author.email
page.last_commit.author.name
page.last_commit.author.time
-
page.last_commit.committer
page.last_commit.committer.email
page.last_commit.committer.name
page.last_commit.committer.time
page.last_commit.message
page.last_commit.sha
page.last_commit.time
page.last_commit.time_epoch
-
page.last_modified_at
page.last_modified_at_formatted
site.data.meta[data_file].last_commit
site.data.meta[data_file].last_modified_at
site.data.meta[data_file].last_modified_at_formatted
last_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_file
is the name of the file within_data/
, e.g.foo.json
-
.yml
,.yaml
,.json
,.tsv
, and.csv
supported
See page.last_commit
for further information.
site.data.meta[data_file].last_modified_at
-
data_file
is the name of the file within_data/
, e.g.foo.json
-
.yml
,.yaml
,.json
,.tsv
, and.csv
supported
See page.last_modified_at
for further information.
site.data.meta[data_file].last_modified_at_formatted
-
data_file
is the name of the file within_data/
, e.g.foo.json
-
.yml
,.yaml
,.json
,.tsv
, and.csv
supported
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?
I don’t think so: my understanding is that GitHub Pages performs a shallow clone. I have not tried!