hgkw/keyword.py
changeset 408 33e5b8a51e47
parent 407 60c993ce6acb
child 410 64cd77f6f7f1
equal deleted inserted replaced
407:60c993ce6acb 408:33e5b8a51e47
   101 def utcdate(date):
   101 def utcdate(date):
   102     '''Returns hgdate in cvs-like UTC format.'''
   102     '''Returns hgdate in cvs-like UTC format.'''
   103     return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
   103     return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
   104 
   104 
   105 
   105 
   106 _kwtemplater = _cmd = None
   106 # make keyword tools accessible
       
   107 kwx = { 'templater': None, 'hgcmd': None }
   107  
   108  
   108 # store originals of monkeypatches
   109 # store originals of monkeypatches
   109 _patchfile_init = patch.patchfile.__init__
   110 _patchfile_init = patch.patchfile.__init__
   110 _patch_diff = patch.diff
   111 _patch_diff = patch.diff
   111 _dispatch_parse = dispatch._parse
   112 _dispatch_parse = dispatch._parse
   113 def _kwpatchfile_init(self, ui, fname, missing=False):
   114 def _kwpatchfile_init(self, ui, fname, missing=False):
   114     '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
   115     '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
   115     rejects or conflicts due to expanded keywords in working dir.'''
   116     rejects or conflicts due to expanded keywords in working dir.'''
   116     _patchfile_init(self, ui, fname, missing=missing)
   117     _patchfile_init(self, ui, fname, missing=missing)
   117     # shrink keywords read from working dir
   118     # shrink keywords read from working dir
   118     self.lines = _kwtemplater.shrinklines(self.fname, self.lines)
   119     kwt = kwx['templater']
       
   120     self.lines = kwt.shrinklines(self.fname, self.lines)
   119 
   121 
   120 def _kw_diff(repo, node1=None, node2=None, files=None, match=util.always,
   122 def _kw_diff(repo, node1=None, node2=None, files=None, match=util.always,
   121              fp=None, changes=None, opts=None):
   123              fp=None, changes=None, opts=None):
   122     # only expand if comparing against working dir
   124     # only expand if comparing against working dir
   123     if node2 is not None:
   125     if node2 is not None:
   124         _kwtemplater.matcher = util.never
   126         kwx['templater'].matcher = util.never
   125     if node1 is not None and node1 != repo.changectx().node():
   127     if node1 is not None and node1 != repo.changectx().node():
   126         _kwtemplater.restrict = True
   128         kwx['templater'].restrict = True
   127     _patch_diff(repo, node1=node1, node2=node2, files=files, match=match,
   129     _patch_diff(repo, node1=node1, node2=node2, files=files, match=match,
   128                 fp=fp, changes=changes, opts=opts)
   130                 fp=fp, changes=changes, opts=opts)
   129 
   131 
   130 def _kwweb_changeset(web, req, tmpl):
   132 def _kwweb_changeset(web, req, tmpl):
   131     '''Wraps webcommands.changeset turning off keyword expansion.'''
   133     '''Wraps webcommands.changeset turning off keyword expansion.'''
   132     _kwtemplater.matcher = util.never
   134     kwx['templater'].matcher = util.never
   133     return web.changeset(tmpl, web.changectx(req))
   135     return web.changeset(tmpl, web.changectx(req))
   134 
   136 
   135 def _kwweb_filediff(web, req, tmpl):
   137 def _kwweb_filediff(web, req, tmpl):
   136     '''Wraps webcommands.filediff turning off keyword expansion.'''
   138     '''Wraps webcommands.filediff turning off keyword expansion.'''
   137     _kwtemplater.matcher = util.never
   139     kwx['templater'].matcher = util.never
   138     return web.filediff(tmpl, web.filectx(req))
   140     return web.filediff(tmpl, web.filectx(req))
   139 
   141 
   140 def _kwdispatch_parse(ui, args):
   142 def _kwdispatch_parse(ui, args):
   141     '''Monkeypatch dispatch._parse to obtain running hg command.'''
   143     '''Monkeypatch dispatch._parse to obtain running hg command.'''
   142     global _cmd
   144     cmd, func, args, options, cmdoptions = _dispatch_parse(ui, args)
   143     _cmd, func, args, options, cmdoptions = _dispatch_parse(ui, args)
   145     kwx['hgcmd'] = cmd
   144     return _cmd, func, args, options, cmdoptions
   146     return cmd, func, args, options, cmdoptions
   145 
   147 
   146 # dispatch._parse is run before reposetup, so wrap it here
   148 # dispatch._parse is run before reposetup, so wrap it here
   147 dispatch._parse = _kwdispatch_parse
   149 dispatch._parse = _kwdispatch_parse
   148 
   150 
   149 
   151 
   164 
   166 
   165     def __init__(self, ui, repo, inc, exc):
   167     def __init__(self, ui, repo, inc, exc):
   166         self.ui = ui
   168         self.ui = ui
   167         self.repo = repo
   169         self.repo = repo
   168         self.matcher = util.matcher(repo.root, inc=inc, exc=exc)[1]
   170         self.matcher = util.matcher(repo.root, inc=inc, exc=exc)[1]
   169         self.restrict = _cmd in restricted.split()
   171         self.restrict = kwx['hgcmd'] in restricted.split()
   170 
   172 
   171         kwmaps = self.ui.configitems('keywordmaps')
   173         kwmaps = self.ui.configitems('keywordmaps')
   172         if kwmaps: # override default templates
   174         if kwmaps: # override default templates
   173             kwmaps = [(k, templater.parsestring(v, quoted=False))
   175             kwmaps = [(k, templater.parsestring(v, quoted=False))
   174                       for (k, v) in kwmaps]
   176                       for (k, v) in kwmaps]
   269     Subclass of filelog to hook into its read, add, cmp methods.
   271     Subclass of filelog to hook into its read, add, cmp methods.
   270     Keywords are "stored" unexpanded, and processed on reading.
   272     Keywords are "stored" unexpanded, and processed on reading.
   271     '''
   273     '''
   272     def __init__(self, opener, path):
   274     def __init__(self, opener, path):
   273         super(kwfilelog, self).__init__(opener, path)
   275         super(kwfilelog, self).__init__(opener, path)
       
   276         self.kwt = kwx['templater']
   274         self.path = path
   277         self.path = path
   275 
   278 
   276     def read(self, node):
   279     def read(self, node):
   277         '''Expands keywords when reading filelog.'''
   280         '''Expands keywords when reading filelog.'''
   278         data = super(kwfilelog, self).read(node)
   281         data = super(kwfilelog, self).read(node)
   279         return _kwtemplater.expand(self.path, node, data)
   282         return self.kwt.expand(self.path, node, data)
   280 
   283 
   281     def add(self, text, meta, tr, link, p1=None, p2=None):
   284     def add(self, text, meta, tr, link, p1=None, p2=None):
   282         '''Removes keyword substitutions when adding to filelog.'''
   285         '''Removes keyword substitutions when adding to filelog.'''
   283         text = _kwtemplater.shrink(self.path, text)
   286         text = self.kwt.shrink(self.path, text)
   284         return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2)
   287         return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2)
   285 
   288 
   286     def cmp(self, node, text):
   289     def cmp(self, node, text):
   287         '''Removes keyword substitutions for comparison.'''
   290         '''Removes keyword substitutions for comparison.'''
   288         text = _kwtemplater.shrink(self.path, text)
   291         text = self.kwt.shrink(self.path, text)
   289         if self.renamed(node):
   292         if self.renamed(node):
   290             t2 = super(kwfilelog, self).read(node)
   293             t2 = super(kwfilelog, self).read(node)
   291             return t2 != text
   294             return t2 != text
   292         return revlog.revlog.cmp(self, node, text)
   295         return revlog.revlog.cmp(self, node, text)
   293 
   296 
   294 def _status(ui, repo, *pats, **opts):
   297 def _status(ui, repo, kwt, *pats, **opts):
   295     '''Bails out if [keyword] configuration is not active.
   298     '''Bails out if [keyword] configuration is not active.
   296     Returns status of working directory.'''
   299     Returns status of working directory.'''
   297     if _kwtemplater:
   300     if kwt:
   298         files, match, anypats = cmdutil.matchpats(repo, pats, opts)
   301         files, match, anypats = cmdutil.matchpats(repo, pats, opts)
   299         return repo.status(files=files, match=match, list_clean=True)
   302         return repo.status(files=files, match=match, list_clean=True)
   300     if ui.configitems('keyword'):
   303     if ui.configitems('keyword'):
   301         raise util.Abort(_('[keyword] patterns cannot match'))
   304         raise util.Abort(_('[keyword] patterns cannot match'))
   302     raise util.Abort(_('no [keyword] patterns configured'))
   305     raise util.Abort(_('no [keyword] patterns configured'))
   303 
   306 
   304 def _kwfwrite(ui, repo, expand, *pats, **opts):
   307 def _kwfwrite(ui, repo, expand, *pats, **opts):
   305     '''Selects files and passes them to kwtemplater.overwrite.'''
   308     '''Selects files and passes them to kwtemplater.overwrite.'''
   306     status = _status(ui, repo, *pats, **opts)
   309     kwt = kwx['templater']
       
   310     status = _status(ui, repo, kwt, *pats, **opts)
   307     modified, added, removed, deleted, unknown, ignored, clean = status
   311     modified, added, removed, deleted, unknown, ignored, clean = status
   308     if modified or added or removed or deleted:
   312     if modified or added or removed or deleted:
   309         raise util.Abort(_('outstanding uncommitted changes in given files'))
   313         raise util.Abort(_('outstanding uncommitted changes in given files'))
   310     wlock = lock = None
   314     wlock = lock = None
   311     try:
   315     try:
   312         wlock = repo.wlock()
   316         wlock = repo.wlock()
   313         lock = repo.lock()
   317         lock = repo.lock()
   314         _kwtemplater.overwrite(expand=expand, files=clean)
   318         kwt.overwrite(expand=expand, files=clean)
   315     finally:
   319     finally:
   316         del wlock, lock
   320         del wlock, lock
   317 
   321 
   318 
   322 
   319 def demo(ui, repo, *args, **opts):
   323 def demo(ui, repo, *args, **opts):
   411 
   415 
   412     Crosscheck which files in working directory are potential targets for
   416     Crosscheck which files in working directory are potential targets for
   413     keyword expansion.
   417     keyword expansion.
   414     That is, files matched by [keyword] config patterns but not symlinks.
   418     That is, files matched by [keyword] config patterns but not symlinks.
   415     '''
   419     '''
   416     status = _status(ui, repo, *pats, **opts)
   420     kwt = kwx['templater']
       
   421     status = _status(ui, repo, kwt, *pats, **opts)
   417     modified, added, removed, deleted, unknown, ignored, clean = status
   422     modified, added, removed, deleted, unknown, ignored, clean = status
   418     files = modified + added + clean
   423     files = modified + added + clean
   419     if opts.get('untracked'):
   424     if opts.get('untracked'):
   420         files += unknown
   425         files += unknown
   421     files.sort()
   426     files.sort()
   422     wctx = repo.workingctx()
   427     wctx = repo.workingctx()
   423     islink = lambda p: 'l' in wctx.fileflags(p)
   428     islink = lambda p: 'l' in wctx.fileflags(p)
   424     kwfiles = [f for f in files if _kwtemplater.iskwfile(f, islink)]
   429     kwfiles = [f for f in files if kwt.iskwfile(f, islink)]
   425     cwd = pats and repo.getcwd() or ''
   430     cwd = pats and repo.getcwd() or ''
   426     kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
   431     kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
   427     if opts.get('all') or opts.get('ignore'):
   432     if opts.get('all') or opts.get('ignore'):
   428         kwfstats += (('I', [f for f in files if f not in kwfiles]),)
   433         kwfstats += (('I', [f for f in files if f not in kwfiles]),)
   429     for char, filenames in kwfstats:
   434     for char, filenames in kwfstats:
   450     Wraps commit to overwrite configured files with updated
   455     Wraps commit to overwrite configured files with updated
   451     keyword substitutions.
   456     keyword substitutions.
   452     This is done for local repos only, and only if there are
   457     This is done for local repos only, and only if there are
   453     files configured at all for keyword substitution.'''
   458     files configured at all for keyword substitution.'''
   454 
   459 
   455     global _kwtemplater
       
   456 
       
   457     try:
   460     try:
   458         if (not repo.local() or _cmd in nokwcommands.split() 
   461         if (not repo.local() or kwx['hgcmd'] in nokwcommands.split() 
   459             or '.hg' in util.splitpath(repo.root)
   462             or '.hg' in util.splitpath(repo.root)
   460             or repo._url.startswith('bundle:')):
   463             or repo._url.startswith('bundle:')):
   461             return
   464             return
   462     except AttributeError:
   465     except AttributeError:
   463         pass
   466         pass
   469         else:
   472         else:
   470             exc.append(pat)
   473             exc.append(pat)
   471     if not inc:
   474     if not inc:
   472         return
   475         return
   473 
   476 
   474     _kwtemplater = kwtemplater(ui, repo, inc, exc)
   477     kwx['templater'] = kwt = kwtemplater(ui, repo, inc, exc)
   475 
   478 
   476     class kwrepo(repo.__class__):
   479     class kwrepo(repo.__class__):
   477         def file(self, f):
   480         def file(self, f):
   478             if f[0] == '/':
   481             if f[0] == '/':
   479                 f = f[1:]
   482                 f = f[1:]
   480             return kwfilelog(self.sopener, f)
   483             return kwfilelog(self.sopener, f)
   481 
   484 
   482         def wread(self, filename):
   485         def wread(self, filename):
   483             data = super(kwrepo, self).wread(filename)
   486             data = super(kwrepo, self).wread(filename)
   484             return _kwtemplater.wread(filename, data)
   487             return kwt.wread(filename, data)
   485 
   488 
   486         def commit(self, files=None, text='', user=None, date=None,
   489         def commit(self, files=None, text='', user=None, date=None,
   487                    match=util.always, force=False, force_editor=False,
   490                    match=util.always, force=False, force_editor=False,
   488                    p1=None, p2=None, extra={}, empty_ok=False):
   491                    p1=None, p2=None, extra={}, empty_ok=False):
   489             wlock = lock = None
   492             wlock = lock = None
   518 
   521 
   519                 # restore commit hooks
   522                 # restore commit hooks
   520                 for name, cmd in commithooks.iteritems():
   523                 for name, cmd in commithooks.iteritems():
   521                     ui.setconfig('hooks', name, cmd)
   524                     ui.setconfig('hooks', name, cmd)
   522                 if node is not None:
   525                 if node is not None:
   523                     _kwtemplater.overwrite(node=node)
   526                     kwt.overwrite(node=node)
   524                     repo.hook('commit', node=node, parent1=_p1, parent2=_p2)
   527                     repo.hook('commit', node=node, parent1=_p1, parent2=_p2)
   525                 return node
   528                 return node
   526             finally:
   529             finally:
   527                 del wlock, lock
   530                 del wlock, lock
   528 
   531