--- 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