# HG changeset patch # User Christian Ebert # Date 1168277238 -3600 # Node ID fd5d3a974ea712e44517ba2bdc0c2ef6f28f1baa # Parent b285ab731fffc806b3be9b1b954437ffa14eff8b Implement self initializing pretxncommit hook This is more expensive than overriding commit(), but a) uses more "official" interface b) less code c) easier to maintain Get all changes up to cee5fef33cf8 in here as well. diff -r b285ab731fff -r fd5d3a974ea7 hgkw/keyword.py --- a/hgkw/keyword.py Fri Jan 05 14:51:58 2007 +0100 +++ b/hgkw/keyword.py Mon Jan 08 18:27:18 2007 +0100 @@ -10,22 +10,17 @@ files (like LaTeX packages), that are mostly addressed to an audience not running a version control system. -Supported keywords are (changeset 000000000000): - $Revision: 000000000000 $ - $Author: Your Name $ - $Date: %a %b %d %H:%M:%S %Y %z $ - $RCSFile: basename,v $ - $Source: /path/to/basename,v $ - $Id: basename,v 000000000000 %Y-%m-%d %H:%M:%S %z shortname $ - $Header: /path/to/basename,v 000000000000 %Y-%m-%d %H:%M:%S %z shortname $ +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 -The extension, according to its hackish nature, is a hybrid and consists -actually in 2 parts: - - 1. pure extension code (reposetup) that is triggered on checkout and - logging of changes. - 2. a pretxncommit hook (hgrc (5)) that expands keywords immediately - at commit time in the working directory. +Install: + Copy keyword.py in hgext folder. Simple setup in hgrc: @@ -34,16 +29,10 @@ # filename patterns for expansion are configured in this section [keyword] - *.sty = expand + **.py = expand ... - - # set up pretxncommit hook - [hooks] - pretxncommit = - pretxncommit.keyword = python:hgext.keyword.pretxnkw ''' -from mercurial.i18n import _ from mercurial import context, util import os.path, re @@ -56,11 +45,9 @@ '''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' @@ -71,14 +58,24 @@ util.shortuser(Author)) Header = '%s %s' % (Source, revdateauth) Id = '%s %s' % (RCSFile, revdateauth) - return '$%s: %s $' % (matchobj.group(1), eval(matchobj.group(1))) -def kwfmatchers(ui, repo): - '''Returns filename matchers from ui keyword section.''' - return [util.matcher(repo.root, '', [pat], [], [])[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 = [] + fmatchers = [util.matcher(repo.root, '', [pat], [], [])[1] for pat, opt in ui.configitems('keyword') if opt == 'expand'] + for f in files: + for mf in fmatchers: + if mf(f): + candidates.append(f) + break + return candidates def reposetup(ui, repo): @@ -94,44 +91,53 @@ 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 not self._path.startswith('.hg') and not util.binary(data): - for mf in kwfmatchers(ui, self._repo): - if mf(self._path): - ui.debug(_('expanding keywords in %s\n' % self._path)) - return re_kw.sub(lambda m: - kwexpand(m, self._repo, self._path, - fileid=node, filelog=self), - data) + if self.iskwcandidate(data): + return re_kw.sub(lambda m: + kwexpand(m, self._repo, self._path, + fileid=node, filelog=self), data) return data - def size(self, rev): - '''Overrides filelog's size() to use kwfilelog.read().''' - node = revlog.node(self, rev) - if super(kwfilelog, self).renamed(node): - return len(self.read(node)) - return revlog.size(self, rev) + 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): - '''Overrides filelog's cmp() to use kwfilelog.read().''' - if super(kwfilelog, self).renamed(node): - t2 = self.read(node) - return t2 != 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 + ui.setconfig('hooks', + 'pretxncommit.keyword', 'python:hgext.keyword.pretxnkw') 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 _ from mercurial import cmdutil, commands import sys @@ -144,20 +150,13 @@ files, match, anypats = cmdutil.matchpats(repo, sysargs, cmdopts) 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 - fmatchers = kwfmatchers(ui, repo) - for f in candidates: - for mf in fmatchers: - if mf(f): - 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) - break + 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)