hgkw/keyword.py
changeset 207 affc18a621f5
parent 206 8d16b70359da
child 208 5afdcec8a01f
equal deleted inserted replaced
206:8d16b70359da 207:affc18a621f5
    95 commands.optionalrepo += ' kwdemo'
    95 commands.optionalrepo += ' kwdemo'
    96 
    96 
    97 def utcdate(date):
    97 def utcdate(date):
    98     '''Returns hgdate in cvs-like UTC format.'''
    98     '''Returns hgdate in cvs-like UTC format.'''
    99     return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
    99     return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
   100 
       
   101 def keywordmatcher(ui, repo):
       
   102     '''Collects include/exclude filename patterns for expansion
       
   103     candidates of current configuration. Returns filename matching
       
   104     function if include patterns exist, None otherwise.'''
       
   105     inc, exc = [], ['.hg*']
       
   106     for pat, opt in ui.configitems('keyword'):
       
   107         if opt != 'ignore':
       
   108             inc.append(pat)
       
   109         else:
       
   110             exc.append(pat)
       
   111     if not inc:
       
   112         return None
       
   113     return util.matcher(repo.root, inc=inc, exc=exc)[1]
       
   114 
   100 
   115 class kwtemplater(object):
   101 class kwtemplater(object):
   116     '''
   102     '''
   117     Sets up keyword templates, corresponding keyword regex, and
   103     Sets up keyword templates, corresponding keyword regex, and
   118     provides keyword substitution functions.
   104     provides keyword substitution functions.
   125         'Source': '{root}/{file},v',
   111         'Source': '{root}/{file},v',
   126         'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
   112         'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
   127         'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
   113         'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
   128     }
   114     }
   129 
   115 
   130     def __init__(self, ui, repo, path='', node=None, expand=True):
   116     def __init__(self, ui, repo, expand, path='', node=None):
   131         self.ui = ui
   117         self.ui = ui
   132         self.repo = repo
   118         self.repo = repo
       
   119         self.t = expand or None
   133         self.path = path
   120         self.path = path
   134         self.node = node
   121         self.node = node
   135         self.t = expand or None
       
   136 
   122 
   137         templates = dict(ui.configitems('keywordmaps'))
   123         templates = dict(ui.configitems('keywordmaps'))
   138         if templates:
   124         if templates:
   139             for k in templates.keys():
   125             for k in templates.keys():
   140                 templates[k] = templater.parsestring(templates[k],
   126                 templates[k] = templater.parsestring(templates[k],
   190         '''Returns text with all keyword substitutions removed.'''
   176         '''Returns text with all keyword substitutions removed.'''
   191         if util.binary(text):
   177         if util.binary(text):
   192             return text
   178             return text
   193         return self.re_kw.sub(r'$\1$', text)
   179         return self.re_kw.sub(r'$\1$', text)
   194 
   180 
   195     def overwrite(self, candidates, man, commit=True):
   181     def overwrite(self, candidates, man, commit):
   196         '''Overwrites files in working directory if keywords are detected.
   182         '''Overwrites files in working directory if keywords are detected.
   197         Keywords are expanded if keyword templater is initialized,
   183         Keywords are expanded if keyword templater is initialized,
   198         otherwise their substitution is removed.'''
   184         otherwise their substitution is removed.'''
   199         expand = self.t is not None
   185         expand = self.t is not None
   200         action = ('shrinking', 'expanding')[expand]
   186         action = ('shrinking', 'expanding')[expand]
   201         notify = (self.ui.note, self.ui.debug)[commit]
   187         notify = (self.ui.note, self.ui.debug)[commit]
   202         files = []
   188         files = []
   203         for f in candidates:
   189         for f in candidates:
   204             fp = self.repo.file(f, kwcnt=True, kwexp=expand)
   190             fp = self.repo.file(f, kwexp=expand, kwcnt=True)
   205             data, cnt = fp.read(man[f])
   191             data, kwfound = fp.read(man[f])
   206             if cnt:
   192             if kwfound:
   207                 notify(_('overwriting %s %s keywords\n') % (f, action))
   193                 notify(_('overwriting %s %s keywords\n') % (f, action))
   208                 try:
   194                 try:
   209                     self.repo.wwrite(f, data, man.flags(f))
   195                     self.repo.wwrite(f, data, man.flags(f))
   210                 except AttributeError:
   196                 except AttributeError:
   211                     # older versions want file descriptor as 3. optional arg
   197                     # older versions want file descriptor as 3. optional arg
   245         if self.renamed(node):
   231         if self.renamed(node):
   246             t2 = super(kwfilelog, self).read(node)
   232             t2 = super(kwfilelog, self).read(node)
   247             return t2 != text
   233             return t2 != text
   248         return super(kwfilelog, self).cmp(node, text)
   234         return super(kwfilelog, self).cmp(node, text)
   249 
   235 
   250 def _overwrite(ui, repo, files=None, expand=True):
   236 def _keywordmatcher(ui, repo):
       
   237     '''Collects include/exclude filename patterns for expansion
       
   238     candidates of current configuration. Returns filename matching
       
   239     function if include patterns exist, None otherwise.'''
       
   240     inc, exc = [], ['.hg*']
       
   241     for pat, opt in ui.configitems('keyword'):
       
   242         if opt != 'ignore':
       
   243             inc.append(pat)
       
   244         else:
       
   245             exc.append(pat)
       
   246     if inc:
       
   247         return util.matcher(repo.root, inc=inc, exc=exc)[1]
       
   248     return None
       
   249 
       
   250 def _overwrite(ui, repo, files, expand):
   251     '''Expands/shrinks keywords in working directory.'''
   251     '''Expands/shrinks keywords in working directory.'''
   252     wlock = None
   252     wlock = None
   253     try:
   253     try:
   254         wlock = repo.wlock()
   254         wlock = repo.wlock()
   255         bail_if_changed(repo)
   255         bail_if_changed(repo)
   256         ctx = repo.changectx()
   256         ctx = repo.changectx()
   257         if not ctx:
   257         if not ctx:
   258             raise hg.RepoError(_('no revision checked out'))
   258             raise hg.RepoError(_('no revision checked out'))
   259         kwfmatcher = keywordmatcher(ui, repo)
   259         kwfmatcher = _keywordmatcher(ui, repo)
   260         if kwfmatcher is None:
   260         if kwfmatcher is None:
   261             ui.warn(_('no files configured for keyword expansion\n'))
   261             ui.warn(_('no files configured for keyword expansion\n'))
   262             return
   262             return
   263         m = ctx.manifest()
   263         m = ctx.manifest()
   264         if files:
   264         if files:
   267             files = m.keys()
   267             files = m.keys()
   268         files = [f for f in files if kwfmatcher(f) and not os.path.islink(f)]
   268         files = [f for f in files if kwfmatcher(f) and not os.path.islink(f)]
   269         if not files:
   269         if not files:
   270             ui.warn(_('files not configured for expansion or untracked\n'))
   270             ui.warn(_('files not configured for expansion or untracked\n'))
   271             return
   271             return
   272         kwt = kwtemplater(ui, repo, node=ctx.node(), expand=expand)
   272         commit = False
   273         kwt.overwrite(files, m, commit=False)
   273         kwt = kwtemplater(ui, repo, expand, node=ctx.node())
       
   274         kwt.overwrite(files, m, commit)
   274         wlock = None
   275         wlock = None
   275     finally:
   276     finally:
   276         del wlock
   277         del wlock
   277 
   278 
   278 
   279 
   282     run before:
   283     run before:
   283                disabling keyword expansion
   284                disabling keyword expansion
   284                changing keyword expansion configuration
   285                changing keyword expansion configuration
   285     or if you experience problems with "hg import"
   286     or if you experience problems with "hg import"
   286     '''
   287     '''
   287     _overwrite(ui, repo, files=args, expand=False)
   288     expand = False
       
   289     _overwrite(ui, repo, args, expand)
   288 
   290 
   289 def expand(ui, repo, *args):
   291 def expand(ui, repo, *args):
   290     '''expand keywords in working directory
   292     '''expand keywords in working directory
   291 
   293 
   292     run after (re)enabling keyword expansion
   294     run after (re)enabling keyword expansion
   293     '''
   295     '''
   294     _overwrite(ui, repo, files=args)
   296     expand = True
       
   297     _overwrite(ui, repo, args, expand)
   295 
   298 
   296 def demo(ui, repo, *args, **opts):
   299 def demo(ui, repo, *args, **opts):
   297     '''print [keywordmaps] configuration and an expansion example
   300     '''print [keywordmaps] configuration and an expansion example
   298 
   301 
   299     show current, custom, or default keyword template maps and their expansion
   302     show current, custom, or default keyword template maps and their expansion
   367             return aliases[0]
   370             return aliases[0]
   368 
   371 
   369     if not repo.local() or _getcmd() in nokwcommands:
   372     if not repo.local() or _getcmd() in nokwcommands:
   370         return
   373         return
   371 
   374 
   372     kwfmatcher = keywordmatcher(ui, repo)
   375     kwfmatcher = _keywordmatcher(ui, repo)
   373     if kwfmatcher is None:
   376     if kwfmatcher is None:
   374         return
   377         return
   375 
   378 
   376     class kwrepo(repo.__class__):
   379     class kwrepo(repo.__class__):
   377         def file(self, f, kwcnt=False, kwexp=True):
   380         def file(self, f, kwexp=True, kwcnt=False):
   378             if f[0] == '/':
   381             if f[0] == '/':
   379                 f = f[1:]
   382                 f = f[1:]
   380             if kwfmatcher(f):
   383             if kwfmatcher(f):
   381                 kwt = kwtemplater(ui, self, path=f, expand=kwexp)
   384                 kwt = kwtemplater(ui, self, kwexp, path=f)
   382                 return kwfilelog(self.sopener, f, kwt, kwcnt)
   385                 return kwfilelog(self.sopener, f, kwt, kwcnt)
   383             return filelog.filelog(self.sopener, f)
   386             return filelog.filelog(self.sopener, f)
   384 
   387 
   385         def commit(self, files=None, text='', user=None, date=None,
   388         def commit(self, files=None, text='', user=None, date=None,
   386                    match=util.always, force=False, lock=None, wlock=None,
   389                    match=util.always, force=False, lock=None, wlock=None,
   402                     candidates = [f for f in cl[3] if kwfmatcher(f)
   405                     candidates = [f for f in cl[3] if kwfmatcher(f)
   403                                   and f not in removed
   406                                   and f not in removed
   404                                   and not os.path.islink(self.wjoin(f))]
   407                                   and not os.path.islink(self.wjoin(f))]
   405                     if candidates:
   408                     if candidates:
   406                         m = self.manifest.read(cl[0])
   409                         m = self.manifest.read(cl[0])
   407                         kwt = kwtemplater(ui, self, node=node)
   410                         expand = commit = True
   408                         kwt.overwrite(candidates, m)
   411                         kwt = kwtemplater(ui, self, expand, node=node)
       
   412                         kwt.overwrite(candidates, m, commit)
   409                 wlock = None
   413                 wlock = None
   410                 return node
   414                 return node
   411             finally:
   415             finally:
   412                 del wlock
   416                 del wlock
   413 
   417