# HG changeset patch # User Christian Ebert # Date 1168451369 -3600 # Node ID c55a1a72d7af8b4ecaa350e492ceee644c2394b4 # Parent f8f0296775adab4273944f8f765f94819888bf40# Parent e29cf107baf2e6e56124ce757f550120aa1ece3b Discard head of decodefilter branch diff -r e29cf107baf2 -r c55a1a72d7af .hgtags --- a/.hgtags Fri Dec 15 01:21:56 2006 +0100 +++ b/.hgtags Wed Jan 10 18:49:29 2007 +0100 @@ -1,1 +1,3 @@ -56a61a5c696da7148c22b54a25158432ef801ab4 filename +536c1797202d57efb77bea098e10968ff01602ce universal_scheme +ba000e29ecf3b8df09e0fd363a78cabbe3c2a78f cvs_scheme +1fe48bf82d056f1ece05baccab888357c10c5ab8 r0.1 diff -r e29cf107baf2 -r c55a1a72d7af hgkw/hgkwdecode.py --- a/hgkw/hgkwdecode.py Fri Dec 15 01:21:56 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -# $Hg: hgkw/hgkwdecode.py,v$ - -from mercurial.demandload import demandload -demandload(globals(), 'hgkw:kwutil mercurial:localrepo re sys') - -def kwdecode(): - '''Expands keywords into IO stream.''' - - repo = localrepo.localrepository(None) - - kword = kwutil.mkkw(repo, tip=False, node='') - re_kw = kwutil.rekw() - - sys.stdout.write(re_kw.sub(kword, sys.stdin.read())) diff -r e29cf107baf2 -r c55a1a72d7af hgkw/hgkwencode.py --- a/hgkw/hgkwencode.py Fri Dec 15 01:21:56 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -# $Hg: hgkw/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 e29cf107baf2 -r c55a1a72d7af hgkw/keyword.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgkw/keyword.py Wed Jan 10 18:49:29 2007 +0100 @@ -0,0 +1,164 @@ +# 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): + from mercurial import filelog, revlog + + if not repo.local(): + return + + class kwrepo(repo.__class__): + def file(self, f): + if f[0] == '/': + f = f[1:] + 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 iskwcandidate(self, data): + '''Decides whether to act on keywords.''' + return (kwfmatches(ui, self._repo, [self._path]) + and not util.binary(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): + '''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=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 e29cf107baf2 -r c55a1a72d7af hgkw/kwutil.py --- a/hgkw/kwutil.py Fri Dec 15 01:21:56 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -# $Hg: hgkw/kwutil.py,v$ - -from mercurial.demandload import demandload -demandload(globals(), 'mercurial:util re') - -def mkkw(repo, tip=False, node=''): - '''Gathers info for Hg keyword and returns it as raw replacement string.''' - - # get hex - if tip: - p = repo.changelog.tip() - else: - p = repo.dirstate.parents()[0] - - if not node: - node = repo.changectx(p) - - user, date = repo.changelog.read(p)[1:3] - user = util.shortuser(user) - date = util.datestr(date=date, format=util.defaultdateformats[0]) - # %Y-%m-%d %H:%M:%S - - return r'\1 %s %s %s $' % (node, date, user) - - -def rekw(): - '''Returns compiled regex to detect hg keywords.''' - - return re.compile(r'([$]Hg: .+?,v).*?\$') diff -r e29cf107baf2 -r c55a1a72d7af hgkw/pretxnkw.py --- a/hgkw/pretxnkw.py Fri Dec 15 01:21:56 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -# $Hg: hgkw/pretxnkw.py,v$ - -from mercurial.i18n import gettext as _ -from mercurial.demandload import demandload -demandload(globals(), 'hgkw:kwutil mercurial:util re') - -kwencodefilter = 'hgkwencode' - -def pretxnkw(ui=None, repo=None, hooktype='', **args): - '''Important: returns False on success, True on failure.''' - - node = args['node'][0:12] - - if not ui or not repo or not node or hooktype != 'pretxncommit': - # bail out with error - return True - - modified, added = repo.status()[:2] - candidates = modified + added - - files = [] - for pat, cmd in repo.ui.configitems('encode'): - if cmd.endswith(kwencodefilter): - mf = util.matcher(repo.root, '', [pat], [], [])[1] - for candidate in candidates: - if mf(candidate): - files.append(candidate) - - if not files: # nothing to do - return False - - re_kw = kwutil.rekw() - kword = kwutil.mkkw(repo, tip=True, node=node) - - re_kwcheck = re.compile(r'[$]Hg: (.*?),v.*?\$') - - for f in files: - - data = repo.wfile(f).read() - - # check for keywords with incorrect filename - # eg. if you forgot to update filename manually after "hg mv" - invalids = [m for m in map(str, re_kwcheck.findall(data)) - if m != f] - if invalids: - invalids = ['%sHg: %s,v$' % ('$', i) for i in invalids] - ui.warn(_('%d invalid keyword filenames in file %s:\n' - '%s\nplease correct to %sHg: %s,v$\n' - % (len(invalids), f, ', '.join(invalids), '$', f))) - return True - - # substitute (Hg|Id): ,v.* - data, kwct = re_kw.subn(kword, data) - - if kwct: - # backup file and write with expanded keyword - ui.note(_('expanding keywords in %s\n' % f)) - absname = repo.wjoin(f) - util.copyfile(absname, absname+'~') - repo.wfile(f, 'w').write(data) - - return False diff -r e29cf107baf2 -r c55a1a72d7af hgkwdecode --- a/hgkwdecode Fri Dec 15 01:21:56 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -#!/usr/bin/env python -# $Hg: hgkwdecode,v$ - -from hgkw import hgkwdecode - -hgkwdecode.kwdecode() diff -r e29cf107baf2 -r c55a1a72d7af hgkwencode --- a/hgkwencode Fri Dec 15 01:21:56 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()