Implement self initializing pretxncommit hook self_initializing_hook
authorChristian Ebert <blacktrash@gmx.net>
Mon, 08 Jan 2007 18:27:18 +0100
branchself_initializing_hook
changeset 81 fd5d3a974ea7
parent 68 b285ab731fff
child 82 9bf0f7db5928
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.
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 <address@example.com> $
-    $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)