Patchwork [06,of,11,sparse,V3] sparse: move pruning of temporary includes into core

login
register
mail settings
Submitter Gregory Szorc
Date July 7, 2017, 1:18 a.m.
Message ID <5aee17f8fb13a0197b15.1499390307@ubuntu-vm-main>
Download mbox | patch
Permalink /patch/22078/
State Accepted
Headers show

Comments

Gregory Szorc - July 7, 2017, 1:18 a.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1499376798 25200
#      Thu Jul 06 14:33:18 2017 -0700
# Node ID 5aee17f8fb13a0197b15e3c6304fb88a87ff70de
# Parent  5eb28d023f051b0e3e1b3cec3f5ab8b2d6cd2a87
sparse: move pruning of temporary includes into core

This was our last method on the custom repo type, meaning we could
remove that custom type and inline the 2 lines of code into
reposetup().

As part of the move, instead of wrapping merge.update() from
the sparse extension, we inline the function call. The ported
function now no-ops if sparse isn't enabled, making it safe to
always call.

The call site in update() may not be the most appropriate. But
it matches the previous behavior, which is the safest thing
to do. It can be improved later.

Patch

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.