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

2018-09-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG7ce9dea3a14a: localrepo: move repo creation logic out of 
localrepository.__init__ (API) (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D4534?vs=10911=10924

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')
+
+

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

2018-09-11 Thread indygreg (Gregory Szorc)
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')