After a successful conversion of an old SVN repository into Git, with svn2git
, I have been tasked with reproducing the $Revision$
keyword expansion, (or a close approximation of it).
So I ...
added a
svn-r
annotated tag for SVN's rev0in
.git/attributes
added* filter=revsion
in
.git/configure
added[filter "revsion"] smudge = /bin/sed -e 's/\\$Revision\\$/$Revision: '$(GIT_EXEC_PATH=/usr/lib/git-core/ /usr/bin/git describe --match svn-r)'$/g' clean = /bin/sed -e 's/\\$Revision: [^$]*\\$/$Revision$/g'
... and it works, but is doing the wrong thing.
Whenever I do a checkout, it expand the $Revision$
the git describe
of the previous HEAD
(before the checkout). So that when I am on master~1 and doing git checkout master
. I get the expansion for master~1 and not for master.
Just to make sure that the early evaluation was not the fault of the $(...)
in .git/config
I also tried to move this code into its own script, but to no avail.
Hence my question: Is there a way to make git describe
that runs by a smudge filter to describe the commit after the checkout?
TL;DR: a (tested) solution
Try this post-checkout hook (now tested, albeit lightly; I put it in my scripts repository on GitHub as well):
To improve performance, you can modify it to operate only on files that are likely to use
$Revision$
(your attribute defines this as "all files" so I used that here).Long
I thought about this problem a bit this morning. As you have observed, it is simply that
git checkout
has not yet updated theHEAD
reference at the time it is populating the index and work-tree while changing commits. Ultimately, it seems too annoying to attempt to compute whatgit checkout
is about to setHEAD to
. You might instead use a post-checkout hook.It's not clear yet whether this should be something to use instead of the smudge filter, or in addition to the smudge filter, but I think in addition to is correct. You almost certainly still want the clean filter to operate as usual.
In any case, a post-checkout hook gets:
(There is bug in
git checkout
and/or the documentation here. The last sentence says "cannot affect the outcome", but that's not true in two ways:git checkout
. This makes the checkout appear to have failed if the exit status of the hook is nonzero.It's the last that I intend to use here.)
Your goal is to make the smudge filter run when
HEAD
is updated. Looking at the source code for builtin/checkout.c, we find that for the "change commits" case,git checkout
first populates the index and work-tree, then updates theHEAD
ref (first highlighted line), then runs thepost-checkout
hook with the two hash IDs (the first one will be the special null-hash in some cases) and the flag set to 1.File checkouts, which by definition don't change commits, run the hook with the flag set to 0. The two hash IDs will always match, which is why the flag test is almost certainly unnecessary.
Doing the file checkouts will re-run the smudge filter. Since
HEAD
has now been updated,$Revision$
will expand the way you want. The obvious bad thing about this is that every work-tree file must be updated twice! There is another issue, which the Python code above works around by removing the supposedly-unmodified files, forcinggit checkout
to re-extract them from index to work-tree.