summaryrefslogtreecommitdiff
path: root/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/keyword.py
diff options
context:
space:
mode:
authorNishanth Amuluru2011-01-08 11:20:57 +0530
committerNishanth Amuluru2011-01-08 11:20:57 +0530
commit65411d01d448ff0cd4abd14eee14cf60b5f8fc20 (patch)
treeb4c404363c4c63a61d6e2f8bd26c5b057c1fb09d /eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/keyword.py
parent2e35094d43b4cc6974172e1febf76abb50f086ec (diff)
downloadpytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.tar.gz
pytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.tar.bz2
pytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.zip
Added buildout stuff and made changes accordingly
--HG-- rename : profile/management/__init__.py => eggs/djangorecipe-0.20-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/djangorecipe-0.20-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/infrae.subversion-1.4.5-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/infrae.subversion-1.4.5-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/py-1.4.0-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/py-1.4.0-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/zc.buildout-1.5.2-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/zc.buildout-1.5.2-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/zc.recipe.egg-1.3.2-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/zc.recipe.egg-1.3.2-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => parts/django/Django.egg-info/dependency_links.txt rename : taskapp/models.py => parts/django/django/conf/app_template/models.py rename : taskapp/tests.py => parts/django/django/conf/app_template/tests.py rename : taskapp/views.py => parts/django/django/conf/app_template/views.py rename : taskapp/views.py => parts/django/django/contrib/gis/tests/geo3d/views.py rename : profile/management/__init__.py => parts/django/tests/modeltests/delete/__init__.py rename : profile/management/__init__.py => parts/django/tests/modeltests/files/__init__.py rename : profile/management/__init__.py => parts/django/tests/modeltests/invalid_models/__init__.py rename : profile/management/__init__.py => parts/django/tests/modeltests/m2m_signals/__init__.py rename : profile/management/__init__.py => parts/django/tests/modeltests/model_package/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/management/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/management/commands/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/models.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/delete_regress/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/file_storage/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/max_lengths/__init__.py rename : profile/forms.py => pytask/profile/forms.py rename : profile/management/__init__.py => pytask/profile/management/__init__.py rename : profile/management/commands/seed_db.py => pytask/profile/management/commands/seed_db.py rename : profile/models.py => pytask/profile/models.py rename : profile/templatetags/user_tags.py => pytask/profile/templatetags/user_tags.py rename : taskapp/tests.py => pytask/profile/tests.py rename : profile/urls.py => pytask/profile/urls.py rename : profile/utils.py => pytask/profile/utils.py rename : profile/views.py => pytask/profile/views.py rename : static/css/base.css => pytask/static/css/base.css rename : taskapp/tests.py => pytask/taskapp/tests.py rename : taskapp/views.py => pytask/taskapp/views.py rename : templates/base.html => pytask/templates/base.html rename : templates/profile/browse_notifications.html => pytask/templates/profile/browse_notifications.html rename : templates/profile/edit.html => pytask/templates/profile/edit.html rename : templates/profile/view.html => pytask/templates/profile/view.html rename : templates/profile/view_notification.html => pytask/templates/profile/view_notification.html rename : templates/registration/activate.html => pytask/templates/registration/activate.html rename : templates/registration/activation_email.txt => pytask/templates/registration/activation_email.txt rename : templates/registration/activation_email_subject.txt => pytask/templates/registration/activation_email_subject.txt rename : templates/registration/logged_out.html => pytask/templates/registration/logged_out.html rename : templates/registration/login.html => pytask/templates/registration/login.html rename : templates/registration/logout.html => pytask/templates/registration/logout.html rename : templates/registration/password_change_done.html => pytask/templates/registration/password_change_done.html rename : templates/registration/password_change_form.html => pytask/templates/registration/password_change_form.html rename : templates/registration/password_reset_complete.html => pytask/templates/registration/password_reset_complete.html rename : templates/registration/password_reset_confirm.html => pytask/templates/registration/password_reset_confirm.html rename : templates/registration/password_reset_done.html => pytask/templates/registration/password_reset_done.html rename : templates/registration/password_reset_email.html => pytask/templates/registration/password_reset_email.html rename : templates/registration/password_reset_form.html => pytask/templates/registration/password_reset_form.html rename : templates/registration/registration_complete.html => pytask/templates/registration/registration_complete.html rename : templates/registration/registration_form.html => pytask/templates/registration/registration_form.html rename : utils.py => pytask/utils.py
Diffstat (limited to 'eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/keyword.py')
-rw-r--r--eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/keyword.py649
1 files changed, 649 insertions, 0 deletions
diff --git a/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/keyword.py b/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/keyword.py
new file mode 100644
index 0000000..9060714
--- /dev/null
+++ b/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/keyword.py
@@ -0,0 +1,649 @@
+# keyword.py - $Keyword$ expansion for Mercurial
+#
+# Copyright 2007-2010 Christian Ebert <blacktrash@gmx.net>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+#
+# $Id$
+#
+# Keyword expansion hack against the grain of a DSCM
+#
+# There are many good reasons why this is not needed in a distributed
+# SCM, still it may be useful in very small projects based on single
+# files (like LaTeX packages), that are mostly addressed to an
+# audience not running a version control system.
+#
+# For in-depth discussion refer to
+# <http://mercurial.selenic.com/wiki/KeywordPlan>.
+#
+# Keyword expansion is based on Mercurial's changeset template mappings.
+#
+# Binary files are not touched.
+#
+# Files to act upon/ignore are specified in the [keyword] section.
+# Customized keyword template mappings in the [keywordmaps] section.
+#
+# Run "hg help keyword" and "hg kwdemo" to get info on configuration.
+
+'''expand keywords in tracked files
+
+This extension expands RCS/CVS-like or self-customized $Keywords$ in
+tracked text files selected by your configuration.
+
+Keywords are only expanded in local repositories and not stored in the
+change history. The mechanism can be regarded as a convenience for the
+current user or for archive distribution.
+
+Keywords expand to the changeset data pertaining to the latest change
+relative to the working directory parent of each file.
+
+Configuration is done in the [keyword], [keywordset] and [keywordmaps]
+sections of hgrc files.
+
+Example::
+
+ [keyword]
+ # expand keywords in every python file except those matching "x*"
+ **.py =
+ x* = ignore
+
+ [keywordset]
+ # prefer svn- over cvs-like default keywordmaps
+ svn = True
+
+.. note::
+ The more specific you are in your filename patterns the less you
+ lose speed in huge repositories.
+
+For [keywordmaps] template mapping and expansion demonstration and
+control run :hg:`kwdemo`. See :hg:`help templates` for a list of
+available templates and filters.
+
+Three additional date template filters are provided::
+
+ utcdate "2006/09/18 15:13:13"
+ svnutcdate "2006-09-18 15:13:13Z"
+ svnisodate "2006-09-18 08:13:13 -700 (Mon, 18 Sep 2006)"
+
+The default template mappings (view with :hg:`kwdemo -d`) can be
+replaced with customized keywords and templates. Again, run
+:hg:`kwdemo` to control the results of your configuration changes.
+
+Before changing/disabling active keywords, run :hg:`kwshrink` to avoid
+the risk of inadvertently storing expanded keywords in the change
+history.
+
+To force expansion after enabling it, or a configuration change, run
+:hg:`kwexpand`.
+
+Expansions spanning more than one line and incremental expansions,
+like CVS' $Log$, are not supported. A keyword template map "Log =
+{desc}" expands to the first line of the changeset description.
+'''
+
+from mercurial import commands, context, cmdutil, dispatch, filelog, extensions
+from mercurial import localrepo, match, patch, templatefilters, templater, util
+from mercurial.hgweb import webcommands
+from mercurial.i18n import _
+import os, re, shutil, tempfile
+
+commands.optionalrepo += ' kwdemo'
+
+# hg commands that do not act on keywords
+nokwcommands = ('add addremove annotate bundle export grep incoming init log'
+ ' outgoing push tip verify convert email glog')
+
+# hg commands that trigger expansion only when writing to working dir,
+# not when reading filelog, and unexpand when reading from working dir
+restricted = 'merge kwexpand kwshrink record qrecord resolve transplant'
+
+# names of extensions using dorecord
+recordextensions = 'record'
+
+# date like in cvs' $Date
+utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S')
+# date like in svn's $Date
+svnisodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
+# date like in svn's $Id
+svnutcdate = lambda x: util.datestr((x[0], 0), '%Y-%m-%d %H:%M:%SZ')
+
+# make keyword tools accessible
+kwtools = {'templater': None, 'hgcmd': ''}
+
+
+def _defaultkwmaps(ui):
+ '''Returns default keywordmaps according to keywordset configuration.'''
+ templates = {
+ 'Revision': '{node|short}',
+ 'Author': '{author|user}',
+ }
+ kwsets = ({
+ 'Date': '{date|utcdate}',
+ 'RCSfile': '{file|basename},v',
+ 'RCSFile': '{file|basename},v', # kept for backwards compatibility
+ # with hg-keyword
+ 'Source': '{root}/{file},v',
+ 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
+ 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
+ }, {
+ 'Date': '{date|svnisodate}',
+ 'Id': '{file|basename},v {node|short} {date|svnutcdate} {author|user}',
+ 'LastChangedRevision': '{node|short}',
+ 'LastChangedBy': '{author|user}',
+ 'LastChangedDate': '{date|svnisodate}',
+ })
+ templates.update(kwsets[ui.configbool('keywordset', 'svn')])
+ return templates
+
+def _shrinktext(text, subfunc):
+ '''Helper for keyword expansion removal in text.
+ Depending on subfunc also returns number of substitutions.'''
+ return subfunc(r'$\1$', text)
+
+def _preselect(wstatus, changed):
+ '''Retrieves modfied and added files from a working directory state
+ and returns the subset of each contained in given changed files
+ retrieved from a change context.'''
+ modified, added = wstatus[:2]
+ modified = [f for f in modified if f in changed]
+ added = [f for f in added if f in changed]
+ return modified, added
+
+
+class kwtemplater(object):
+ '''
+ Sets up keyword templates, corresponding keyword regex, and
+ provides keyword substitution functions.
+ '''
+
+ def __init__(self, ui, repo, inc, exc):
+ self.ui = ui
+ self.repo = repo
+ self.match = match.match(repo.root, '', [], inc, exc)
+ self.restrict = kwtools['hgcmd'] in restricted.split()
+ self.record = False
+
+ kwmaps = self.ui.configitems('keywordmaps')
+ if kwmaps: # override default templates
+ self.templates = dict((k, templater.parsestring(v, False))
+ for k, v in kwmaps)
+ else:
+ self.templates = _defaultkwmaps(self.ui)
+ escaped = '|'.join(map(re.escape, self.templates.keys()))
+ self.re_kw = re.compile(r'\$(%s)\$' % escaped)
+ self.re_kwexp = re.compile(r'\$(%s): [^$\n\r]*? \$' % escaped)
+
+ templatefilters.filters.update({'utcdate': utcdate,
+ 'svnisodate': svnisodate,
+ 'svnutcdate': svnutcdate})
+
+ def substitute(self, data, path, ctx, subfunc):
+ '''Replaces keywords in data with expanded template.'''
+ def kwsub(mobj):
+ kw = mobj.group(1)
+ ct = cmdutil.changeset_templater(self.ui, self.repo,
+ False, None, '', False)
+ ct.use_template(self.templates[kw])
+ self.ui.pushbuffer()
+ ct.show(ctx, root=self.repo.root, file=path)
+ ekw = templatefilters.firstline(self.ui.popbuffer())
+ return '$%s: %s $' % (kw, ekw)
+ return subfunc(kwsub, data)
+
+ def expand(self, path, node, data):
+ '''Returns data with keywords expanded.'''
+ if not self.restrict and self.match(path) and not util.binary(data):
+ ctx = self.repo.filectx(path, fileid=node).changectx()
+ return self.substitute(data, path, ctx, self.re_kw.sub)
+ return data
+
+ def iskwfile(self, cand, ctx):
+ '''Returns subset of candidates which are configured for keyword
+ expansion are not symbolic links.'''
+ return [f for f in cand if self.match(f) and not 'l' in ctx.flags(f)]
+
+ def overwrite(self, ctx, candidates, lookup, expand, rekw=False):
+ '''Overwrites selected files expanding/shrinking keywords.'''
+ if self.restrict or lookup or self.record: # exclude kw_copy
+ candidates = self.iskwfile(candidates, ctx)
+ if not candidates:
+ return
+ kwcmd = self.restrict and lookup # kwexpand/kwshrink
+ if self.restrict or expand and lookup:
+ mf = ctx.manifest()
+ fctx = ctx
+ subn = (self.restrict or rekw) and self.re_kw.subn or self.re_kwexp.subn
+ msg = (expand and _('overwriting %s expanding keywords\n')
+ or _('overwriting %s shrinking keywords\n'))
+ for f in candidates:
+ if self.restrict:
+ data = self.repo.file(f).read(mf[f])
+ else:
+ data = self.repo.wread(f)
+ if util.binary(data):
+ continue
+ if expand:
+ if lookup:
+ fctx = self.repo.filectx(f, fileid=mf[f]).changectx()
+ data, found = self.substitute(data, f, fctx, subn)
+ elif self.restrict:
+ found = self.re_kw.search(data)
+ else:
+ data, found = _shrinktext(data, subn)
+ if found:
+ self.ui.note(msg % f)
+ self.repo.wwrite(f, data, ctx.flags(f))
+ if kwcmd:
+ self.repo.dirstate.normal(f)
+ elif self.record:
+ self.repo.dirstate.normallookup(f)
+
+ def shrink(self, fname, text):
+ '''Returns text with all keyword substitutions removed.'''
+ if self.match(fname) and not util.binary(text):
+ return _shrinktext(text, self.re_kwexp.sub)
+ return text
+
+ def shrinklines(self, fname, lines):
+ '''Returns lines with keyword substitutions removed.'''
+ if self.match(fname):
+ text = ''.join(lines)
+ if not util.binary(text):
+ return _shrinktext(text, self.re_kwexp.sub).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):
+ '''
+ Subclass of filelog to hook into its read, add, cmp methods.
+ Keywords are "stored" unexpanded, and processed on reading.
+ '''
+ def __init__(self, opener, kwt, path):
+ super(kwfilelog, self).__init__(opener, path)
+ self.kwt = kwt
+ self.path = path
+
+ def read(self, node):
+ '''Expands keywords when reading filelog.'''
+ data = super(kwfilelog, self).read(node)
+ if self.renamed(node):
+ return 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 = self.kwt.shrink(self.path, text)
+ return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
+
+ def cmp(self, node, text):
+ '''Removes keyword substitutions for comparison.'''
+ text = self.kwt.shrink(self.path, text)
+ return super(kwfilelog, self).cmp(node, text)
+
+def _status(ui, repo, kwt, *pats, **opts):
+ '''Bails out if [keyword] configuration is not active.
+ Returns status of working directory.'''
+ if kwt:
+ return repo.status(match=cmdutil.match(repo, pats, opts), clean=True,
+ unknown=opts.get('unknown') or opts.get('all'))
+ if ui.configitems('keyword'):
+ raise util.Abort(_('[keyword] patterns cannot match'))
+ raise util.Abort(_('no [keyword] patterns configured'))
+
+def _kwfwrite(ui, repo, expand, *pats, **opts):
+ '''Selects files and passes them to kwtemplater.overwrite.'''
+ wctx = repo[None]
+ if len(wctx.parents()) > 1:
+ raise util.Abort(_('outstanding uncommitted merge'))
+ kwt = kwtools['templater']
+ wlock = repo.wlock()
+ try:
+ 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'))
+ kwt.overwrite(wctx, clean, True, expand)
+ finally:
+ wlock.release()
+
+def demo(ui, repo, *args, **opts):
+ '''print [keywordmaps] configuration and an expansion example
+
+ Show current, custom, or default keyword template maps and their
+ expansions.
+
+ Extend the current configuration by specifying maps as arguments
+ and using -f/--rcfile to source an external hgrc file.
+
+ Use -d/--default to disable current configuration.
+
+ See :hg:`help templates` for information on templates and filters.
+ '''
+ def demoitems(section, items):
+ ui.write('[%s]\n' % section)
+ for k, v in sorted(items):
+ ui.write('%s = %s\n' % (k, v))
+
+ fn = 'demo.txt'
+ tmpdir = tempfile.mkdtemp('', 'kwdemo.')
+ ui.note(_('creating temporary repository at %s\n') % tmpdir)
+ repo = localrepo.localrepository(ui, tmpdir, True)
+ ui.setconfig('keyword', fn, '')
+
+ uikwmaps = ui.configitems('keywordmaps')
+ if args or opts.get('rcfile'):
+ ui.status(_('\n\tconfiguration using custom keyword template maps\n'))
+ if uikwmaps:
+ ui.status(_('\textending current template maps\n'))
+ if opts.get('default') or not uikwmaps:
+ ui.status(_('\toverriding default template maps\n'))
+ if opts.get('rcfile'):
+ ui.readconfig(opts.get('rcfile'))
+ if args:
+ # simulate hgrc parsing
+ rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
+ fp = repo.opener('hgrc', 'w')
+ fp.writelines(rcmaps)
+ fp.close()
+ ui.readconfig(repo.join('hgrc'))
+ kwmaps = dict(ui.configitems('keywordmaps'))
+ elif opts.get('default'):
+ ui.status(_('\n\tconfiguration using default keyword template maps\n'))
+ kwmaps = _defaultkwmaps(ui)
+ if uikwmaps:
+ ui.status(_('\tdisabling current template maps\n'))
+ for k, v in kwmaps.iteritems():
+ ui.setconfig('keywordmaps', k, v)
+ else:
+ ui.status(_('\n\tconfiguration using current keyword template maps\n'))
+ kwmaps = dict(uikwmaps) or _defaultkwmaps(ui)
+
+ uisetup(ui)
+ reposetup(ui, repo)
+ ui.write('[extensions]\nkeyword =\n')
+ demoitems('keyword', ui.configitems('keyword'))
+ demoitems('keywordmaps', kwmaps.iteritems())
+ keywords = '$' + '$\n$'.join(sorted(kwmaps.keys())) + '$\n'
+ repo.wopener(fn, 'w').write(keywords)
+ repo[None].add([fn])
+ ui.note(_('\nkeywords written to %s:\n') % fn)
+ ui.note(keywords)
+ repo.dirstate.setbranch('demobranch')
+ for name, cmd in ui.configitems('hooks'):
+ if name.split('.', 1)[0].find('commit') > -1:
+ repo.ui.setconfig('hooks', name, '')
+ msg = _('hg keyword configuration and expansion example')
+ ui.note("hg ci -m '%s'\n" % msg)
+ repo.commit(text=msg)
+ ui.status(_('\n\tkeywords expanded\n'))
+ ui.write(repo.wread(fn))
+ shutil.rmtree(tmpdir, ignore_errors=True)
+
+def expand(ui, repo, *pats, **opts):
+ '''expand keywords in the working directory
+
+ Run after (re)enabling keyword expansion.
+
+ kwexpand refuses to run if given files contain local changes.
+ '''
+ # 3rd argument sets expansion to True
+ _kwfwrite(ui, repo, True, *pats, **opts)
+
+def files(ui, repo, *pats, **opts):
+ '''show files configured for keyword expansion
+
+ List which files in the working directory are matched by the
+ [keyword] configuration patterns.
+
+ Useful to prevent inadvertent keyword expansion and to speed up
+ execution by including only files that are actual candidates for
+ expansion.
+
+ See :hg:`help keyword` on how to construct patterns both for
+ inclusion and exclusion of files.
+
+ With -A/--all and -v/--verbose the codes used to show the status
+ of files are::
+
+ K = keyword expansion candidate
+ k = keyword expansion candidate (not tracked)
+ I = ignored
+ i = ignored (not tracked)
+ '''
+ kwt = kwtools['templater']
+ status = _status(ui, repo, kwt, *pats, **opts)
+ cwd = pats and repo.getcwd() or ''
+ modified, added, removed, deleted, unknown, ignored, clean = status
+ files = []
+ if not opts.get('unknown') or opts.get('all'):
+ files = sorted(modified + added + clean)
+ wctx = repo[None]
+ kwfiles = kwt.iskwfile(files, wctx)
+ kwunknown = kwt.iskwfile(unknown, wctx)
+ if not opts.get('ignore') or opts.get('all'):
+ showfiles = kwfiles, kwunknown
+ else:
+ showfiles = [], []
+ if opts.get('all') or opts.get('ignore'):
+ showfiles += ([f for f in files if f not in kwfiles],
+ [f for f in unknown if f not in kwunknown])
+ for char, filenames in zip('KkIi', showfiles):
+ fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
+ for f in filenames:
+ ui.write(fmt % repo.pathto(f, cwd))
+
+def shrink(ui, repo, *pats, **opts):
+ '''revert expanded keywords in the working directory
+
+ Run before changing/disabling active keywords or if you experience
+ problems with :hg:`import` or :hg:`merge`.
+
+ kwshrink refuses to run if given files contain local changes.
+ '''
+ # 3rd argument sets expansion to False
+ _kwfwrite(ui, repo, False, *pats, **opts)
+
+
+def uisetup(ui):
+ ''' Monkeypatches dispatch._parse to retrieve user command.'''
+
+ def kwdispatch_parse(orig, ui, args):
+ '''Monkeypatch dispatch._parse to obtain running hg command.'''
+ cmd, func, args, options, cmdoptions = orig(ui, args)
+ kwtools['hgcmd'] = cmd
+ return cmd, func, args, options, cmdoptions
+
+ extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
+
+def reposetup(ui, repo):
+ '''Sets up repo as kwrepo for keyword substitution.
+ Overrides file method to return kwfilelog instead of filelog
+ if file matches user configuration.
+ Wraps commit to overwrite configured files with updated
+ keyword substitutions.
+ Monkeypatches patch and webcommands.'''
+
+ try:
+ if (not repo.local() or kwtools['hgcmd'] in nokwcommands.split()
+ or '.hg' in util.splitpath(repo.root)
+ or repo._url.startswith('bundle:')):
+ return
+ except AttributeError:
+ pass
+
+ inc, exc = [], ['.hg*']
+ for pat, opt in ui.configitems('keyword'):
+ if opt != 'ignore':
+ inc.append(pat)
+ else:
+ exc.append(pat)
+ if not inc:
+ return
+
+ kwtools['templater'] = kwt = kwtemplater(ui, repo, inc, exc)
+
+ class kwrepo(repo.__class__):
+ def file(self, f):
+ if f[0] == '/':
+ f = f[1:]
+ return kwfilelog(self.sopener, kwt, f)
+
+ def wread(self, filename):
+ data = super(kwrepo, self).wread(filename)
+ return kwt.wread(filename, data)
+
+ def commit(self, *args, **opts):
+ # use custom commitctx for user commands
+ # other extensions can still wrap repo.commitctx directly
+ self.commitctx = self.kwcommitctx
+ try:
+ return super(kwrepo, self).commit(*args, **opts)
+ finally:
+ del self.commitctx
+
+ def kwcommitctx(self, ctx, error=False):
+ n = super(kwrepo, self).commitctx(ctx, error)
+ # no lock needed, only called from repo.commit() which already locks
+ if not kwt.record:
+ restrict = kwt.restrict
+ kwt.restrict = True
+ kwt.overwrite(self[n], sorted(ctx.added() + ctx.modified()),
+ False, True)
+ kwt.restrict = restrict
+ return n
+
+ def rollback(self, dryrun=False):
+ wlock = self.wlock()
+ try:
+ if not dryrun:
+ changed = self['.'].files()
+ ret = super(kwrepo, self).rollback(dryrun)
+ if not dryrun:
+ ctx = self['.']
+ modified, added = _preselect(self[None].status(), changed)
+ kwt.overwrite(ctx, modified, True, True)
+ kwt.overwrite(ctx, added, True, False)
+ return ret
+ finally:
+ wlock.release()
+
+ # monkeypatches
+ def kwpatchfile_init(orig, self, ui, fname, opener,
+ missing=False, eolmode=None):
+ '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
+ rejects or conflicts due to expanded keywords in working dir.'''
+ orig(self, ui, fname, opener, missing, eolmode)
+ # shrink keywords read from working dir
+ self.lines = kwt.shrinklines(self.fname, self.lines)
+
+ def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
+ opts=None, prefix=''):
+ '''Monkeypatch patch.diff to avoid expansion.'''
+ kwt.restrict = True
+ return orig(repo, node1, node2, match, changes, opts, prefix)
+
+ def kwweb_skip(orig, web, req, tmpl):
+ '''Wraps webcommands.x turning off keyword expansion.'''
+ kwt.match = util.never
+ return orig(web, req, tmpl)
+
+ def kw_copy(orig, ui, repo, pats, opts, rename=False):
+ '''Wraps cmdutil.copy so that copy/rename destinations do not
+ contain expanded keywords.
+ Note that the source of a regular file destination may also be a
+ symlink:
+ hg cp sym x -> x is symlink
+ cp sym x; hg cp -A sym x -> x is file (maybe expanded keywords)
+ For the latter we have to follow the symlink to find out whether its
+ target is configured for expansion and we therefore must unexpand the
+ keywords in the destination.'''
+ orig(ui, repo, pats, opts, rename)
+ if opts.get('dry_run'):
+ return
+ wctx = repo[None]
+ cwd = repo.getcwd()
+
+ def haskwsource(dest):
+ '''Returns true if dest is a regular file and configured for
+ expansion or a symlink which points to a file configured for
+ expansion. '''
+ source = repo.dirstate.copied(dest)
+ if 'l' in wctx.flags(source):
+ source = util.canonpath(repo.root, cwd,
+ os.path.realpath(source))
+ return kwt.match(source)
+
+ candidates = [f for f in repo.dirstate.copies() if
+ not 'l' in wctx.flags(f) and haskwsource(f)]
+ kwt.overwrite(wctx, candidates, False, False)
+
+ def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts):
+ '''Wraps record.dorecord expanding keywords after recording.'''
+ wlock = repo.wlock()
+ try:
+ # record returns 0 even when nothing has changed
+ # therefore compare nodes before and after
+ kwt.record = True
+ ctx = repo['.']
+ wstatus = repo[None].status()
+ ret = orig(ui, repo, commitfunc, *pats, **opts)
+ recctx = repo['.']
+ if ctx != recctx:
+ modified, added = _preselect(wstatus, recctx.files())
+ kwt.restrict = False
+ kwt.overwrite(recctx, modified, False, True)
+ kwt.overwrite(recctx, added, False, True, True)
+ kwt.restrict = True
+ return ret
+ finally:
+ wlock.release()
+
+ repo.__class__ = kwrepo
+
+ def kwfilectx_cmp(orig, self, fctx):
+ # keyword affects data size, comparing wdir and filelog size does
+ # not make sense
+ if (fctx._filerev is None and
+ (self._repo._encodefilterpats or
+ kwt.match(fctx.path()) and not 'l' in fctx.flags()) or
+ self.size() == fctx.size()):
+ return self._filelog.cmp(self._filenode, fctx.data())
+ return True
+
+ extensions.wrapfunction(context.filectx, 'cmp', kwfilectx_cmp)
+ extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
+ extensions.wrapfunction(patch, 'diff', kw_diff)
+ extensions.wrapfunction(cmdutil, 'copy', kw_copy)
+ for c in 'annotate changeset rev filediff diff'.split():
+ extensions.wrapfunction(webcommands, c, kwweb_skip)
+ for name in recordextensions.split():
+ try:
+ record = extensions.find(name)
+ extensions.wrapfunction(record, 'dorecord', kw_dorecord)
+ except KeyError:
+ pass
+
+cmdtable = {
+ 'kwdemo':
+ (demo,
+ [('d', 'default', None, _('show default keyword template maps')),
+ ('f', 'rcfile', '',
+ _('read maps from rcfile'), _('FILE'))],
+ _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
+ 'kwexpand': (expand, commands.walkopts,
+ _('hg kwexpand [OPTION]... [FILE]...')),
+ 'kwfiles':
+ (files,
+ [('A', 'all', None, _('show keyword status flags of all files')),
+ ('i', 'ignore', None, _('show files excluded from expansion')),
+ ('u', 'unknown', None, _('only show unknown (not tracked) files')),
+ ] + commands.walkopts,
+ _('hg kwfiles [OPTION]... [FILE]...')),
+ 'kwshrink': (shrink, commands.walkopts,
+ _('hg kwshrink [OPTION]... [FILE]...')),
+}