hgkw/keyword.py
branchkwmap-templates
changeset 115 520818841684
parent 109 b2cc6a8d4a18
child 116 265714c59607
equal deleted inserted replaced
109:b2cc6a8d4a18 115:520818841684
    12 not running a version control system.
    12 not running a version control system.
    13 
    13 
    14 For in-depth discussion refer to
    14 For in-depth discussion refer to
    15 <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
    15 <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
    16 
    16 
    17 You can either use the default cvs-like keywords or provide your
    17 Keywords are only expanded in local repositories and not logged by
    18 own in hgrc.
    18 Mercurial internally. The mechanism can be regarded as a convenience
    19 
    19 for the current user and may be turned off anytime.
    20 It is recommended to enable this extension on a per-repo basis only.
    20 
    21 You can still configure keywordmaps globally.
    21 Substitution takes place on every commit and update of the working
    22 
    22 repository.
    23 Expansions spanning more than one line are truncated to their first line.
    23 
    24 Incremental expansion (like CVS' $Log$) is not supported.
    24 Keyword expansion is based on Mercurial's changeset template mappings.
       
    25 The extension provides an additional UTC-date filter.
       
    26 
       
    27 The user has the choice either to create his own keywords and their
       
    28 expansions or to use the CVS-like default ones.
    25 
    29 
    26 Default $keywords$ and their $keyword: substition $ are:
    30 Default $keywords$ and their $keyword: substition $ are:
    27     Revision: changeset id
    31     Revision: changeset id
    28     Author:   username
    32     Author:   username
    29     Date:     %Y/%m/%d %H:%M:%S [UTC]
    33     Date:     %Y/%m/%d %H:%M:%S [UTC]
    30     RCSFile:  basename,v
    34     RCSFile:  basename,v
    31     Source:   /path/to/basename,v
    35     Source:   /path/to/basename,v
    32     Id:       basename,v csetid %Y/%m/%d %H:%M:%S username
    36     Id:       basename,v csetid %Y/%m/%d %H:%M:%S username
    33     Header:   /path/to/basename,v csetid %Y/%m/%d %H:%M:%S username
    37     Header:   /path/to/basename,v csetid %Y/%m/%d %H:%M:%S username
    34 
    38 
       
    39 Expansions spanning more than one line are truncated to their first line.
       
    40 Incremental expansion (like CVS' $Log$) is not supported.
       
    41 
    35 Simple setup in hgrc:
    42 Simple setup in hgrc:
    36 
    43 
    37     # enable extension
    44     # enable extension
    38     hgext.keyword = /full/path/to/script
    45     keyword = /full/path/to/keyword.py
    39     # or, if script in hgext folder:
    46     # or, if script in hgext folder:
    40     # hgext.keyword =
    47     # hgext.keyword =
    41     
    48     
    42     # filename patterns for expansion are configured in this section
    49     # filename patterns for expansion are configured in this section
    43     # files matching patterns with value 'ignore' are ignored
    50     # files matching patterns with value 'ignore' are ignored
    55 
    62 
    56 from mercurial.i18n import gettext as _
    63 from mercurial.i18n import gettext as _
    57 # above line for backwards compatibility; can be changed to
    64 # above line for backwards compatibility; can be changed to
    58 #   from mercurial.i18n import _
    65 #   from mercurial.i18n import _
    59 # some day
    66 # some day
    60 from mercurial import cmdutil, templater, util
    67 from mercurial import context, filelog, revlog
    61 from mercurial.node import *
    68 from mercurial import commands, cmdutil, templater, util
       
    69 from mercurial.node import bin
    62 import os.path, re, sys, time
    70 import os.path, re, sys, time
    63 
    71 
    64 deftemplates = {
    72 deftemplates = {
    65         'Revision': '{node|short}',
    73         'Revision': '{node|short}',
    66         'Author': '{author|user}',
    74         'Author': '{author|user}',
    73 
    81 
    74 def utcdate(date):
    82 def utcdate(date):
    75     '''Returns hgdate in cvs-like UTC format.'''
    83     '''Returns hgdate in cvs-like UTC format.'''
    76     return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
    84     return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
    77 
    85 
       
    86 def getmodulename():
       
    87     '''Makes sure pretxncommit-hook can import keyword module
       
    88     regardless of where its located.'''
       
    89     for k, v in sys.modules.iteritems():
       
    90         if v is None:
       
    91             continue
       
    92         if not hasattr(v, '__file__'):
       
    93             continue
       
    94         if v.__file__.startswith(__file__):
       
    95             return k
       
    96     else:
       
    97         sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
       
    98         return os.path.splitext(os.path.basename(__file__))[0]
       
    99 
    78 def kwfmatches(ui, repo, files):
   100 def kwfmatches(ui, repo, files):
    79     '''Selects candidates for keyword substitution
   101     '''Selects candidates for keyword substitution
    80     configured in keyword section in hgrc.'''
   102     configured in keyword section in hgrc.'''
    81     inc = [pat for pat, opt in ui.configitems('keyword') if opt != 'ignore']
   103     inc, exc = [], []
       
   104     for pat, opt in ui.configitems('keyword'):
       
   105         if opt != 'ignore':
       
   106             inc.append(pat)
       
   107         else:
       
   108             exc.append(pat)
    82     if not inc:
   109     if not inc:
    83         ui.warn(_('keyword: no filename globs for expansion\n'))
   110         ui.debug(_('keyword: no filename globs for substitution\n'))
    84         return []
   111         return []
    85     exc = [pat for pat, opt in ui.configitems('keyword') if opt == 'ignore']
       
    86     kwfmatcher = util.matcher(repo.root, inc=inc, exc=['.hg*']+exc)[1]
   112     kwfmatcher = util.matcher(repo.root, inc=inc, exc=['.hg*']+exc)[1]
    87     return [f for f in files if kwfmatcher(f)]
   113     return [f for f in files if kwfmatcher(f)]
    88 
   114 
    89 
   115 
    90 class kwtemplater(object):
   116 class kwtemplater(object):
   107         kw = mobj.group(1)
   133         kw = mobj.group(1)
   108         template = templater.parsestring(self.templates[kw], quoted=False)
   134         template = templater.parsestring(self.templates[kw], quoted=False)
   109         self.t.use_template(template)
   135         self.t.use_template(template)
   110         self.ui.pushbuffer()
   136         self.ui.pushbuffer()
   111         self.t.show(changenode=node, root=self.repo.root, file=path)
   137         self.t.show(changenode=node, root=self.repo.root, file=path)
   112         expansion = templater.firstline(self.ui.popbuffer())
   138         kwsub = templater.firstline(self.ui.popbuffer())
   113         return '$%s: %s $' % (kw, expansion)
   139         return '$%s: %s $' % (kw, kwsub)
   114 
   140 
   115 
   141 
   116 def reposetup(ui, repo):
   142 def reposetup(ui, repo):
   117     '''Sets up repo, and filelog especially, as kwrepo and kwfilelog
   143     '''Sets up repo, and filelog especially, as kwrepo and kwfilelog
   118     for keyword substitution.  This is done for local repos only.'''
   144     for keyword substitution.  This is done for local repos only.'''
   119 
   145 
   120     from mercurial import context, filelog, revlog
       
   121     if not repo.local():
   146     if not repo.local():
   122         return
   147         return
       
   148 
   123 
   149 
   124     class kwrepo(repo.__class__):
   150     class kwrepo(repo.__class__):
   125         def file(self, f):
   151         def file(self, f):
   126             if f[0] == '/':
   152             if f[0] == '/':
   127                 f = f[1:]
   153                 f = f[1:]
   170                 text = self.kwt.re_kw.sub(r'$\1$', text)
   196                 text = self.kwt.re_kw.sub(r'$\1$', text)
   171             return super(kwfilelog, self).cmp(node, text)
   197             return super(kwfilelog, self).cmp(node, text)
   172 
   198 
   173     filelog.filelog = kwfilelog
   199     filelog.filelog = kwfilelog
   174     repo.__class__ = kwrepo
   200     repo.__class__ = kwrepo
   175     # make pretxncommit hook import kwmodule regardless of where it's located
   201     # configure pretxncommit hook
   176     for k, v in sys.modules.iteritems():
   202     ui.setconfig('hooks', 'pretxncommit.keyword',
   177         if v is None:
   203             'python:%s.pretxnkw' % getmodulename())
   178             continue
       
   179         if not hasattr(v, '__file__'):
       
   180             continue
       
   181         if v.__file__.startswith(__file__):
       
   182             mod = k
       
   183             break
       
   184     else:
       
   185         sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
       
   186         mod = os.path.splitext(os.path.basename(__file__))[0]
       
   187     ui.setconfig('hooks', 'pretxncommit.keyword', 'python:%s.pretxnkw' % mod)
       
   188     del mod
       
   189 
   204 
   190 
   205 
   191 def pretxnkw(ui, repo, hooktype, **args):
   206 def pretxnkw(ui, repo, hooktype, **args):
   192     '''pretxncommit hook that collects candidates for keyword expansion
   207     '''pretxncommit hook that collects candidates for keyword expansion
   193     on commit and expands keywords in working dir.'''
   208     on commit and expands keywords in working dir.'''
   194     from mercurial import commands
       
   195 
   209 
   196     cmd, sysargs, globalopts, cmdopts = commands.parse(ui, sys.argv[1:])[1:]
   210     cmd, sysargs, globalopts, cmdopts = commands.parse(ui, sys.argv[1:])[1:]
   197     if repr(cmd).split()[1] in ('tag', 'import_'):
   211     if repr(cmd).split()[1] in ('tag', 'import_'):
   198         return
   212         return
   199 
   213