Pure extension by inserting keyword expansion localrepo.localrepository.commit solo-extension
authorChristian Ebert <blacktrash@gmx.net>
Sat, 06 Jan 2007 14:34:49 +0100
branchsolo-extension
changeset 69 4c5d9635b517
parent 68 b285ab731fff
child 70 f5818ce4c2bd
Pure extension by inserting keyword expansion localrepo.localrepository.commit Replaced self.ui with ui in commit(). This works, or seems to work, but can it be done without a complete copy of commit()?
hgkw/keyword.py
--- a/hgkw/keyword.py	Fri Jan 05 14:51:58 2007 +0100
+++ b/hgkw/keyword.py	Sat Jan 06 14:34:49 2007 +0100
@@ -10,41 +10,30 @@
 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 $
-
-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.
+Supported $keywords$ 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 %s shortname
+    Header:   /path/to/basename,v csetid %Y-%m-%d %H:%M:%S %s shortname
 
 Simple setup in hgrc:
 
     # enable extension
+    # keyword.py in hgext folder, specify full path otherwise
     hgext.keyword =
     
     # 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.node import *
 from mercurial.i18n import _
-from mercurial import context, util
+from mercurial import context, filelog, revlog, util
 import os.path, re
 
 
@@ -53,14 +42,12 @@
 
 
 def kwexpand(matchobj, repo, path, changeid=None, fileid=None, filelog=None):
-    '''Called by kwfilelog.read and pretxnkw.
+    '''Called by kwrepo.commit and kwfilelog.read.
     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,18 +58,27 @@
             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):
-    from mercurial import filelog, revlog
 
     if not repo.local():
         return
@@ -93,6 +89,164 @@
                 f = f[1:]
             return filelog.filelog(self.sopener, f, self, self.revlogversion)
 
+        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={}):
+
+                commit = []
+                remove = []
+                changed = []
+                use_dirstate = (p1 is None) # not rawcommit
+                extra = extra.copy()
+
+                if use_dirstate:
+                    if files:
+                        for f in files:
+                            s = self.dirstate.state(f)
+                            if s in 'nmai':
+                                commit.append(f)
+                            elif s == 'r':
+                                remove.append(f)
+                            else:
+                                ui.warn(_("%s not tracked!\n") % f)
+                    else:
+                        changes = self.status(match=match)[:5]
+                        modified, added, removed, deleted, unknown = changes
+                        commit = modified + added
+                        remove = removed
+                else:
+                    commit = files
+
+                if use_dirstate:
+                    p1, p2 = self.dirstate.parents()
+                    update_dirstate = True
+                else:
+                    p1, p2 = p1, p2 or nullid
+                    update_dirstate = (self.dirstate.parents()[0] == p1)
+
+                c1 = self.changelog.read(p1)
+                c2 = self.changelog.read(p2)
+                m1 = self.manifest.read(c1[0]).copy()
+                m2 = self.manifest.read(c2[0])
+
+                if use_dirstate:
+                    branchname = self.workingctx().branch()
+                    try:
+                        branchname = branchname.decode('UTF-8').encode('UTF-8')
+                    except UnicodeDecodeError:
+                        raise util.Abort(_('branch name not in UTF-8!'))
+                else:
+                    branchname = ""
+
+                if use_dirstate:
+                    oldname = c1[5].get("branch", "") # stored in UTF-8
+                    if not commit and not remove and not force and p2 == nullid and \
+                           branchname == oldname:
+                        ui.status(_("nothing changed\n"))
+                        return None
+
+                xp1 = hex(p1)
+                if p2 == nullid: xp2 = ''
+                else: xp2 = hex(p2)
+
+                self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
+
+                if not wlock:
+                    wlock = self.wlock()
+                if not lock:
+                    lock = self.lock()
+                tr = self.transaction()
+
+                # check in files
+                new = {}
+                linkrev = self.changelog.count()
+                commit.sort()
+                is_exec = util.execfunc(self.root, m1.execf)
+                is_link = util.linkfunc(self.root, m1.linkf)
+                for f in commit:
+                    ui.note(f + "\n")
+                    try:
+                        new[f] = self.filecommit(f, m1, m2, linkrev, tr, changed)
+                        m1.set(f, is_exec(f), is_link(f))
+                    except OSError:
+                        if use_dirstate:
+                            ui.warn(_("trouble committing %s!\n") % f)
+                            raise
+                        else:
+                            remove.append(f)
+
+                # update manifest
+                m1.update(new)
+                remove.sort()
+                removed = []
+
+                for f in remove:
+                    if f in m1:
+                        del m1[f]
+                        removed.append(f)
+                mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0], (new, removed))
+
+                # add changeset
+                new = new.keys()
+                new.sort()
+
+                user = user or ui.username()
+                if not text or force_editor:
+                    edittext = []
+                    if text:
+                        edittext.append(text)
+                    edittext.append("")
+                    edittext.append("HG: user: %s" % user)
+                    if p2 != nullid:
+                        edittext.append("HG: branch merge")
+                    edittext.extend(["HG: changed %s" % f for f in changed])
+                    edittext.extend(["HG: removed %s" % f for f in removed])
+                    if not changed and not remove:
+                        edittext.append("HG: no files changed")
+                    edittext.append("")
+                    # run editor in the repository root
+                    olddir = os.getcwd()
+                    os.chdir(self.root)
+                    text = ui.edit("\n".join(edittext), user)
+                    os.chdir(olddir)
+
+                lines = [line.rstrip() for line in text.rstrip().splitlines()]
+                while lines and not lines[0]:
+                    del lines[0]
+                if not lines:
+                    return None
+                text = '\n'.join(lines)
+                if branchname:
+                    extra["branch"] = branchname
+                n = self.changelog.add(mn, changed + removed, text, tr, p1, p2,
+                                       user, date, extra)
+                self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
+                          parent2=xp2)
+
+                # substitute keywords
+                for f in kwfmatches(ui, self, changed):
+                    data = self.wfile(f).read()
+                    if not util.binary(data):
+                        data, kwct = re_kw.subn(lambda m:
+                                kwexpand(m, self, f, changeid=hex(n)),
+                                data)
+                        if kwct:
+                            ui.debug(_('overwriting %s expanding keywords\n'
+                                % f))
+                            self.wfile(f, 'w').write(data)
+
+                tr.close()
+
+                if use_dirstate or update_dirstate:
+                    self.dirstate.setparents(n)
+                    if use_dirstate:
+                        self.dirstate.update(new, "n")
+                        self.dirstate.forget(removed)
+
+                self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
+                return n
+
+    
     class kwfilelog(filelog.filelog):
         def __init__(self, opener, path, repo,
                      defversion=revlog.REVLOG_DEFAULT_VERSION):
@@ -102,14 +256,12 @@
 
         def read(self, node):
             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 not util.binary(data) and \
+                    kwfmatches(ui, self._repo, [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)
             return data
 
         def size(self, rev):
@@ -127,37 +279,3 @@
 
     filelog.filelog = kwfilelog
     repo.__class__ = kwrepo
-
-
-def pretxnkw(ui, repo, hooktype, **args):
-    '''pretxncommit hook that collects candidates for keyword expansion
-    on commit and expands keywords in working dir.'''
-    from mercurial import cmdutil, commands
-    import sys
-
-    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]
-    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