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
--- 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: