Skip to main content
Martin Hähnel

Back At It

Changelog

  • 2025-10-18 - Added a footnote about 11ty's gray-matter fork
  • 2025-10-19 - Updated the last version of the writeHashesToFile to what I actually had finished with

Back at it. We continue to work on the versioning system today that I wrote about in more detail in A Small Step. One thing that I didn't really think about is how the idea of having older versions would interact with the current version a lot. So let's do that now. From the last post, here's the rough idea again:

  • Write a script that adds all commit hashes that pertain to a certain blogpost to the frontmatter
  • Read these hashes and - somehow - extract these older versions from the git history on building the site
  • Build UI that allows switching from one version to another

One of the problems is that it is unclear when the script is supposed to run. The normal order of operations so far has been:

  1. write/code
  2. commit
  3. push
  4. (build + deploy)

Where does the - run script to add commit hashes to posts - fit in? And also: A new version in a weird way denotes an old version. Like if I update an older post, let's say I add something new to Post Hubs. That means that the version (the hash) before the current one should be part of the hashes. But that difference is only apparent after the current writing is done and committed. So we end up in a weird state, where we can't really tell when a new version exists... except if we compare with the current state of uncommitted changes! E.g.

  1. update post (implies a new version)
  2. run script (on the basis of the current working tree)
  3. commit

I think that's it actually. I started that paragraph thinking we have an chicken and egg problem but instead we have a chicken and egg solution! And the good thing about this is that the script will only ever touch files that are changed in the current work tree. I think that means we can even use a git hook for it! Very nice. This should do the trick. So two hooks.

  1. update post
  2. commit
    1. pre-commit hook to create a .commit file
    2. post-commit hook
      1. if .commit exists
        1. run script
        2. amend changes to the current commit
  3. push
  4. (build + deploy)

This is good, but I need to check if WorkingCopy (iOS Git Client) has commit hook support and how that works within Obsidian Git.

If I ever want to work on my blog from my iPad, I need to figure out a way to add missing version hashes later. I imagine that I can give the script not only one path but multiple paths for the working tree solution needed for the git hook. But depending on the path (file or folder) we will check for only that file or all (markdown) files within it, recursively.

And I will not write that script in bash. I probably should write it in node as the blog is a JavaScript project. Fine.

First Script Version

Let's start by doing most of this manually.

import { execSync } from "child_process";

const args = process.argv.slice(2);
const isTesting = args.includes("--testing");

function execute() {
	if (!isTesting) {
		console.log("This script is only for testing purposes.");
		return;
	}
	const gitLog = execSync(
		`git log --pretty=oneline --follow -- apps/blog/content/blog/2025/10/03/Write\\ Like\\ You\\'re\\ Ron\\ Jeffries.md`,
	).toString();
	console.log("Commits for the Ron Jeffries post:\n", gitLog);
	const fileContent = execSync(
		`git cat-file -p a55686c28205d910943a15f7d2e1a4873cdd6b5d:apps/blog/content/blog/2025/10/03/Write\\ Like\\ You\\'re\\ Ron\\ Jeffries.md`,
	).toString();
	console.log(
		"File content for the Ron Jeffries post (first version):\n",
		fileContent,
	);
	console.log("Bye...");
}

execute();

This prints out:

Commits for the Ron Jeffries post:
 62cfd070c0e314110fb2fa3f621be40b665f7e3c Commit by obsidan-git
bb85ce9e5259d3bad6963e8ddab2ac9ffa1e3b56 Commit by obsidan-git
9402b41142602cd4a5f658646cf26944d7c7c04e Commit by obsidan-git
192cbd19e13b9d891ec19077c131c4b53aa69fd1 Fix BuildInPublic tag
a55686c28205d910943a15f7d2e1a4873cdd6b5d Commit by obsidan-git

File content for the Ron Jeffries post (first version):
 ---
title: Write Like You're Ron Jeffries
aliases: Write Like You're Ron Jeffries
draft: false
date: 2025-10-03T09:40:46+03:00
lastmod: 2025-10-03T14:15:20+03:00
tags:
  - Dev
---

I know of [Ron Jeffires] [...]

Bye...

Okay. For now we just want to note the hashes, not retrieve their contents. Let's write them to a file.

	// ...
	writeHashesToFile(gitLog);

	console.log("Bye...");
}

function writeHashesToFile(gitLog) {
	const commitHashes = gitLog
		.split("\n")
		.map((line) => line.split(" ")[0])
		.filter((hash) => hash);

	console.log(
		"Commit hashes for the Ron Jeffries post:\n",
		commitHashes.join("\n"),
	);

	// Write the commit hashes to a temporary file
	fs.writeFileSync(TMP_FILE, commitHashes.join("\n"));
}

// ...

That works. But now we need to make sure that we not only write it but write it to a specific location in the file, leaving the rest of it untouched. Let's create a small example markdown file and use the tool gray-matter (which Eleventy uses internally, too)[1] to parse and encode the frontmatter of markdown files.

---
title: I am a friendly test file
---

Hello.
function writeHashesToFile(gitLog) {
	const commitHashes = gitLog
		.split("\n")
		.map((line) => line.split(" ")[0]) // get first part (hash)
		.filter((hash) => hash); // remove empty lines

	console.log(
		"Commit hashes for the Ron Jeffries post:\n",
		commitHashes.join("\n"),
	);

	//parse markdown file and write hashes to hashes key (add if not exists) of frontmatter
	const markdownContent = fs.readFileSync(TMP_FILE, "utf-8");
	const parsed = matter(markdownContent);
	parsed.data.hashes = [...(parsed.data.hashes || []), ...commitHashes];

	fs.writeFileSync(TMP_FILE, matter.stringify(parsed.content, parsed.data));
}
---
title: I am a friendly test file
hashes:
  - 62cfd070c0e314110fb2fa3f621be40b665f7e3c
  - bb85ce9e5259d3bad6963e8ddab2ac9ffa1e3b56
  - 9402b41142602cd4a5f658646cf26944d7c7c04e
  - 192cbd19e13b9d891ec19077c131c4b53aa69fd1
  - a55686c28205d910943a15f7d2e1a4873cdd6b5d
---

Hello.

Another small step taken.


  1. EDIT: I should switch to use 11y's own fork which I found when I looked through what is ahead of us for Eleventy 4. ↩︎