CVS/RCS-like Keyword Substitution
Why You Don't Need It
Keyword expansion is not supported in the core of Mercurial because it's of limited utility in a modern system and it's problematic from performance and binary integrity standpoints. For most human uses, one can simply ask the revision control system. And for situations where the revision control system is not available, the tag is very likely to be incorrect!
One common use that remains is automatically integrating version information into a software build. This is pretty easily accomplished without keyword substitution by doing something like the following in your Makefile:
HGVERSION = $(shell hg id)
CFLAGS = -DHGVERSION="\"${HGVERSION}\""
Note that because Mercurial has atomic changesets and globally unique changeset identifiers, a single id is enough to recover the precise revision of every file in a build.
Basic Emulation
If that's not enough for you, it is possible to emulate this behavior with an extension.
Providing keyword substitution is probably best done with an extension. It could also be done with file encode/decode filters but getting access to the necessary context is slight tricky there. It's also hard to do in the localrepo.wread and wwrite methods as no context is available there either. The simplest place seems to be in the filelog add and read methods, though this requires giving filelog access to the wider repository scope, which is a layering violation.
An example extension that provides $Author$ and $Date$ keywords is attached (keyword.py)
See also KeywordExtension.
Keyword update intervals
Keywords in a certain file have to be updated in different intervals:
- On any repository update or commit, e.g. for the hash of the current working directory parent.
- On updating the file, e.g. for the hash of the changeset which introduced the file or last changed the file.
On creating the file, e.g. for the path of the file or the equivalent of $RCSFile$.
As the most interesting keywords (something like $Id$ or $Revision$ need always to be updated, expanding the keywords should be done always. Checking keywords for every file on every repository update/commit would take much time, so probably a list of files containing keywords should be maintained by the extension.
As an alternative, we can just ignore this part of the CVS design and only update keywords on checkouts.
CVS Keywords
Citing Open Source Development with CVS, 3rd Edition: List of Keywords which is Copyright 1999, 2000 Karl Fogel <kfogel@red-bean.com> and licensed under the GNU GPL v>=2:
- These are all the dollar-sign-delimited keywords that CVS recognizes. Following is a list of the keyword, a brief description, and an example of its expanded form:
$Author$ - Author of the change:
$Author: jrandom $
$Date$ - The date and time of the change, in UTC (GMT):
$Date: 1999/08/23 18:21:13 $
$Header$ - Various pieces of information thought to be useful: full path to the RCS file in the repository, revision, date (in UTC), author, state, and locker. (Lockers are rare; although in the following example, qsmith has a lock.):
$Header: /usr/local/newrepos/myproj/hello.c,v 1.1 1999/06/01 03:21:13 jrandom Exp qsmith $
$Id$ - Like $Header$, but without the full path to the RCS file:
$Id: hello.c,v 1.1 1999/06/01 03:21:13 jrandom Exp qsmith $
$Log$ - The log message of this revision, along with the revision number, date, and author. Unlike other keywords, the previous expansions are not replaced. Instead, they are pushed down, so that the newest expansion appears at the top of an ever-growing stack of $Log$ messages:
$Log: hello.c,v $ Revision 1.12 1999/07/19 06:12:43 jrandom
say hello in Aramaic
Any text preceding the $Log$ keyword on the same line will be prepended to the downward expansions too; this is so that if you use it in a comment in a program source file, all of the expansion is commented, too.
$Locker$ - Name of the person who has a lock on this revision (usually no one):
$Locker: qsmith $
$Name$ - Name of the sticky tag:
$Name: release_1_14 $
$RCSfile$ - Name of the RCS file in the repository:
$RCSfile: hello.c,v $
$Revision$ - Revision number:
$Revision: 1.1 $
$Source$ - Full path to the RCS file in the repository:
$Source: /usr/local/newrepos/myproj/hello.c,v $
$State$ - State of this revision:
$State: Exp $
