hgkw/keyword.py
changeset 813 9d01f9cab5e2
parent 812 d11a6a561253
child 814 0588121c815b
equal deleted inserted replaced
812:d11a6a561253 813:9d01f9cab5e2
    94 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
    94 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
    95                 ' log outgoing push rename tip verify convert email glog')
    95                 ' log outgoing push rename tip verify convert email glog')
    96 
    96 
    97 # hg commands that trigger expansion only when writing to working dir,
    97 # hg commands that trigger expansion only when writing to working dir,
    98 # not when reading filelog, and unexpand when reading from working dir
    98 # not when reading filelog, and unexpand when reading from working dir
    99 restricted = 'merge record qrecord resolve transplant'
    99 restricted = 'merge kwexpand kwshrink record qrecord resolve transplant'
   100 
   100 
   101 # commands using dorecord
   101 # commands using dorecord
   102 recordcommands = 'record qrecord'
   102 recordcommands = 'record qrecord'
   103 # names of extensions using dorecord
   103 # names of extensions using dorecord
   104 recordextensions = 'record'
   104 recordextensions = 'record'
   136         'LastChangedDate': '{date|svnisodate}',
   136         'LastChangedDate': '{date|svnisodate}',
   137     })
   137     })
   138     templates.update(kwsets[ui.configbool('keywordset', 'svn')])
   138     templates.update(kwsets[ui.configbool('keywordset', 'svn')])
   139     return templates
   139     return templates
   140 
   140 
       
   141 def _shrinktext(text, subfunc):
       
   142     '''Helper for keyword expansion removal in text.
       
   143     Depending on subfunc also returns number of substitutions.'''
       
   144     return subfunc(r'$\1$', text)
       
   145 
       
   146 
   141 class kwtemplater(object):
   147 class kwtemplater(object):
   142     '''
   148     '''
   143     Sets up keyword templates, corresponding keyword regex, and
   149     Sets up keyword templates, corresponding keyword regex, and
   144     provides keyword substitution functions.
   150     provides keyword substitution functions.
   145     '''
   151     '''
   189         '''Returns true if path matches [keyword] pattern
   195         '''Returns true if path matches [keyword] pattern
   190         and is not a symbolic link.
   196         and is not a symbolic link.
   191         Caveat: localrepository._link fails on Windows.'''
   197         Caveat: localrepository._link fails on Windows.'''
   192         return self.match(path) and not 'l' in flagfunc(path)
   198         return self.match(path) and not 'l' in flagfunc(path)
   193 
   199 
   194     def overwrite(self, ctx, candidates, iswctx, expand, changed):
   200     def overwrite(self, ctx, candidates, lookup, expand):
   195         '''Overwrites selected files expanding/shrinking keywords.'''
   201         '''Overwrites selected files expanding/shrinking keywords.'''
   196         if changed is not None:
       
   197             candidates = [f for f in candidates if f in changed]
       
   198         candidates = [f for f in candidates if self.iskwfile(f, ctx.flags)]
   202         candidates = [f for f in candidates if self.iskwfile(f, ctx.flags)]
   199         if candidates:
   203         if not candidates:
   200             restrict = self.restrict
   204             return
   201             self.restrict = True        # do not expand when reading
   205         commit = self.restrict and not lookup
   202             rollback = kwtools['hgcmd'] == 'rollback'
   206         if self.restrict or expand and lookup:
   203             mf = ctx.manifest()
   207             mf = ctx.manifest()
   204             msg = (expand and _('overwriting %s expanding keywords\n')
   208         fctx = ctx
   205                    or _('overwriting %s shrinking keywords\n'))
   209         msg = (expand and _('overwriting %s expanding keywords\n')
   206             for f in candidates:
   210                or _('overwriting %s shrinking keywords\n'))
   207                 if not self.record and not rollback:
   211         for f in candidates:
   208                     data = self.repo.file(f).read(mf[f])
   212             if self.restrict:
   209                 else:
   213                 data = self.repo.file(f).read(mf[f])
   210                     data = self.repo.wread(f)
   214             else:
   211                 if util.binary(data):
   215                 data = self.repo.wread(f)
   212                     continue
   216             if util.binary(data):
   213                 if expand:
   217                 continue
   214                     if iswctx:
   218             if expand:
   215                         ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
   219                 if lookup:
   216                     data, found = self.substitute(data, f, ctx,
   220                     fctx = self.repo.filectx(f, fileid=mf[f]).changectx()
   217                                                   self.re_kw.subn)
   221                 data, found = self.substitute(data, f, fctx, self.re_kw.subn)
   218                 else:
   222             elif self.restrict:
   219                     found = self.re_kw.search(data)
   223                 found = self.re_kw.search(data)
   220                 if found:
   224             else:
   221                     self.ui.note(msg % f)
   225                 data, found = _shrinktext(data, self.re_kw.subn)
   222                     self.repo.wwrite(f, data, mf.flags(f))
   226             if found:
   223                     if iswctx and not rollback:
   227                 self.ui.note(msg % f)
   224                         self.repo.dirstate.normal(f)
   228                 self.repo.wwrite(f, data, ctx.flags(f))
   225                     elif self.record:
   229                 if commit:
   226                         self.repo.dirstate.normallookup(f)
   230                     self.repo.dirstate.normal(f)
   227             self.restrict = restrict
   231                 elif self.record:
   228 
   232                     self.repo.dirstate.normallookup(f)
   229     def shrinktext(self, text):
       
   230         '''Unconditionally removes all keyword substitutions from text.'''
       
   231         return self.re_kw.sub(r'$\1$', text)
       
   232 
   233 
   233     def shrink(self, fname, text):
   234     def shrink(self, fname, text):
   234         '''Returns text with all keyword substitutions removed.'''
   235         '''Returns text with all keyword substitutions removed.'''
   235         if self.match(fname) and not util.binary(text):
   236         if self.match(fname) and not util.binary(text):
   236             return self.shrinktext(text)
   237             return _shrinktext(text, self.re_kw.sub)
   237         return text
   238         return text
   238 
   239 
   239     def shrinklines(self, fname, lines):
   240     def shrinklines(self, fname, lines):
   240         '''Returns lines with keyword substitutions removed.'''
   241         '''Returns lines with keyword substitutions removed.'''
   241         if self.match(fname):
   242         if self.match(fname):
   242             text = ''.join(lines)
   243             text = ''.join(lines)
   243             if not util.binary(text):
   244             if not util.binary(text):
   244                 return self.shrinktext(text).splitlines(True)
   245                 return _shrinktext(text, self.re_kw.sub).splitlines(True)
   245         return lines
   246         return lines
   246 
   247 
   247     def wread(self, fname, data):
   248     def wread(self, fname, data):
   248         '''If in restricted mode returns data read from wdir with
   249         '''If in restricted mode returns data read from wdir with
   249         keyword substitutions removed.'''
   250         keyword substitutions removed.'''
   297     try:
   298     try:
   298         status = _status(ui, repo, kwt, *pats, **opts)
   299         status = _status(ui, repo, kwt, *pats, **opts)
   299         modified, added, removed, deleted, unknown, ignored, clean = status
   300         modified, added, removed, deleted, unknown, ignored, clean = status
   300         if modified or added or removed or deleted:
   301         if modified or added or removed or deleted:
   301             raise util.Abort(_('outstanding uncommitted changes'))
   302             raise util.Abort(_('outstanding uncommitted changes'))
   302         kwt.overwrite(wctx, clean, True, expand, None)
   303         kwt.overwrite(wctx, clean, True, expand)
   303     finally:
   304     finally:
   304         wlock.release()
   305         wlock.release()
   305 
   306 
   306 def demo(ui, repo, *args, **opts):
   307 def demo(ui, repo, *args, **opts):
   307     '''print [keywordmaps] configuration and an expansion example
   308     '''print [keywordmaps] configuration and an expansion example
   500 
   501 
   501         def kwcommitctx(self, ctx, error=False):
   502         def kwcommitctx(self, ctx, error=False):
   502             n = super(kwrepo, self).commitctx(ctx, error)
   503             n = super(kwrepo, self).commitctx(ctx, error)
   503             # no lock needed, only called from repo.commit() which already locks
   504             # no lock needed, only called from repo.commit() which already locks
   504             if not kwt.record:
   505             if not kwt.record:
       
   506                 restrict = kwt.restrict
       
   507                 kwt.restrict = True
   505                 kwt.overwrite(self[n], sorted(ctx.added() + ctx.modified()),
   508                 kwt.overwrite(self[n], sorted(ctx.added() + ctx.modified()),
   506                               False, True, None)
   509                               False, True)
       
   510                 kwt.restrict = restrict
   507             return n
   511             return n
   508 
   512 
   509         def rollback(self, dryrun=False):
   513         def rollback(self, dryrun=False):
   510             wlock = repo.wlock()
   514             wlock = repo.wlock()
   511             try:
   515             try:
   513                     changed = self['.'].files()
   517                     changed = self['.'].files()
   514                 ret = super(kwrepo, self).rollback(dryrun)
   518                 ret = super(kwrepo, self).rollback(dryrun)
   515                 if not dryrun:
   519                 if not dryrun:
   516                     ctx = self['.']
   520                     ctx = self['.']
   517                     modified, added = self[None].status()[:2]
   521                     modified, added = self[None].status()[:2]
   518                     kwt.overwrite(ctx, added, True, False, changed)
   522                     modified = [f for f in modified if f in changed]
   519                     kwt.overwrite(ctx, modified, True, True, changed)
   523                     added = [f for f in added if f in changed]
       
   524                     kwt.overwrite(ctx, added, True, False)
       
   525                     kwt.overwrite(ctx, modified, True, True)
   520                 return ret
   526                 return ret
   521             finally:
   527             finally:
   522                 wlock.release()
   528                 wlock.release()
   523 
   529 
   524     # monkeypatches
   530     # monkeypatches
   549             # therefore compare nodes before and after
   555             # therefore compare nodes before and after
   550             ctx = repo['.']
   556             ctx = repo['.']
   551             ret = orig(ui, repo, commitfunc, *pats, **opts)
   557             ret = orig(ui, repo, commitfunc, *pats, **opts)
   552             recordctx = repo['.']
   558             recordctx = repo['.']
   553             if ctx != recordctx:
   559             if ctx != recordctx:
   554                 kwt.overwrite(recordctx, recordctx.files(),
   560                 candidates = [f for f in recordctx.files() if f in recordctx]
   555                               False, True, recordctx)
   561                 kwt.restrict = False
       
   562                 kwt.overwrite(recordctx, candidates, False, True)
       
   563                 kwt.restrict = True
   556             return ret
   564             return ret
   557         finally:
   565         finally:
   558             wlock.release()
   566             wlock.release()
   559 
   567 
   560     repo.__class__ = kwrepo
   568     repo.__class__ = kwrepo