mirror of
https://github.com/ChenQihan666/MyBlog-Next.git
synced 2026-08-14 19:41:02 +08:00
docs: adds an article about git hook date setting (#211)
* feat: adds post on git hook date setting * fix: make necessary adjustments and fix typo --------- Co-authored-by: Sat Naing <satnaingdev@gmail.com>
This commit is contained in:
@@ -0,0 +1,160 @@
|
|||||||
|
---
|
||||||
|
author: Simon Smale
|
||||||
|
pubDatetime: 2024-01-03T20:40:08Z
|
||||||
|
modDatetime:
|
||||||
|
title: How to use Git Hooks to set Created and Modified Dates
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
tags:
|
||||||
|
- docs
|
||||||
|
- FAQ
|
||||||
|
canonicalURL: https://smale.codes/posts/setting-dates-via-git-hooks/
|
||||||
|
description: How to use Git Hooks to set your Created and Modified Dates on AstroPaper
|
||||||
|
---
|
||||||
|
|
||||||
|
In this post I will explain how to use the pre-commit Git hook to automate the input of the created (`pubDatetime`) and modified (`modDatetime`) in the AstroPaper blog theme frontmatter
|
||||||
|
|
||||||
|
## Table of contents
|
||||||
|
|
||||||
|
## Have them Everywhere
|
||||||
|
|
||||||
|
[Git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) are great for automating tasks like [adding](https://gist.github.com/SSmale/3b380e5bbed3233159fb7031451726ea) or [checking](https://itnext.io/using-git-hooks-to-enforce-branch-naming-policy-ffd81fa01e5e) the branch name to your commit messages or [stopping you committing plain text secrets](https://gist.github.com/SSmale/367deee757a9b2e119d241e120249000). Their biggest flaw is that client-side hooks are per machine.
|
||||||
|
|
||||||
|
You can get around this by having a `hooks` directory and manually copy them to the `.git/hooks` directory or set up a symlink, but this all requires you to remember to set it up, and that is not something I am good at doing.
|
||||||
|
|
||||||
|
As this project uses npm, we are able to make use of a package called [Husky](https://typicode.github.io/husky/) (this is already installed in AstroPaper) to automatically install the hooks for us.
|
||||||
|
|
||||||
|
## The Hook
|
||||||
|
|
||||||
|
As we want this hook to run as we commit the code to update the dates and then have that as part of our change we are going to use the `pre-commit` hook. This has already been set up by this AstroPaper project, but if it hadn't, you would run `npx husky add .husky/pre-commit 'echo "This is our new pre-commit hook"'`.
|
||||||
|
|
||||||
|
Navigating to the `hooks/pre-commit` file, we are going to add one or both of the following snippets.
|
||||||
|
|
||||||
|
### Updating the modified date when a file is edited
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Modified files, update the modDatetime
|
||||||
|
git diff --cached --name-status | egrep -i "^(M).*\.(md)$" | while read a b; do
|
||||||
|
cat $b | sed "/---.*/,/---.*/s/^modDatetime:.*$/modDatetime: $(date -u "+%Y-%m-%dT%H:%M:%SZ")/" > tmp
|
||||||
|
mv tmp $b
|
||||||
|
git add $b
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
`git diff --cached --name-status` gets the files from git that have been staged for committing. The output looks like:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
A src/content/blog/setting-dates-via-git-hooks.md
|
||||||
|
```
|
||||||
|
|
||||||
|
The letter at the start denotes what action has been taken, in the above example the file has been added. Modified files have `M`
|
||||||
|
|
||||||
|
We pipe that output into the grep command where we are looking at each line to find that have been modified. The line needs to start with `M` (`^(M)`), have any number of characters after that (`.*`) and end with the `.md` file extension (`.(md)$`).This is going to filter out the lines that are not modified markdown files `egrep -i "^(M).*\.(md)$"`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Improvement - More Explicit
|
||||||
|
|
||||||
|
This could be added to only look for files that we markdown files in the `blog` directory, as these are the only ones that will have the right frontmatter
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
The regex will capture the two parts, the letter and the file path. We are going to pipe this list into a while loop to iterate over the matching lines and assign the letter to `a` and the path to `b`. We are going to ignore `a` for now.
|
||||||
|
|
||||||
|
The next part with the sed command is a bit magical to me as I don't often use it, it was copied from [another blog post on doing something similar](https://mademistakes.com/notes/adding-last-modified-timestamps-with-git/). In essence, it is looking inside the frontmatter tags (`---`) of the file to find the `pubDatetime:` key, getting the full line and replacing it with the `pubDatetime: $(date -u "+%Y-%m-%dT%H:%M:%SZ")/"` same key again and the current datetime formatted correctly.
|
||||||
|
|
||||||
|
This replacement is in the context of the whole file so we put that into a temporary file (`> tmp`), then we move (`mv`) the new file into the location of the old file, overwriting it. This is then added to git ready to be committed as if we made the change ourselves.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### NOTE
|
||||||
|
|
||||||
|
For the `sed` to work the frontmatter needs to already have the `modDatetime` key in the frontmatter. There are some other changes you will need to make for the app to build with a blank date, see [further down](#empty-moddatetime-changes)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Adding the Date for new files
|
||||||
|
|
||||||
|
Adding the date for a new file is the same process as above, but this time we are looking for lines that have been added (`A`) and we are going to replace the `pubDatetime` value.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# New files, add/update the pubDatetime
|
||||||
|
git diff --cached --name-status | egrep -i "^(A).*\.(md)$" | while read a b; do
|
||||||
|
cat $b | sed "/---.*/,/---.*/s/^pubDatetime:.*$/pubDatetime: $(date -u "+%Y-%m-%dT%H:%M:%SZ")/" > tmp
|
||||||
|
mv tmp $b
|
||||||
|
git add $b
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Improvement - Only Loop Once
|
||||||
|
|
||||||
|
We could use the `a` variable to switch inside the loop and either update the `modDatetime` or add the `pubDatetime` in one loop.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Populating the frontmatter
|
||||||
|
|
||||||
|
If your IDE supports snippets then there is the option to create a custom snippet to populate the frontmatter.[In AstroPaper v4 will come with one for VSCode by default.](https://github.com/satnaing/astro-paper/pull/206)
|
||||||
|
|
||||||
|
<video autoplay muted="muted" controls plays-inline="true" class="border border-skin-line">
|
||||||
|
<source src="https://github.com/satnaing/astro-paper/assets/17761689/e13babbc-2d78-405d-8758-ca31915e41b0" type="video/mp4">
|
||||||
|
</video>
|
||||||
|
|
||||||
|
## Empty `modDatetime` changes
|
||||||
|
|
||||||
|
To allow Astro to compile the markdown and do its thing, it needs to know what is expected in the frontmatter. It does this via the config in `src/content/config.ts`
|
||||||
|
|
||||||
|
To allow the key to be there with no value we need to edit line 10 to add the `.nullable()` function.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const blog = defineCollection({
|
||||||
|
type: "content",
|
||||||
|
schema: ({ image }) =>
|
||||||
|
z.object({
|
||||||
|
author: z.string().default(SITE.author),
|
||||||
|
pubDatetime: z.date(),
|
||||||
|
- modDatetime: z.date().optional(),
|
||||||
|
+ modDatetime: z.date().optional().nullable(),
|
||||||
|
title: z.string(),
|
||||||
|
featured: z.boolean().optional(),
|
||||||
|
draft: z.boolean().optional(),
|
||||||
|
tags: z.array(z.string()).default(["others"]),
|
||||||
|
ogImage: image()
|
||||||
|
.refine(img => img.width >= 1200 && img.height >= 630, {
|
||||||
|
message: "OpenGraph image must be at least 1200 X 630 pixels!",
|
||||||
|
})
|
||||||
|
.or(z.string())
|
||||||
|
.optional(),
|
||||||
|
description: z.string(),
|
||||||
|
canonicalURL: z.string().optional(),
|
||||||
|
readingTime: z.string().optional(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
To stop the IDE complaining in the blog engine files I have also done the following:
|
||||||
|
|
||||||
|
1. added `| null` to line 15 in `src/layouts/Layout.astro` so that it looks like
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface Props {
|
||||||
|
title?: string;
|
||||||
|
author?: string;
|
||||||
|
description?: string;
|
||||||
|
ogImage?: string;
|
||||||
|
canonicalURL?: string;
|
||||||
|
pubDatetime?: Date;
|
||||||
|
modDatetime?: Date | null;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
1. added `| null` to line 5 in `src/components/Datetime.tsx` so that it looks like
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface DatetimesProps {
|
||||||
|
pubDatetime: string | Date;
|
||||||
|
modDatetime: string | Date | undefined | null;
|
||||||
|
}
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user