hgkw/keyword.py
branch0.9.2compat
changeset 322 b1ac738404f8
parent 312 b92767fb8fb5
child 329 27f2e2126447
equal deleted inserted replaced
312:b92767fb8fb5 322:b1ac738404f8
   279 class kwfilelog(filelog.filelog):
   279 class kwfilelog(filelog.filelog):
   280     '''
   280     '''
   281     Subclass of filelog to hook into its read, add, cmp methods.
   281     Subclass of filelog to hook into its read, add, cmp methods.
   282     Keywords are "stored" unexpanded, and processed on reading.
   282     Keywords are "stored" unexpanded, and processed on reading.
   283     '''
   283     '''
   284     def __init__(self, opener, path, kwtemplater):
   284     def __init__(self, opener, path):
   285         super(kwfilelog, self).__init__(opener, path)
   285         super(kwfilelog, self).__init__(opener, path)
   286         self.kwtemplater = kwtemplater
   286         _kwtemplater.path = path
   287         self.kwtemplater.path = path
       
   288 
   287 
   289     def kwctread(self, node, expand):
   288     def kwctread(self, node, expand):
   290         '''Reads expanding and counting keywords
   289         '''Reads expanding and counting keywords
   291         (only called from kwtemplater.overwrite).'''
   290         (only called from kwtemplater.overwrite).'''
   292         data = super(kwfilelog, self).read(node)
   291         data = super(kwfilelog, self).read(node)
   293         return self.kwtemplater.process(node, data, expand)
   292         return _kwtemplater.process(node, data, expand)
   294 
   293 
   295     def read(self, node):
   294     def read(self, node):
   296         '''Expands keywords when reading filelog.'''
   295         '''Expands keywords when reading filelog.'''
   297         data = super(kwfilelog, self).read(node)
   296         data = super(kwfilelog, self).read(node)
   298         return self.kwtemplater.expand(node, data)
   297         return _kwtemplater.expand(node, data)
   299 
   298 
   300     def add(self, text, meta, tr, link, p1=None, p2=None):
   299     def add(self, text, meta, tr, link, p1=None, p2=None):
   301         '''Removes keyword substitutions when adding to filelog.'''
   300         '''Removes keyword substitutions when adding to filelog.'''
   302         text = self.kwtemplater.shrink(text)
   301         text = _kwtemplater.shrink(text)
   303         return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2)
   302         return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2)
   304 
   303 
   305     def cmp(self, node, text):
   304     def cmp(self, node, text):
   306         '''Removes keyword substitutions for comparison.'''
   305         '''Removes keyword substitutions for comparison.'''
   307         text = self.kwtemplater.shrink(text)
   306         text = _kwtemplater.shrink(text)
   308         if self.renamed(node):
   307         if self.renamed(node):
   309             t2 = super(kwfilelog, self).read(node)
   308             t2 = super(kwfilelog, self).read(node)
   310             return t2 != text
   309             return t2 != text
   311         return revlog.revlog.cmp(self, node, text)
   310         return revlog.revlog.cmp(self, node, text)
   312 
   311 
   313 def _iskwfile(f, kwtemplater, link):
   312 def _iskwfile(f, link):
   314     return not link(f) and kwtemplater.matcher(f)
   313     return not link(f) and _kwtemplater.matcher(f)
   315 
   314 
   316 def _status(ui, repo, kwtemplater, *pats, **opts):
   315 def _status(ui, repo, *pats, **opts):
   317     '''Bails out if [keyword] configuration is not active.
   316     '''Bails out if [keyword] configuration is not active.
   318     Returns status of working directory.'''
   317     Returns status of working directory.'''
   319     if kwtemplater:
   318     if _kwtemplater:
   320         files, match, anypats = cmdutil.matchpats(repo, pats, opts)
   319         files, match, anypats = cmdutil.matchpats(repo, pats, opts)
   321         return repo.status(files=files, match=match, list_clean=True)
   320         return repo.status(files=files, match=match, list_clean=True)
   322     if ui.configitems('keyword'):
   321     if ui.configitems('keyword'):
   323         raise util.Abort(_('[keyword] patterns cannot match'))
   322         raise util.Abort(_('[keyword] patterns cannot match'))
   324     raise util.Abort(_('no [keyword] patterns configured'))
   323     raise util.Abort(_('no [keyword] patterns configured'))
   325 
   324 
   326 def _overwrite(ui, repo, kwtemplater, node=None, expand=True, files=None):
   325 def _overwrite(ui, repo, node=None, expand=True, files=None):
   327     '''Overwrites selected files expanding/shrinking keywords.'''
   326     '''Overwrites selected files expanding/shrinking keywords.'''
   328     ctx = repo.changectx(node)
   327     ctx = repo.changectx(node)
   329     mf = ctx.manifest()
   328     mf = ctx.manifest()
   330     if files is None:
   329     if files is None:
   331         notify = ui.debug # commit
   330         notify = ui.debug # commit
   332         files = [f for f in ctx.files() if mf.has_key(f)]
   331         files = [f for f in ctx.files() if mf.has_key(f)]
   333     else:
   332     else:
   334         notify = ui.note  # kwexpand/kwshrink
   333         notify = ui.note  # kwexpand/kwshrink
   335     candidates = [f for f in files if _iskwfile(f, kwtemplater, mf.linkf)]
   334     candidates = [f for f in files if _iskwfile(f, mf.linkf)]
   336     if candidates:
   335     if candidates:
   337         overwritten = []
   336         overwritten = []
   338         candidates.sort()
   337         candidates.sort()
   339         action = expand and 'expanding' or 'shrinking'
   338         action = expand and 'expanding' or 'shrinking'
   340         kwtemplater.node = node or ctx.node()
   339         _kwtemplater.node = node or ctx.node()
   341         for f in candidates:
   340         for f in candidates:
   342             fp = repo.file(f, kwmatch=True)
   341             fp = repo.file(f, kwmatch=True)
   343             data, kwfound = fp.kwctread(mf[f], expand)
   342             data, kwfound = fp.kwctread(mf[f], expand)
   344             if kwfound:
   343             if kwfound:
   345                 notify(_('overwriting %s %s keywords\n') % (f, action))
   344                 notify(_('overwriting %s %s keywords\n') % (f, action))
   347                 overwritten.append(f)
   346                 overwritten.append(f)
   348         _normal(repo, overwritten)
   347         _normal(repo, overwritten)
   349 
   348 
   350 def _kwfwrite(ui, repo, expand, *pats, **opts):
   349 def _kwfwrite(ui, repo, expand, *pats, **opts):
   351     '''Selects files and passes them to _overwrite.'''
   350     '''Selects files and passes them to _overwrite.'''
   352     global _kwtemplater
   351     status = _status(ui, repo, *pats, **opts)
   353     status = _status(ui, repo, _kwtemplater, *pats, **opts)
       
   354     modified, added, removed, deleted, unknown, ignored, clean = status
   352     modified, added, removed, deleted, unknown, ignored, clean = status
   355     if modified or added or removed or deleted:
   353     if modified or added or removed or deleted:
   356         raise util.Abort(_('outstanding uncommitted changes in given files'))
   354         raise util.Abort(_('outstanding uncommitted changes in given files'))
   357     wlock = lock = None
   355     wlock = lock = None
   358     try:
   356     try:
   359         wlock = repo.wlock()
   357         wlock = repo.wlock()
   360         lock = repo.lock()
   358         lock = repo.lock()
   361         _overwrite(ui, repo, _kwtemplater, expand=expand, files=clean)
   359         _overwrite(ui, repo, expand=expand, files=clean)
   362     finally:
   360     finally:
   363         del wlock, lock
   361         del wlock, lock
   364 
   362 
   365 
   363 
   366 def demo(ui, repo, *args, **opts):
   364 def demo(ui, repo, *args, **opts):
   388     branchname = 'demobranch'
   386     branchname = 'demobranch'
   389     tmpdir = tempfile.mkdtemp('', 'kwdemo.')
   387     tmpdir = tempfile.mkdtemp('', 'kwdemo.')
   390     ui.note(_('creating temporary repo at %s\n') % tmpdir)
   388     ui.note(_('creating temporary repo at %s\n') % tmpdir)
   391     repo = localrepo.localrepository(ui, path=tmpdir, create=True)
   389     repo = localrepo.localrepository(ui, path=tmpdir, create=True)
   392     ui.setconfig('keyword', fn, '')
   390     ui.setconfig('keyword', fn, '')
   393     if args or opts['rcfile']:
   391     if args or opts.get('rcfile'):
   394         kwstatus = 'custom'
   392         kwstatus = 'custom'
   395     if opts['rcfile']:
   393     if opts.get('rcfile'):
   396         ui.readconfig(opts['rcfile'])
   394         ui.readconfig(opts.get('rcfile'))
   397     if opts['default']:
   395     if opts.get('default'):
   398         kwstatus = 'default'
   396         kwstatus = 'default'
   399         kwmaps = kwtemplater.templates
   397         kwmaps = kwtemplater.templates
   400         if ui.configitems('keywordmaps'):
   398         if ui.configitems('keywordmaps'):
   401             # override maps from optional rcfile
   399             # override maps from optional rcfile
   402             for k, v in kwmaps.items():
   400             for k, v in kwmaps.items():
   406         rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
   404         rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
   407         fp = repo.opener('hgrc', 'w')
   405         fp = repo.opener('hgrc', 'w')
   408         fp.writelines(rcmaps)
   406         fp.writelines(rcmaps)
   409         fp.close()
   407         fp.close()
   410         ui.readconfig(repo.join('hgrc'))
   408         ui.readconfig(repo.join('hgrc'))
   411     if not opts['default']:
   409     if not opts.get('default'):
   412         kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
   410         kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
   413     reposetup(ui, repo)
   411     reposetup(ui, repo)
   414     for k, v in ui.configitems('extensions'):
   412     for k, v in ui.configitems('extensions'):
   415         if k.endswith('keyword'):
   413         if k.endswith('keyword'):
   416             extension = '%s = %s' % (k, v)
   414             extension = '%s = %s' % (k, v)
   459 
   457 
   460     Crosscheck which files in working directory are potential targets for
   458     Crosscheck which files in working directory are potential targets for
   461     keyword expansion.
   459     keyword expansion.
   462     That is, files matched by [keyword] config patterns but not symlinks.
   460     That is, files matched by [keyword] config patterns but not symlinks.
   463     '''
   461     '''
   464     global _kwtemplater
   462     status = _status(ui, repo, *pats, **opts)
   465     status = _status(ui, repo, _kwtemplater, *pats, **opts)
       
   466     modified, added, removed, deleted, unknown, ignored, clean = status
   463     modified, added, removed, deleted, unknown, ignored, clean = status
   467     if opts['untracked']:
   464     if opts.get('untracked'):
   468         files = modified + added + unknown + clean
   465         files = modified + added + unknown + clean
   469     else:
   466     else:
   470         files = modified + added + clean
   467         files = modified + added + clean
   471     files.sort()
   468     files.sort()
   472     # use the full definition of repo._link for backwards compatibility
   469     # use the full definition of repo._link for backwards compatibility
   473     kwfiles = [f for f in files if _kwtemplater.matcher(f)
   470     kwfiles = [f for f in files if _kwtemplater.matcher(f)
   474                and not os.path.islink(repo.wjoin(f))]
   471                and not os.path.islink(repo.wjoin(f))]
   475     cwd = pats and repo.getcwd() or ''
   472     cwd = pats and repo.getcwd() or ''
   476     allf = opts['all']
   473     kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
   477     ignore = opts['ignore']
   474     if opts.get('all') or opts.get('ignore'):
   478     if ignore:
       
   479         kwfstats = ()
       
   480     else:
       
   481         kwfstats = (('K', kwfiles),)
       
   482     if allf or ignore:
       
   483         kwfstats += (('I', [f for f in files if f not in kwfiles]),)
   475         kwfstats += (('I', [f for f in files if f not in kwfiles]),)
   484     for char, filenames in kwfstats:
   476     for char, filenames in kwfstats:
   485         format = (allf or ui.verbose) and '%s %%s\n' % char or '%s\n'
   477         format = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
   486         for f in filenames:
   478         for f in filenames:
   487             ui.write(format % _pathto(repo, f, cwd))
   479             ui.write(format % _pathto(repo, f, cwd))
   488 
   480 
   489 def shrink(ui, repo, *pats, **opts):
   481 def shrink(ui, repo, *pats, **opts):
   490     '''revert expanded keywords in working directory
   482     '''revert expanded keywords in working directory
   505     Wraps commit to overwrite configured files with updated
   497     Wraps commit to overwrite configured files with updated
   506     keyword substitutions.
   498     keyword substitutions.
   507     This is done for local repos only, and only if there are
   499     This is done for local repos only, and only if there are
   508     files configured at all for keyword substitution.'''
   500     files configured at all for keyword substitution.'''
   509 
   501 
   510     nokwcommands = ('add', 'addremove', 'bundle', 'clone', 'copy', 'export',
   502     def kwbailout():
   511                     'grep', 'identify', 'incoming', 'init', 'outgoing', 'push',
   503         '''Obtains command via simplified cmdline parsing,
   512                     'remove', 'rename', 'rollback', 'convert')
   504         returns True if keyword expansion not needed.'''
   513 
   505         nokwcommands = ('add', 'addremove', 'bundle', 'clone', 'copy',
   514     def _getcmd():
   506                         'export', 'grep', 'identify', 'incoming', 'init',
   515         '''Simplified argument parsing as we are only interested in command.'''
   507                         'outgoing', 'push', 'remove', 'rename', 'rollback',
       
   508                         'convert')
   516         args = fancyopts(sys.argv[1:], commands.globalopts, {})
   509         args = fancyopts(sys.argv[1:], commands.globalopts, {})
   517         if args:
   510         if args:
   518             aliases, i = findcmd(ui, args[0], commands.table)
   511             aliases, i = findcmd(ui, args[0], commands.table)
   519             return aliases[0]
   512             return aliases[0] in nokwcommands
   520 
   513 
   521     if not repo.local() or _getcmd() in nokwcommands:
   514     if not repo.local() or kwbailout():
   522         return
   515         return
   523 
   516 
   524     inc, exc = [], ['.hgtags']
   517     inc, exc = [], ['.hgtags']
   525     for pat, opt in ui.configitems('keyword'):
   518     for pat, opt in ui.configitems('keyword'):
   526         if opt != 'ignore':
   519         if opt != 'ignore':
   536     class kwrepo(repo.__class__):
   529     class kwrepo(repo.__class__):
   537         def file(self, f, kwmatch=False):
   530         def file(self, f, kwmatch=False):
   538             if f[0] == '/':
   531             if f[0] == '/':
   539                 f = f[1:]
   532                 f = f[1:]
   540             if kwmatch or _kwtemplater.matcher(f):
   533             if kwmatch or _kwtemplater.matcher(f):
   541                 return kwfilelog(self.sopener, f, _kwtemplater)
   534                 return kwfilelog(self.sopener, f)
   542             return filelog.filelog(self.sopener, f)
   535             return filelog.filelog(self.sopener, f)
   543 
   536 
   544         def _commit(self, files, text, user, date, match, force, lock, wlock,
   537         def _commit(self, files, text, user, date, match, force, lock, wlock,
   545                     force_editor, p1, p2, extra):
   538                     force_editor, p1, p2, extra):
   546             '''Private commit wrapper for backwards compatibility.'''
   539             '''Private commit wrapper for backwards compatibility.'''
   596 
   589 
   597                 # restore commit hooks
   590                 # restore commit hooks
   598                 for name, cmd in commithooks:
   591                 for name, cmd in commithooks:
   599                     ui.setconfig('hooks', name, cmd)
   592                     ui.setconfig('hooks', name, cmd)
   600                 if node is not None:
   593                 if node is not None:
   601                     _overwrite(ui, self, _kwtemplater, node=node)
   594                     _overwrite(ui, self, node=node)
   602                     repo.hook('commit', node=node, parent1=_p1, parent2=_p2)
   595                     repo.hook('commit', node=node, parent1=_p1, parent2=_p2)
   603                 return node
   596                 return node
   604             finally:
   597             finally:
   605                 del _wlock, _lock
   598                 del _wlock, _lock
   606 
   599