D4534: localrepo: move repo creation logic out of localrepository.__init__

indygreg (Gregory Szorc) phabricator at mercurial-scm.org
Wed Sep 12 00:28:17 UTC 2018


indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  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()``.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4534

AFFECTED FILES
  hgext/keyword.py
  mercurial/localrepo.py
  tests/test-status-inprocess.py

CHANGE DETAILS

diff --git a/tests/test-status-inprocess.py b/tests/test-status-inprocess.py
--- a/tests/test-status-inprocess.py
+++ b/tests/test-status-inprocess.py
@@ -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:
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -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)
diff --git a/hgext/keyword.py b/hgext/keyword.py
--- a/hgext/keyword.py
+++ b/hgext/keyword.py
@@ -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



To: indygreg, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list