# HG changeset patch # User Gregory Szorc # Date 1536698819 25200 # Node ID 7ce9dea3a14a9142dadb2083f34a22834f5e9b70 # Parent 41aa5dced975ae052089cd50ff6539c63a0ad5aa 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 diff -r 41aa5dced975 -r 7ce9dea3a14a hgext/keyword.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 diff -r 41aa5dced975 -r 7ce9dea3a14a mercurial/localrepo.py --- 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) diff -r 41aa5dced975 -r 7ce9dea3a14a tests/test-status-inprocess.py --- 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: