In the first part, I showed how I can make Git add information about the commit in every file I want it to in such a way that it is updated every time I Git-archive the project. The second part was devoted to integrating this workflow in Emacs and mu4e. Today I finish with showing my Git hook protecting me from accidentally messing up.
One issue with this setup is that I can accidentally commit the values (like the commit hash etc.) I got from one of the other authors instead of the placeholder. Using Magit’s magit-discard
on a region is easy, but forgetting to do it is even easier. So, git hooks to the rescue – this is perfectly suited to a Git pre-commit
hook:
#!/bin/bash git diff --name-only --cached | while read -r filename; do if ! grep -q "\$Format:" <(head -n3 "$filename") then echo File "$filename" contains the commit data, please revert to the placeholders! exit 1 fi done
Notice that I used a few tricks here:
git diff --name-only --cached
gives us the list of files about to be committed,while
loop, which reads it line by line grep -q
to quiet grep and only make it return a non-zero code when nothing is found,!
to negate the exit status of grep,head
to only search the first few (10 by default) lines of each file,Notice also that the above is not very robust: if the filenames contain spaces, things might break down. (Use IFS= read
in that case.) In my case, this is not a concern, so I didn’t bother.
The only remaining thing to do is to ensure that I won’t accidentally commit things from other people as myself. I know two methods of doing this: one is an extremely dirty hack, and the other one relies on a not-so-well-documented feature of Git. Go figure.
The dirty hack is to set the user name in the per-repository Git configuration to empty strings. This makes it impossible to commit anything (even if you use git commit --author
), since Git complains about empty ident name (for <email@example.com>) not allowed
. The trick is to set the environment variables GIT_COMMITTER_NAME
and GIT_COMMITTER_EMAIL
(for some very strange reason, they override even the local Git settings). Now, if you don’t specify your author data, the commit will fail, but if you do, everything will be fine.
The more elegant way is to use a client-side Git hook. In this case, the pre-commit
hook is again our best bet. The problem is, it does not get any parameters. Happily for us, it has an undocumented feature: it has access to some environment variables, one of them being GIT_AUTHOR_EMAIL
. (I learned about it here; it is actually described in the docs, but that seems not to imply that the pre-commit
hook is going to get it.) So, we can use this pre-commit
hook:
#!/bin/bash if ! grep "@allowed.domain$" <(echo $GIT_AUTHOR_EMAIL) then echo Please correctly set the commit author! exit 1 fi
(Of course, I merged both the above scripts to one pre-commit
script.)
Problem solved!