hgext/obsolete.py
changeset 343 6b92f8d5ae58
parent 334 fb83210bce32
child 348 afbe4fc8de42
--- a/hgext/obsolete.py	Tue Jul 03 14:18:33 2012 +0200
+++ b/hgext/obsolete.py	Tue Jul 03 11:35:31 2012 +0200
@@ -120,11 +120,10 @@
 ### Patch changectx
 #############################
 
-def obsolete(ctx):
-    """is the changeset obsolete by other"""
-    if ctx.node()is None:
-        return False
-    return bool(ctx._repo.obsoletedby(ctx.node())) and ctx.phase()
+# core one is buggy
+def obsolete(self):
+    """True if the changeset is obsolete"""
+    return self.node() in self._repo.obsstore.precursors and self.phase()
 
 context.changectx.obsolete = obsolete
 
@@ -410,34 +409,6 @@
     except KeyError:
         pass  # rebase not found
 
-# Pushkey mechanism for mutable
-#########################################
-
-def listmarkers(repo):
-    """List markers over pushkey"""
-    if not repo.obsstore:
-        return {}
-    data = repo.obsstore._writemarkers()
-    return {'dump': base85.b85encode(data)}
-
-def pushmarker(repo, key, old, new):
-    """Push markers over pushkey"""
-    if key != 'dump':
-        repo.ui.warn(_('unknown key: %r') % key)
-        return 0
-    if old:
-        repo.ui.warn(_('unexpected old value') % key)
-        return 0
-    data = base85.b85decode(new)
-    lock = repo.lock()
-    try:
-        repo.obsstore.mergemarkers(data)
-        return 1
-    finally:
-        lock.release()
-
-pushkey.register('obsolete', pushmarker, listmarkers)
-
 ### Discovery wrapping
 #############################
 
@@ -550,20 +521,6 @@
 cmdtable = {}
 command = cmdutil.command(cmdtable)
 
-@command('debugobsolete', [], _('SUBJECT OBJECT'))
-def cmddebugobsolete(ui, repo, subject, object):
-    """add an obsolete relation between two nodes
-
-    The subject is expected to be a newer version of the object.
-    """
-    lock = repo.lock()
-    try:
-        sub = repo[subject]
-        obj = repo[object]
-        repo.addobsolete(sub.node(), obj.node())
-    finally:
-        lock.release()
-    return 0
 
 @command('debugconvertobsolete', [], '')
 def cmddebugconvertobsolete(ui, repo):
@@ -646,6 +603,15 @@
         ui.warn(_('Working directory parent is obsolete\n'))
     return res
 
+def wrapmaycreateobsmarker(origfn, ui, repo, *args, **opts):
+    lock = repo.lock()
+    try:
+        res = origfn(ui, repo, *args, **opts)
+        repo._turn_extinct_secret()
+    finally:
+        lock.release()
+    return res
+
 def noextinctsvisibleheads(orig, repo):
     repo._turn_extinct_secret()
     return orig(repo)
@@ -657,11 +623,8 @@
         lock = repo.lock()
         try:
             meta = {
-                'subjects':  [new],
-                'object': oldnode,
-                'date':  util.makedate(),
+                'date':  '%i %i' % util.makedate(),
                 'user': ui.username(),
-                'reason': 'commit --amend',
                 }
             repo.obsstore.create(oldnode, [new], 0, meta)
             repo._clearobsoletecache()
@@ -673,6 +636,7 @@
 def uisetup(ui):
     extensions.wrapcommand(commands.table, "update", wrapmayobsoletewc)
     extensions.wrapcommand(commands.table, "pull", wrapmayobsoletewc)
+    extensions.wrapcommand(commands.table, "debugobsolete", wrapmaycreateobsmarker)
     if util.safehasattr(cmdutil, 'amend'):
         extensions.wrapfunction(cmdutil, 'amend', wrapcmdutilamend)
     extensions.wrapfunction(discovery, 'findcommonoutgoing', wrapfindcommonoutgoing)
@@ -763,158 +727,6 @@
     a.update('\0')
     return a.digest()
 
-# mercurial backport
-
-def encodemeta(meta):
-    """Return encoded metadata string to string mapping.
-
-    Assume no ':' in key and no '\0' in both key and value."""
-    for key, value in meta.iteritems():
-        if ':' in key or '\0' in key:
-            raise ValueError("':' and '\0' are forbidden in metadata key'")
-        if '\0' in value:
-            raise ValueError("':' are forbidden in metadata value'")
-    return '\0'.join(['%s:%s' % (k, meta[k]) for k in sorted(meta)])
-
-def decodemeta(data):
-    """Return string to string dictionary from encoded version."""
-    d = {}
-    for l in data.split('\0'):
-        if l:
-            key, value = l.split(':')
-            d[key] = value
-    return d
-
-# data used for parsing and writing
-_fmversion = 0
-_fmfixed   = '>BIB20s'
-_fmnode = '20s'
-_fmfsize = struct.calcsize(_fmfixed)
-_fnodesize = struct.calcsize(_fmnode)
-
-def _readmarkers(data):
-    """Read and enumerate markers from raw data"""
-    off = 0
-    diskversion = _unpack('>B', data[off:off + 1])[0]
-    off += 1
-    if diskversion != _fmversion:
-        raise util.Abort(_('parsing obsolete marker: unknown version %r')
-                         % diskversion)
-
-    # Loop on markers
-    l = len(data)
-    while off + _fmfsize <= l:
-        # read fixed part
-        cur = data[off:off + _fmfsize]
-        off += _fmfsize
-        nbsuc, mdsize, flags, pre = _unpack(_fmfixed, cur)
-        # read replacement
-        sucs = ()
-        if nbsuc:
-            s = (_fnodesize * nbsuc)
-            cur = data[off:off + s]
-            sucs = _unpack(_fmnode * nbsuc, cur)
-            off += s
-        # read metadata
-        # (metadata will be decoded on demand)
-        metadata = data[off:off + mdsize]
-        if len(metadata) != mdsize:
-            raise util.Abort(_('parsing obsolete marker: metadata is too '
-                               'short, %d bytes expected, got %d')
-                             % (len(metadata), mdsize))
-        off += mdsize
-        yield (pre, sucs, flags, metadata)
-
-class obsstore(object):
-    """Store obsolete markers
-
-    Markers can be accessed with two mappings:
-    - precursors: old -> set(new)
-    - successors: new -> set(old)
-    """
-
-    def __init__(self):
-        self._all = []
-        # new markers to serialize
-        self._new = []
-        self.precursors = {}
-        self.successors = {}
-
-    def __iter__(self):
-        return iter(self._all)
-
-    def __nonzero__(self):
-        return bool(self._all)
-
-    def create(self, prec, succs=(), flag=0, metadata=None):
-        """obsolete: add a new obsolete marker
-
-        * ensuring it is hashable
-        * check mandatory metadata
-        * encode metadata
-        """
-        if metadata is None:
-            metadata = {}
-        if len(prec) != 20:
-            raise ValueError(prec)
-        for succ in succs:
-            if len(succ) != 20:
-                raise ValueError(prec)
-        marker = (str(prec), tuple(succs), int(flag), encodemeta(metadata))
-        self.add(marker)
-
-    def add(self, marker):
-        """Add a new marker to the store
-
-        This marker still needs to be written to disk"""
-        self._new.append(marker)
-        self._load(marker)
-
-    def loadmarkers(self, data):
-        """Load all markers in data, mark them as known."""
-        for marker in _readmarkers(data):
-            self._load(marker)
-
-    def mergemarkers(self, data):
-        other = set(_readmarkers(data))
-        local = set(self._all)
-        new = other - local
-        for marker in new:
-            self.add(marker)
-
-    def flushmarkers(self, stream):
-        """Write all markers to a stream
-
-        After this operation, "new" markers are considered "known"."""
-        self._writemarkers(stream)
-        self._new[:] = []
-
-    def _load(self, marker):
-        self._all.append(marker)
-        pre, sucs = marker[:2]
-        self.precursors.setdefault(pre, set()).add(marker)
-        for suc in sucs:
-            self.successors.setdefault(suc, set()).add(marker)
-
-    def _writemarkers(self, stream=None):
-        # Kept separate from flushmarkers(), it will be reused for
-        # markers exchange.
-        if stream is None:
-            final = []
-            w = final.append
-        else:
-            w = stream.write
-        w(_pack('>B', _fmversion))
-        for marker in self._all:
-            pre, sucs, flags, metadata = marker
-            nbsuc = len(sucs)
-            format = _fmfixed + (_fmnode * nbsuc)
-            data = [nbsuc, len(metadata), flags, pre]
-            data.extend(sucs)
-            w(_pack(format, *data))
-            w(metadata)
-        if stream is None:
-            return ''.join(final)
 
 
 ### repo subclassing
@@ -950,31 +762,19 @@
             """return the set of node that <node> make obsolete (sub)"""
             return set(marker[0] for marker in self.obsstore.successors.get(node, []))
 
-        @storecache('obsstore')
-        def obsstore(self):
-            if not getattr(self, '_importoldobsolete', False):
-                data = repo.opener.tryread('obsolete-relations')
-                if not data:
-                    data = repo.sopener.tryread('obsoletemarkers')
-                if data:
-                    raise util.Abort('old format of obsolete marker detected!\n'
-                                     'run `hg debugconvertobsolete` once.')
-            store = obsstore()
-            data = self.sopener.tryread('obsstore')
-            if data:
-                store.loadmarkers(data)
-            return store
-
         @util.propertycache
         def _obsoleteset(self):
             """the set of obsolete revision"""
+            data = repo.opener.tryread('obsolete-relations')
+            if not data:
+                data = repo.sopener.tryread('obsoletemarkers')
+            if data:
+                raise util.Abort('old format of obsolete marker detected!\n'
+                                 'run `hg debugconvertobsolete` once.')
             obs = set()
             nm = self.changelog.nodemap
-            for obj in self.obsstore.precursors:
-                try: # /!\api change in Hg 2.2 (e8d37b78acfb22ae2c1fb126c2)/!\
-                    rev = nm.get(obj)
-                except TypeError:  #XXX to remove while breaking Hg 2.1 support
-                    rev = nm.get(obj, None)
+            for prec in self.obsstore.precursors:
+                rev = nm.get(prec)
                 if rev is not None:
                     obs.add(rev)
             return obs
@@ -1034,9 +834,8 @@
             lock = self.lock()
             try:
                 meta = {
-                    'date':  util.makedate(),
+                    'date':  '%i %i' % util.makedate(),
                     'user': ui.username(),
-                    'reason': 'unknown',
                     }
                 subs = (sub == nullid) and [] or [sub]
                 mid = self.obsstore.create(obj, subs, 0, meta)
@@ -1051,7 +850,7 @@
             # Assume oldnodes are all descendants of a single rev
             rootrevs = self.revs('roots(%ln)', oldnodes)
             assert len(rootrevs) == 1, rootrevs
-            rootnode = self[rootrevs[0]].node()
+            #rootnode = self[rootrevs[0]].node()
             for n in oldnodes:
                 self.addobsolete(newnode, n)
 
@@ -1064,27 +863,6 @@
             expobs = [c.node() for c in repo.set(query)]
             phases.retractboundary(repo, 2, expobs)
 
-        ### Disk IO
-
-        def lock(self, *args, **kwargs):
-            l = olock(*args, **kwargs)
-            if not getattr(l.releasefn, 'obspatched', False):
-                oreleasefn = l.releasefn
-                def releasefn(*args, **kwargs):
-                    if 'obsstore' in vars(self) and self.obsstore._new:
-                        f = self.sopener('obsstore', 'wb', atomictemp=True)
-                        try:
-                            self.obsstore.flushmarkers(f)
-                            f.close()
-                        except: # re-raises
-                            f.discard()
-                            raise
-                    oreleasefn(*args, **kwargs)
-                releasefn.obspatched = True
-                l.releasefn = releasefn
-            return l
-
-
         ### pull // push support
 
         def pull(self, remote, *args, **kwargs):
@@ -1092,13 +870,8 @@
             l = repo.lock()
             try:
                 result = opull(remote, *args, **kwargs)
-                remoteobs = remote.listkeys('obsolete')
-                if 'dump' in remoteobs:
-                    data = base85.b85decode(remoteobs['dump'])
-                    self.obsstore.mergemarkers(data)
-                    self._clearobsoletecache()
-                    self._turn_extinct_secret()
-                    return result
+                self._turn_extinct_secret()
+                return result
             finally:
                 l.release()
 
@@ -1106,46 +879,26 @@
             """wrapper around pull that pull obsolete relation"""
             self._turn_extinct_secret()
             result = opush(remote, *args, **opts)
-            if 'obsolete' in self.listkeys('namespaces') and self.obsstore:
-                data = self.obsstore._writemarkers()
-                r = remote.pushkey('obsolete', 'dump', '',
-                                   base85.b85encode(data))
-                if not r:
-                    self.ui.warn(_('failed to push obsolete markers!\n'))
             self._turn_extinct_secret()
-
             return result
 
 
         ### rollback support
 
         # /!\ api change in  Hg 2.2 (97efd26eb9576f39590812ea9) /!\
-        if util.safehasattr(repo, '_journalfiles'): # Hg 2.2
-            def _journalfiles(self):
-                return o_journalfiles() + (self.sjoin('journal.obsstore'),) 
+        def _journalfiles(self):
+            return o_journalfiles() + (self.sjoin('journal.obsstore'),) 
 
-            def _writejournal(self, desc):
-                """wrapped version of _writejournal that save obsolete data"""
-                o_writejournal(desc)
-                filename = 'obsstore'
-                filepath = self.sjoin(filename)
-                if os.path.exists(filepath):
-                    journalname = 'journal.' + filename
-                    journalpath = self.sjoin(journalname)
-                    util.copyfile(filepath, journalpath)
+        def _writejournal(self, desc):
+            """wrapped version of _writejournal that save obsolete data"""
+            o_writejournal(desc)
+            filename = 'obsstore'
+            filepath = self.sjoin(filename)
+            if os.path.exists(filepath):
+                journalname = 'journal.' + filename
+                journalpath = self.sjoin(journalname)
+                util.copyfile(filepath, journalpath)
 
-        else: # XXX removing this bloc will break Hg 2.1 support
-            def _writejournal(self, desc):
-                """wrapped version of _writejournal that save obsolete data"""
-                entries = list(o_writejournal(desc))
-                filename = 'obsstore'
-                filepath = self.sjoin(filename)
-                if  os.path.exists(filepath):
-                    journalname = 'journal.' + filename
-                    journalpath = self.sjoin(journalname)
-                    util.copyfile(filepath, journalpath)
-                    entries.append(journalpath)
-                return tuple(entries)
 
         def _rollback(self, dryrun, force):
             """wrapped version of _rollback that restore obsolete data"""