hgext/keyword.py
changeset 33071 279c072a5c49
parent 33070 735218be6122
child 34086 a39dce4a76b8
equal deleted inserted replaced
33070:735218be6122 33071:279c072a5c49
   575     kwshrink refuses to run if given files contain local changes.
   575     kwshrink refuses to run if given files contain local changes.
   576     '''
   576     '''
   577     # 3rd argument sets expansion to False
   577     # 3rd argument sets expansion to False
   578     _kwfwrite(ui, repo, False, *pats, **opts)
   578     _kwfwrite(ui, repo, False, *pats, **opts)
   579 
   579 
       
   580 # monkeypatches
       
   581 
       
   582 def kwpatchfile_init(orig, self, ui, gp, backend, store, eolmode=None):
       
   583     '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
       
   584     rejects or conflicts due to expanded keywords in working dir.'''
       
   585     orig(self, ui, gp, backend, store, eolmode)
       
   586     kwt = getattr(getattr(backend, 'repo', None), '_keywordkwt', None)
       
   587     if kwt:
       
   588         # shrink keywords read from working dir
       
   589         self.lines = kwt.shrinklines(self.fname, self.lines)
       
   590 
       
   591 def kwdiff(orig, repo, *args, **kwargs):
       
   592     '''Monkeypatch patch.diff to avoid expansion.'''
       
   593     kwt = getattr(repo, '_keywordkwt', None)
       
   594     if kwt:
       
   595         restrict = kwt.restrict
       
   596         kwt.restrict = True
       
   597     try:
       
   598         for chunk in orig(repo, *args, **kwargs):
       
   599             yield chunk
       
   600     finally:
       
   601         if kwt:
       
   602             kwt.restrict = restrict
       
   603 
       
   604 def kwweb_skip(orig, web, req, tmpl):
       
   605     '''Wraps webcommands.x turning off keyword expansion.'''
       
   606     kwt = getattr(web.repo, '_keywordkwt', None)
       
   607     if kwt:
       
   608         origmatch = kwt.match
       
   609         kwt.match = util.never
       
   610     try:
       
   611         for chunk in orig(web, req, tmpl):
       
   612             yield chunk
       
   613     finally:
       
   614         if kwt:
       
   615             kwt.match = origmatch
       
   616 
       
   617 def kw_amend(orig, ui, repo, commitfunc, old, extra, pats, opts):
       
   618     '''Wraps cmdutil.amend expanding keywords after amend.'''
       
   619     kwt = getattr(repo, '_keywordkwt', None)
       
   620     if kwt is None:
       
   621         return orig(ui, repo, commitfunc, old, extra, pats, opts)
       
   622     with repo.wlock():
       
   623         kwt.postcommit = True
       
   624         newid = orig(ui, repo, commitfunc, old, extra, pats, opts)
       
   625         if newid != old.node():
       
   626             ctx = repo[newid]
       
   627             kwt.restrict = True
       
   628             kwt.overwrite(ctx, ctx.files(), False, True)
       
   629             kwt.restrict = False
       
   630         return newid
       
   631 
       
   632 def kw_copy(orig, ui, repo, pats, opts, rename=False):
       
   633     '''Wraps cmdutil.copy so that copy/rename destinations do not
       
   634     contain expanded keywords.
       
   635     Note that the source of a regular file destination may also be a
       
   636     symlink:
       
   637     hg cp sym x                -> x is symlink
       
   638     cp sym x; hg cp -A sym x   -> x is file (maybe expanded keywords)
       
   639     For the latter we have to follow the symlink to find out whether its
       
   640     target is configured for expansion and we therefore must unexpand the
       
   641     keywords in the destination.'''
       
   642     kwt = getattr(repo, '_keywordkwt', None)
       
   643     if kwt is None:
       
   644         return orig(ui, repo, pats, opts, rename)
       
   645     with repo.wlock():
       
   646         orig(ui, repo, pats, opts, rename)
       
   647         if opts.get('dry_run'):
       
   648             return
       
   649         wctx = repo[None]
       
   650         cwd = repo.getcwd()
       
   651 
       
   652         def haskwsource(dest):
       
   653             '''Returns true if dest is a regular file and configured for
       
   654             expansion or a symlink which points to a file configured for
       
   655             expansion. '''
       
   656             source = repo.dirstate.copied(dest)
       
   657             if 'l' in wctx.flags(source):
       
   658                 source = pathutil.canonpath(repo.root, cwd,
       
   659                                            os.path.realpath(source))
       
   660             return kwt.match(source)
       
   661 
       
   662         candidates = [f for f in repo.dirstate.copies() if
       
   663                       'l' not in wctx.flags(f) and haskwsource(f)]
       
   664         kwt.overwrite(wctx, candidates, False, False)
       
   665 
       
   666 def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts):
       
   667     '''Wraps record.dorecord expanding keywords after recording.'''
       
   668     kwt = getattr(repo, '_keywordkwt', None)
       
   669     if kwt is None:
       
   670         return orig(ui, repo, commitfunc, *pats, **opts)
       
   671     with repo.wlock():
       
   672         # record returns 0 even when nothing has changed
       
   673         # therefore compare nodes before and after
       
   674         kwt.postcommit = True
       
   675         ctx = repo['.']
       
   676         wstatus = ctx.status()
       
   677         ret = orig(ui, repo, commitfunc, *pats, **opts)
       
   678         recctx = repo['.']
       
   679         if ctx != recctx:
       
   680             modified, added = _preselect(wstatus, recctx.files())
       
   681             kwt.restrict = False
       
   682             kwt.overwrite(recctx, modified, False, True)
       
   683             kwt.overwrite(recctx, added, False, True, True)
       
   684             kwt.restrict = True
       
   685         return ret
       
   686 
       
   687 def kwfilectx_cmp(orig, self, fctx):
       
   688     if fctx._customcmp:
       
   689         return fctx.cmp(self)
       
   690     kwt = getattr(self._repo, '_keywordkwt', None)
       
   691     if kwt is None:
       
   692         return orig(self, fctx)
       
   693     # keyword affects data size, comparing wdir and filelog size does
       
   694     # not make sense
       
   695     if (fctx._filenode is None and
       
   696         (self._repo._encodefilterpats or
       
   697          kwt.match(fctx.path()) and 'l' not in fctx.flags() or
       
   698          self.size() - 4 == fctx.size()) or
       
   699         self.size() == fctx.size()):
       
   700         return self._filelog.cmp(self._filenode, fctx.data())
       
   701     return True
   580 
   702 
   581 def uisetup(ui):
   703 def uisetup(ui):
   582     ''' Monkeypatches dispatch._parse to retrieve user command.'''
   704     ''' Monkeypatches dispatch._parse to retrieve user command.
       
   705     Overrides file method to return kwfilelog instead of filelog
       
   706     if file matches user configuration.
       
   707     Wraps commit to overwrite configured files with updated
       
   708     keyword substitutions.
       
   709     Monkeypatches patch and webcommands.'''
   583 
   710 
   584     def kwdispatch_parse(orig, ui, args):
   711     def kwdispatch_parse(orig, ui, args):
   585         '''Monkeypatch dispatch._parse to obtain running hg command.'''
   712         '''Monkeypatch dispatch._parse to obtain running hg command.'''
   586         cmd, func, args, options, cmdoptions = orig(ui, args)
   713         cmd, func, args, options, cmdoptions = orig(ui, args)
   587         kwtools['hgcmd'] = cmd
   714         kwtools['hgcmd'] = cmd
   588         return cmd, func, args, options, cmdoptions
   715         return cmd, func, args, options, cmdoptions
   589 
   716 
   590     extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
   717     extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
   591 
   718 
       
   719     extensions.wrapfunction(context.filectx, 'cmp', kwfilectx_cmp)
       
   720     extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
       
   721     extensions.wrapfunction(patch, 'diff', kwdiff)
       
   722     extensions.wrapfunction(cmdutil, 'amend', kw_amend)
       
   723     extensions.wrapfunction(cmdutil, 'copy', kw_copy)
       
   724     extensions.wrapfunction(cmdutil, 'dorecord', kw_dorecord)
       
   725     for c in nokwwebcommands.split():
       
   726         extensions.wrapfunction(webcommands, c, kwweb_skip)
       
   727 
   592 def reposetup(ui, repo):
   728 def reposetup(ui, repo):
   593     '''Sets up repo as kwrepo for keyword substitution.
   729     '''Sets up repo as kwrepo for keyword substitution.'''
   594     Overrides file method to return kwfilelog instead of filelog
       
   595     if file matches user configuration.
       
   596     Wraps commit to overwrite configured files with updated
       
   597     keyword substitutions.
       
   598     Monkeypatches patch and webcommands.'''
       
   599 
   730 
   600     try:
   731     try:
   601         if (not repo.local() or kwtools['hgcmd'] in nokwcommands.split()
   732         if (not repo.local() or kwtools['hgcmd'] in nokwcommands.split()
   602             or '.hg' in util.splitpath(repo.root)
   733             or '.hg' in util.splitpath(repo.root)
   603             or repo._url.startswith('bundle:')):
   734             or repo._url.startswith('bundle:')):
   663                 finally:
   794                 finally:
   664                     kwt.restrict = origrestrict
   795                     kwt.restrict = origrestrict
   665 
   796 
   666     repo.__class__ = kwrepo
   797     repo.__class__ = kwrepo
   667     repo._keywordkwt = kwt
   798     repo._keywordkwt = kwt
   668 
       
   669     # monkeypatches
       
   670     def kwpatchfile_init(orig, self, ui, gp, backend, store, eolmode=None):
       
   671         '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
       
   672         rejects or conflicts due to expanded keywords in working dir.'''
       
   673         orig(self, ui, gp, backend, store, eolmode)
       
   674         kwt = getattr(getattr(backend, 'repo', None), '_keywordkwt', None)
       
   675         if kwt:
       
   676             # shrink keywords read from working dir
       
   677             self.lines = kwt.shrinklines(self.fname, self.lines)
       
   678 
       
   679     def kwdiff(orig, repo, *args, **kwargs):
       
   680         '''Monkeypatch patch.diff to avoid expansion.'''
       
   681         kwt = getattr(repo, '_keywordkwt', None)
       
   682         if kwt:
       
   683             restrict = kwt.restrict
       
   684             kwt.restrict = True
       
   685         try:
       
   686             for chunk in orig(repo, *args, **kwargs):
       
   687                 yield chunk
       
   688         finally:
       
   689             if kwt:
       
   690                 kwt.restrict = restrict
       
   691 
       
   692     def kwweb_skip(orig, web, req, tmpl):
       
   693         '''Wraps webcommands.x turning off keyword expansion.'''
       
   694         kwt = getattr(web.repo, '_keywordkwt', None)
       
   695         if kwt:
       
   696             origmatch = kwt.match
       
   697             kwt.match = util.never
       
   698         try:
       
   699             for chunk in orig(web, req, tmpl):
       
   700                 yield chunk
       
   701         finally:
       
   702             if kwt:
       
   703                 kwt.match = origmatch
       
   704 
       
   705     def kw_amend(orig, ui, repo, commitfunc, old, extra, pats, opts):
       
   706         '''Wraps cmdutil.amend expanding keywords after amend.'''
       
   707         kwt = getattr(repo, '_keywordkwt', None)
       
   708         if kwt is None:
       
   709             return orig(ui, repo, commitfunc, old, extra, pats, opts)
       
   710         with repo.wlock():
       
   711             kwt.postcommit = True
       
   712             newid = orig(ui, repo, commitfunc, old, extra, pats, opts)
       
   713             if newid != old.node():
       
   714                 ctx = repo[newid]
       
   715                 kwt.restrict = True
       
   716                 kwt.overwrite(ctx, ctx.files(), False, True)
       
   717                 kwt.restrict = False
       
   718             return newid
       
   719 
       
   720     def kw_copy(orig, ui, repo, pats, opts, rename=False):
       
   721         '''Wraps cmdutil.copy so that copy/rename destinations do not
       
   722         contain expanded keywords.
       
   723         Note that the source of a regular file destination may also be a
       
   724         symlink:
       
   725         hg cp sym x                -> x is symlink
       
   726         cp sym x; hg cp -A sym x   -> x is file (maybe expanded keywords)
       
   727         For the latter we have to follow the symlink to find out whether its
       
   728         target is configured for expansion and we therefore must unexpand the
       
   729         keywords in the destination.'''
       
   730         kwt = getattr(repo, '_keywordkwt', None)
       
   731         if kwt is None:
       
   732             return orig(ui, repo, pats, opts, rename)
       
   733         with repo.wlock():
       
   734             orig(ui, repo, pats, opts, rename)
       
   735             if opts.get('dry_run'):
       
   736                 return
       
   737             wctx = repo[None]
       
   738             cwd = repo.getcwd()
       
   739 
       
   740             def haskwsource(dest):
       
   741                 '''Returns true if dest is a regular file and configured for
       
   742                 expansion or a symlink which points to a file configured for
       
   743                 expansion. '''
       
   744                 source = repo.dirstate.copied(dest)
       
   745                 if 'l' in wctx.flags(source):
       
   746                     source = pathutil.canonpath(repo.root, cwd,
       
   747                                                os.path.realpath(source))
       
   748                 return kwt.match(source)
       
   749 
       
   750             candidates = [f for f in repo.dirstate.copies() if
       
   751                           'l' not in wctx.flags(f) and haskwsource(f)]
       
   752             kwt.overwrite(wctx, candidates, False, False)
       
   753 
       
   754     def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts):
       
   755         '''Wraps record.dorecord expanding keywords after recording.'''
       
   756         kwt = getattr(repo, '_keywordkwt', None)
       
   757         if kwt is None:
       
   758             return orig(ui, repo, commitfunc, *pats, **opts)
       
   759         with repo.wlock():
       
   760             # record returns 0 even when nothing has changed
       
   761             # therefore compare nodes before and after
       
   762             kwt.postcommit = True
       
   763             ctx = repo['.']
       
   764             wstatus = ctx.status()
       
   765             ret = orig(ui, repo, commitfunc, *pats, **opts)
       
   766             recctx = repo['.']
       
   767             if ctx != recctx:
       
   768                 modified, added = _preselect(wstatus, recctx.files())
       
   769                 kwt.restrict = False
       
   770                 kwt.overwrite(recctx, modified, False, True)
       
   771                 kwt.overwrite(recctx, added, False, True, True)
       
   772                 kwt.restrict = True
       
   773             return ret
       
   774 
       
   775     def kwfilectx_cmp(orig, self, fctx):
       
   776         if fctx._customcmp:
       
   777             return fctx.cmp(self)
       
   778         kwt = getattr(self._repo, '_keywordkwt', None)
       
   779         if kwt is None:
       
   780             return orig(self, fctx)
       
   781         # keyword affects data size, comparing wdir and filelog size does
       
   782         # not make sense
       
   783         if (fctx._filenode is None and
       
   784             (self._repo._encodefilterpats or
       
   785              kwt.match(fctx.path()) and 'l' not in fctx.flags() or
       
   786              self.size() - 4 == fctx.size()) or
       
   787             self.size() == fctx.size()):
       
   788             return self._filelog.cmp(self._filenode, fctx.data())
       
   789         return True
       
   790 
       
   791     extensions.wrapfunction(context.filectx, 'cmp', kwfilectx_cmp)
       
   792     extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
       
   793     extensions.wrapfunction(patch, 'diff', kwdiff)
       
   794     extensions.wrapfunction(cmdutil, 'amend', kw_amend)
       
   795     extensions.wrapfunction(cmdutil, 'copy', kw_copy)
       
   796     extensions.wrapfunction(cmdutil, 'dorecord', kw_dorecord)
       
   797     for c in nokwwebcommands.split():
       
   798         extensions.wrapfunction(webcommands, c, kwweb_skip)