# HG changeset patch # User Christian Ebert # Date 1168451529 -3600 # Node ID 790aa37e93aa6af9fbefd6d29d3ba74f4c9db5bb # Parent c55a1a72d7af8b4ecaa350e492ceee644c2394b4# Parent 67e9fb23a32b013b05dfc68c3ddde494bd3eb001 Discard head of modular branch diff -r 67e9fb23a32b -r 790aa37e93aa .hgtags --- a/.hgtags Thu Dec 21 16:00:45 2006 +0100 +++ b/.hgtags Wed Jan 10 18:52:09 2007 +0100 @@ -1,2 +1,3 @@ 536c1797202d57efb77bea098e10968ff01602ce universal_scheme ba000e29ecf3b8df09e0fd363a78cabbe3c2a78f cvs_scheme +1fe48bf82d056f1ece05baccab888357c10c5ab8 r0.1 diff -r 67e9fb23a32b -r 790aa37e93aa hgkw/keyword.py --- a/hgkw/keyword.py Thu Dec 21 16:00:45 2006 +0100 +++ b/hgkw/keyword.py Wed Jan 10 18:52:09 2007 +0100 @@ -1,12 +1,84 @@ -from mercurial.i18n import _ -from mercurial import filelog, revlog, context, util -import re +# keyword.py - keyword expansion for mercurial +# $Id$ + +'''keyword expansion hack against the grain of a DSCM + +This extension lets you expand RCS/CVS-like keywords in a Mercurial +repository. + +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. + +Supported $keywords$ and their $keyword: substition $ are: + Revision: changeset id + Author: full username + Date: %a %b %d %H:%M:%S %Y %z $ + RCSFile: basename,v + Source: /path/to/basename,v + Id: basename,v csetid %Y-%m-%d %H:%M:%S %z shortname + Header: /path/to/basename,v csetid %Y-%m-%d %H:%M:%S %z shortname + +Simple setup in hgrc: + + # enable extension + hgext.keyword = + # or, if script not in hgext folder: + # hgext.keyword = /full/path/to/script + + # filename patterns for expansion are configured in this section + [keyword] + **.py = expand + ... +''' + +from mercurial import context, util +import os.path, re, sys + + +re_kw = re.compile( + r'\$(Id|Header|Author|Date|Revision|RCSFile|Source)[^$]*?\$') + + +def kwexpand(matchobj, repo, path, changeid=None, fileid=None, filelog=None): + '''Called by kwfilelog.read and pretxnkw. + Sets supported keywords as local variables and evaluates them to + their expansion if matchobj is equal to string representation.''' + c = context.filectx(repo, path, + changeid=changeid, fileid=fileid, filelog=filelog) + date = c.date() + Revision = c.changectx() + Author = c.user() + RCSFile = os.path.basename(path)+',v' + Source = repo.wjoin(path)+',v' + Date = util.datestr(date=date) + revdateauth = '%s %s %s' % (Revision, + util.datestr(date=date, format=util.defaultdateformats[0]), + util.shortuser(Author)) + Header = '%s %s' % (Source, revdateauth) + Id = '%s %s' % (RCSFile, revdateauth) + return '$%s: %s $' % (matchobj.group(1), eval(matchobj.group(1))) + +def kwfmatches(ui, repo, files): + '''Selects candidates for keyword substitution + configured in keyword section in hgrc.''' + files = [f for f in files if not f.startswith('.hg')] + if not files: + return [] + candidates = [] + kwfmatchers = [util.matcher(repo.root, '', [pat], [], [])[1] + for pat, opt in ui.configitems('keyword') if opt == 'expand'] + for f in files: + for mf in kwfmatchers: + if mf(f): + candidates.append(f) + break + return candidates + def reposetup(ui, repo): - try: - from hgkw import kwutil - except ImportError, e: - raise util.Abort(_('%s\nkeyword extension needs package hgkw\n') % e) + from mercurial import filelog, revlog if not repo.local(): return @@ -18,44 +90,75 @@ return filelog.filelog(self.sopener, f, self, self.revlogversion) class kwfilelog(filelog.filelog): + ''' + Superclass over filelog to customize it's read, add, cmp methods. + Keywords are "stored" unexpanded, and expanded on reading. + ''' def __init__(self, opener, path, repo, defversion=revlog.REVLOG_DEFAULT_VERSION): super(kwfilelog, self).__init__(opener, path, defversion) self._repo = repo self._path = path - def read(self, node): - data = super(kwfilelog, self).read(node) - if util.binary(data): - return data - - c = context.filectx(self._repo, self._path, fileid=node, - filelog=self) - f = c.path() - if f.startswith('.hg'): - return data + def iskwcandidate(self, data): + '''Decides whether to act on keywords.''' + return (kwfmatches(ui, self._repo, [self._path]) + and not util.binary(data)) - for pat, opt in self._repo.ui.configitems('keyword'): - if opt == 'expand': - mf = util.matcher(self._repo.root, - '', [pat], [], [])[1] - if mf(f): - - def kwexpand(matchobj): - return kwutil.kwexpand(matchobj, - self._repo, c.changectx(), f, - c.date(), c.user()) - - re_kw = re.compile(r'\$(%s)\$' % kwutil.hgkeywords) - return re_kw.sub(kwexpand, data) + def read(self, node): + '''Substitutes keywords when reading filelog.''' + data = super(kwfilelog, self).read(node) + if self.iskwcandidate(data): + return re_kw.sub(lambda m: + kwexpand(m, self._repo, self._path, + fileid=node, filelog=self), data) return data def add(self, text, meta, tr, link, p1=None, p2=None): - if (not util.binary(text) and - self._repo.ui.config('keyword', 'remove', True)): - re_kw = re.compile(r'\$(%s): [^$]+? \$' % kwutil.hgkeywords) + '''Removes keyword substitutions when adding to filelog.''' + if self.iskwcandidate(text): text = re_kw.sub(r'$\1$', text) - return super(kwfilelog, self).add(text, meta, tr, link, p1, p2) + return super(kwfilelog, self).add(text, + meta, tr, link, p1=None, p2=None) + + def cmp(self, node, text): + '''Removes keyword substitutions for comparison.''' + if self.iskwcandidate(text): + text = re_kw.sub(r'$\1$', text) + return super(kwfilelog, self).cmp(node, text) filelog.filelog = kwfilelog repo.__class__ = kwrepo + # import keyword from pretxncommit hook even if it is not in hgext folder + sys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) + ui.setconfig('hooks', 'pretxncommit.keyword', 'python:%s.pretxnkw' + % os.path.splitext(os.path.basename(__file__))[0]) + + +def pretxnkw(ui, repo, hooktype, **args): + '''pretxncommit hook that collects candidates for keyword expansion + on commit and expands keywords in working dir.''' + from mercurial.i18n import gettext as _ + # above line for backwards compatibility; can be changed to + # from mercurial.i18n import _ + # some day + from mercurial import cmdutil, commands + + if hooktype != 'pretxncommit': + return True + + cmd, sysargs, globalopts, cmdopts = commands.parse(ui, sys.argv[1:])[1:] + if repr(cmd).split()[1] in ('tag', 'import_'): + return False + + files, match, anypats = cmdutil.matchpats(repo, sysargs, cmdopts) + modified, added = repo.status(files=files, match=match)[:2] + + for f in kwfmatches(ui, repo, modified+added): + data = repo.wfile(f).read() + if not util.binary(data): + data, kwct = re_kw.subn(lambda m: + kwexpand(m, repo, f, changeid=args['node']), data) + if kwct: + ui.debug(_('overwriting %s expanding keywords\n' % f)) + repo.wfile(f, 'w').write(data) diff -r 67e9fb23a32b -r 790aa37e93aa hgkw/kwutil.py --- a/hgkw/kwutil.py Thu Dec 21 16:00:45 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -''' -kwutil provides required little helpers for the Mercurial -keyword extension and the Python pretxncommit hook pretxnkw. -''' - -from mercurial import util -import os.path - -# supported keywords for use in regexes -hgkeywords = 'Id|Header|Author|Date|Revision|RCSFile|Source' - -def kwexpand(matchobj, repo, Revision, f, date, Author): - '''Called by keyword extension and pretxnkw pretxncomit hook, - sets supported keywords as local variables and evaluates them - to their expansion if matchobj is equal to their string - representation.''' - RCSFile = os.path.basename(f)+',v' - Source = os.path.join(repo.root, f)+',v' - Date = util.datestr(date) - revdateauth = '%s %s %s' % (Revision, - util.datestr(date=date, format=util.defaultdateformats[0]), - # %Y-%m-%d %H:%M:%S - util.shortuser(Author)) - Header = '%s %s' % (Source, revdateauth) - Id = '%s %s' % (RCSFile, revdateauth) - return '$%s: %s $' % (matchobj.group(1), eval(matchobj.group(1))) diff -r 67e9fb23a32b -r 790aa37e93aa hgkw/pretxnkw.py --- a/hgkw/pretxnkw.py Thu Dec 21 16:00:45 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -from hgkw import kwutil -from mercurial.i18n import _ -from mercurial import cmdutil, commands, util -import re, sys - -def pretxnkw(ui, repo, hooktype, **args): - '''Collects candidates for keyword expansion on commit - and expands keywords in working dir. - NOTE: for use in combination with hgext.keyword!''' - - if hooktype != 'pretxncommit': - # bail out with error - return True - - # reparse args, opts again as pretxncommit hook is silent about them - cmd, sysargs, globalopts, cmdopts = commands.parse(ui, sys.argv[1:])[1:] - # exclude tag and import - if repr(cmd).split()[1] in ('tag', 'import_'): - return False - files, match, anypats = cmdutil.matchpats(repo, sysargs, cmdopts) - - # validity checks should have been done already - modified, added = repo.status(files=files, match=match)[:2] - candidates = [f for f in modified + added if not f.startswith('.hg')] - - if not candidates: - return False - - # only check files that have hgkwencode assigned as encode filter - files = [] - # python2.4: files = set() - for pat, opt in repo.ui.configitems('keyword'): - if opt == 'expand': - mf = util.matcher(repo.root, '', [pat], [], [])[1] - for candidate in candidates: - if mf(candidate) and candidate not in files: - files.append(candidate) - # python2.4: - # if mf(candidate): files.add(candidate) - - if not files: # nothing to do - return False - - user, date = repo.changelog.read(repo.changelog.tip())[1:3] - re_kw = re.compile(r'\$(%s)(: [^$]+? )?\$' % kwutil.hgkeywords) - - for f in files: - data = repo.wfile(f).read() - if not util.binary(data): - - def kwexpand(matchobj): - return kwutil.kwexpand(matchobj, - repo, args['node'][:12], f, date, user) - - data, kwct = re_kw.subn(kwexpand, data) - if kwct: - ui.note(_('expanding keywords in %s\n' % f)) - # backup file? - repo.wfile(f, 'w').write(data)