Package: mercurial
Version: 0.9.4-1
Severity: important
Tags: patch

With the latest mercurial version, I experience repository corruption
in the sense that multiple heads are erroneously being generated in a
repository after a normal commit, *without* doing any merging at all.

I first noticed this behaviour when trying to convert a darcs repository
to mercurial using tailor (Debian package 0.9.28-2). tailor works
by successively transferring the changes for each foreign revision to the
mercurial repository, doing a commit with mercurial for each revision.

With mercurial 0.9.3, this produced a valid mercurial repository. With
version 0.9.4, however, I receive a repository with ~10 different heads
(instead of a single one, "tip") in every 100 changesets committed.

At first, I thought that tailor was to blame for this. But then,
recently, I got the exact same faulty behaviour with two other
repositories which I use for managing /etc, so mercurial had to be
at fault.

Now, in the upstream repository, this seems to be fixed already. With
hg bisect, I tracked the fix down to the following changeset:

  changeset:   4952:a11921d24ec4
  user:        Alexis S. L. Carvalho <[EMAIL PROTECTED]>
  date:        Thu Jul 19 19:43:25 2007 -0300
  files:       mercurial/dirstate.py
  description:
  add dirstate._dirtypl variable

  Theoretically, it's possible to forget modified dirstate
  parents by doing:

  dirstate.invalidate()
  dirstate.setparents(p1, p2)
  dirstate._map

  The final access to _map should call _read(), which will
  unconditionally overwrite dirstate._pl.

  This doesn't actually happen right now because invalidate
  accidentally ends up rebuilding dirstate._map.



I have to admit that I am not familiar with the inner workings of
mercurial, but applying this changeset to pristine mercurial 0.9.4
resolved the issue. (This indicates that the situation described above
is definitely not merely theoretical at all.)

Could you have a further look into this, and maybe apply this changeset
in consequence? This bug seems pretty nasty and should probably be
fixed sooner rather than later. 

Regards,
Peter


-- System Information:
Debian Release: lenny/sid
  APT prefers unstable
  APT policy: (500, 'unstable'), (500, 'stable'), (400, 'testing'), (1, 
'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 2.6.22-maia
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/bash

Versions of packages mercurial depends on:
ii  libc6                         2.6-5      GNU C Library: Shared libraries
ii  python                        2.4.4-6    An interactive high-level object-o
ii  python-support                0.6.4      automated rebuilding support for p
ii  python2.5                     2.5.1-3    An interactive high-level object-o

Versions of packages mercurial recommends:
ii  rcs                           5.7-18     The GNU Revision Control System
ii  tk8.4 [wish]                  8.4.15-1   Tk toolkit for Tcl and X11, v8.4 -
ii  tkdiff                        1:4.1.3-1  graphical side by side "diff" util

-- no debconf information
changeset:   4952:a11921d24ec4
user:        Alexis S. L. Carvalho <[EMAIL PROTECTED]>
date:        Thu Jul 19 19:43:25 2007 -0300
summary:     add dirstate._dirtypl variable

diff -r 667290b6c95e -r a11921d24ec4 mercurial/dirstate.py
--- a/mercurial/dirstate.py	Thu Jul 19 19:43:25 2007 -0300
+++ b/mercurial/dirstate.py	Thu Jul 19 19:43:25 2007 -0300
@@ -21,6 +21,7 @@ class dirstate(object):
         self._opener = opener
         self._root = root
         self._dirty = 0
+        self._dirtypl = 0
         self._ui = ui
 
     def __getattr__(self, name):
@@ -114,6 +115,7 @@ class dirstate(object):
 
     def setparents(self, p1, p2=nullid):
         self.markdirty()
+        self._dirtypl = 1
         self._pl = p1, p2
 
     def setbranch(self, branch):
@@ -126,7 +128,8 @@ class dirstate(object):
     def _read(self):
         self._map = {}
         self._copymap = {}
-        self._pl = [nullid, nullid]
+        if not self._dirtypl:
+            self._pl = [nullid, nullid]
         try:
             st = self._opener("dirstate").read()
         except IOError, err:
@@ -135,7 +138,8 @@ class dirstate(object):
         if not st:
             return
 
-        self._pl = [st[:20], st[20: 40]]
+        if not self._dirtypl:
+            self._pl = [st[:20], st[20: 40]]
 
         # deref fields so they will be local in loop
         dmap = self._map
@@ -262,6 +266,7 @@ class dirstate(object):
         st.write(cs.getvalue())
         st.rename()
         self._dirty = 0
+        self._dirtypl = 0
 
     def filterfiles(self, files):
         ret = {}

Reply via email to