--- a/README Thu Apr 27 20:52:09 2017 +0200
+++ b/README Wed May 03 12:31:40 2017 +0200
@@ -112,10 +112,13 @@
Changelog
=========
-
-6.0.2 - in progress
+6.1.0 - in progress
-------------------
+ - improve message about obsolete working copy parent,
+ - improve message issued when accessing hidden nodes (4.2 only),
+ - introduce a new caches to reduce the impact of evolution on read-only commands,
+ - add a 'experimental.auto-publish' config. See `hg help -e evolve` for details.
- fix the propagation of some some cache invalidation,
6.0.1 -- 2017-04-20
--- a/hgext3rd/evolve/__init__.py Thu Apr 27 20:52:09 2017 +0200
+++ b/hgext3rd/evolve/__init__.py Wed May 03 12:31:40 2017 +0200
@@ -7,15 +7,43 @@
# GNU General Public License version 2 or any later version.
"""extends Mercurial feature related to Changeset Evolution
-This extension provides several commands to mutate history and deal with
-resulting issues.
-
-It also:
-
- - enables the "Changeset Obsolescence" feature of Mercurial,
- - alters core commands and extensions that rewrite history to use
- this feature,
- - improves some aspect of the early implementation in Mercurial core
+This extension:
+
+- provides several commands to mutate history and deal with resulting issues,
+- enable the changeset-evolution feature for Mercurial,
+- improves some aspect of the early implementation in Mercurial core,
+
+Note that a version dedicated to server usage only (no local working copy) is
+available as 'evolve.serveronly'.
+
+While many feature related to changeset evolution are directly handled by core
+this extensions contains significant additions recommended to any user of
+changeset evolution.
+
+With the extensions various evolution events will display warning (new unstable
+changesets, obsolete working copy parent, improved error when accessing hidden
+revision, etc).
+
+In addition, the extension contains better discovery protocol for obsolescence
+markers. This means less obs-markers will have to be pushed and pulled around,
+speeding up such operation.
+
+Some improvement and bug fixes available in newer version of Mercurial are also
+backported to older version of Mercurial by this extension. Some older
+experimental protocol are also supported for a longer time in the extensions to
+help people transitioning. (The extensions is currently compatible down to
+Mercurial version 3.8).
+
+New Config:
+
+ [experimental]
+ # Set to control the behavior when pushing draft changesets to a publishing
+ # repository. Possible value:
+ # * ignore: current core behavior (default)
+ # * warn: proceed with the push, but issue a warning
+ # * abort: abort the push
+ auto-publish = ignore
+
"""
@@ -104,6 +132,7 @@
revset,
scmutil,
templatekw,
+ obsolete
)
from mercurial.commands import walkopts, commitopts, commitopts2, mergetoolopts
@@ -113,9 +142,11 @@
from . import (
checkheads,
debugcmd,
- obsexchange,
exthelper,
metadata,
+ obscache,
+ obsexchange,
+ safeguard,
utility,
)
@@ -147,6 +178,8 @@
eh.merge(debugcmd.eh)
eh.merge(obsexchange.eh)
eh.merge(checkheads.eh)
+eh.merge(safeguard.eh)
+eh.merge(obscache.eh)
uisetup = eh.final_uisetup
extsetup = eh.final_extsetup
reposetup = eh.final_reposetup
@@ -449,11 +482,102 @@
# This section take care of issue warning to the user when troubles appear
+def _getobsoletereason(repo, revnode):
+ """ Return a tuple containing:
+ - the reason a revision is obsolete (diverged, pruned or superseed)
+ - the list of successors short node if the revision is neither pruned
+ or has diverged
+ """
+ successorssets = obsolete.successorssets(repo, revnode)
+
+ if len(successorssets) == 0:
+ # The commit has been pruned
+ return ('pruned', [])
+ elif len(successorssets) > 1:
+ return ('diverged', [])
+ else:
+ # No divergence, only one set of successors
+ successors = [node.short(node_id) for node_id in successorssets[0]]
+
+ if len(successors) == 1:
+ return ('superseed', successors)
+ else:
+ return ('superseed_split', successors)
+
def _warnobsoletewc(ui, repo):
- if repo['.'].obsolete():
- ui.warn(_('working directory parent is obsolete!\n'))
- if (not ui.quiet) and obsolete.isenabled(repo, commandopt):
- ui.warn(_("(use 'hg evolve' to update to its successor)\n"))
+ rev = repo['.']
+
+ if not rev.obsolete():
+ return
+
+ msg = _("working directory parent is obsolete! (%s)\n")
+ shortnode = node.short(rev.node())
+
+ ui.warn(msg % shortnode)
+
+ # Check that evolve is activated for performance reasons
+ if ui.quiet or not obsolete.isenabled(repo, commandopt):
+ return
+
+ # Show a warning for helping the user to solve the issue
+ reason, successors = _getobsoletereason(repo, rev.node())
+
+ if reason == 'pruned':
+ solvemsg = _("use 'hg evolve' to update to its parent successor")
+ elif reason == 'diverged':
+ debugcommand = "hg evolve -list --divergent"
+ basemsg = _("%s has diverged, use '%s' to resolve the issue")
+ solvemsg = basemsg % (shortnode, debugcommand)
+ elif reason == 'superseed':
+ msg = _("use 'hg evolve' to update to its successor: %s")
+ solvemsg = msg % successors[0]
+ elif reason == 'superseed_split':
+ msg = _("use 'hg evolve' to update to its tipmost successor: %s")
+
+ if len(successors) <= 2:
+ solvemsg = msg % ", ".join(successors)
+ else:
+ firstsuccessors = ", ".join(successors[:2])
+ remainingnumber = len(successors) - 2
+ successorsmsg = _("%s and %d more") % (firstsuccessors, remainingnumber)
+ solvemsg = msg % successorsmsg
+ else:
+ raise ValueError(reason)
+
+ ui.warn("(%s)\n" % solvemsg)
+
+if util.safehasattr(context, '_filterederror'):
+ # if < hg-4.2 we do not update the message
+ @eh.wrapfunction(context, '_filterederror')
+ def evolve_filtererror(original, repo, changeid):
+ """build an exception to be raised about a filtered changeid
+
+ This is extracted in a function to help extensions (eg: evolve) to
+ experiment with various message variants."""
+ if repo.filtername.startswith('visible'):
+
+ unfilteredrepo = repo.unfiltered()
+ rev = unfilteredrepo[changeid]
+ reason, successors = _getobsoletereason(unfilteredrepo, rev.node())
+
+ # Be more precise in cqse the revision is superseed
+ if reason == 'superseed':
+ reason = _("successor: %s") % successors[0]
+ elif reason == 'superseed_split':
+ if len(successors) <= 2:
+ reason = _("successors: %s") % ", ".join(successors)
+ else:
+ firstsuccessors = ", ".join(successors[:2])
+ remainingnumber = len(successors) - 2
+ successorsmsg = _("%s and %d more") % (firstsuccessors, remainingnumber)
+ reason = _("successors: %s") % successorsmsg
+
+ msg = _("hidden revision '%s'") % changeid
+ hint = _('use --hidden to access hidden revisions; %s') % reason
+ return error.FilteredRepoLookupError(msg, hint=hint)
+ msg = _("filtered revision '%s' (not in '%s' subset)")
+ msg %= (changeid, repo.filtername)
+ return error.FilteredRepoLookupError(msg)
@eh.wrapcommand("update")
@eh.wrapcommand("pull")
--- a/hgext3rd/evolve/metadata.py Thu Apr 27 20:52:09 2017 +0200
+++ b/hgext3rd/evolve/metadata.py Wed May 03 12:31:40 2017 +0200
@@ -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.
-__version__ = '6.0.1'
+__version__ = '6.1.0.dev'
testedwith = '3.8.4 3.9.2 4.0.2 4.1.1'
minimumhgversion = '3.8'
buglink = 'https://bz.mercurial-scm.org/'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/evolve/obscache.py Wed May 03 12:31:40 2017 +0200
@@ -0,0 +1,399 @@
+# Code dedicated to an cache around obsolescence property
+#
+# This module content aims at being upstreamed.
+#
+# Copyright 2017 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+import hashlib
+import struct
+import weakref
+import errno
+
+from mercurial import (
+ localrepo,
+ obsolete,
+ phases,
+ node,
+)
+
+from . import (
+ exthelper,
+)
+
+eh = exthelper.exthelper()
+
+try:
+ obsstorefilecache = localrepo.localrepository.obsstore
+except AttributeError:
+ # XXX hg-3.8 compat
+ #
+ # mercurial 3.8 has issue with accessing file cache property from their
+ # cache. This is fix by 36fbd72c2f39fef8ad52d7c559906c2bc388760c in core
+ # and shipped in 3.9
+ obsstorefilecache = localrepo.localrepository.__dict__['obsstore']
+
+# obsstore is a filecache so we have do to some spacial dancing
+@eh.wrapfunction(obsstorefilecache, 'func')
+def obsstorewithcache(orig, repo):
+ obsstore = orig(repo)
+ obsstore.obscache = obscache(repo.unfiltered())
+
+ class cachekeyobsstore(obsstore.__class__):
+
+ _obskeysize = 200
+
+ def cachekey(self, index=None):
+ """return (current-length, cachekey)
+
+ 'current-length': is the current length of the obsstore storage file,
+ 'cachekey' is the hash of the last 200 bytes ending at 'index'.
+
+ if 'index' is unspecified, current obsstore length is used.
+ Cacheckey will be set to null id if the obstore is empty.
+
+ If the index specified is higher than the current obsstore file
+ length, cachekey will be set to None."""
+ # default value
+ obsstoresize = 0
+ keydata = ''
+ # try to get actual data from the obsstore
+ try:
+ with self.svfs('obsstore') as obsfile:
+ obsfile.seek(0, 2)
+ obsstoresize = obsfile.tell()
+ if index is None:
+ index = obsstoresize
+ elif obsstoresize < index:
+ return obsstoresize, None
+ actualsize = min(index, self._obskeysize)
+ if actualsize:
+ obsfile.seek(index - actualsize, 0)
+ keydata = obsfile.read(actualsize)
+ except (OSError, IOError) as e:
+ if e.errno != errno.ENOENT:
+ raise
+ key = hashlib.sha1(keydata).digest()
+ return obsstoresize, key
+
+ obsstore.__class__ = cachekeyobsstore
+
+ return obsstore
+
+emptykey = (node.nullrev, node.nullid, 0, 0, node.nullid)
+
+def getcachekey(repo):
+ """get a cache key covering the changesets and obsmarkers content
+
+ IT contains the following data. Combined with 'upgradeneeded' it allows to
+ do iterative upgrade for cache depending of theses two data.
+
+ The cache key parts are"
+ - tip-rev,
+ - tip-node,
+ - obsstore-length (nb markers),
+ - obsstore-file-size (in bytes),
+ - obsstore "cache key"
+ """
+ assert repo.filtername is None
+ cl = repo.changelog
+ index, key = repo.obsstore.cachekey()
+ tiprev = len(cl) - 1
+ return (tiprev,
+ cl.node(tiprev),
+ len(repo.obsstore),
+ index,
+ key)
+
+def upgradeneeded(repo, key):
+ """return (valid, start-rev, start-obs-idx)
+
+ 'valid': is "False" if older cache value needs invalidation,
+
+ 'start-rev': first revision not in the cache. None if cache is up to date,
+
+ 'start-obs-idx': index of the first obs-markers not in the cache. None is
+ up to date.
+ """
+
+ # XXX ideally, this function would return a bounded amount of changeset and
+ # obsmarkers and the associated new cache key. Otherwise we are exposed to
+ # a race condition between the time the cache is updated and the new cache
+ # key is computed. (however, we do not want to compute the full new cache
+ # key in all case because we want to skip reading the obsstore content. We
+ # could have a smarter implementation here.
+ #
+ # In pratice the cache is only updated after each transaction within a
+ # lock. So we should be fine. We could enforce this with a new repository
+ # requirement (or fix the race, that is not too hard).
+ invalid = (False, 0, 0)
+ if key is None:
+ return invalid
+
+ ### Is the cache valid ?
+ keytiprev, keytipnode, keyobslength, keyobssize, keyobskey = key
+ # check for changelog strip
+ cl = repo.changelog
+ tiprev = len(cl) - 1
+ if (tiprev < keytiprev
+ or cl.node(keytiprev) != keytipnode):
+ return invalid
+ # check for obsstore strip
+ obssize, obskey = repo.obsstore.cachekey(index=keyobssize)
+ if obskey != keyobskey:
+ return invalid
+
+ ### cache is valid, is there anything to update
+
+ # any new changesets ?
+ startrev = None
+ if keytiprev < tiprev:
+ startrev = keytiprev + 1
+
+ # any new markers
+ startidx = None
+ if keyobssize < obssize:
+ startidx = keyobslength
+
+ return True, startrev, startidx
+
+class obscache(object):
+ """cache the "does a rev" is the precursors of some obsmarkers data
+
+ This is not directly holding the "is this revision obsolete" information,
+ because phases data gets into play here. However, it allow to compute the
+ "obsolescence" set without reading the obsstore content.
+
+ Implementation note #1:
+
+ The obsstore is implementing only half of the transaction logic it
+ should. It properly record the starting point of the obsstore to allow
+ clean rollback. However it still write to the obsstore file directly
+ during the transaction. Instead it should be keeping data in memory and
+ write to a '.pending' file to make the data vailable for hooks.
+
+ This cache is not going futher than what the obstore is doing, so it does
+ not has any '.pending' logic. When the obsstore gains proper '.pending'
+ support, adding it to this cache should not be too hard. As the flag
+ always move from 0 to 1, we could have a second '.pending' cache file to
+ be read. If flag is set in any of them, the value is 1. For the same
+ reason, updating the file in place should be possible.
+
+ Implementation note #2:
+
+ Instead of having a large final update run, we could update this cache at
+ the level adding a new changeset or a new obsmarkers. More on this in the
+ 'update code'.
+
+ Implementation note #3:
+
+ Storage-wise, we could have a "start rev" to avoid storing useless
+ zero. That would be especially useful for the '.pending' overlay.
+ """
+
+ _filepath = 'cache/evoext-obscache-00'
+ _headerformat = '>q20sQQ20s'
+
+ def __init__(self, repo):
+ self._vfs = repo.vfs
+ # The cache key parts are"
+ # - tip-rev,
+ # - tip-node,
+ # - obsstore-length (nb markers),
+ # - obsstore-file-size (in bytes),
+ # - obsstore "cache key"
+ self._cachekey = None
+ self._ondiskkey = None
+ self._data = bytearray()
+
+ def get(self, rev):
+ """return True if "rev" is used as "precursors for any obsmarkers
+
+ Make sure the cache has been updated to match the repository content before using it"""
+ return self._data[rev]
+
+ def clear(self):
+ """invalidate the cache content"""
+ self._cachekey = None
+ self._data = bytearray()
+
+ def uptodate(self, repo):
+ if self._cachekey is None:
+ self.load(repo)
+ valid, startrev, startidx = upgradeneeded(repo, self._cachekey)
+ return (valid and startrev is None and startidx is None)
+
+ def update(self, repo):
+ """Iteratively update the cache with new repository data"""
+ # If we do not have any data, try loading from disk
+ if self._cachekey is None:
+ self.load(repo)
+
+ valid, startrev, startidx = upgradeneeded(repo, self._cachekey)
+ if not valid:
+ self.clear()
+
+ if startrev is None and startidx is None:
+ return
+
+ # process the new changesets
+ cl = repo.changelog
+ if startrev is not None:
+ node = cl.node
+ # Note:
+ #
+ # Newly added changeset might be affected by obsolescence markers
+ # we already have locally. So we needs to have soem global
+ # knowledge about the markers to handle that question. Right this
+ # requires parsing all markers in the obsstore. However, we could
+ # imagine using various optimisation (eg: bloom filter, other on
+ # disk cache) to remove this full parsing.
+ #
+ # For now we stick to the simpler approach or paying the
+ # performance cost on new changesets.
+ succs = repo.obsstore.successors
+ for r in cl.revs(startrev):
+ if node(r) in succs:
+ val = 1
+ else:
+ val = 0
+ self._data.append(val)
+ assert len(self._data) == len(cl), (len(self._data), len(cl))
+
+ # process the new obsmarkers
+ if startidx is not None:
+ rev = cl.nodemap.get
+ markers = repo.obsstore._all
+ # Note:
+ #
+ # There are no actually needs to load the full obsstore here,
+ # since we only read the latest ones. We do it for simplicity in
+ # the first implementation. Loading the full obsstore has a
+ # performance cost and should go away in this case too. We have
+ # two simples options for that:
+ #
+ # 1) provide and API to start reading markers from a byte offset
+ # (we have that data in the cache key)
+ #
+ # 2) directly update the cache at a lower level, in the code
+ # responsible for adding a markers.
+ #
+ # Option 2 is probably a bit more invasive, but more solid on the long run
+
+ for i in xrange(startidx, len(repo.obsstore)):
+ r = rev(markers[i][0])
+ # If markers affect a newly added nodes, it would have been
+ # caught in the previous loop, (so we skip < startrev)
+ if r is not None and (startrev is None or r < startrev):
+ self._data[r] = 1
+
+ assert repo._currentlock(repo._lockref) is not None
+ # XXX note that there are a potential race condition here, since the
+ # repo "might" have changed side the cache update above. However, this
+ # code will only be running in a lock so we ignore the issue for now.
+ #
+ # To work around this, 'upgradeneeded' should return a bounded amount
+ # of changeset and markers to read with their associated cachekey. see
+ # 'upgradeneeded' for detail.
+ self._cachekey = getcachekey(repo)
+
+ def save(self, repo):
+ """save the data to disk"""
+
+ # XXX it happens that the obsstore is (buggilly) always up to date on disk
+ if self._cachekey is None or self._cachekey == self._ondiskkey:
+ return
+
+ with repo.vfs(self._filepath, 'w', atomictemp=True) as cachefile:
+ headerdata = struct.pack(self._headerformat, *self._cachekey)
+ cachefile.write(headerdata)
+ cachefile.write(self._data)
+
+ def load(self, repo):
+ """load data from disk"""
+ assert repo.filtername is None
+
+ data = repo.vfs.tryread(self._filepath)
+ if not data:
+ self._cachekey = emptykey
+ self._data = bytearray()
+ else:
+ headersize = struct.calcsize(self._headerformat)
+ self._cachekey = struct.unpack(self._headerformat, data[:headersize])
+ self._data = bytearray(data[headersize:])
+ self._ondiskkey = self._cachekey
+
+def _computeobsoleteset(orig, repo):
+ """the set of obsolete revisions"""
+ obs = set()
+ repo = repo.unfiltered()
+ notpublic = repo._phasecache.getrevset(repo, (phases.draft, phases.secret))
+ if notpublic:
+ obscache = repo.obsstore.obscache
+ # Since we warm the cache at the end of every transaction, the cache
+ # should be up to date. However a non-enabled client might have touced
+ # the repository.
+ #
+ # Updating the cache without a lock is sloppy, so we fallback to the
+ # old method and rely on the fact the next transaction will write the
+ # cache down anyway.
+ #
+ # With the current implementation updating the cache will requires to
+ # load the obsstore anyway. Once loaded, hitting the obsstore directly
+ # will be about as fast..
+ if not obscache.uptodate(repo):
+ if repo.currenttransaction() is None:
+ repo.ui.log('evoext-obscache',
+ 'obscache is out of date, '
+ 'falling back to slower obsstore version\n')
+ repo.ui.debug('obscache is out of date')
+ return orig(repo)
+ else:
+ # If a transaction is open, it is worthwhile to update and use the
+ # cache as it will be written on disk when the transaction close.
+ obscache.update(repo)
+ isobs = obscache.get
+ for r in notpublic:
+ if isobs(r):
+ obs.add(r)
+ return obs
+
+@eh.uisetup
+def cachefuncs(ui):
+ orig = obsolete.cachefuncs['obsolete']
+ wrapped = lambda repo: _computeobsoleteset(orig, repo)
+ obsolete.cachefuncs['obsolete'] = wrapped
+
+@eh.reposetup
+def setupcache(ui, repo):
+
+ class obscacherepo(repo.__class__):
+
+ @localrepo.unfilteredmethod
+ def destroyed(self):
+ if 'obsstore' in vars(self):
+ self.obsstore.obscache.clear()
+
+ def transaction(self, *args, **kwargs):
+ tr = super(obscacherepo, self).transaction(*args, **kwargs)
+ reporef = weakref.ref(self)
+
+ def _warmcache(tr):
+ repo = reporef()
+ if repo is None:
+ return
+ repo = repo.unfiltered()
+ # As pointed in 'obscache.update', we could have the
+ # changelog and the obsstore in charge of updating the
+ # cache when new items goes it. The tranaction logic would
+ # then only be involved for the 'pending' and final saving
+ # logic.
+ self.obsstore.obscache.update(repo)
+ self.obsstore.obscache.save(repo)
+
+ tr.addpostclose('warmcache-obscache', _warmcache)
+ return tr
+
+ repo.__class__ = obscacherepo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/evolve/safeguard.py Wed May 03 12:31:40 2017 +0200
@@ -0,0 +1,42 @@
+# Code dedicated to adding various "safeguard" around evolution
+#
+# Some of these will be pollished and upstream when mature. Some other will be
+# replaced by better alternative later.
+#
+# Copyright 2017 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from mercurial import error
+
+from mercurial.i18n import _
+
+from . import exthelper
+
+eh = exthelper.exthelper()
+
+@eh.reposetup
+def setuppublishprevention(ui, repo):
+
+ class noautopublishrepo(repo.__class__):
+
+ def checkpush(self, pushop):
+ super(noautopublishrepo, self).checkpush(pushop)
+ behavior = repo.ui.config('experimental', 'auto-publish', 'default')
+ remotephases = pushop.remote.listkeys('phases')
+ publishing = remotephases.get('publishing', False)
+ if behavior in ('warn', 'abort') and publishing:
+ if pushop.revs is None:
+ published = repo.filtered('served').revs("not public()")
+ else:
+ published = repo.revs("::%ln - public()", pushop.revs)
+ if published:
+ if behavior == 'warn':
+ repo.ui.warn(_('%i changesets about to be published\n') % len(published))
+ elif behavior == 'abort':
+ msg = _('push would publish 1 changesets')
+ hint = _("behavior controlled by 'experimental.auto-publish' config")
+ raise error.Abort(msg, hint=hint)
+
+ repo.__class__ = noautopublishrepo
--- a/hgext3rd/topic/__init__.py Thu Apr 27 20:52:09 2017 +0200
+++ b/hgext3rd/topic/__init__.py Wed May 03 12:31:40 2017 +0200
@@ -154,7 +154,6 @@
def reposetup(ui, repo):
- orig = repo.__class__
if not isinstance(repo, localrepo.localrepository):
return # this can be a peer in the ssh case (puzzling)
@@ -171,7 +170,7 @@
if repo.currenttopic != repo['.'].topic():
# bypass the core "nothing changed" logic
self.ui.setconfig('ui', 'allowemptycommit', True)
- return orig.commit(self, *args, **kwargs)
+ return super(topicrepo, self).commit(*args, **kwargs)
finally:
self.ui.restoreconfig(backup)
@@ -187,7 +186,7 @@
# we are amending and need to remove a topic
del ctx.extra()[constants.extrakey]
with topicmap.usetopicmap(self):
- return orig.commitctx(self, ctx, error=error)
+ return super(topicrepo, self).commitctx(ctx, error=error)
@property
def topics(self):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-obshistory.t Wed May 03 12:31:40 2017 +0200
@@ -0,0 +1,516 @@
+This test file test the various messages when accessing obsolete
+revisions.
+
+Global setup
+============
+
+ $ . $TESTDIR/testlib/common.sh
+ $ cat >> $HGRCPATH <<EOF
+ > [ui]
+ > interactive = true
+ > [phases]
+ > publish=False
+ > [extensions]
+ > evolve =
+ > EOF
+
+Test output on amended commit
+=============================
+
+Test setup
+----------
+
+ $ hg init $TESTTMP/local-amend
+ $ cd $TESTTMP/local-amend
+ $ mkcommit ROOT
+ $ mkcommit A0
+ $ echo 42 >> A0
+ $ hg amend -m "A1"
+ $ hg log --hidden -G
+ @ changeset: 3:a468dc9b3633
+ | tag: tip
+ | parent: 0:ea207398892e
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A1
+ |
+ | x changeset: 2:f137d23bb3e1
+ | | user: test
+ | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | summary: temporary amend commit for 471f378eab4c
+ | |
+ | x changeset: 1:471f378eab4c
+ |/ user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 0:ea207398892e
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: ROOT
+
+Actual test
+-----------
+
+ $ hg update 471f378eab4c
+ abort: hidden revision '471f378eab4c'!
+ (use --hidden to access hidden revisions; successor: a468dc9b3633)
+ [255]
+ $ hg update --hidden "desc(A0)"
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ working directory parent is obsolete! (471f378eab4c)
+ (use 'hg evolve' to update to its successor: a468dc9b3633)
+
+Test output with pruned commit
+==============================
+
+Test setup
+----------
+
+ $ hg init $TESTTMP/local-prune
+ $ cd $TESTTMP/local-prune
+ $ mkcommit ROOT
+ $ mkcommit A0 # 0
+ $ mkcommit B0 # 1
+ $ hg log --hidden -G
+ @ changeset: 2:0dec01379d3b
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: B0
+ |
+ o changeset: 1:471f378eab4c
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 0:ea207398892e
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: ROOT
+
+ $ hg prune -r 'desc(B0)'
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ working directory now at 471f378eab4c
+ 1 changesets pruned
+
+Actual test
+-----------
+
+ $ hg up 1
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg up 0dec01379d3b
+ abort: hidden revision '0dec01379d3b'!
+ (use --hidden to access hidden revisions; pruned)
+ [255]
+ $ hg up --hidden -r 'desc(B0)'
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ working directory parent is obsolete! (0dec01379d3b)
+ (use 'hg evolve' to update to its parent successor)
+
+Test output with splitted commit
+================================
+
+Test setup
+----------
+
+ $ hg init $TESTTMP/local-split
+ $ cd $TESTTMP/local-split
+ $ mkcommit ROOT
+ $ echo 42 >> a
+ $ echo 43 >> b
+ $ hg commit -A -m "A0"
+ adding a
+ adding b
+ $ hg log --hidden -G
+ @ changeset: 1:471597cad322
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 0:ea207398892e
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: ROOT
+
+ $ hg split -r 'desc(A0)' -d "0 0" << EOF
+ > y
+ > y
+ > n
+ > n
+ > y
+ > y
+ > EOF
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ adding a
+ adding b
+ diff --git a/a b/a
+ new file mode 100644
+ examine changes to 'a'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +42
+ record change 1/2 to 'a'? [Ynesfdaq?] y
+
+ diff --git a/b b/b
+ new file mode 100644
+ examine changes to 'b'? [Ynesfdaq?] n
+
+ created new head
+ Done splitting? [yN] n
+ diff --git a/b b/b
+ new file mode 100644
+ examine changes to 'b'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +43
+ record this change to 'b'? [Ynesfdaq?] y
+
+ no more change to split
+
+ $ hg log --hidden -G
+ @ changeset: 3:f257fde29c7a
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 2:337fec4d2edc
+ | parent: 0:ea207398892e
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ | x changeset: 1:471597cad322
+ |/ user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 0:ea207398892e
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: ROOT
+
+Actual test
+-----------
+
+ $ hg update 471597cad322
+ abort: hidden revision '471597cad322'!
+ (use --hidden to access hidden revisions; successors: 337fec4d2edc, f257fde29c7a)
+ [255]
+ $ hg update --hidden 'min(desc(A0))'
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ working directory parent is obsolete! (471597cad322)
+ (use 'hg evolve' to update to its tipmost successor: 337fec4d2edc, f257fde29c7a)
+
+Test output with lots of splitted commit
+========================================
+
+Test setup
+----------
+
+ $ hg init $TESTTMP/local-lots-split
+ $ cd $TESTTMP/local-lots-split
+ $ mkcommit ROOT
+ $ echo 42 >> a
+ $ echo 43 >> b
+ $ echo 44 >> c
+ $ echo 45 >> d
+ $ hg commit -A -m "A0"
+ adding a
+ adding b
+ adding c
+ adding d
+ $ hg log --hidden -G
+ @ changeset: 1:de7290d8b885
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 0:ea207398892e
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: ROOT
+
+
+ $ hg split -r 'desc(A0)' -d "0 0" << EOF
+ > y
+ > y
+ > n
+ > n
+ > n
+ > n
+ > y
+ > y
+ > n
+ > n
+ > n
+ > y
+ > y
+ > n
+ > n
+ > y
+ > y
+ > EOF
+ 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+ adding a
+ adding b
+ adding c
+ adding d
+ diff --git a/a b/a
+ new file mode 100644
+ examine changes to 'a'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +42
+ record change 1/4 to 'a'? [Ynesfdaq?] y
+
+ diff --git a/b b/b
+ new file mode 100644
+ examine changes to 'b'? [Ynesfdaq?] n
+
+ diff --git a/c b/c
+ new file mode 100644
+ examine changes to 'c'? [Ynesfdaq?] n
+
+ diff --git a/d b/d
+ new file mode 100644
+ examine changes to 'd'? [Ynesfdaq?] n
+
+ created new head
+ Done splitting? [yN] n
+ diff --git a/b b/b
+ new file mode 100644
+ examine changes to 'b'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +43
+ record change 1/3 to 'b'? [Ynesfdaq?] y
+
+ diff --git a/c b/c
+ new file mode 100644
+ examine changes to 'c'? [Ynesfdaq?] n
+
+ diff --git a/d b/d
+ new file mode 100644
+ examine changes to 'd'? [Ynesfdaq?] n
+
+ Done splitting? [yN] n
+ diff --git a/c b/c
+ new file mode 100644
+ examine changes to 'c'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +44
+ record change 1/2 to 'c'? [Ynesfdaq?] y
+
+ diff --git a/d b/d
+ new file mode 100644
+ examine changes to 'd'? [Ynesfdaq?] n
+
+ Done splitting? [yN] n
+ diff --git a/d b/d
+ new file mode 100644
+ examine changes to 'd'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +45
+ record this change to 'd'? [Ynesfdaq?] y
+
+ no more change to split
+
+ $ hg log --hidden -G
+ @ changeset: 5:c7f044602e9b
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 4:1ae8bc733a14
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 3:f257fde29c7a
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 2:337fec4d2edc
+ | parent: 0:ea207398892e
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ | x changeset: 1:de7290d8b885
+ |/ user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 0:ea207398892e
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: ROOT
+
+Actual test
+-----------
+
+ $ hg update de7290d8b885
+ abort: hidden revision 'de7290d8b885'!
+ (use --hidden to access hidden revisions; successors: 337fec4d2edc, f257fde29c7a and 2 more)
+ [255]
+ $ hg update --hidden 'min(desc(A0))'
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ working directory parent is obsolete! (de7290d8b885)
+ (use 'hg evolve' to update to its tipmost successor: 337fec4d2edc, f257fde29c7a and 2 more)
+
+Test output with folded commit
+==============================
+
+Test setup
+----------
+
+ $ hg init $TESTTMP/local-fold
+ $ cd $TESTTMP/local-fold
+ $ mkcommit ROOT
+ $ mkcommit A0
+ $ mkcommit B0
+ $ hg log --hidden -G
+ @ changeset: 2:0dec01379d3b
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: B0
+ |
+ o changeset: 1:471f378eab4c
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 0:ea207398892e
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: ROOT
+
+ $ hg fold --exact -r 'desc(A0) + desc(B0)' --date "0 0" -m "C0"
+ 2 changesets folded
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg log --hidden -G
+ @ changeset: 3:eb5a0daa2192
+ | tag: tip
+ | parent: 0:ea207398892e
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: C0
+ |
+ | x changeset: 2:0dec01379d3b
+ | | user: test
+ | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | summary: B0
+ | |
+ | x changeset: 1:471f378eab4c
+ |/ user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 0:ea207398892e
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: ROOT
+
+ Actual test
+ -----------
+
+ $ hg update 471f378eab4c
+ abort: hidden revision '471f378eab4c'!
+ (use --hidden to access hidden revisions; successor: eb5a0daa2192)
+ [255]
+ $ hg update --hidden 'desc(A0)'
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ working directory parent is obsolete! (471f378eab4c)
+ (use 'hg evolve' to update to its successor: eb5a0daa2192)
+ $ hg update 0dec01379d3b
+ working directory parent is obsolete! (471f378eab4c)
+ (use 'hg evolve' to update to its successor: eb5a0daa2192)
+ abort: hidden revision '0dec01379d3b'!
+ (use --hidden to access hidden revisions; successor: eb5a0daa2192)
+ [255]
+ $ hg update --hidden 'desc(B0)'
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ working directory parent is obsolete! (0dec01379d3b)
+ (use 'hg evolve' to update to its successor: eb5a0daa2192)
+
+Test output with divergence
+===========================
+
+Test setup
+----------
+
+ $ hg init $TESTTMP/local-divergence
+ $ cd $TESTTMP/local-divergence
+ $ mkcommit ROOT
+ $ mkcommit A0
+ $ hg amend -m "A1"
+ $ hg log --hidden -G
+ @ changeset: 2:fdf9bde5129a
+ | tag: tip
+ | parent: 0:ea207398892e
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A1
+ |
+ | x changeset: 1:471f378eab4c
+ |/ user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 0:ea207398892e
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: ROOT
+
+ $ hg update --hidden 'desc(A0)'
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ working directory parent is obsolete! (471f378eab4c)
+ (use 'hg evolve' to update to its successor: fdf9bde5129a)
+ $ hg amend -m "A2"
+ 2 new divergent changesets
+ $ hg log --hidden -G
+ @ changeset: 3:65b757b745b9
+ | tag: tip
+ | parent: 0:ea207398892e
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | trouble: divergent
+ | summary: A2
+ |
+ | o changeset: 2:fdf9bde5129a
+ |/ parent: 0:ea207398892e
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | trouble: divergent
+ | summary: A1
+ |
+ | x changeset: 1:471f378eab4c
+ |/ user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: A0
+ |
+ o changeset: 0:ea207398892e
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: ROOT
+
+Actual test
+-----------
+
+ $ hg update 471f378eab4c
+ abort: hidden revision '471f378eab4c'!
+ (use --hidden to access hidden revisions; diverged)
+ [255]
+ $ hg update --hidden 'desc(A0)'
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ working directory parent is obsolete! (471f378eab4c)
+ (471f378eab4c has diverged, use 'hg evolve -list --divergent' to resolve the issue)
--- a/tests/test-evolve.t Thu Apr 27 20:52:09 2017 +0200
+++ b/tests/test-evolve.t Wed May 03 12:31:40 2017 +0200
@@ -722,7 +722,7 @@
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg fold --from 6 # want to run hg fold 6
abort: hidden revision '6'!
- (use --hidden to access hidden revisions)
+ (use --hidden to access hidden revisions; successor: af636757ce3b)
[255]
$ hg log -r 11 --template '{desc}\n'
add 3
--- a/tests/test-inhibit.t Thu Apr 27 20:52:09 2017 +0200
+++ b/tests/test-inhibit.t Wed May 03 12:31:40 2017 +0200
@@ -364,7 +364,7 @@
$ hg export 1 3
abort: hidden revision '1'!
- (use --hidden to access hidden revisions)
+ (use --hidden to access hidden revisions; pruned)
[255]
@@ -432,7 +432,7 @@
$ hg rebase -s 10 -d 3
abort: hidden revision '3'!
- (use --hidden to access hidden revisions)
+ (use --hidden to access hidden revisions; pruned)
[255]
$ hg rebase -r ad78ff7d621f -r 53a94305e133 -d 2db36d8066ff --config experimental.rebaseskipobsolete=0
Warning: accessing hidden changesets 2db36d8066ff for write operation
@@ -699,7 +699,7 @@
$ hg up 15
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- working directory parent is obsolete!
+ working directory parent is obsolete! (2d66e189f5b5)
$ cat >> $HGRCPATH <<EOF
> [experimental]
> evolution=all
@@ -803,7 +803,7 @@
$ hg push -r 003a4735afde $pwd/inhibit2
pushing to $TESTTMP/inhibit2
abort: hidden revision '003a4735afde'!
- (use --hidden to access hidden revisions)
+ (use --hidden to access hidden revisions; successor: 71eb4f100663)
[255]
Visible commits can still be pushed
--- a/tests/test-obsolete-push.t Thu Apr 27 20:52:09 2017 +0200
+++ b/tests/test-obsolete-push.t Wed May 03 12:31:40 2017 +0200
@@ -44,3 +44,50 @@
comparing with ../clone
searching for changes
0:1994f17a630e@default(obsolete/draft) A
+ $ cd ..
+
+Test options to prevent implicite publishing of changesets
+----------------------------------------------------------
+
+
+ $ hg clone source strict-publish-client --pull
+ requesting all changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files
+ 1 new obsolescence markers
+ updating to branch default
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd strict-publish-client
+ $ echo c > c
+ $ hg ci -qAm C c
+
+abort behavior
+
+ $ cat >> .hg/hgrc <<eof
+ > [experimental]
+ > auto-publish = abort
+ > eof
+ $ hg push -r .
+ pushing to $TESTTMP/source
+ abort: push would publish 1 changesets
+ (behavior controlled by 'experimental.auto-publish' config)
+ [255]
+ $ hg push
+ pushing to $TESTTMP/source
+ abort: push would publish 1 changesets
+ (behavior controlled by 'experimental.auto-publish' config)
+ [255]
+
+warning behavior
+
+ $ echo 'auto-publish = warn' >> .hg/hgrc
+ $ hg push
+ pushing to $TESTTMP/source
+ 1 changesets about to be published
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 0 changesets with 0 changes to 1 files
--- a/tests/test-obsolete.t Thu Apr 27 20:52:09 2017 +0200
+++ b/tests/test-obsolete.t Wed May 03 12:31:40 2017 +0200
@@ -121,7 +121,7 @@
4
- 725c380fe99b
$ hg up --hidden 3 -q
- working directory parent is obsolete!
+ working directory parent is obsolete! (0d3f46688ccc)
(reported by parents too)
$ hg parents
changeset: 3:0d3f46688ccc
@@ -130,8 +130,8 @@
date: Thu Jan 01 00:00:00 1970 +0000
summary: add obsol_c
- working directory parent is obsolete!
- (use 'hg evolve' to update to its successor)
+ working directory parent is obsolete! (0d3f46688ccc)
+ (use 'hg evolve' to update to its successor: 725c380fe99b)
$ mkcommit d # 5 (on 3)
1 new unstable changesets
$ qlog -r 'obsolete()'
@@ -206,7 +206,7 @@
0
- 1f0dee641bb7
$ hg up --hidden 3 -q
- working directory parent is obsolete!
+ working directory parent is obsolete! (0d3f46688ccc)
$ mkcommit obsol_d # 6
created new head
1 new unstable changesets
@@ -263,7 +263,7 @@
[1]
$ hg up --hidden -q .^ # 3
- working directory parent is obsolete!
+ working directory parent is obsolete! (0d3f46688ccc)
$ mkcommit "obsol_d'" # 7
created new head
1 new unstable changesets
@@ -351,7 +351,7 @@
Test rollback support
$ hg up --hidden .^ -q # 3
- working directory parent is obsolete!
+ working directory parent is obsolete! (0d3f46688ccc)
$ mkcommit "obsol_d''"
created new head
1 new unstable changesets
@@ -687,7 +687,7 @@
$ hg up --hidden 3 -q
- working directory parent is obsolete!
+ working directory parent is obsolete! (0d3f46688ccc)
$ hg evolve
parent is obsolete with multiple successors:
[4] add obsol_c'
@@ -704,8 +704,8 @@
$ hg up --hidden 2
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
- working directory parent is obsolete!
- (use 'hg evolve' to update to its successor)
+ working directory parent is obsolete! (4538525df7e2)
+ (4538525df7e2 has diverged, use 'hg evolve -list --divergent' to resolve the issue)
$ hg export 9468a5f5d8b2 | hg import -
applying patch from stdin
1 new unstable changesets
--- a/tests/test-prune.t Thu Apr 27 20:52:09 2017 +0200
+++ b/tests/test-prune.t Wed May 03 12:31:40 2017 +0200
@@ -305,7 +305,7 @@
1 changesets pruned
$ hg id -ir dcbb326fdec2
abort: hidden revision 'dcbb326fdec2'!
- (use --hidden to access hidden revisions)
+ (use --hidden to access hidden revisions; pruned)
[255]
$ hg id -ir d62d843c9a01
d62d843c9a01
@@ -339,7 +339,7 @@
$ hg tag --remove --local c
$ hg id -ir 6:2702dd0c91e7
abort: hidden revision '6'!
- (use --hidden to access hidden revisions)
+ (use --hidden to access hidden revisions; pruned)
[255]
$ hg debugobsstorestat
--- a/tests/test-stabilize-result.t Thu Apr 27 20:52:09 2017 +0200
+++ b/tests/test-stabilize-result.t Wed May 03 12:31:40 2017 +0200
@@ -222,8 +222,8 @@
$ hg amend
$ hg up --hidden 15
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- working directory parent is obsolete!
- (use 'hg evolve' to update to its successor)
+ working directory parent is obsolete! (3932c176bbaa)
+ (use 'hg evolve' to update to its successor: d2f173e25686)
$ mv a a.old
$ echo 'jungle' > a
$ cat a.old >> a
@@ -335,8 +335,8 @@
$ hg up --hidden 15
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- working directory parent is obsolete!
- (use 'hg evolve' to update to its successor)
+ working directory parent is obsolete! (3932c176bbaa)
+ (use 'hg evolve' to update to its successor: f344982e63c4)
$ echo 'gotta break' >> a
$ hg amend
2 new divergent changesets
--- a/tests/test-touch.t Thu Apr 27 20:52:09 2017 +0200
+++ b/tests/test-touch.t Wed May 03 12:31:40 2017 +0200
@@ -33,8 +33,8 @@
$ hg commit -m ab --amend
$ hg up --hidden 1
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- working directory parent is obsolete!
- (use 'hg evolve' to update to its successor)
+ working directory parent is obsolete! (*) (glob)
+ (use 'hg evolve' to update to its successor: *) (glob)
$ hg log -G
o 3:[0-9a-f]{12} ab (re)
--- a/tests/test-tutorial.t Thu Apr 27 20:52:09 2017 +0200
+++ b/tests/test-tutorial.t Wed May 03 12:31:40 2017 +0200
@@ -741,15 +741,15 @@
pulling from $TESTTMP/local (glob)
searching for changes
no changes found
- working directory parent is obsolete!
- (use 'hg evolve' to update to its successor)
+ working directory parent is obsolete! (bf1b0d202029)
+ (use 'hg evolve' to update to its successor: ee942144f952)
now let's see where we are, and update to the successor
$ hg parents
bf1b0d202029 (draft): animals
- working directory parent is obsolete!
- (use 'hg evolve' to update to its successor)
+ working directory parent is obsolete! (bf1b0d202029)
+ (use 'hg evolve' to update to its successor: ee942144f952)
$ hg evolve
update:[8] animals
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-uncommit.t Thu Apr 27 20:52:09 2017 +0200
+++ b/tests/test-uncommit.t Wed May 03 12:31:40 2017 +0200
@@ -239,8 +239,8 @@
$ hg up -C 3 --hidden
8 files updated, 0 files merged, 1 files removed, 0 files unresolved
(leaving bookmark touncommit-bm)
- working directory parent is obsolete!
- (use 'hg evolve' to update to its successor)
+ working directory parent is obsolete! (5eb72dbe0cb4)
+ (use 'hg evolve' to update to its successor: e8db4aa611f6)
$ hg --config extensions.purge= purge
$ hg uncommit -I 'set:added() and e'
2 new divergent changesets
@@ -285,8 +285,8 @@
$ hg up -C 3 --hidden
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- working directory parent is obsolete!
- (use 'hg evolve' to update to its successor)
+ working directory parent is obsolete! (5eb72dbe0cb4)
+ (5eb72dbe0cb4 has diverged, use 'hg evolve -list --divergent' to resolve the issue)
$ hg --config extensions.purge= purge
$ hg uncommit --all -X e
1 new divergent changesets
--- a/tests/test-userguide.t Thu Apr 27 20:52:09 2017 +0200
+++ b/tests/test-userguide.t Wed May 03 12:31:40 2017 +0200
@@ -39,7 +39,7 @@
$ hg commit --amend -u alice -d '2 0' -m 'implement feature Y'
$ hg shortlog -q -r fe0ecd3bd2a4
abort: hidden revision 'fe0ecd3bd2a4'!
- (use --hidden to access hidden revisions)
+ (use --hidden to access hidden revisions; successor: 934359450037)
[255]
$ hg --hidden shortlog -G
@ 3:934359450037 draft implement feature Y