I was pondering putting /etc
and /usr/local/etc
under version control next time I build a personal server. For those deployed in real production environments we normally keep configuration files and whatnot under version control and then deploy new versions out to the servers. This time I wondered about using Git and just working locally - really just there as a nice undo option!
So looking around at some of the issues I might come across I me up with the following:
Now those are all things Git doesn’t really do out of the box, and for good reasons, but I guess this use case is a little different and I wanted to experiment and play. So taking keyword expansion for this post.
CVS can automagically replace things like $ID$ with a string expansion of commit information, the author, date, etc. Now because git doesn’t really track files as such it’s not out of the box functionality in Git and indeed provokes much outrage on mailing lists around the globe… However it is quite easy to do something similar in Git using filters. These essentially filter the data going in and out of the repository. They are often used to clean code up on the way in. In Git terminology you clean something going in (ie add - staging) and you smudge something coming out (ie checkout).
In your .git/config
file you can add filters thus:
[filter "idexpansion"]
smudge = /path/to/my/script/smudge_id.rb
clean = /path/to/my/script/clean_id.rb
Or you can add them with the git config command thus:
$ git config --global filter.idexpansion.clean /path/to/my/script/clean_id.rb
$ git config --global filter.idexpansion.smudge /path/to/my/script/smudge_id.rb
Right in those examples we’ve added a filter called idexpansion and told it to use two scripts: clean_id.rb and smudge_id.rb to do the work. Ensure you either give the full paths to the scripts or they are in your path.
Now to use it! We need to edit your .git/info/attributes file to define which file types our new filter should act on (you could also put this into a .attributes file).
*.txt filter=idexpansion
That would mean all txt files would be run via our filter. Now onto those scripts! The scripts receive the contents of the files on STDIN. So the first script here looks for $ID$ and replaces it with some data gathered from the Git logs.
#! /usr/bin/env ruby
class='c1'>#
# Filter to perform variable expansion within checked out
# files. i.e. Replacing things like $ID$ and $DATE$, etc.
#
# Following expansion happens:
#
# $ID$ -> "$ID: 1de184a on Tue Apr 27 08:54:36 2010 +0100 by Someone $"
#
data = STDIN.read
info = `git log --pretty=format:"%h on %ad by %cn" -1`
puts data.gsub('$ID$', '$ID: ' + info.to_s + ' $')
This script looks for a pattern matching the expansion and replaces it with $ID$ again.
#! /usr/bin/env ruby
#
# Filter undo variable expansion within checked out
# files. i.e. Replacing the actual data with $ID$, etc.
#
# Following contraction happens:
#
# "$ID: 1de184a on Tue Apr 27 08:54:36 2010 +0100 by Someone $" -> $ID$
#
data = STDIN.read
puts data.gsub(/\$ID: \S+ on \S{3} \S{3} [0-3][0-9] [0-2]\d:[0-5]\d:[0-5]\d \d{4} [+-]\d{4} by .+ \$/, '$ID$')
Testing …
$ echo '# $ID$' > test.txt
$ git add test.txt
$ git commit -m "testing expansion"
$ rm test.txt
$ git checkout ./test.txt
$ cat test.txt
# $ID: 1de184a on Tue Apr 27 08:54:36 2010 +0100 by Someone $
So if I want it I can do expansion, yah!
Written on 27 Apr 2010 and categorised in NIX, tagged as ID, CVS, Git, Smudge, and Clean