[PATCH 1 of 3] dirstate: add begin/endparentchange to dirstate

Mads Kiilerich mads at kiilerich.com
Mon Sep 8 13:27:32 CDT 2014


On 09/05/2014 09:36 PM, Durham Goode wrote:
> # HG changeset patch
> # User Durham Goode <durham at fb.com>
> # Date 1409942069 25200
> #      Fri Sep 05 11:34:29 2014 -0700
> # Node ID 8c6d502c92c8ed8dfa79b8fac4c8a2cc9e6c4153
> # Parent  188b8aa2120b03eead618ba150319074f4e3b42b
> dirstate: add begin/endparentchange to dirstate
>
> It's possible for the dirstate to become incoherent (issue4353) if there is an
> exception in the middle of the dirstate parent and entries being written (like
> if the user ctrl+c's). This change adds begin/endparentchange which a future
> patch will require to be set before changing the dirstate parent.  This will
> allow us to prevent writing the dirstate in the event of an exception while
> changing the parent.
>
> diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
> --- a/mercurial/dirstate.py
> +++ b/mercurial/dirstate.py
> @@ -44,6 +44,17 @@
>           self._lastnormaltime = 0
>           self._ui = ui
>           self._filecache = {}
> +        self._parentwriters = 0
> +
> +    def beginparentchange(self):
> +        self._parentwriters += 1
> +
> +    def endparentchange(self):
> +        if self._parentwriters > 0:
> +            self._parentwriters -= 1

The primary use case for this might be parent changes but it seems like 
it is a general transaction concept that should have a more generic name.

> +
> +    def pendingparentchange(self):
> +        return self._parentwriters > 0
>   
>       @propertycache
>       def _map(self):
> @@ -300,6 +311,7 @@
>                   delattr(self, a)
>           self._lastnormaltime = 0
>           self._dirty = False
> +        self._parentwriters = 0
>   
>       def copy(self, source, dest):
>           """Mark dest as a copy of source. Unmark dest if source is None."""
> diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
> --- a/mercurial/localrepo.py
> +++ b/mercurial/localrepo.py
> @@ -1108,7 +1108,11 @@
>               return l
>   
>           def unlock():
> -            self.dirstate.write()
> +            if self.dirstate.pendingparentchange():
> +                self.dirstate.invalidate()
> +            else:
> +                self.dirstate.write()
> +

Shouldn't this "don't write with pending parent change" live inside 
dirstate? Why expose it?

I also wonder if it would be a good idea to have some built-in 
verification that the count reaches 0 unless we are in an exceptional 
case. That would probably require some way of indicating whether it is a 
normal or exceptional unlock we are doing ... which perhaps could be 
used by itself instead of this semaphore. Hmm ... I don't know where 
this is going. I will keep wondering.

/Mads


More information about the Mercurial-devel mailing list