Patchwork [09,of,13,sparse,V2] sparse: move config parsing into core

login
register
mail settings
Submitter Gregory Szorc
Date July 6, 2017, 7:36 p.m.
Message ID <e3662ac026a6db43875a.1499369795@ubuntu-vm-main>
Download mbox | patch
Permalink /patch/22038/
State Accepted
Headers show

Comments

Gregory Szorc - July 6, 2017, 7:36 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1499368443 25200
#      Thu Jul 06 12:14:03 2017 -0700
# Node ID e3662ac026a6db43875acb4b89507c0e9d77f16f
# Parent  18bb106ccbe320d2be62d8c28b8ef756dad9426d
sparse: move config parsing into core

This patch marks the beginning of moving code from the sparse
extension into core. The goal is to move as much of the
functionality as possible into core, where it will be an
experimental feature. The extension will likely continue to
exist to enable the feature and provide UI elements.

As part of the move, the repo method was converted to a module
function. It doesn't need to exist on repos.

An error message was also updated to reflect that an error isn't
necessarily from the .hg/sparse file. The API should be updated
later to pass in a filename so the error can be more descriptive.

Copyright of the added file was copied from the sparse extension.

Patch

diff --git a/hgext/sparse.py b/hgext/sparse.py
--- a/hgext/sparse.py
+++ b/hgext/sparse.py
@@ -92,6 +92,7 @@  from mercurial import (
     match as matchmod,
     merge as mergemod,
     registrar,
+    sparse,
     util,
 )
 
@@ -407,40 +408,6 @@  def _setupdirstate(ui):
 
 def _wraprepo(ui, repo):
     class SparseRepo(repo.__class__):
-        def readsparseconfig(self, raw):
-            """Takes a string sparse config and returns the includes,
-            excludes, and profiles it specified.
-            """
-            includes = set()
-            excludes = set()
-            current = includes
-            profiles = []
-            for line in raw.split('\n'):
-                line = line.strip()
-                if not line or line.startswith('#'):
-                    # empty or comment line, skip
-                    continue
-                elif line.startswith('%include '):
-                    line = line[9:].strip()
-                    if line:
-                        profiles.append(line)
-                elif line == '[include]':
-                    if current != includes:
-                        raise error.Abort(_('.hg/sparse cannot have includes ' +
-                            'after excludes'))
-                    continue
-                elif line == '[exclude]':
-                    current = excludes
-                elif line:
-                    if line.strip().startswith('/'):
-                        self.ui.warn(_('warning: sparse profile cannot use' +
-                                       ' paths starting with /, ignoring %s\n')
-                                       % line)
-                        continue
-                    current.add(line)
-
-            return includes, excludes, profiles
-
         def getsparsepatterns(self, rev):
             """Returns the include/exclude patterns specified by the
             given rev.
@@ -452,7 +419,7 @@  def _wraprepo(ui, repo):
                 raise error.Abort(_("cannot parse sparse patterns from " +
                     "working copy"))
 
-            includes, excludes, profiles = self.readsparseconfig(raw)
+            includes, excludes, profiles = sparse.parseconfig(self.ui, raw)
 
             ctx = self[rev]
             if profiles:
@@ -475,8 +442,8 @@  def _wraprepo(ui, repo):
                         else:
                             self.ui.debug(msg)
                         continue
-                    pincludes, pexcludes, subprofs = \
-                        self.readsparseconfig(raw)
+                    pincludes, pexcludes, subprofs = sparse.parseconfig(
+                        self.ui, raw)
                     includes.update(pincludes)
                     excludes.update(pexcludes)
                     for subprofile in subprofs:
@@ -787,7 +754,7 @@  def _config(ui, repo, pats, opts, includ
         raw = repo.vfs.tryread('sparse')
         if raw:
             oldinclude, oldexclude, oldprofiles = map(
-                set, repo.readsparseconfig(raw))
+                set, sparse.parseconfig(ui, raw))
         else:
             oldinclude = set()
             oldexclude = set()
@@ -846,7 +813,7 @@  def _import(ui, repo, files, opts, force
 
         # read current configuration
         raw = repo.vfs.tryread('sparse')
-        oincludes, oexcludes, oprofiles = repo.readsparseconfig(raw)
+        oincludes, oexcludes, oprofiles = sparse.parseconfig(ui, raw)
         includes, excludes, profiles = map(
                 set, (oincludes, oexcludes, oprofiles))
 
@@ -863,8 +830,8 @@  def _import(ui, repo, files, opts, force
         changed = False
         for file in files:
             with util.posixfile(util.expandpath(file)) as importfile:
-                iincludes, iexcludes, iprofiles = repo.readsparseconfig(
-                    importfile.read())
+                iincludes, iexcludes, iprofiles = sparse.parseconfig(
+                    ui, importfile.read())
                 oldsize = len(includes) + len(excludes) + len(profiles)
                 includes.update(iincludes - aincludes)
                 excludes.update(iexcludes - aexcludes)
@@ -897,7 +864,7 @@  def _import(ui, repo, files, opts, force
 def _clear(ui, repo, files, force=False):
     with repo.wlock():
         raw = repo.vfs.tryread('sparse')
-        includes, excludes, profiles = repo.readsparseconfig(raw)
+        includes, excludes, profiles = sparse.parseconfig(ui, raw)
 
         if includes or excludes:
             oldstatus = repo.status()
diff --git a/mercurial/sparse.py b/mercurial/sparse.py
new file mode 100644
--- /dev/null
+++ b/mercurial/sparse.py
@@ -0,0 +1,48 @@ 
+# sparse.py - functionality for sparse checkouts
+#
+# Copyright 2014 Facebook, Inc.
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from __future__ import absolute_import
+
+from .i18n import _
+from . import (
+    error,
+)
+
+def parseconfig(ui, raw):
+    """Parse sparse config file content.
+
+    Returns a tuple of includes, excludes, and profiles.
+    """
+    includes = set()
+    excludes = set()
+    current = includes
+    profiles = []
+    for line in raw.split('\n'):
+        line = line.strip()
+        if not line or line.startswith('#'):
+            # empty or comment line, skip
+            continue
+        elif line.startswith('%include '):
+            line = line[9:].strip()
+            if line:
+                profiles.append(line)
+        elif line == '[include]':
+            if current != includes:
+                # TODO pass filename into this API so we can report it.
+                raise error.Abort(_('sparse config cannot have includes ' +
+                                    'after excludes'))
+            continue
+        elif line == '[exclude]':
+            current = excludes
+        elif line:
+            if line.strip().startswith('/'):
+                ui.warn(_('warning: sparse profile cannot use' +
+                          ' paths starting with /, ignoring %s\n') % line)
+                continue
+            current.add(line)
+
+    return includes, excludes, profiles