Git filtering

August 11, 2009


Excellent idea Ton!

How to make a custom filter in git to expand the string $Hash$ to something more usefull, ala the $Id$ (which git already supports), but then with more info (committer, date, etc.).

Which also helped in this case was the Pro Git book which is, as of now, a must buy.

But as always is the case between Ton and me, I find his scripts too long :-) So I miekified his solution.

First we need two scripts, one to expand $Hash$ and another one to collapse it again:


id=$(git show -s --pretty=format:%h\ %ci\ ${SUDO_USER:-${LOGNAME}}%n)
sed -e 's!\([[:space:]]*\$[H]ash\)\$!\1: '"${id} "'\$!'

and git.collapse:

sed -e 's!\([[:space:]]*\$[H]ash\):.*\$!\1\$!'

And a final check to see if they are working:

 % echo '$Hash$' | git.expand
 $Hash: 8c84acf 2009-08-11 19:28:20 +0200 miekg $

Looking nice.

% echo '$Hash$' | git.expand | git.collapse

Wonderful - the scripts work. Now we need to set up the filtering in git.


As Ton also explained you need to add the filtering commands to your global ~/.gitconfig and then edit a .gitattributes to enable this on a per repository basis.

In ~/.gitconfig add:

[filter "hash"]
smudge = git.expand
clean = git.collapse

And in your .gitattributes add:

*   filter=hash

See gitattributes(5) for more information on this.


Now run a little test:

% echo '$Hash$' > h
% git add h
% git commit -m'testadd' -- h
Created commit b5189fd: testadd
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 h

And, and… and…:

% cat h

Huh, unexpanded? Well yes, you need to remove and checkout the file for it all to work:

% rm h && git checkout -- h && cat h
$Hash: b5189fd 2009-08-11 19:50:19 +0200 miekg $

Putting it all together

So now I only need a little wrapper around vi and git to do all this automatically. So here is vit2:

CODE(sh){ #!/bin/zsh

© Copyright Miek Gieben, 2009 Licensed under GPL v3

expands $[H]ash$ to $[H]ash: short_hash date committer $

with the help of git filter

zparseopts -D -K – m:=o_msg # -m mesg is supported [[ $? != 0 ]] && exit 1 o_msg=“$o_msg[2]”

for file in “$@”; do dir=“$(dirname “$file”)” cd “$dir” 2>/dev/null || continue base=“$(basename “$file”)” chmod +w “$base” 2>/dev/null

if ${EDITOR:-/usr/bin/vi} "$base"; then
[[ ! -e "$base" ]] && exit 0    # nothing saved
git add "$base"
if [[ -z "$o_msg" ]]; then
    git commit -- "$base"
    git commit -m "$o_msg" -- "$base"
rm "$base"  && git checkout -- "$base"

chmod a-w "$base" 2>/dev/null
cd - >/dev/null

done }CODE

That’s all folks!

And thanks Ton.