-    ≉  Estimated 8 min read

Update Jekyll blog entry meta-data

If you track your jekyll post with git, preferably if you hosting your github pages, than you can utilize the git attributes to automatically update post meta-data. For instance we replace the modified information in the yaml frontmatter (that is the jekyll metadata) every time you do a update on a jekyll post or page.

General Mechanism

Check Customizing Git - Git Attributes and Customizing Git - Git Hooks to understand the mechanism in detail.

In short:

  • write a script that replaces the date with current date
  • configure git to call that filter on every commit
  • setup filter for respective jekyll files, i.e. markdown

Replacing Modified Timestamp

Before we I configure the git hook, I have to find the appropriate command to do that. Therefore create an example yaml frontmatter.

tan@omega:~$ cat test.txt
---
layout: post
status: publish
published: true
title: Reindex Watcher Indices with Curator
author: tan
date: '14.07.2017 10:48'
modified: '2017-07-14 16:58:43 +0200'
categories:
- IT
tags:
- elasticsearch
---

Use sed to replace existing field with the new timestamp.

sed -i "s/modified:.*/modified: \'$(date +'%Y-%m-%d %H:%M:%S %z')\'/" test.txt

Check the modified output

tan@omega:~$ cat test.txt
---
layout: post
status: publish
published: true
title: Reindex Watcher Indices with Curator
author: tan
date: '14.07.2017 10:48'
modified: '2017-07-25 10:36:08 +0200'
categories:
- IT
tags:
- elasticsearch
---

The git manual has a ruby program example

tan@omega:~/bin$ cat expand_date 
#! /usr/bin/env ruby
data = STDIN.read
last_date = `git log --pretty=format:"%ad" -1`
puts data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')

Filters

It turns out that you can write your own filters for doing substitutions in files on commit/checkout. These are called “clean” and “smudge” filters.

  • smudge - before they’re checked out (on checkout)
  • clean - before they’re staged in (on add)

The manual uses the smudge filter for the date expansion, but in my case it must be clean. I want the date substituted with the current timestamp during the commit.

This must be done in the directory where the git repository is.

Filters from Example:

$ git config filter.dater.smudge expand_date
$ git config filter.dater.clean 'perl -pe "s/\\\$Date[^\\\$]*\\\$/\\\$Date\\\$/"'

Check with gitconfig -l if the settings are applied.

Setup Git Attributes

Create in the respective git repository the .gitattributes file. To apply the filter to all jekyll files, add the file extension for markdown and assign the filter, which was previously configured.

tan@omega:~/Sources/cinhtau.github.io$ cat .gitattributes 
*.md filter=dater

The basic problem is that filters only work on checkout and add. Following the example I always had to remove and make a checkout the file to get the date substitution. This is a very extensive effort.

To modify a file on commit mostly the pre-commit hook is the right way to accomplish this.

Pre-Commit Hook

Git has examples files in the .git/hooks directory. Based on that I wrote this simple pre-commit script. It must be named pre-commit and must be placed in the hooks directory.

#!/bin/bash

if git rev-parse --verify HEAD >/dev/null 2>&1 ; then
   against=HEAD
else
   # Initial commit: diff against an empty tree object
   against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

staged_files=`git diff-index --name-status --cached $against      | # Find all staged files
                egrep -i '^(A|M).*\.(md)$' 		                  | # Only process jekyll files
                sed -e 's/^[AM][[:space:]]*//'                    | # Remove leading git info
                sort                                              | # Remove duplicates
                uniq`


partially_staged_files=`git status --porcelain --untracked-files=no | # Find all staged files
                        egrep -i '^(A|M)M '                         | # Filter only partially staged files
                        sed -e 's/^[AM]M[[:space:]]*//'             | # Remove leading git info
                        sort                                        | # Remove duplicates
                        uniq`

# Merge staged files and partially staged files
staged_and_partially_staged_files=${staged_files}$'\n'${partially_staged_files}

# Remove all files that are staged *AND* partially staged
# Thus we get only the fully staged files
fully_staged_files=`echo "$staged_and_partially_staged_files" | sort | uniq -u`

for FILE in $fully_staged_files ; do
    # substitute every jekyll modified timestamp with current timestamp
    sed -i "1,10s/modified:.*/modified: \'$(date +'%Y-%m-%d %H:%M:%S %z')\'/" "$FILE"
done

Pay attention, to replace only the modified in the frontmatter, sed is limited to lines 1 to 10. Ensure that modified is listed in this range.

sed -i "1,10s/modified:.*/modified: \'$(date +'%Y-%m-%d %H:%M:%S %z')\'/" "$FILE"

Testing

Now let’s do some testing. Show modified files.

tan@omega:~/sources/cinhtau.github.io$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        renamed:    scripts/prepare-commit-jekyll -> scripts/pre-commit-jekyll

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   .gitattributes
        modified:   Gemfile        
        modified:   _posts/2017/07/2017-07-17-update-jekyll-meta-data.md

Commit the jekyll post

tan@omega:~/sources/cinhtau.github.io$ git commit _posts/2017/07/2017-07-17-update-jekyll-meta-data.md -m "test pre-commit"
[master cefec94] test pre-commit
 1 file changed, 11 insertions(+), 7 deletions(-)

Check the date substitution. :raised_hands:

tan@omega:~/sources/cinhtau.github.io$ head _posts/2017/07/2017-07-17-update-jekyll-meta-data.md
---
layout: post
published: true
title: Update Jekyll blog entry meta-data
author: cinhtau
date: '24.07.2017 07:49'
modified: '2017-07-25 10:36:08 +0200'
categories:
- IT
tags:

Tan-Vinh Nguyen

Just a coder

Similar Stories


Linux

Rename multiple files

My current migration from WordPress to Jekyll took me the WordPress exporter of Jekyll. It generated html files. I want to migrate them to markdown, therefore I needed a quick solution to rename... Read on

IT

Create file templates for Jekyll posts

Writing Jekyll posts always require a YAML front matter block content. To speed up the task you may create a file template for IntelliJ or Webstorm, depending which IDE from Jetbrains you are us... Read on