hgkw/keyword.py
branchsolo-extension
changeset 69 4c5d9635b517
parent 68 b285ab731fff
child 71 f7a2a246740c
--- 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