hgkw/keyword.py
changeset 476 0c36b6c991f9
parent 461 2275041e7deb
parent 475 3989df387ff7
child 479 18f235294dda
equal deleted inserted replaced
461:2275041e7deb 476:0c36b6c991f9
    78 Expansions spanning more than one line and incremental expansions,
    78 Expansions spanning more than one line and incremental expansions,
    79 like CVS' $Log$, are not supported. A keyword template map
    79 like CVS' $Log$, are not supported. A keyword template map
    80 "Log = {desc}" expands to the first line of the changeset description.
    80 "Log = {desc}" expands to the first line of the changeset description.
    81 '''
    81 '''
    82 
    82 
    83 from mercurial import commands, cmdutil, context, dispatch, filelog, revlog
    83 from mercurial import commands, cmdutil, dispatch, filelog, revlog
    84 from mercurial import patch, localrepo, templater, templatefilters, util
    84 from mercurial import patch, localrepo, templater, templatefilters, util
    85 from mercurial.hgweb import webcommands
    85 from mercurial.hgweb import webcommands
    86 from mercurial.node import nullid, hex
    86 from mercurial.node import nullid, hex
    87 from mercurial.i18n import _
    87 from mercurial.i18n import _
    88 import re, shutil, tempfile, time
    88 import re, shutil, tempfile, time
   142                                               False, '', False)
   142                                               False, '', False)
   143 
   143 
   144     def getnode(self, path, fnode):
   144     def getnode(self, path, fnode):
   145         '''Derives changenode from file path and filenode.'''
   145         '''Derives changenode from file path and filenode.'''
   146         # used by kwfilelog.read and kwexpand
   146         # used by kwfilelog.read and kwexpand
   147         c = context.filectx(self.repo, path, fileid=fnode)
   147         c = self.repo.filectx(path, fileid=fnode)
   148         return c.node()
   148         return c.node()
   149 
   149 
   150     def substitute(self, data, path, node, subfunc):
   150     def substitute(self, data, path, node, subfunc):
   151         '''Replaces keywords in data with expanded template.'''
   151         '''Replaces keywords in data with expanded template.'''
   152         def kwsub(mobj):
   152         def kwsub(mobj):
   163         if not self.restrict and self.matcher(path) and not util.binary(data):
   163         if not self.restrict and self.matcher(path) and not util.binary(data):
   164             changenode = self.getnode(path, node)
   164             changenode = self.getnode(path, node)
   165             return self.substitute(data, path, changenode, self.re_kw.sub)
   165             return self.substitute(data, path, changenode, self.re_kw.sub)
   166         return data
   166         return data
   167 
   167 
   168     def iskwfile(self, path, islink):
   168     def iskwfile(self, path, flagfunc):
   169         '''Returns true if path matches [keyword] pattern
   169         '''Returns true if path matches [keyword] pattern
   170         and is not a symbolic link.
   170         and is not a symbolic link.
   171         Caveat: localrepository._link fails on Windows.'''
   171         Caveat: localrepository._link fails on Windows.'''
   172         return self.matcher(path) and not islink(path)
   172         return self.matcher(path) and not 'l' in flagfunc(path)
   173 
   173 
   174     def overwrite(self, node, expand, files):
   174     def overwrite(self, node, expand, files):
   175         '''Overwrites selected files expanding/shrinking keywords.'''
   175         '''Overwrites selected files expanding/shrinking keywords.'''
   176         ctx = self.repo.changectx(node)
       
   177         mf = ctx.manifest()
       
   178         if node is not None:     # commit
   176         if node is not None:     # commit
       
   177             ctx = self.repo[node]
       
   178             mf = ctx.manifest()
   179             files = [f for f in ctx.files() if f in mf]
   179             files = [f for f in ctx.files() if f in mf]
   180             notify = self.ui.debug
   180             notify = self.ui.debug
   181         else:                    # kwexpand/kwshrink
   181         else:                    # kwexpand/kwshrink
       
   182             ctx = self.repo['.']
       
   183             mf = ctx.manifest()
   182             notify = self.ui.note
   184             notify = self.ui.note
   183         candidates = [f for f in files if self.iskwfile(f, mf.linkf)]
   185         candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
   184         if candidates:
   186         if candidates:
   185             self.restrict = True # do not expand when reading
   187             self.restrict = True # do not expand when reading
   186             candidates.sort()
       
   187             action = expand and 'expanding' or 'shrinking'
   188             action = expand and 'expanding' or 'shrinking'
   188             for f in candidates:
   189             for f in candidates:
   189                 fp = self.repo.file(f)
   190                 fp = self.repo.file(f)
   190                 data = fp.read(mf[f])
   191                 data = fp.read(mf[f])
   191                 if util.binary(data):
   192                 if util.binary(data):
   251         if self.renamed(node):
   252         if self.renamed(node):
   252             t2 = super(kwfilelog, self).read(node)
   253             t2 = super(kwfilelog, self).read(node)
   253             return t2 != text
   254             return t2 != text
   254         return revlog.revlog.cmp(self, node, text)
   255         return revlog.revlog.cmp(self, node, text)
   255 
   256 
   256 def _status(ui, repo, kwt, *pats, **opts):
   257 def _status(ui, repo, kwt, unknown, *pats, **opts):
   257     '''Bails out if [keyword] configuration is not active.
   258     '''Bails out if [keyword] configuration is not active.
   258     Returns status of working directory.'''
   259     Returns status of working directory.'''
   259     if kwt:
   260     if kwt:
   260         matcher = cmdutil.match(repo, pats, opts)
   261         matcher = cmdutil.match(repo, pats, opts)
   261         return repo.status(match=matcher, list_clean=True)
   262         return repo.status(match=matcher, unknown=unknown, clean=True)
   262     if ui.configitems('keyword'):
   263     if ui.configitems('keyword'):
   263         raise util.Abort(_('[keyword] patterns cannot match'))
   264         raise util.Abort(_('[keyword] patterns cannot match'))
   264     raise util.Abort(_('no [keyword] patterns configured'))
   265     raise util.Abort(_('no [keyword] patterns configured'))
   265 
   266 
   266 def _kwfwrite(ui, repo, expand, *pats, **opts):
   267 def _kwfwrite(ui, repo, expand, *pats, **opts):
   267     '''Selects files and passes them to kwtemplater.overwrite.'''
   268     '''Selects files and passes them to kwtemplater.overwrite.'''
   268     if repo.dirstate.parents()[1] != nullid:
   269     if repo.dirstate.parents()[1] != nullid:
   269         raise util.Abort(_('outstanding uncommitted merge'))
   270         raise util.Abort(_('outstanding uncommitted merge'))
   270     kwt = kwtools['templater']
   271     kwt = kwtools['templater']
   271     status = _status(ui, repo, kwt, *pats, **opts)
   272     status = _status(ui, repo, kwt, False, *pats, **opts)
   272     modified, added, removed, deleted, unknown, ignored, clean = status
   273     modified, added, removed, deleted = status[:4]
   273     if modified or added or removed or deleted:
   274     if modified or added or removed or deleted:
   274         raise util.Abort(_('outstanding uncommitted changes'))
   275         raise util.Abort(_('outstanding uncommitted changes'))
   275     wlock = lock = None
   276     wlock = lock = None
   276     try:
   277     try:
   277         wlock = repo.wlock()
   278         wlock = repo.wlock()
   278         lock = repo.lock()
   279         lock = repo.lock()
   279         kwt.overwrite(None, expand, clean)
   280         kwt.overwrite(None, expand, status[6])
   280     finally:
   281     finally:
   281         del wlock, lock
   282         del wlock, lock
   282 
   283 
   283 
   284 
   284 def demo(ui, repo, *args, **opts):
   285 def demo(ui, repo, *args, **opts):
   378     Crosscheck which files in working directory are potential targets for
   379     Crosscheck which files in working directory are potential targets for
   379     keyword expansion.
   380     keyword expansion.
   380     That is, files matched by [keyword] config patterns but not symlinks.
   381     That is, files matched by [keyword] config patterns but not symlinks.
   381     '''
   382     '''
   382     kwt = kwtools['templater']
   383     kwt = kwtools['templater']
   383     status = _status(ui, repo, kwt, *pats, **opts)
   384     status = _status(ui, repo, kwt, opts.get('untracked'), *pats, **opts)
   384     modified, added, removed, deleted, unknown, ignored, clean = status
   385     modified, added, removed, deleted, unknown, ignored, clean = status
   385     files = modified + added + clean
   386     files = util.sort(modified + added + clean + unknown)
   386     if opts.get('untracked'):
   387     wctx = repo[None]
   387         files += unknown
   388     kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
   388     files.sort()
       
   389     wctx = repo.workingctx()
       
   390     islink = lambda p: 'l' in wctx.fileflags(p)
       
   391     kwfiles = [f for f in files if kwt.iskwfile(f, islink)]
       
   392     cwd = pats and repo.getcwd() or ''
   389     cwd = pats and repo.getcwd() or ''
   393     kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
   390     kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
   394     if opts.get('all') or opts.get('ignore'):
   391     if opts.get('all') or opts.get('ignore'):
   395         kwfstats += (('I', [f for f in files if f not in kwfiles]),)
   392         kwfstats += (('I', [f for f in files if f not in kwfiles]),)
   396     for char, filenames in kwfstats:
   393     for char, filenames in kwfstats:
   511                 fp=None, changes=None, opts=None):
   508                 fp=None, changes=None, opts=None):
   512         '''Monkeypatch patch.diff to avoid expansion except when
   509         '''Monkeypatch patch.diff to avoid expansion except when
   513         comparing against working dir.'''
   510         comparing against working dir.'''
   514         if node2 is not None:
   511         if node2 is not None:
   515             kwt.matcher = util.never
   512             kwt.matcher = util.never
   516         elif node1 is not None and node1 != repo.dirstate.parents()[0]:
   513         elif node1 is not None and node1 != repo['.'].node():
   517             kwt.restrict = True
   514             kwt.restrict = True
   518         patch_diff(repo, node1, node2, match, fp, changes, opts)
   515         patch_diff(repo, node1, node2, match, fp, changes, opts)
   519 
   516 
   520     def kwweb_annotate(web, req, tmpl):
   517     def kwweb_annotate(web, req, tmpl):
   521         '''Wraps webcommands.annotate turning off keyword expansion.'''
   518         '''Wraps webcommands.annotate turning off keyword expansion.'''