changeset 17090:e64d416b6b94

merge with crew
author Matt Mackall <mpm@selenic.com>
date Sun, 01 Jul 2012 21:19:57 -0500
parents 75f4180509a4 (diff) 0c1d10351869 (current diff)
children f7a2849ef8cd
files tests/test-histedit-commute tests/test-histedit-commute.out tests/test-histedit-drop tests/test-histedit-drop.out tests/test-histedit-edit tests/test-histedit-edit.out tests/test-histedit-fold tests/test-histedit-fold-non-commute tests/test-histedit-fold-non-commute.out tests/test-histedit-fold.out tests/test-histedit-no-change tests/test-histedit-no-change.out tests/test-histedit-non-commute tests/test-histedit-non-commute-abort tests/test-histedit-non-commute-abort.out tests/test-histedit-non-commute.out tests/test-histedit-outgoing tests/test-histedit-outgoing.out
diffstat 12 files changed, 501 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/commands.py	Mon Jul 02 01:48:12 2012 +0200
+++ b/mercurial/commands.py	Sun Jul 01 21:19:57 2012 -0500
@@ -17,7 +17,7 @@
 import minirst, revset, fileset
 import dagparser, context, simplemerge
 import random, setdiscovery, treediscovery, dagutil, pvec
-import phases
+import phases, obsolete
 
 table = {}
 
@@ -2049,6 +2049,31 @@
     flags = repo.known([bin(s) for s in ids])
     ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
 
+@command('debugobsolete', [] + commitopts2,
+         _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
+def debugobsolete(ui, repo, precursor=None, *successors, **opts):
+    """create arbitrary obsolete marker"""
+    if precursor is not None:
+        metadata = {}
+        if 'date' in opts:
+            metadata['date'] = opts['date']
+        metadata['user'] = opts['user'] or ui.username()
+        succs = tuple(bin(succ) for succ in successors)
+        l = repo.lock()
+        try:
+            repo.obsstore.create(bin(precursor), succs, 0, metadata)
+        finally:
+            l.release()
+    else:
+        for mctx in obsolete.allmarkers(repo):
+            ui.write(hex(mctx.precnode()))
+            for repl in mctx.succnodes():
+                ui.write(' ')
+                ui.write(hex(repl))
+            ui.write(' %X ' % mctx._data[2])
+            ui.write(mctx.metadata())
+            ui.write('\n')
+
 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
     '''access the pushkey key/value protocol
--- a/mercurial/context.py	Mon Jul 02 01:48:12 2012 +0200
+++ b/mercurial/context.py	Sun Jul 01 21:19:57 2012 -0500
@@ -230,6 +230,10 @@
         for d in self._repo.changelog.descendants([self._rev]):
             yield changectx(self._repo, d)
 
+    def obsolete(self):
+        """True if the changeset is obsolete"""
+        return self.node() in self._repo.obsstore.obsoleted
+
     def _fileinfo(self, path):
         if '_manifest' in self.__dict__:
             try:
--- a/mercurial/localrepo.py	Mon Jul 02 01:48:12 2012 +0200
+++ b/mercurial/localrepo.py	Sun Jul 01 21:19:57 2012 -0500
@@ -4,12 +4,11 @@
 #
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
-
 from node import bin, hex, nullid, nullrev, short
 from i18n import _
-import repo, changegroup, subrepo, discovery, pushkey
+import repo, changegroup, subrepo, discovery, pushkey, obsolete
 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
-import lock, transaction, store, encoding
+import lock, transaction, store, encoding, base85
 import scmutil, util, extensions, hook, error, revset
 import match as matchmod
 import merge as mergemod
@@ -192,6 +191,14 @@
     def _phasecache(self):
         return phases.phasecache(self, self._phasedefaults)
 
+    @storecache('obsstore')
+    def obsstore(self):
+        store = obsolete.obsstore()
+        data = self.sopener.tryread('obsstore')
+        if data:
+            store.loadmarkers(data)
+        return store
+
     @storecache('00changelog.i')
     def changelog(self):
         c = changelog.changelog(self.sopener)
@@ -983,6 +990,16 @@
             self.store.write()
             if '_phasecache' in vars(self):
                 self._phasecache.write()
+            if 'obsstore' in vars(self) and self.obsstore._new:
+                # XXX: transaction logic should be used here. But for
+                # now rewriting the whole file is good enough.
+                f = self.sopener('obsstore', 'wb', atomictemp=True)
+                try:
+                    self.obsstore.flushmarkers(f)
+                    f.close()
+                except: # re-raises
+                    f.discard()
+                    raise
             for k, ce in self._filecache.items():
                 if k == 'dirstate':
                     continue
@@ -1656,6 +1673,11 @@
                 # Remote is old or publishing all common changesets
                 # should be seen as public
                 phases.advanceboundary(self, phases.public, subset)
+
+            remoteobs = remote.listkeys('obsolete')
+            if 'dump' in remoteobs:
+                data = base85.b85decode(remoteobs['dump'])
+                self.obsstore.mergemarkers(data)
         finally:
             lock.release()
 
@@ -1796,6 +1818,12 @@
                         if not r:
                             self.ui.warn(_('updating %s to public failed!\n')
                                             % newremotehead)
+                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'))
             finally:
                 if lock is not None:
                     lock.release()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/obsolete.py	Sun Jul 01 21:19:57 2012 -0500
@@ -0,0 +1,279 @@
+# obsolete.py - obsolete markers handling
+#
+# Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
+#                Logilab SA        <contact@logilab.fr>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+"""Obsolete markers handling
+
+An obsolete marker maps an old changeset to a list of new
+changesets. If the list of new changesets is empty, the old changeset
+is said to be "killed". Otherwise, the old changeset is being
+"replaced" by the new changesets.
+
+Obsolete markers can be used to record and distribute changeset graph
+transformations performed by history rewriting operations, and help
+building new tools to reconciliate conflicting rewriting actions. To
+facilitate conflicts resolution, markers include various annotations
+besides old and news changeset identifiers, such as creation date or
+author name.
+
+
+Format
+------
+
+Markers are stored in an append-only file stored in
+'.hg/store/obsstore'.
+
+The file starts with a version header:
+
+- 1 unsigned byte: version number, starting at zero.
+
+
+The header is followed by the markers. Each marker is made of:
+
+- 1 unsigned byte: number of new changesets "R", could be zero.
+
+- 1 unsigned 32-bits integer: metadata size "M" in bytes.
+
+- 1 byte: a bit field. It is reserved for flags used in obsolete
+  markers common operations, to avoid repeated decoding of metadata
+  entries.
+
+- 20 bytes: obsoleted changeset identifier.
+
+- N*20 bytes: new changesets identifiers.
+
+- M bytes: metadata as a sequence of nul-terminated strings. Each
+  string contains a key and a value, separated by a color ':', without
+  additional encoding. Keys cannot contain '\0' or ':' and values
+  cannot contain '\0'.
+"""
+import struct
+from mercurial import util, base85
+from i18n import _
+
+_pack = struct.pack
+_unpack = struct.unpack
+
+
+
+# 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)
+
+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
+
+class marker(object):
+    """Wrap obsolete marker raw data"""
+
+    def __init__(self, repo, data):
+        # the repo argument will be used to create changectx in later version
+        self._repo = repo
+        self._data = data
+        self._decodedmeta = None
+
+    def precnode(self):
+        """Precursor changeset node identifier"""
+        return self._data[0]
+
+    def succnodes(self):
+        """List of successor changesets node identifiers"""
+        return self._data[1]
+
+    def metadata(self):
+        """Decoded metadata dictionary"""
+        if self._decodedmeta is None:
+            self._decodedmeta = decodemeta(self._data[3])
+        return self._decodedmeta
+
+    def date(self):
+        """Creation date as (unixtime, offset)"""
+        parts = self.metadata()['date'].split(' ')
+        return (float(parts[0]), int(parts[1]))
+
+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)
+
+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()
+
+def allmarkers(repo):
+    """all obsolete markers known in a repository"""
+    for markerdata in repo.obsstore:
+        yield marker(repo, markerdata)
+
+def precursormarkers(ctx):
+    """obsolete marker making this changeset obsolete"""
+    for data in ctx._repo.obsstore.precursors.get(ctx.node(), ()):
+        yield marker(ctx._repo, data)
+
+def successormarkers(ctx):
+    """obsolete marker marking this changeset as a successors"""
+    for data in ctx._repo.obsstore.successors.get(ctx.node(), ()):
+        yield marker(ctx._repo, data)
+
--- a/mercurial/pushkey.py	Mon Jul 02 01:48:12 2012 +0200
+++ b/mercurial/pushkey.py	Sun Jul 01 21:19:57 2012 -0500
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import bookmarks, phases
+import bookmarks, phases, obsolete
 
 def _nslist(repo):
     n = {}
@@ -16,6 +16,7 @@
 _namespaces = {"namespaces": (lambda *x: False, _nslist),
                "bookmarks": (bookmarks.pushbookmark, bookmarks.listbookmarks),
                "phases": (phases.pushphase, phases.listphases),
+               "obsolete": (obsolete.pushmarker, obsolete.listmarkers),
               }
 
 def register(namespace, pushkey, listkeys):
--- a/tests/test-bookmarks-pushpull.t	Mon Jul 02 01:48:12 2012 +0200
+++ b/tests/test-bookmarks-pushpull.t	Sun Jul 01 21:19:57 2012 -0500
@@ -40,6 +40,7 @@
   bookmarks	
   phases	
   namespaces	
+  obsolete	
   $ hg debugpushkey ../a bookmarks
   Y	4e3505fd95835d721066b76e75dbb8cc554d7f77
   X	4e3505fd95835d721066b76e75dbb8cc554d7f77
@@ -214,6 +215,7 @@
   bookmarks	
   phases	
   namespaces	
+  obsolete	
   $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
   Y	4efff6d98829d9c824c621afd6e3f01865f5439f
   foobar	9b140be1080824d768c5a4691a564088eede71f9
--- a/tests/test-debugcomplete.t	Mon Jul 02 01:48:12 2012 +0200
+++ b/tests/test-debugcomplete.t	Sun Jul 01 21:19:57 2012 -0500
@@ -86,6 +86,7 @@
   debugindexdot
   debuginstall
   debugknown
+  debugobsolete
   debugpushkey
   debugpvec
   debugrebuildstate
@@ -236,6 +237,7 @@
   debugindexdot: 
   debuginstall: 
   debugknown: 
+  debugobsolete: date, user
   debugpushkey: 
   debugpvec: 
   debugrebuildstate: rev
--- a/tests/test-hook.t	Mon Jul 02 01:48:12 2012 +0200
+++ b/tests/test-hook.t	Sun Jul 01 21:19:57 2012 -0500
@@ -198,6 +198,7 @@
   listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
   no changes found
   listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
+  listkeys hook: HG_NAMESPACE=obsolete HG_VALUES={}
   listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
   adding remote bookmark bar
   importing bookmark bar
--- a/tests/test-http-proxy.t	Mon Jul 02 01:48:12 2012 +0200
+++ b/tests/test-http-proxy.t	Sun Jul 01 21:19:57 2012 -0500
@@ -105,20 +105,24 @@
   * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=phases (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=obsolete (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=phases (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=obsolete (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=phases (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=obsolete (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=phases (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=obsolete (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
 
--- a/tests/test-https.t	Mon Jul 02 01:48:12 2012 +0200
+++ b/tests/test-https.t	Sun Jul 01 21:19:57 2012 -0500
@@ -125,6 +125,7 @@
   adding file changes
   added 1 changesets with 4 changes to 4 files
   warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
+  warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
   updating to branch default
   4 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg verify -R copy-pull
@@ -154,6 +155,7 @@
   added 1 changesets with 1 changes to 1 files
   warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
   changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=https://localhost:$HGPORT/
+  warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
   (run 'hg update' to get a working copy)
   $ cd ..
 
@@ -182,6 +184,7 @@
   pulling from https://localhost:$HGPORT/
   searching for changes
   no changes found
+  warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
 
 cacert mismatch
 
@@ -194,6 +197,7 @@
   pulling from https://127.0.0.1:$HGPORT/
   searching for changes
   no changes found
+  warning: 127.0.0.1 certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
   $ hg -R copy-pull pull --config web.cacerts=pub-other.pem
   abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
   [255]
@@ -202,6 +206,7 @@
   pulling from https://localhost:$HGPORT/
   searching for changes
   no changes found
+  warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
 
 Test server cert which isn't valid yet
 
@@ -259,6 +264,7 @@
   pulling from https://localhost:$HGPORT/
   searching for changes
   no changes found
+  warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
 
 Test https with cacert and fingerprint through proxy
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-obsolete.t	Sun Jul 01 21:19:57 2012 -0500
@@ -0,0 +1,143 @@
+
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -m "add $1"
+  > }
+  $ getid() {
+  >    hg id --debug -ir "desc('$1')"
+  > }
+
+
+  $ hg init tmpa
+  $ cd tmpa
+
+Killing a single changeset without replacement
+
+  $ mkcommit kill_me
+  $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
+  $ hg debugobsolete
+  97b7c2d76b1845ed3eb988cd612611e72406cef0 0 {'date': '0 0', 'user': 'babar'}
+  $ cd ..
+
+Killing a single changeset with replacement
+
+  $ hg init tmpb
+  $ cd tmpb
+  $ mkcommit a
+  $ mkcommit b
+  $ mkcommit original_c
+  $ hg up "desc('b')"
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit new_c
+  created new head
+  $ hg debugobsolete `getid original_c`  `getid new_c` -d '56 12'
+  $ hg debugobsolete
+  245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
+
+do it again (it read the obsstore before adding new changeset)
+
+  $ hg up '.^'
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit new_2_c
+  created new head
+  $ hg debugobsolete -d '1337 0' `getid new_c` `getid new_2_c`
+  $ hg debugobsolete
+  245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
+  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
+
+Register two markers with a missing node
+
+  $ hg up '.^'
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit new_3_c
+  created new head
+  $ hg debugobsolete -d '1338 0' `getid new_2_c` 1337133713371337133713371337133713371337
+  $ hg debugobsolete -d '1339 0' 1337133713371337133713371337133713371337 `getid new_3_c`
+  $ hg debugobsolete
+  245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
+  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
+  ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
+  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
+
+  $ cd ..
+
+Exchange Test
+============================
+
+Destination repo does not have any data
+---------------------------------------
+
+Try to pull markers
+
+  $ hg init tmpc
+  $ cd tmpc
+  $ hg pull ../tmpb
+  pulling from ../tmpb
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 6 changesets with 6 changes to 6 files (+3 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg debugobsolete
+  ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
+  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
+  245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
+  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
+
+  $ cd ..
+
+Try to pull markers
+
+  $ hg init tmpd
+  $ hg -R tmpb push tmpd
+  pushing to tmpd
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 6 changesets with 6 changes to 6 files (+3 heads)
+  $ hg -R tmpd debugobsolete
+  ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
+  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
+  245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
+  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
+
+
+Destination repo have existing data
+---------------------------------------
+
+On pull
+
+  $ hg init tmpe
+  $ cd tmpe
+  $ hg debugobsolete -d '1339 0' 2448244824482448244824482448244824482448 1339133913391339133913391339133913391339
+  $ hg pull ../tmpb
+  pulling from ../tmpb
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 6 changesets with 6 changes to 6 files (+3 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg debugobsolete
+  2448244824482448244824482448244824482448 1339133913391339133913391339133913391339 0 {'date': '1339 0', 'user': 'test'}
+  ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
+  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
+  245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
+  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
+
+On push
+
+  $ hg push ../tmpc
+  pushing to ../tmpc
+  searching for changes
+  no changes found
+  [1]
+  $ hg -R ../tmpc debugobsolete
+  ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
+  cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
+  245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
+  1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
+  2448244824482448244824482448244824482448 1339133913391339133913391339133913391339 0 {'date': '1339 0', 'user': 'test'}
--- a/tests/test-ssh.t	Mon Jul 02 01:48:12 2012 +0200
+++ b/tests/test-ssh.t	Sun Jul 01 21:19:57 2012 -0500
@@ -167,6 +167,7 @@
   bookmarks	
   phases	
   namespaces	
+  obsolete	
   $ hg book foo -r 0
   $ hg out -B
   comparing with ssh://user@dummy/remote