Patchwork [V2] init: changed creation order of 00changelog and requires files (issue3960)

login
register
mail settings
Submitter Lucas Moscovicz
Date Jan. 14, 2014, 9:46 p.m.
Message ID <5fb7f56049148d73db25.1389735977@devrs047.prn2.facebook.com>
Download mbox | patch
Permalink /patch/3321/
State Rejected
Headers show

Comments

Lucas Moscovicz - Jan. 14, 2014, 9:46 p.m.
# HG changeset patch
# User Lucas Moscovicz <lmoscovicz@fb.com>
# Date 1389639192 28800
#      Mon Jan 13 10:53:12 2014 -0800
# Node ID 5fb7f56049148d73db2566d8b5d7ba791bed938b
# Parent  f694cd81b600b65d23dcdc7a02cfd6a57dd1d018
init: changed creation order of 00changelog and requires files (issue3960)

When initializing a repo with hg init if another client cloned the repo after
the 00changelog file was created but before the requires file was created they
would get an error about unknown revlog format.  To fix it, I changed the
creation order of those files since the changelog file can be created at any
time during the initialization.

Also, to solve the race problem that came up where an old client commited
something to the repo before both the requires and the 00changelog file were
created and then the 00changelog file would overwrite that commit, the initial
name of the .hg folder was changed to .hgtmp, getting renamed back to .hg once
the requires and 00changelog files are created leaving no point in time where
an old client can commit to the new styled repo.
Augie Fackler - Feb. 28, 2014, 7:56 p.m.
On Jan 14, 2014, at 4:46 PM, Lucas Moscovicz <lmoscovicz@fb.com> wrote:

> # HG changeset patch
> # User Lucas Moscovicz <lmoscovicz@fb.com>
> # Date 1389639192 28800
> #      Mon Jan 13 10:53:12 2014 -0800
> # Node ID 5fb7f56049148d73db2566d8b5d7ba791bed938b
> # Parent  f694cd81b600b65d23dcdc7a02cfd6a57dd1d018
> init: changed creation order of 00changelog and requires files (issue3960)

This looks completely reasonable to me, but I'd like mpm to comment...

> 
> When initializing a repo with hg init if another client cloned the repo after
> the 00changelog file was created but before the requires file was created they
> would get an error about unknown revlog format.  To fix it, I changed the
> creation order of those files since the changelog file can be created at any
> time during the initialization.
> 
> Also, to solve the race problem that came up where an old client commited
> something to the repo before both the requires and the 00changelog file were
> created and then the 00changelog file would overwrite that commit, the initial
> name of the .hg folder was changed to .hgtmp, getting renamed back to .hg once
> the requires and 00changelog files are created leaving no point in time where
> an old client can commit to the new styled repo.
> 
> diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
> --- a/mercurial/localrepo.py
> +++ b/mercurial/localrepo.py
> @@ -195,26 +195,36 @@
> 
>         if not self.vfs.isdir():
>             if create:
> -                if not self.wvfs.exists():
> -                    self.wvfs.makedirs()
> -                self.vfs.makedir(notindexed=True)
> -                requirements = self._baserequirements(create)
> -                if self.ui.configbool('format', 'usestore', True):
> -                    self.vfs.mkdir("store")
> -                    requirements.append("store")
> -                    if self.ui.configbool('format', 'usefncache', True):
> -                        requirements.append("fncache")
> -                        if self.ui.configbool('format', 'dotencode', True):
> -                            requirements.append('dotencode')
> -                    # create an invalid changelog
> -                    self.vfs.append(
> -                        "00changelog.i",
> -                        '\0\0\0\2' # represents revlogv2
> -                        ' dummy changelog to prevent using the old repo layout'
> -                    )
> -                if self.ui.configbool('format', 'generaldelta', False):
> -                    requirements.append("generaldelta")
> -                requirements = set(requirements)
> +                oldvfs = self.vfs
> +                try:
> +                    self.vfs = scmutil.vfs(self.wvfs.join(".hgtmp"))
> +                    self.opener = self.vfs
> +                    if not self.wvfs.exists():
> +                        self.wvfs.makedirs()
> +                    self.vfs.makedir(notindexed=True)
> +                    requirements = self._baserequirements(create)
> +                    if self.ui.configbool('format', 'usestore', True):
> +                        self.vfs.mkdir("store")
> +                        requirements.append("store")
> +                        if self.ui.configbool('format', 'usefncache', True):
> +                            requirements.append("fncache")
> +                            if self.ui.configbool('format', 'dotencode', True):
> +                                requirements.append('dotencode')
> +                    if self.ui.configbool('format', 'generaldelta', False):
> +                        requirements.append("generaldelta")
> +                    requirements = set(requirements)
> +                    self._writerequirements(requirements)
> +                    if self.ui.configbool('format', 'usestore', True):
> +                        # create an invalid changelog
> +                        self.vfs.append(
> +                            "00changelog.i",
> +                            '\0\0\0\2' # represents revlogv2
> +                            ' dummy changelog to prevent using the old repo layout'
> +                        )
> +                    self.wvfs.rename(".hgtmp", ".hg")
> +                finally:
> +                    self.vfs = oldvfs
> +                    self.opener = oldvfs
>             else:
>                 raise error.RepoError(_("repository %s not found") % path)
>         elif create:
> @@ -247,9 +257,6 @@
>         self.sjoin = self.store.join
>         self.vfs.createmode = self.store.createmode
>         self._applyrequirements(requirements)
> -        if create:
> -            self._writerequirements()
> -
> 
>         self._branchcaches = {}
>         self.filterpats = {}
> @@ -285,9 +292,11 @@
>         if chunkcachesize is not None:
>             self.sopener.options['chunkcachesize'] = chunkcachesize
> 
> -    def _writerequirements(self):
> +    def _writerequirements(self, requirements=None):
>         reqfile = self.opener("requires", "w")
> -        for r in sorted(self.requirements):
> +        if requirements is None:
> +            requirements = self.requirements
> +        for r in sorted(requirements):
>             reqfile.write("%s\n" % r)
>         reqfile.close()
> 
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel

Patch

diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -195,26 +195,36 @@ 
 
         if not self.vfs.isdir():
             if create:
-                if not self.wvfs.exists():
-                    self.wvfs.makedirs()
-                self.vfs.makedir(notindexed=True)
-                requirements = self._baserequirements(create)
-                if self.ui.configbool('format', 'usestore', True):
-                    self.vfs.mkdir("store")
-                    requirements.append("store")
-                    if self.ui.configbool('format', 'usefncache', True):
-                        requirements.append("fncache")
-                        if self.ui.configbool('format', 'dotencode', True):
-                            requirements.append('dotencode')
-                    # create an invalid changelog
-                    self.vfs.append(
-                        "00changelog.i",
-                        '\0\0\0\2' # represents revlogv2
-                        ' dummy changelog to prevent using the old repo layout'
-                    )
-                if self.ui.configbool('format', 'generaldelta', False):
-                    requirements.append("generaldelta")
-                requirements = set(requirements)
+                oldvfs = self.vfs
+                try:
+                    self.vfs = scmutil.vfs(self.wvfs.join(".hgtmp"))
+                    self.opener = self.vfs
+                    if not self.wvfs.exists():
+                        self.wvfs.makedirs()
+                    self.vfs.makedir(notindexed=True)
+                    requirements = self._baserequirements(create)
+                    if self.ui.configbool('format', 'usestore', True):
+                        self.vfs.mkdir("store")
+                        requirements.append("store")
+                        if self.ui.configbool('format', 'usefncache', True):
+                            requirements.append("fncache")
+                            if self.ui.configbool('format', 'dotencode', True):
+                                requirements.append('dotencode')
+                    if self.ui.configbool('format', 'generaldelta', False):
+                        requirements.append("generaldelta")
+                    requirements = set(requirements)
+                    self._writerequirements(requirements)
+                    if self.ui.configbool('format', 'usestore', True):
+                        # create an invalid changelog
+                        self.vfs.append(
+                            "00changelog.i",
+                            '\0\0\0\2' # represents revlogv2
+                            ' dummy changelog to prevent using the old repo layout'
+                        )
+                    self.wvfs.rename(".hgtmp", ".hg")
+                finally:
+                    self.vfs = oldvfs
+                    self.opener = oldvfs
             else:
                 raise error.RepoError(_("repository %s not found") % path)
         elif create:
@@ -247,9 +257,6 @@ 
         self.sjoin = self.store.join
         self.vfs.createmode = self.store.createmode
         self._applyrequirements(requirements)
-        if create:
-            self._writerequirements()
-
 
         self._branchcaches = {}
         self.filterpats = {}
@@ -285,9 +292,11 @@ 
         if chunkcachesize is not None:
             self.sopener.options['chunkcachesize'] = chunkcachesize
 
-    def _writerequirements(self):
+    def _writerequirements(self, requirements=None):
         reqfile = self.opener("requires", "w")
-        for r in sorted(self.requirements):
+        if requirements is None:
+            requirements = self.requirements
+        for r in sorted(requirements):
             reqfile.write("%s\n" % r)
         reqfile.close()