diff --git a/hgext/sparse.py b/hgext/sparse.py --- a/hgext/sparse.py +++ b/hgext/sparse.py @@ -119,7 +119,8 @@ def reposetup(ui, repo): if not util.safehasattr(repo, 'dirstate'): return - _wraprepo(ui, repo) + if 'dirstate' in repo._filecache: + repo.dirstate.repo = repo def replacefilecache(cls, propname, replacement): """Replace a filecache property with a new class. This allows changing the @@ -224,17 +225,6 @@ def _setupupdates(ui): extensions.wrapfunction(mergemod, 'calculateupdates', _calculateupdates) - def _update(orig, repo, node, branchmerge, *args, **kwargs): - results = orig(repo, node, branchmerge, *args, **kwargs) - - # If we're updating to a location, clean up any stale temporary includes - # (ex: this happens during hg rebase --abort). - if not branchmerge and util.safehasattr(repo, 'prunetemporaryincludes'): - repo.prunetemporaryincludes() - return results - - extensions.wrapfunction(mergemod, 'update', _update) - def _setupcommit(ui): def _refreshoncommit(orig, self, node): """Refresh the checkout when commits touch .hgsparse @@ -251,8 +241,7 @@ def _setupcommit(ui): origsparsematch = sparse.matcher(repo) _refresh(repo.ui, repo, origstatus, origsparsematch, True) - if util.safehasattr(repo, 'prunetemporaryincludes'): - repo.prunetemporaryincludes() + sparse.prunetemporaryincludes(repo) extensions.wrapfunction(context.committablectx, 'markcommitted', _refreshoncommit) @@ -403,47 +392,6 @@ def _setupdirstate(ui): return orig(self, *args) extensions.wrapfunction(dirstate.dirstate, func, _wrapper) -def _wraprepo(ui, repo): - class SparseRepo(repo.__class__): - def prunetemporaryincludes(self): - if repo.vfs.exists('tempsparse'): - origstatus = self.status() - modified, added, removed, deleted, a, b, c = origstatus - if modified or added or removed or deleted: - # Still have pending changes. Don't bother trying to prune. - return - - sparsematch = sparse.matcher(self, includetemp=False) - dirstate = self.dirstate - actions = [] - dropped = [] - tempincludes = sparse.readtemporaryincludes(self) - for file in tempincludes: - if file in dirstate and not sparsematch(file): - message = 'dropping temporarily included sparse files' - actions.append((file, None, message)) - dropped.append(file) - - typeactions = collections.defaultdict(list) - typeactions['r'] = actions - mergemod.applyupdates(self, typeactions, self[None], self['.'], - False) - - # Fix dirstate - for file in dropped: - dirstate.drop(file) - - self.vfs.unlink('tempsparse') - sparse.invalidatesignaturecache(self) - msg = _("cleaned up %d temporarily added file(s) from the " - "sparse checkout\n") - ui.status(msg % len(tempincludes)) - - if 'dirstate' in repo._filecache: - repo.dirstate.repo = repo - - repo.__class__ = SparseRepo - @command('^debugsparse', [ ('I', 'include', False, _('include files in the sparse checkout')), ('X', 'exclude', False, _('exclude files in the sparse checkout')), diff --git a/mercurial/merge.py b/mercurial/merge.py --- a/mercurial/merge.py +++ b/mercurial/merge.py @@ -1514,6 +1514,8 @@ def update(repo, node, branchmerge, forc Return the same tuple as applyupdates(). """ + # Avoid cycle. + from . import sparse # This function used to find the default destination if node was None, but # that's now in destutil.py. @@ -1703,6 +1705,11 @@ def update(repo, node, branchmerge, forc if not branchmerge: repo.dirstate.setbranch(p2.branch()) + # If we're updating to a location, clean up any stale temporary includes + # (ex: this happens during hg rebase --abort). + if not branchmerge: + sparse.prunetemporaryincludes(repo) + if not partial: repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3]) return stats diff --git a/mercurial/sparse.py b/mercurial/sparse.py --- a/mercurial/sparse.py +++ b/mercurial/sparse.py @@ -7,6 +7,7 @@ from __future__ import absolute_import +import collections import hashlib import os @@ -15,6 +16,7 @@ from .node import nullid from . import ( error, match as matchmod, + merge as mergemod, pycompat, ) @@ -197,6 +199,41 @@ def addtemporaryincludes(repo, additiona includes.add(i) writetemporaryincludes(repo, includes) +def prunetemporaryincludes(repo): + if not enabled or not repo.vfs.exists('tempsparse'): + return + + origstatus = repo.status() + modified, added, removed, deleted, a, b, c = origstatus + if modified or added or removed or deleted: + # Still have pending changes. Don't bother trying to prune. + return + + sparsematch = matcher(repo, includetemp=False) + dirstate = repo.dirstate + actions = [] + dropped = [] + tempincludes = readtemporaryincludes(repo) + for file in tempincludes: + if file in dirstate and not sparsematch(file): + message = _('dropping temporarily included sparse files') + actions.append((file, None, message)) + dropped.append(file) + + typeactions = collections.defaultdict(list) + typeactions['r'] = actions + mergemod.applyupdates(repo, typeactions, repo[None], repo['.'], False) + + # Fix dirstate + for file in dropped: + dirstate.drop(file) + + repo.vfs.unlink('tempsparse') + invalidatesignaturecache(repo) + msg = _('cleaned up %d temporarily added file(s) from the ' + 'sparse checkout\n') + repo.ui.status(msg % len(tempincludes)) + def matcher(repo, revs=None, includetemp=True): """Obtain a matcher for sparse working directories for the given revs.