Update your files header using git hooks
Published: 2022-11-10 18:38:36

If was not enouth Linux Torvalds Invented Linux he also created git ...

## The git hooks
There are automated actions git takes when you commit, pull etc. For me is essential having a great file header indicating when it was modified its purpuse references and even tags, so I can grep my wiki, for instance, and find out things I forget as time goes by. 

## The Last (Changed|Modified): line

As an example lets see what I have on the first three lines of my nvim autocommands.lua file:

```
-- File: ~/.config/nvim/lua/core/autocmds.lua
-- Last Change: Wed, 09 Nov 2022 19:27:15
-- vim:set ft=lua softtabstop=2 shiftwidth=2 tabstop=2 expandtab nolist:
```

## Changing the "Last Change" when you run a git commit

You need to have an executable file (chmod u+x file) on your `repository/.git/hooks/pre-commit`

In my case it goes like this:
```sh
#!/bin/sh
# Filename: ~/.config/nvim/.git/pre-commit
# Last Change: Thu, 10 Nov 2022 - 12:16
# vim:nolist conceallevel=0 softtabstop=4 shiftwidth=4 tabstop=4 expandtab ft=sh:
# References:
# https://toroid.org/git-last-modified
# https://mademistakes.com/notes/adding-last-modified-timestamps-with-git/

# NOTE: 
# This script does not have any guarantee that it
# will work correctly, make sure
# you do all the necessary tests and
# backups to avoid any hassle

modified=$(git diff --cached --name-only --diff-filter=ACMR)

[ -z $modified ] && exit 0

for f in $modified; do
    data=$(date -r $f "+%a, %d %b %Y - %T")
    sed -ri "1,7s/^(\S*\s*Last (Change|Modified):).*/\1 $data/gi" $f
    git add $f
done
```

Now, every time you commit, if any of your files was modified or added git will run this script befor commiting your changes, hence updating your Last Change line. 

## Explaining the commands (the way we learn)
The line...

```sh
modified=$(git status --short |  awk '$1 ~ /[MA]/ {print $2}')
```

... Generates an array with all modified and added files in your repo, the `$1` of awk means field 1 and the tilda means: match against Regular Expressions, in this case M or A. 

The `-r` option of date command displays the last modification time of FILE and we also add a format on which we want to get this "last modification time" using this string: `"+%a, %d %b %Y - %H:%M:%S"`. For more you can reda the manpage for date: `man date` 
 
The `sed` command it is like a state of art Unix command:

```sh
sed -ri "1,7s/^((\S+\s+)?Last (change|modified):).*/\1 $data/gi" $f 
```

Use extended regex (avoiding) lots of backslashes, from line 1 to 7, starting for the very first character of the line `^` start a regex group ()  that contains any non space `\S` at least `+` followed one space `\s` at least one. This first group is optional because of a question mark after that `?` followed by a literal Last and one literal space, followed by one of these two words (change|modified), followed by a colon `:`, followed by anything. 

In the substitution part we get the first group (the bigger one) that contaings the `(\S+\s+)?` and we say, beloved sed, please use the \1 first group here as substitution followed by the value of the `$data` variable. 

```
-r ................... extended regex
-i ................... edit file in place
1,7 .................. range of substitution
^ .................... start of the line
() ................... regex group
\S ................... any non space
\s ................... space
+ .................... at least one
? .................... optional
(Modified|Changed) ... modified or changed
\1 ................... repeat the first regex group content
```

NOTE: To be able to use shell variables in sed you need to use double quotes. 

NOTE: The `gi` at the end of sed command means globally substitute ignoring case. 

## References:
1 - https://toroid.org/git-last-modified
2 - https://toroid.org/git-last-modified