Mercurial > hg
diff hgext/keyword.py @ 33071:279c072a5c49
keyword: wrap functions only once at loading keyword extension
Before this patch, some functions are wrapped in reposetup(), but this
causes redundant nested wrapping, if two ore more repositories enable
keyword extension (e.g. hgweb serves multiple repositories).
Now, there is no need to define these wrapper functions in
reposetup(), because previous patches made them not directly refer to
kwtemplater instanciated in reposetup().
This patch factors these wrapper functions out from reposetup(), and
uses them to wrap functions only at once at loading keyword extension.
author | FUJIWARA Katsunori <foozy@lares.dti.ne.jp> |
---|---|
date | Mon, 26 Jun 2017 03:47:11 +0900 |
parents | 735218be6122 |
children | a39dce4a76b8 |
line wrap: on
line diff
--- a/hgext/keyword.py Mon Jun 26 03:46:17 2017 +0900 +++ b/hgext/keyword.py Mon Jun 26 03:47:11 2017 +0900 @@ -577,9 +577,136 @@ # 3rd argument sets expansion to False _kwfwrite(ui, repo, False, *pats, **opts) +# monkeypatches + +def kwpatchfile_init(orig, self, ui, gp, backend, store, eolmode=None): + '''Monkeypatch/wrap patch.patchfile.__init__ to avoid + rejects or conflicts due to expanded keywords in working dir.''' + orig(self, ui, gp, backend, store, eolmode) + kwt = getattr(getattr(backend, 'repo', None), '_keywordkwt', None) + if kwt: + # shrink keywords read from working dir + self.lines = kwt.shrinklines(self.fname, self.lines) + +def kwdiff(orig, repo, *args, **kwargs): + '''Monkeypatch patch.diff to avoid expansion.''' + kwt = getattr(repo, '_keywordkwt', None) + if kwt: + restrict = kwt.restrict + kwt.restrict = True + try: + for chunk in orig(repo, *args, **kwargs): + yield chunk + finally: + if kwt: + kwt.restrict = restrict + +def kwweb_skip(orig, web, req, tmpl): + '''Wraps webcommands.x turning off keyword expansion.''' + kwt = getattr(web.repo, '_keywordkwt', None) + if kwt: + origmatch = kwt.match + kwt.match = util.never + try: + for chunk in orig(web, req, tmpl): + yield chunk + finally: + if kwt: + kwt.match = origmatch + +def kw_amend(orig, ui, repo, commitfunc, old, extra, pats, opts): + '''Wraps cmdutil.amend expanding keywords after amend.''' + kwt = getattr(repo, '_keywordkwt', None) + if kwt is None: + return orig(ui, repo, commitfunc, old, extra, pats, opts) + with repo.wlock(): + kwt.postcommit = True + newid = orig(ui, repo, commitfunc, old, extra, pats, opts) + if newid != old.node(): + ctx = repo[newid] + kwt.restrict = True + kwt.overwrite(ctx, ctx.files(), False, True) + kwt.restrict = False + return newid + +def kw_copy(orig, ui, repo, pats, opts, rename=False): + '''Wraps cmdutil.copy so that copy/rename destinations do not + contain expanded keywords. + Note that the source of a regular file destination may also be a + symlink: + hg cp sym x -> x is symlink + cp sym x; hg cp -A sym x -> x is file (maybe expanded keywords) + For the latter we have to follow the symlink to find out whether its + target is configured for expansion and we therefore must unexpand the + keywords in the destination.''' + kwt = getattr(repo, '_keywordkwt', None) + if kwt is None: + return orig(ui, repo, pats, opts, rename) + with repo.wlock(): + orig(ui, repo, pats, opts, rename) + if opts.get('dry_run'): + return + wctx = repo[None] + cwd = repo.getcwd() + + def haskwsource(dest): + '''Returns true if dest is a regular file and configured for + expansion or a symlink which points to a file configured for + expansion. ''' + source = repo.dirstate.copied(dest) + if 'l' in wctx.flags(source): + source = pathutil.canonpath(repo.root, cwd, + os.path.realpath(source)) + return kwt.match(source) + + candidates = [f for f in repo.dirstate.copies() if + 'l' not in wctx.flags(f) and haskwsource(f)] + kwt.overwrite(wctx, candidates, False, False) + +def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts): + '''Wraps record.dorecord expanding keywords after recording.''' + kwt = getattr(repo, '_keywordkwt', None) + if kwt is None: + return orig(ui, repo, commitfunc, *pats, **opts) + with repo.wlock(): + # record returns 0 even when nothing has changed + # therefore compare nodes before and after + kwt.postcommit = True + ctx = repo['.'] + wstatus = ctx.status() + ret = orig(ui, repo, commitfunc, *pats, **opts) + recctx = repo['.'] + if ctx != recctx: + modified, added = _preselect(wstatus, recctx.files()) + kwt.restrict = False + kwt.overwrite(recctx, modified, False, True) + kwt.overwrite(recctx, added, False, True, True) + kwt.restrict = True + return ret + +def kwfilectx_cmp(orig, self, fctx): + if fctx._customcmp: + return fctx.cmp(self) + kwt = getattr(self._repo, '_keywordkwt', None) + if kwt is None: + return orig(self, fctx) + # keyword affects data size, comparing wdir and filelog size does + # not make sense + if (fctx._filenode is None and + (self._repo._encodefilterpats or + kwt.match(fctx.path()) and 'l' not in fctx.flags() or + self.size() - 4 == fctx.size()) or + self.size() == fctx.size()): + return self._filelog.cmp(self._filenode, fctx.data()) + return True def uisetup(ui): - ''' Monkeypatches dispatch._parse to retrieve user command.''' + ''' Monkeypatches dispatch._parse to retrieve user command. + Overrides file method to return kwfilelog instead of filelog + if file matches user configuration. + Wraps commit to overwrite configured files with updated + keyword substitutions. + Monkeypatches patch and webcommands.''' def kwdispatch_parse(orig, ui, args): '''Monkeypatch dispatch._parse to obtain running hg command.''' @@ -589,13 +716,17 @@ extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse) + extensions.wrapfunction(context.filectx, 'cmp', kwfilectx_cmp) + extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init) + extensions.wrapfunction(patch, 'diff', kwdiff) + extensions.wrapfunction(cmdutil, 'amend', kw_amend) + extensions.wrapfunction(cmdutil, 'copy', kw_copy) + extensions.wrapfunction(cmdutil, 'dorecord', kw_dorecord) + for c in nokwwebcommands.split(): + extensions.wrapfunction(webcommands, c, kwweb_skip) + def reposetup(ui, repo): - '''Sets up repo as kwrepo for keyword substitution. - Overrides file method to return kwfilelog instead of filelog - if file matches user configuration. - Wraps commit to overwrite configured files with updated - keyword substitutions. - Monkeypatches patch and webcommands.''' + '''Sets up repo as kwrepo for keyword substitution.''' try: if (not repo.local() or kwtools['hgcmd'] in nokwcommands.split() @@ -665,134 +796,3 @@ repo.__class__ = kwrepo repo._keywordkwt = kwt - - # monkeypatches - def kwpatchfile_init(orig, self, ui, gp, backend, store, eolmode=None): - '''Monkeypatch/wrap patch.patchfile.__init__ to avoid - rejects or conflicts due to expanded keywords in working dir.''' - orig(self, ui, gp, backend, store, eolmode) - kwt = getattr(getattr(backend, 'repo', None), '_keywordkwt', None) - if kwt: - # shrink keywords read from working dir - self.lines = kwt.shrinklines(self.fname, self.lines) - - def kwdiff(orig, repo, *args, **kwargs): - '''Monkeypatch patch.diff to avoid expansion.''' - kwt = getattr(repo, '_keywordkwt', None) - if kwt: - restrict = kwt.restrict - kwt.restrict = True - try: - for chunk in orig(repo, *args, **kwargs): - yield chunk - finally: - if kwt: - kwt.restrict = restrict - - def kwweb_skip(orig, web, req, tmpl): - '''Wraps webcommands.x turning off keyword expansion.''' - kwt = getattr(web.repo, '_keywordkwt', None) - if kwt: - origmatch = kwt.match - kwt.match = util.never - try: - for chunk in orig(web, req, tmpl): - yield chunk - finally: - if kwt: - kwt.match = origmatch - - def kw_amend(orig, ui, repo, commitfunc, old, extra, pats, opts): - '''Wraps cmdutil.amend expanding keywords after amend.''' - kwt = getattr(repo, '_keywordkwt', None) - if kwt is None: - return orig(ui, repo, commitfunc, old, extra, pats, opts) - with repo.wlock(): - kwt.postcommit = True - newid = orig(ui, repo, commitfunc, old, extra, pats, opts) - if newid != old.node(): - ctx = repo[newid] - kwt.restrict = True - kwt.overwrite(ctx, ctx.files(), False, True) - kwt.restrict = False - return newid - - def kw_copy(orig, ui, repo, pats, opts, rename=False): - '''Wraps cmdutil.copy so that copy/rename destinations do not - contain expanded keywords. - Note that the source of a regular file destination may also be a - symlink: - hg cp sym x -> x is symlink - cp sym x; hg cp -A sym x -> x is file (maybe expanded keywords) - For the latter we have to follow the symlink to find out whether its - target is configured for expansion and we therefore must unexpand the - keywords in the destination.''' - kwt = getattr(repo, '_keywordkwt', None) - if kwt is None: - return orig(ui, repo, pats, opts, rename) - with repo.wlock(): - orig(ui, repo, pats, opts, rename) - if opts.get('dry_run'): - return - wctx = repo[None] - cwd = repo.getcwd() - - def haskwsource(dest): - '''Returns true if dest is a regular file and configured for - expansion or a symlink which points to a file configured for - expansion. ''' - source = repo.dirstate.copied(dest) - if 'l' in wctx.flags(source): - source = pathutil.canonpath(repo.root, cwd, - os.path.realpath(source)) - return kwt.match(source) - - candidates = [f for f in repo.dirstate.copies() if - 'l' not in wctx.flags(f) and haskwsource(f)] - kwt.overwrite(wctx, candidates, False, False) - - def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts): - '''Wraps record.dorecord expanding keywords after recording.''' - kwt = getattr(repo, '_keywordkwt', None) - if kwt is None: - return orig(ui, repo, commitfunc, *pats, **opts) - with repo.wlock(): - # record returns 0 even when nothing has changed - # therefore compare nodes before and after - kwt.postcommit = True - ctx = repo['.'] - wstatus = ctx.status() - ret = orig(ui, repo, commitfunc, *pats, **opts) - recctx = repo['.'] - if ctx != recctx: - modified, added = _preselect(wstatus, recctx.files()) - kwt.restrict = False - kwt.overwrite(recctx, modified, False, True) - kwt.overwrite(recctx, added, False, True, True) - kwt.restrict = True - return ret - - def kwfilectx_cmp(orig, self, fctx): - if fctx._customcmp: - return fctx.cmp(self) - kwt = getattr(self._repo, '_keywordkwt', None) - if kwt is None: - return orig(self, fctx) - # keyword affects data size, comparing wdir and filelog size does - # not make sense - if (fctx._filenode is None and - (self._repo._encodefilterpats or - kwt.match(fctx.path()) and 'l' not in fctx.flags() or - self.size() - 4 == fctx.size()) or - self.size() == fctx.size()): - return self._filelog.cmp(self._filenode, fctx.data()) - return True - - extensions.wrapfunction(context.filectx, 'cmp', kwfilectx_cmp) - extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init) - extensions.wrapfunction(patch, 'diff', kwdiff) - extensions.wrapfunction(cmdutil, 'amend', kw_amend) - extensions.wrapfunction(cmdutil, 'copy', kw_copy) - extensions.wrapfunction(cmdutil, 'dorecord', kw_dorecord) - for c in nokwwebcommands.split(): - extensions.wrapfunction(webcommands, c, kwweb_skip)