# HG changeset patch # User Christian Ebert # Date 1184791550 -7200 # Node ID 7a775a8f6fb96dd0bbd29d9adf182a3a783b9991 # Parent b9f2c0853da3bc1cf896a43a1c31cf757318fb9d# Parent 0d2a6c9f8343798c23eef264df660351c5e0c183 Discard kwmap-templates branch; keep only early forked off default diff -r b9f2c0853da3 -r 7a775a8f6fb9 .hgignore --- a/.hgignore Thu Dec 14 08:17:01 2006 +0100 +++ b/.hgignore Wed Jul 18 22:45:50 2007 +0200 @@ -1,7 +1,10 @@ syntax: glob *.pyc +*.pyo *~ *.swp *.orig *.rej + +hgkw/__version__.py diff -r b9f2c0853da3 -r 7a775a8f6fb9 .hgtags --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgtags Wed Jul 18 22:45:50 2007 +0200 @@ -0,0 +1,7 @@ +536c1797202d57efb77bea098e10968ff01602ce universal_scheme +ba000e29ecf3b8df09e0fd363a78cabbe3c2a78f cvs_scheme +1fe48bf82d056f1ece05baccab888357c10c5ab8 r0.1 +2e930f84224222ad6514a3c5dc6e00350e199e92 very_cvs +99dc49c5bcfba9d5b412c5fa6d0bf3ba20d68df1 hgkw_standalone_setup +15e8cd7f5295728b089fc8ba236c0f079572fb1d nohook +0c8b7e5c25a6b9a0d2782eaa3748eb546f5a254f archive diff -r b9f2c0853da3 -r 7a775a8f6fb9 MANIFEST.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MANIFEST.in Wed Jul 18 22:45:50 2007 +0200 @@ -0,0 +1,5 @@ +# $Id$ + +exclude hgkw/__version__.py + +include tests/test-keyword tests/test-keyword.out diff -r b9f2c0853da3 -r 7a775a8f6fb9 README.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.txt Wed Jul 18 22:45:50 2007 +0200 @@ -0,0 +1,45 @@ +$Id$ + +keyword extension for Mercurial SCM +=================================== + +install +------- + +Either copy hgkw/keyword.py into the hgext directory of your +Mercurial installation. +Then add the lines: + +[extensions] +hgext.keyword = + +to your hgrc file. + +Or run "python setup.py install". +See also "pyton setup.py --help". +Then add the line: + +[extensions] +keyword = /path/to/hgkw/keyword.py + +to your hgrc, where /path/to/ is somewhere in your $PYTHONPATH. + + +first steps and online help +--------------------------- + +$ hg keyword help +$ hg kwdemo + + +testing +------- + +Copy hgkw/keyword.py into the hgext directory of your Mercurial +source tree. Copy tests/test-keyword, tests/test-keyword.out into +the tests directory of your Mercurial source tree. Change to that +directory and run: + +$ python run-tests.py test-keyword + +and then keep your fingers crossed ... diff -r b9f2c0853da3 -r 7a775a8f6fb9 hgkw/hgkwencode.py --- a/hgkw/hgkwencode.py Thu Dec 14 08:17:01 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -# $Hg: hgkwencode.py,v$ - -import re, sys - -def kwencode(): - '''Truncates keywords in IO stream.''' - - re_kwtrunc = re.compile(r'([$]Hg: .+?,v) [a-z0-9]{12} [^$]+? \$') - sys.stdout.write(re_kwtrunc.sub(r'\1$', sys.stdin.read())) diff -r b9f2c0853da3 -r 7a775a8f6fb9 hgkw/keyword.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgkw/keyword.py Wed Jul 18 22:45:50 2007 +0200 @@ -0,0 +1,427 @@ +# keyword.py - keyword expansion for Mercurial +# +# Copyright 2007 Christian Ebert +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. +# +# $Id$ +# +# Keyword expansion hack against the grain of a DSCM +# +# There are many good reasons why this is not needed in a distributed +# SCM, still it may be useful in very small projects based on single +# files (like LaTeX packages), that are mostly addressed to an audience +# not running a version control system. +# +# For in-depth discussion refer to +# . +# +# Keyword expansion is based on Mercurial's changeset template mappings. +# The extension provides an additional UTC-date filter ({date|utcdate}). +# +# Expansions spanning more than one line are truncated to their first line. +# Incremental expansion (like CVS' $Log$) is not supported. +# +# Binary files are not touched. +# +# Setup in hgrc: +# +# # enable extension +# keyword = /full/path/to/keyword.py +# # or, if script in hgext folder: +# # hgext.keyword = + +'''keyword expansion in local repositories + +This extension expands RCS/CVS-like or self-customized $Keywords$ +in the text files selected by your configuration. + +Keywords are only expanded in local repositories and not logged by +Mercurial internally. The mechanism can be regarded as a convenience +for the current user or archive distribution. + +Configuration is done in the [keyword] and [keywordmaps] sections of +hgrc files. + +Example: + [extensions] + hgext.keyword = + + [keyword] + # expand keywords in every python file except those matching "x*" + **.py = + x* = ignore + +Note: the more specific you are in your [keyword] filename patterns + the less you lose speed in huge repos. + +For a [keywordmaps] template mapping and expansion demonstration +run "hg kwdemo". + +An additional date template filter {date|utcdate} is provided. + +You can replace the default template mappings with customized keywords +and templates of your choice. +Again, run "hg kwdemo" to control the results of your config changes. + +When you change keyword configuration, especially the active keywords, +and do not want to store expanded keywords in change history, run +"hg kwshrink", and then change configuration. + +Caveat: "hg import" fails if the patch context contains an active + keyword. In that case run "hg kwshrink", reimport, and then + "hg kwexpand". + Or, better, use bundle/unbundle to share changes. +''' + +from mercurial import commands, cmdutil, context, fancyopts +from mercurial import filelog, localrepo, templater, util, hg +from mercurial.i18n import gettext as _ +# findcmd might be in cmdutil or commands +# depending on mercurial version +if hasattr(cmdutil, 'findcmd'): + findcmd = cmdutil.findcmd +else: + findcmd = commands.findcmd +import os, re, shutil, sys, tempfile, time + +commands.optionalrepo += ' kwdemo' + +deftemplates = { + 'Revision': '{node|short}', + 'Author': '{author|user}', + 'Date': '{date|utcdate}', + 'RCSFile': '{file|basename},v', + 'Source': '{root}/{file},v', + 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}', + 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}', +} + +nokwcommands = ('add', 'addremove', 'bundle', 'clone', 'copy', 'export', + 'incoming', 'outgoing', 'push', 'remove', 'rename', 'rollback') + +def utcdate(date): + '''Returns hgdate in cvs-like UTC format.''' + return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0])) + +def getcmd(ui): + '''Returns current hg command.''' + # commands.parse(ui, sys.argv[1:])[0] breaks "hg diff -r" + try: + args = fancyopts.fancyopts(sys.argv[1:], commands.globalopts, {}) + except fancyopts.getopt.GetoptError, inst: + raise commands.ParseError(None, inst) + if args: + cmd = args[0] + aliases, i = findcmd(ui, cmd) + return aliases[0] + +def keywordmatcher(ui, repo): + '''Collects include/exclude filename patterns for expansion + candidates of current configuration. Returns filename matching + function if include patterns exist, None otherwise.''' + inc, exc = [], ['.hg*'] + for pat, opt in ui.configitems('keyword'): + if opt != 'ignore': + inc.append(pat) + else: + exc.append(pat) + if not inc: + return None + return util.matcher(repo.root, inc=inc, exc=exc)[1] + +class kwtemplater(object): + ''' + Sets up keyword templates, corresponding keyword regex, and + provides keyword substitution functions. + ''' + def __init__(self, ui, repo, path='', node=None, expand=True): + self.ui = ui + self.repo = repo + self.path = path + self.node = node + templates = dict(ui.configitems('keywordmaps')) + if templates: + for k in templates.keys(): + templates[k] = templater.parsestring(templates[k], + quoted=False) + self.templates = templates or deftemplates + escaped = [re.escape(k) for k in self.templates.keys()] + self.re_kw = re.compile(r'\$(%s)[^$]*?\$' % '|'.join(escaped)) + if expand: + templater.common_filters['utcdate'] = utcdate + try: + self.t = cmdutil.changeset_templater(ui, repo, + False, '', False) + except TypeError: + # depending on hg rev changeset_templater has extra "brinfo" arg + self.t = cmdutil.changeset_templater(ui, repo, + False, None, '', False) + else: + self.t = None + + def ctxnode(self, node): + '''Obtains missing node from file context.''' + if not self.node: + c = context.filectx(self.repo, self.path, fileid=node) + self.node = c.node() + + def kwsub(self, mobj): + '''Substitutes keyword using corresponding template.''' + kw = mobj.group(1) + self.t.use_template(self.templates[kw]) + self.ui.pushbuffer() + self.t.show(changenode=self.node, root=self.repo.root, file=self.path) + keywordsub = templater.firstline(self.ui.popbuffer()) + return '$%s: %s $' % (kw, keywordsub) + + def expand(self, node, data): + '''Returns data with keywords expanded.''' + if util.binary(data): + return data + self.ctxnode(node) + return self.re_kw.sub(self.kwsub, data) + + def process(self, node, data): + '''Returns a tuple: data, count. + Count is number of keywords/keyword substitutions. + Keywords in data are expanded, if templater was initialized.''' + if util.binary(data): + return data, None + if self.t: + self.ctxnode(node) + return self.re_kw.subn(self.kwsub, data) + return data, self.re_kw.search(data) + + def shrink(self, text): + '''Returns text with all keyword substitutions removed.''' + if util.binary(text): + return text + return self.re_kw.sub(r'$\1$', text) + + def overwrite(self, candidates, man, commit=True): + '''Overwrites files in working directory if keywords are detected. + Keywords are expanded if keyword templater is initialized, + otherwise their substitution is removed.''' + expand = self.t is not None + action = ('shrinking', 'expanding')[expand] + notify = (self.ui.note, self.ui.debug)[commit] + files = [] + for f in candidates: + fp = self.repo.file(f, kwcnt=True, kwexp=expand) + data, cnt = fp.read(man[f]) + if cnt: + notify(_('overwriting %s %s keywords\n') % (f, action)) + try: + self.repo.wwrite(f, data, man.flags(f)) + except AttributeError: + # older versions want file descriptor as 3. optional arg + self.repo.wwrite(f, data) + files.append(f) + if files: + self.repo.dirstate.update(files, 'n') + +class kwfilelog(filelog.filelog): + ''' + Subclass of filelog to hook into its read, add, cmp methods. + Keywords are "stored" unexpanded, and processed on reading. + ''' + def __init__(self, opener, path, kwtemplater, kwcnt): + super(kwfilelog, self).__init__(opener, path) + self.kwtemplater = kwtemplater + self.kwcnt = kwcnt + + def read(self, node): + '''Passes data through kwemplater methods for + either unconditional keyword expansion + or counting of keywords and substitution method + set by the calling overwrite function.''' + data = super(kwfilelog, self).read(node) + if not self.kwcnt: + return self.kwtemplater.expand(node, data) + return self.kwtemplater.process(node, data) + + def add(self, text, meta, tr, link, p1=None, p2=None): + '''Removes keyword substitutions when adding to filelog.''' + text = self.kwtemplater.shrink(text) + return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2) + + def cmp(self, node, text): + '''Removes keyword substitutions for comparison.''' + text = self.kwtemplater.shrink(text) + if self.renamed(node): + t2 = super(kwfilelog, self).read(node) + return t2 != text + return super(kwfilelog, self).cmp(node, text) + +def overwrite(ui, repo, files=None, expand=True): + '''Expands/shrinks keywords in working directory.''' + wlock = repo.wlock() + try: + ctx = repo.changectx() + if not ctx: + raise hg.RepoError(_('no changeset found')) + for changed in repo.status()[:4]: + if changed: + raise util.Abort(_('local changes detected')) + kwfmatcher = keywordmatcher(ui, repo) + if kwfmatcher is None: + ui.warn(_('no files configured for keyword expansion\n')) + return + m = ctx.manifest() + if files: + files = [f for f in files if f in m.keys()] + else: + files = m.keys() + files = [f for f in files if kwfmatcher(f) and not os.path.islink(f)] + if not files: + ui.warn(_('given files not tracked or ' + 'not configured for expansion\n')) + return + kwt = kwtemplater(ui, repo, node=ctx.node(), expand=expand) + kwt.overwrite(files, m, commit=False) + finally: + wlock.release() + + +def shrink(ui, repo, *args): + '''revert expanded keywords in working directory + + run before: + disabling keyword expansion + changing keyword expansion configuration + or if you experience problems with "hg import" + ''' + overwrite(ui, repo, files=args, expand=False) + +def expand(ui, repo, *args): + '''expand keywords in working directory + + run after (re)enabling keyword expansion + ''' + overwrite(ui, repo, files=args) + +def demo(ui, repo, *args, **opts): + '''print [keywordmaps] configuration and an expansion example + + show current, custom, or default keyword template maps and their expansion + ''' + msg = 'hg keyword config and expansion example' + kwstatus = 'current' + fn = 'demo.txt' + tmpdir = tempfile.mkdtemp('', 'kwdemo.') + ui.note(_('creating temporary repo at %s\n') % tmpdir) + _repo = localrepo.localrepository(ui, path=tmpdir, create=True) + # for backwards compatibility + ui = _repo.ui + ui.setconfig('keyword', fn, '') + if opts['default']: + kwstatus = 'default' + kwmaps = deftemplates + else: + if args or opts['rcfile']: + kwstatus = 'custom' + for tmap in args: + k, v = tmap.split('=', 1) + ui.setconfig('keywordmaps', k.strip(), v.strip()) + if opts['rcfile']: + ui.readconfig(opts['rcfile']) + kwmaps = dict(ui.configitems('keywordmaps')) or deftemplates + if ui.configitems('keywordmaps'): + for k, v in kwmaps.items(): + ui.setconfig('keywordmaps', k, v) + reposetup(ui, _repo) + ui.status(_('config with %s keyword template maps:\n') % kwstatus) + ui.write('[keyword]\n%s =\n[keywordmaps]\n' % fn) + for k, v in kwmaps.items(): + ui.write('%s = %s\n' % (k, v)) + path = _repo.wjoin(fn) + keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n' + _repo.wopener(fn, 'w').write(keywords) + _repo.add([fn]) + ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path)) + ui.note(keywords) + ui.note(_("\nhg --repository '%s' commit\n") % tmpdir) + _repo.commit(text=msg) + pathinfo = ('', ' in %s' % path)[ui.verbose] + ui.status(_('\n%s keywords expanded%s:\n') % (kwstatus, pathinfo)) + ui.write(_repo.wread(fn)) + ui.debug(_('\nremoving temporary repo %s\n') % tmpdir) + shutil.rmtree(tmpdir) + + +def reposetup(ui, repo): + '''Sets up repo as kwrepo for keyword substitution. + Overrides file method to return kwfilelog instead of filelog + if file matches user configuration. + Wraps commit to overwrite configured files with updated + keyword substitutions. + This is done for local repos only, and only if there are + files configured at all for keyword substitution.''' + + # for backwards compatibility + ui = repo.ui + + if not repo.local() or getcmd(ui) in nokwcommands: + return + + kwfmatcher = keywordmatcher(ui, repo) + if kwfmatcher is None: + return + + class kwrepo(repo.__class__): + def file(self, f, kwcnt=False, kwexp=True): + if f[0] == '/': + f = f[1:] + if kwfmatcher(f): + kwt = kwtemplater(ui, self, path=f, expand=kwexp) + return kwfilelog(self.sopener, f, kwt, kwcnt) + else: + return filelog.filelog(self.sopener, f) + + def commit(self, files=None, text='', user=None, date=None, + match=util.always, force=False, lock=None, wlock=None, + force_editor=False, p1=None, p2=None, extra={}): + wrelease = False + if not wlock: + wlock = self.wlock() + wrelease = True + try: + removed = self.status(node1=p1, node2=p2, files=files, + match=match, wlock=wlock)[2] + + node = super(kwrepo, + self).commit(files=files, text=text, user=user, + date=date, match=match, force=force, + lock=lock, wlock=wlock, + force_editor=force_editor, + p1=p1, p2=p2, extra=extra) + if node is None: + return node + + cl = self.changelog.read(node) + candidates = [f for f in cl[3] if kwfmatcher(f) + and f not in removed + and not os.path.islink(self.wjoin(f))] + if candidates: + m = self.manifest.read(cl[0]) + kwt = kwtemplater(ui, self, node=node) + kwt.overwrite(candidates, m) + return node + finally: + if wrelease: + wlock.release() + + repo.__class__ = kwrepo + + +cmdtable = { + 'kwdemo': + (demo, + [('d', 'default', None, _('show default keyword template maps')), + ('f', 'rcfile', [], _('read maps from RCFILE'))], + _('hg kwdemo [-d || [-f RCFILE] TEMPLATEMAP ...]')), + 'kwshrink': (shrink, [], _('hg kwshrink [NAME] ...')), + 'kwexpand': (expand, [], _('hg kwexpand [NAME] ...')), +} diff -r b9f2c0853da3 -r 7a775a8f6fb9 hgkw/version.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgkw/version.py Wed Jul 18 22:45:50 2007 +0200 @@ -0,0 +1,46 @@ +# $Id$ + +'''version.py - hgkw version. +Code stolen from Mercurial, and simplified for my needs. +''' + +import os, time + +unknown_version = 'unknown' + +def getversion(doreload=False): + try: + import hgkw.__version__ + if doreload: + reload(hgkw.__version__) + version = hgkw.__version__.version + except ImportError: + version = unknown_version + return version + +def rememberversion(version=None): + if not version and os.path.isdir('.hg'): + # get version from Mercurial + p = os.popen('hg --quiet identify 2> %s' % os.devnull) + ident = p.read()[:-1] + if not p.close() and ident: + if ident[-1] != '+': + version = ident + else: + version = ident[:-1] + version += time.strftime('+%Y%m%d') + if version and version != getversion(): # write version + directory = os.path.dirname(__file__) + for suff in ['py', 'pyc', 'pyo']: + try: + os.unlink(os.path.join(directory, '__version__.%s' % suff)) + except OSError: + pass + f = open(os.path.join(directory, '__version__.py'), 'w') + try: + f.write('# this file is auto-generated\n') + f.write('version = %r\n' % version) + finally: + f.close() + # reload file + getversion(doreload=True) diff -r b9f2c0853da3 -r 7a775a8f6fb9 hgkwencode --- a/hgkwencode Thu Dec 14 08:17:01 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -#!/usr/bin/env python -# $Hg: hgkwencode,v$ - -from hgkw import hgkwencode - -hgkwencode.kwencode() diff -r b9f2c0853da3 -r 7a775a8f6fb9 setup.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup.py Wed Jul 18 22:45:50 2007 +0200 @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# $Id$ + +from distutils.core import setup +import hgkw.version + +# specify version, Mercurial version otherwise +version = '' + +hgkw.version.rememberversion(version) + +setup(name='hgkw', + version=hgkw.version.getversion(), + description='Mercurial keyword extension (standalone)', + author='Christian Ebert', + author_email='blacktrash@gmx.net', + license='GNU GPL', + packages=['hgkw'], + ) diff -r b9f2c0853da3 -r 7a775a8f6fb9 tests/test-keyword --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-keyword Wed Jul 18 22:45:50 2007 +0200 @@ -0,0 +1,133 @@ +#!/bin/sh + +cat <> $HGRCPATH +[extensions] +hgext.keyword = +[keyword] +* = +b = ignore +EOF + +echo % help +hg help keyword + +echo % hg kwdemo +hg --quiet kwdemo --default \ +| sed -e 's![^ ][^ ]*demo.txt,v!/TMP/demo.txt,v!' \ + -e 's/,v [a-z0-9][a-z0-9]* /,v xxxxxxxxxxxx /' \ + -e '/[$]Revision/ s/: [a-z0-9][a-z0-9]* /: xxxxxxxxxxxx /' \ + -e 's! 20[0-9][0-9]/[01][0-9]/[0-3][0-9] [0-2][0-9]:[0-6][0-9]:[0-6][0-9]! 2000/00/00 00:00:00!' + +hg init Test +cd Test + +echo % kwshrink should abort in empty/invalid repo +hg kwshrink + +echo 'expand $Id$' > a +echo 'ignore $Id$' > b +echo % cat +cat a b + +echo % default keyword expansion +echo % commit +hg --debug commit -A -mab -d '0 0' -u 'User Name ' +echo % status +hg status +echo % identify +hg --quiet identify +echo % cat +cat a b +echo % hg cat +hg cat a b + +echo % touch +touch a b +echo % status +hg status + +rm a b +echo % update +hg update +echo % cat +cat a b + +echo % copy +hg cp a c +echo % commit +hg --debug commit -ma2c -d '1 0' -u 'User Name ' +echo % cat a c +cat a c +echo % touch copied c +touch c +echo % status + +echo % rollback +hg rollback +echo % status +hg status +echo % update -C +hg update --clean + +echo % custom keyword expansion +echo % try with kwdemo +hg --quiet kwdemo "Xinfo = {author}: {desc}" + +cat <>$HGRCPATH +[keywordmaps] +Id = {file} {node|short} {date|rfc822date} {author|user} +Xinfo = {author}: {desc} +EOF + +echo % cat +cat a b +echo % hg cat +hg cat a b + +echo '$Xinfo$' >> a +cat <> log +firstline +secondline +EOF + +echo % interrupted commit +HGEDITOR=false hg commit +echo % status +hg status + +echo % commit +hg --debug commit -l log -d '2 0' -u 'User Name ' +rm log +echo % status +hg status + +echo % cat +cat a b +echo % hg cat +hg cat a b + +cd .. +hg clone -r0 Test Test-a +cd Test-a +cat <> .hg/hgrc +[paths] +default = ../Test +EOF +echo % incoming +# remove path to temp dir +hg incoming | sed -e 's/^\(comparing with \).*\(test-keyword.*\)/\1\2/' + +echo % switch off expansion +cd ../Test +echo % kwshrink +hg --debug kwshrink +echo % cat +cat a b +echo % hg cat +hg cat a b + +rm $HGRCPATH +echo % cat +cat a b +echo % hg cat +hg cat a b diff -r b9f2c0853da3 -r 7a775a8f6fb9 tests/test-keyword.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-keyword.out Wed Jul 18 22:45:50 2007 +0200 @@ -0,0 +1,175 @@ +% help +keyword extension - keyword expansion in local repositories + +This extension expands RCS/CVS-like or self-customized $Keywords$ +in the text files selected by your configuration. + +Keywords are only expanded in local repositories and not logged by +Mercurial internally. The mechanism can be regarded as a convenience +for the current user or archive distribution. + +Configuration is done in the [keyword] and [keywordmaps] sections of +hgrc files. + +Example: + [extensions] + hgext.keyword = + + [keyword] + # expand keywords in every python file except those matching "x*" + **.py = + x* = ignore + +Note: the more specific you are in your [keyword] filename patterns + the less you lose speed in huge repos. + +For a [keywordmaps] template mapping and expansion demonstration +run "hg kwdemo". + +An additional date template filter {date|utcdate} is provided. + +You can replace the default template mappings with customized keywords +and templates of your choice. +Again, run "hg kwdemo" to control the results of your config changes. + +When you change keyword configuration, especially the active keywords, +and do not want to store expanded keywords in change history, run +"hg kwshrink", and then change configuration. + +Caveat: "hg import" fails if the patch context contains an active + keyword. In that case run "hg kwshrink", reimport, and then + "hg kwexpand". + Or, better, use bundle/unbundle to share changes. + +list of commands (use "hg help -v keyword" to show aliases and global options): + + kwdemo print [keywordmaps] configuration and an expansion example + kwexpand expand keywords in working directory + kwshrink revert expanded keywords in working directory +% hg kwdemo +[keyword] +demo.txt = +[keywordmaps] +RCSFile = {file|basename},v +Author = {author|user} +Header = {root}/{file},v {node|short} {date|utcdate} {author|user} +Source = {root}/{file},v +Date = {date|utcdate} +Id = {file|basename},v {node|short} {date|utcdate} {author|user} +Revision = {node|short} +$RCSFile: demo.txt,v $ +$Author: test $ +$Header: /TMP/demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $ +$Source: /TMP/demo.txt,v $ +$Date: 2000/00/00 00:00:00 $ +$Id: demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $ +$Revision: xxxxxxxxxxxx $ +% kwshrink should abort in empty/invalid repo +abort: no changeset found! +% cat +expand $Id$ +ignore $Id$ +% default keyword expansion +% commit +adding a +adding b +a +b +overwriting a expanding keywords +% status +% identify +65cbcc9534b0 +% cat +expand $Id: a,v 65cbcc9534b0 1970/01/01 00:00:00 user $ +ignore $Id$ +% hg cat +expand $Id: a,v 65cbcc9534b0 1970/01/01 00:00:00 user $ +ignore $Id$ +% touch +% status +% update +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% cat +expand $Id: a,v 65cbcc9534b0 1970/01/01 00:00:00 user $ +ignore $Id$ +% copy +% commit +c + c: copy a:e6cc15c9eb5fd3c09ec691b667cf6ccd6dfb936e +overwriting c expanding keywords +% cat a c +expand $Id: a,v 65cbcc9534b0 1970/01/01 00:00:00 user $ +expand $Id: c,v 9460ba56f8d0 1970/01/01 00:00:01 user $ +% touch copied c +% status +% rollback +rolling back last transaction +% status +A c +% update -C +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +% custom keyword expansion +% try with kwdemo +[keyword] +demo.txt = +[keywordmaps] +Xinfo = {author}: {desc} +$Xinfo: test: hg keyword config and expansion example $ +% cat +expand $Id: a,v 65cbcc9534b0 1970/01/01 00:00:00 user $ +ignore $Id$ +% hg cat +expand $Id: a 65cbcc9534b0 Thu, 01 Jan 1970 00:00:00 +0000 user $ +ignore $Id$ +% interrupted commit +abort: edit failed: false exited with status 1 +transaction abort! +rollback completed +% status +M a +? log +% commit +a +overwriting a expanding keywords +% status +% cat +expand $Id: a 6ade9dd7b017 Thu, 01 Jan 1970 00:00:02 +0000 user $ +$Xinfo: User Name : firstline $ +ignore $Id$ +% hg cat +expand $Id: a 6ade9dd7b017 Thu, 01 Jan 1970 00:00:02 +0000 user $ +$Xinfo: User Name : firstline $ +ignore $Id$ +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 2 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% incoming +searching for changes +changeset: 1:6ade9dd7b017 +tag: tip +user: User Name +date: Thu Jan 01 00:00:02 1970 +0000 +summary: firstline + +% switch off expansion +% kwshrink +overwriting a shrinking keywords +% cat +expand $Id$ +$Xinfo$ +ignore $Id$ +% hg cat +expand $Id: a 6ade9dd7b017 Thu, 01 Jan 1970 00:00:02 +0000 user $ +$Xinfo: User Name : firstline $ +ignore $Id$ +% cat +expand $Id$ +$Xinfo$ +ignore $Id$ +% hg cat +expand $Id$ +$Xinfo$ +ignore $Id$