--- a/hgkw/keyword.py Tue Feb 12 21:38:30 2008 +0100
+++ b/hgkw/keyword.py Thu Feb 14 15:55:21 2008 +0100
@@ -85,8 +85,8 @@
Or, better, use bundle/unbundle to share changes.
'''
-from mercurial import commands, cmdutil, context, fancyopts
-from mercurial import filelog, localrepo, revlog, templater, util
+from mercurial import commands, cmdutil, context, fancyopts, filelog
+from mercurial import localrepo, patch, revlog, templater, util
from mercurial.node import *
from mercurial.i18n import gettext as _
import getopt, os, re, shutil, tempfile, time
@@ -100,16 +100,17 @@
# hg commands that trigger expansion only when writing to working dir,
# not when reading filelog, and unexpand when reading from working dir
-restricted = 'diff1 record qfold qimport qnew qpush qrefresh qrecord'
+restricted = 'record qfold qimport qnew qpush qrefresh qrecord'
def utcdate(date):
'''Returns hgdate in cvs-like UTC format.'''
return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
-_kwtemplater = _cmd = _cmdoptions = None
+# make keyword tools accessible
+kwx = { 'templater': None, 'hgcmd': None }
-# backwards compatibility hacks
+# monkeypatches and backwards compatibility hacks
try:
# cmdutil.parse moves to dispatch._parse in 18a9fbb5cd78
@@ -123,11 +124,10 @@
_dispatch_parse = commands.parse
def _kwdispatch_parse(ui, args):
- '''Monkeypatch dispatch._parse to obtain
- current command and command options (global _cmd, _cmdoptions).'''
- global _cmd, _cmdoptions
- _cmd, func, args, options, _cmdoptions = _dispatch_parse(ui, args)
- return _cmd, func, args, options, _cmdoptions
+ '''Monkeypatch dispatch._parse to obtain running hg command.'''
+ cmd, func, args, options, cmdoptions = _dispatch_parse(ui, args)
+ kwx['hgcmd'] = cmd
+ return cmd, func, args, options, cmdoptions
try:
setattr(dispatch, '_parse', _kwdispatch_parse)
@@ -140,8 +140,7 @@
try:
# avoid spurious rejects if patchfile is available
- from mercurial.patch import patchfile
- _patchfile_init = patchfile.__init__
+ _patchfile_init = patch.patchfile.__init__
def _kwpatchfile_init(self, ui, fname, missing=False):
'''Monkeypatch/wrap patch.patchfile.__init__ to avoid
@@ -151,30 +150,34 @@
except TypeError:
# "missing" arg added in e90e72c6b4c7
_patchfile_init(self, ui, fname)
- if _kwtemplater.matcher(self.fname):
- # shrink keywords read from working dir
- kwshrunk = _kwtemplater.shrink(''.join(self.lines))
- self.lines = kwshrunk.splitlines(True)
-except ImportError:
+ self.lines = kwx['templater'].shrinklines(self.fname, self.lines)
+except AttributeError:
pass
+_patch_diff = patch.diff
+def _kw_diff(repo, node1=None, node2=None, files=None, match=util.always,
+ fp=None, changes=None, opts=None):
+ # only expand if comparing against working dir
+ if node2 is not None:
+ kwx['templater'].matcher = util.never
+ elif node1 is not None and node1 != repo.changectx().node():
+ kwx['templater'].restrict = True
+ _patch_diff(repo, node1=node1, node2=node2, files=files, match=match,
+ fp=fp, changes=changes, opts=opts)
+
try:
from mercurial.hgweb import webcommands
def _kwweb_changeset(web, req, tmpl):
'''Wraps webcommands.changeset turning off keyword expansion.'''
- try:
- _kwtemplater.matcher = util.never
- except AttributeError:
- pass
+ if kwx['templater']:
+ kwx['templater'].matcher = util.neve
return web.changeset(tmpl, web.changectx(req))
def _kwweb_filediff(web, req, tmpl):
'''Wraps webcommands.filediff turning off keyword expansion.'''
- try:
- _kwtemplater.matcher = util.never
- except AttributeError:
- pass
+ if kwx['templater']:
+ kwx['templater'].matcher = util.neve
return web.filediff(tmpl, web.filectx(req))
webcommands.changeset = webcommands.rev = _kwweb_changeset
@@ -184,17 +187,13 @@
from mercurial.hgweb.hgweb_mod import hgweb
def _kwweb_do_changeset(self, req):
- try:
- _kwtemplater.matcher = util.never
- except AttributeError:
- pass
+ if kwx['templater']:
+ kwx['templater'].matcher = util.never
req.write(self.changeset(self.changectx(req)))
def _kwweb_do_filediff(self, req):
- try:
- _kwtemplater.matcher = util.never
- except AttributeError:
- pass
+ if kwx['templater']:
+ kwx['templater'].matcher = util.never
req.write(self.filediff(self.filectx(req)))
hgweb.do_changeset = hgweb.do_rev = _kwweb_do_changeset
@@ -308,13 +307,11 @@
'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
}
- def __init__(self, ui, repo, inc, exc, hgcmd):
+ def __init__(self, ui, repo, inc, exc):
self.ui = ui
self.repo = repo
self.matcher = util.matcher(repo.root, inc=inc, exc=exc)[1]
- self.restrict = hgcmd in restricted.split()
- self.commitnode = None
- self.path = ''
+ self.restrict = kwx['hgcmd'] in restricted.split()
kwmaps = self.ui.configitems('keywordmaps')
if kwmaps: # override default templates
@@ -338,46 +335,89 @@
return cmdutil.changeset_templater(self.ui, self.repo,
False, None, '', False)
- def substitute(self, node, data, subfunc):
- '''Obtains file's changenode if commit node not given,
- and calls given substitution function.'''
- if self.commitnode:
- fnode = self.commitnode
- else:
- c = context.filectx(self.repo, self.path, fileid=node)
- fnode = c.node()
+ def getnode(self, path, fnode):
+ '''Derives changenode from file context.'''
+ c = context.filectx(self.repo, path, fileid=fnode)
+ return c.node()
+ def substitute(self, data, path, node, subfunc):
+ '''Replaces keywords in data with expanded template.'''
def kwsub(mobj):
- '''Substitutes keyword using corresponding template.'''
kw = mobj.group(1)
self.ct.use_template(self.templates[kw])
self.ui.pushbuffer()
- self.ct.show(changenode=fnode, root=self.repo.root, file=self.path)
+ self.ct.show(changenode=node, root=self.repo.root, file=path)
return '$%s: %s $' % (kw, template_firstline(self.ui.popbuffer()))
-
return subfunc(kwsub, data)
- def expand(self, node, data):
+ def expand(self, path, node, data):
'''Returns data with keywords expanded.'''
- if self.restrict or util.binary(data):
- return data
- return self.substitute(node, data, self.re_kw.sub)
+ if not self.restrict and self.matcher(path) and not util.binary(data):
+ changenode = self.getnode(path, node)
+ return self.substitute(data, path, changenode, self.re_kw.sub)
+ return data
+
+ def iskwfile(self, path, islink):
+ '''Returns true if path matches [keyword] pattern
+ and is not a symbolic link.
+ Caveat: localrepository._link fails on Windows.'''
+ return self.matcher(path) and not islink(path)
- def process(self, node, data, expand):
- '''Returns a tuple: data, count.
- Count is number of keywords/keyword substitutions,
- telling caller whether to act on file containing data.'''
- if util.binary(data):
- return data, None
- if expand:
- return self.substitute(node, data, self.re_kw.subn)
- return data, self.re_kw.search(data)
+ def overwrite(self, node=None, expand=True, files=None):
+ '''Overwrites selected files expanding/shrinking keywords.'''
+ ctx = self.repo.changectx(node)
+ mf = ctx.manifest()
+ if node is not None: # commit
+ files = [f for f in ctx.files() if f in mf]
+ notify = self.ui.debug
+ else: # kwexpand/kwshrink
+ notify = self.ui.note
+ candidates = [f for f in files if self.iskwfile(f, mf.linkf)]
+ if candidates:
+ self.restrict = True # do not expand when reading
+ candidates.sort()
+ action = expand and 'expanding' or 'shrinking'
+ overwritten = []
+ for f in candidates:
+ fp = self.repo.file(f)
+ data = fp.read(mf[f])
+ if util.binary(data):
+ continue
+ if expand:
+ changenode = node or self.getnode(f, mf[f])
+ data, found = self.substitute(data, f, changenode,
+ self.re_kw.subn)
+ else:
+ found = self.re_kw.search(data)
+ if found:
+ notify(_('overwriting %s %s keywords\n') % (f, action))
+ self.repo.wwrite(f, data, mf.flags(f))
+ overwritten.append(f)
+ _normal(self.repo, overwritten)
+ self.restrict = False
- def shrink(self, text):
+ def shrinktext(self, text):
+ '''Unconditionally removes all keyword substitutions from text.'''
+ return self.re_kw.sub(r'$\1$', text)
+
+ def shrink(self, fname, text):
'''Returns text with all keyword substitutions removed.'''
- if util.binary(text):
- return text
- return self.re_kw.sub(r'$\1$', text)
+ if self.matcher(fname) and not util.binary(text):
+ return self.shrinktext(text)
+ return text
+
+ def shrinklines(self, fname, lines):
+ '''Returns lines with keyword substitutions removed.'''
+ if self.matcher(fname):
+ text = ''.join(lines)
+ if not util.binary(text):
+ return self.shrinktext(text).splitlines(True)
+ return lines
+
+ def wread(self, fname, data):
+ '''If in restricted mode returns data read from wdir with
+ keyword substitutions removed.'''
+ return self.restrict and self.shrink(fname, data) or data
class kwfilelog(filelog.filelog):
'''
@@ -386,71 +426,41 @@
'''
def __init__(self, opener, path):
super(kwfilelog, self).__init__(opener, path)
- _kwtemplater.path = path
-
- def kwctread(self, node, expand):
- '''Reads expanding and counting keywords, called from _overwrite.'''
- data = super(kwfilelog, self).read(node)
- return _kwtemplater.process(node, data, expand)
+ self.kwt = kwx['templater']
+ self.path = path
def read(self, node):
'''Expands keywords when reading filelog.'''
data = super(kwfilelog, self).read(node)
- return _kwtemplater.expand(node, data)
+ return self.kwt.expand(self.path, node, data)
def add(self, text, meta, tr, link, p1=None, p2=None):
'''Removes keyword substitutions when adding to filelog.'''
- text = _kwtemplater.shrink(text)
+ text = self.kwt.shrink(self.path, text)
return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2)
def cmp(self, node, text):
'''Removes keyword substitutions for comparison.'''
- text = _kwtemplater.shrink(text)
+ text = self.kwt.shrink(self.path, text)
if self.renamed(node):
t2 = super(kwfilelog, self).read(node)
return t2 != text
return revlog.revlog.cmp(self, node, text)
-def _iskwfile(f, link):
- return not link(f) and _kwtemplater.matcher(f)
-
-def _status(ui, repo, *pats, **opts):
+def _status(ui, repo, kwt, *pats, **opts):
'''Bails out if [keyword] configuration is not active.
Returns status of working directory.'''
- if _kwtemplater:
+ if kwt:
files, match, anypats = cmdutil.matchpats(repo, pats, opts)
return repo.status(files=files, match=match, list_clean=True)
if ui.configitems('keyword'):
raise util.Abort(_('[keyword] patterns cannot match'))
raise util.Abort(_('no [keyword] patterns configured'))
-def _overwrite(ui, repo, node=None, expand=True, files=None):
- '''Overwrites selected files expanding/shrinking keywords.'''
- ctx = repo.changectx(node)
- mf = ctx.manifest()
- if node is not None: # commit
- _kwtemplater.commitnode = node
- files = [f for f in ctx.files() if f in mf]
- notify = ui.debug
- else: # kwexpand/kwshrink
- notify = ui.note
- candidates = [f for f in files if _iskwfile(f, mf.linkf)]
- if candidates:
- overwritten = []
- candidates.sort()
- action = expand and 'expanding' or 'shrinking'
- for f in candidates:
- fp = repo.file(f, kwmatch=True)
- data, kwfound = fp.kwctread(mf[f], expand)
- if kwfound:
- notify(_('overwriting %s %s keywords\n') % (f, action))
- _wwrite(repo, f, data, mf)
- overwritten.append(f)
- _normal(repo, overwritten)
-
def _kwfwrite(ui, repo, expand, *pats, **opts):
- '''Selects files and passes them to _overwrite.'''
- status = _status(ui, repo, *pats, **opts)
+ '''Selects files and passes them to kwtemplater.overwrite.'''
+ kwt = kwx['templater']
+ status = _status(ui, repo, kwt, *pats, **opts)
modified, added, removed, deleted, unknown, ignored, clean = status
if modified or added or removed or deleted:
raise util.Abort(_('outstanding uncommitted changes in given files'))
@@ -458,7 +468,7 @@
try:
wlock = repo.wlock()
lock = repo.lock()
- _overwrite(ui, repo, expand=expand, files=clean)
+ kwt.overwrite(expand=expand, files=clean)
finally:
del wlock, lock
@@ -560,7 +570,8 @@
keyword expansion.
That is, files matched by [keyword] config patterns but not symlinks.
'''
- status = _status(ui, repo, *pats, **opts)
+ kwt = kwx['templater']
+ status = _status(ui, repo, kwt, *pats, **opts)
modified, added, removed, deleted, unknown, ignored, clean = status
files = modified + added + clean
if opts.get('untracked'):
@@ -572,7 +583,7 @@
else:
mf = wctx.manifest()
islink = mf.linkf
- kwfiles = [f for f in files if _iskwfile(f, islink)]
+ kwfiles = [f for f in files if kwt.iskwfile(f, islink)]
cwd = pats and repo.getcwd() or ''
kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
if opts.get('all') or opts.get('ignore'):
@@ -603,11 +614,8 @@
This is done for local repos only, and only if there are
files configured at all for keyword substitution.'''
- global _kwtemplater
- hgcmd, hgcmdopts = _cmd, _cmdoptions
-
try:
- if (not repo.local() or hgcmd in nokwcommands.split()
+ if (not repo.local() or kwx['hgcmd'] in nokwcommands.split()
or '.hg' in repo.root.split(os.sep)
or repo._url.startswith('bundle:')):
return
@@ -623,30 +631,17 @@
if not inc:
return
- if hgcmd == 'diff':
- # only expand if comparing against working dir
- node1, node2 = cmdutil.revpair(repo, hgcmdopts.get('rev'))
- if node2 is not None:
- return
- # shrink if rev is not current node
- if node1 is not None and node1 != repo.changectx().node():
- hgcmd = 'diff1'
-
- _kwtemplater = kwtemplater(ui, repo, inc, exc, hgcmd)
+ kwx['templater'] = kwt = kwtemplater(ui, repo, inc, exc)
class kwrepo(repo.__class__):
- def file(self, f, kwmatch=False):
+ def file(self, f):
if f[0] == '/':
f = f[1:]
- if kwmatch or _kwtemplater.matcher(f):
- return kwfilelog(self.sopener, f)
- return filelog.filelog(self.sopener, f)
+ return kwfilelog(self.sopener, f)
def wread(self, filename):
data = super(kwrepo, self).wread(filename)
- if _kwtemplater.restrict and _kwtemplater.matcher(filename):
- return _kwtemplater.shrink(data)
- return data
+ return kwt.wread(filename, data)
def _commit(self, files, text, user, date, match, force, lock, wlock,
force_editor, p1, p2, extra, empty_ok):
@@ -716,17 +711,16 @@
for name, cmd in commithooks.iteritems():
ui.setconfig('hooks', name, cmd)
if node is not None:
- _overwrite(ui, self, node=node)
+ kwt.overwrite(node=node)
repo.hook('commit', node=node, parent1=_p1, parent2=_p2)
return node
finally:
del _wlock, _lock
repo.__class__ = kwrepo
- try:
- patchfile.__init__ = _kwpatchfile_init
- except NameError:
- pass
+ patch.diff = _kw_diff
+ if hasattr(patch, 'patchfile'):
+ patch.patchfile.__init__ = _kwpatchfile_init
cmdtable = {