localrepo: move repo creation logic out of localrepository.__init__ (API)
authorGregory Szorc <gregory.szorc@gmail.com>
Tue, 11 Sep 2018 13:46:59 -0700
changeset 39548 7ce9dea3a14a
parent 39546 41aa5dced975
child 39549 089fc0db0954
localrepo: move repo creation logic out of localrepository.__init__ (API) It has long bothered me that local repository creation is handled as part of localrepository.__init__. Upcoming changes I want to make around how repositories are initialized and instantiated will make the continued existence of repository creation code in localrepository.__init__ even more awkward. localrepository instances are almost never constructed directly: instead, callers are supposed to go through hg.repository() to obtain a handle on a repository. And hg.repository() calls localrepo.instance() to return a new repo instance. This commit teaches localrepo.instance() to handle the create=True logic. Most of the code for repo construction has been moved to a standalone function. This allows extensions to monkeypatch the function to further customize freshly-created repositories. A few calls to localrepo.localrepository.__init__ that were passing create=True were converted to call localrepo.instance(). .. api:: local repo creation moved out of constructor ``localrepo.localrepository.__init__`` no longer accepts a ``create`` argument to create a new repository. New repository creation is now performed as part of ``localrepo.instance()`` and the bulk of the work is performed by ``localrepo.createrepository()``. Differential Revision: https://phab.mercurial-scm.org/D4534
hgext/keyword.py
mercurial/localrepo.py
tests/test-status-inprocess.py
--- a/hgext/keyword.py	Fri Sep 07 15:57:55 2018 -0700
+++ b/hgext/keyword.py	Tue Sep 11 13:46:59 2018 -0700
@@ -439,7 +439,7 @@
         baseui = ui
     else:
         baseui = repo.baseui
-    repo = localrepo.localrepository(baseui, tmpdir, True)
+    repo = localrepo.instance(baseui, tmpdir, create=True)
     ui.setconfig('keyword', fn, '', 'keyword')
     svn = ui.configbool('keywordset', 'svn')
     # explicitly set keywordset for demo output
--- a/mercurial/localrepo.py	Fri Sep 07 15:57:55 2018 -0700
+++ b/mercurial/localrepo.py	Tue Sep 11 13:46:59 2018 -0700
@@ -426,7 +426,13 @@
         'bisect.state',
     }
 
-    def __init__(self, baseui, path, create=False, intents=None):
+    def __init__(self, baseui, path, intents=None):
+        """Create a new local repository instance.
+
+        Most callers should use ``hg.repository()`` or ``localrepo.instance()``
+        for obtaining a new repository object.
+        """
+
         self.requirements = set()
         self.filtername = None
         # wvfs: rooted at the repository root, used to access the working copy
@@ -475,31 +481,12 @@
                 self.supported.add('exp-compression-%s' % name)
 
         if not self.vfs.isdir():
-            if create:
-                self.requirements = newreporequirements(self.ui)
-
-                if not self.wvfs.exists():
-                    self.wvfs.makedirs()
-                self.vfs.makedir(notindexed=True)
-
-                if 'store' in self.requirements:
-                    self.vfs.mkdir("store")
-
-                    # create an invalid changelog
-                    self.vfs.append(
-                        "00changelog.i",
-                        '\0\0\0\2' # represents revlogv2
-                        ' dummy changelog to prevent using the old repo layout'
-                    )
-            else:
-                try:
-                    self.vfs.stat()
-                except OSError as inst:
-                    if inst.errno != errno.ENOENT:
-                        raise
-                raise error.RepoError(_("repository %s not found") % path)
-        elif create:
-            raise error.RepoError(_("repository %s already exists") % path)
+            try:
+                self.vfs.stat()
+            except OSError as inst:
+                if inst.errno != errno.ENOENT:
+                    raise
+            raise error.RepoError(_("repository %s not found") % path)
         else:
             try:
                 self.requirements = scmutil.readrequires(
@@ -546,8 +533,6 @@
             else: # standard vfs
                 self.svfs.audit = self._getsvfsward(self.svfs.audit)
         self._applyopenerreqs()
-        if create:
-            self._writerequirements()
 
         self._dirstatevalidatewarned = False
 
@@ -2396,8 +2381,15 @@
     return os.path.join(base, name.replace('journal', 'undo', 1))
 
 def instance(ui, path, create, intents=None):
-    return localrepository(ui, util.urllocalpath(path), create,
-                           intents=intents)
+    if create:
+        vfs = vfsmod.vfs(path, expandpath=True, realpath=True)
+
+        if vfs.exists('.hg'):
+            raise error.RepoError(_('repository %s already exists') % path)
+
+        createrepository(ui, vfs)
+
+    return localrepository(ui, util.urllocalpath(path), intents=intents)
 
 def islocal(path):
     return True
@@ -2447,3 +2439,34 @@
         requirements.add('internal-phase')
 
     return requirements
+
+def createrepository(ui, wdirvfs):
+    """Create a new repository in a vfs.
+
+    ``wdirvfs`` is a vfs instance pointing at the working directory.
+    ``requirements`` is a set of requirements for the new repository.
+    """
+    requirements = newreporequirements(ui)
+
+    if not wdirvfs.exists():
+        wdirvfs.makedirs()
+
+    hgvfs = vfsmod.vfs(wdirvfs.join(b'.hg'))
+    hgvfs.makedir(notindexed=True)
+
+    if b'store' in requirements:
+        hgvfs.mkdir(b'store')
+
+        # We create an invalid changelog outside the store so very old
+        # Mercurial versions (which didn't know about the requirements
+        # file) encounter an error on reading the changelog. This
+        # effectively locks out old clients and prevents them from
+        # mucking with a repo in an unknown format.
+        #
+        # The revlog header has version 2, which won't be recognized by
+        # such old clients.
+        hgvfs.append(b'00changelog.i',
+                     b'\0\0\0\2 dummy changelog to prevent using the old repo '
+                     b'layout')
+
+    scmutil.writerequires(hgvfs, requirements)
--- a/tests/test-status-inprocess.py	Fri Sep 07 15:57:55 2018 -0700
+++ b/tests/test-status-inprocess.py	Tue Sep 11 13:46:59 2018 -0700
@@ -22,7 +22,7 @@
 u = uimod.ui.load()
 
 print('% creating repo')
-repo = localrepo.localrepository(u, b'.', create=True)
+repo = localrepo.instance(u, b'.', create=True)
 
 f = open('test.py', 'w')
 try: