# HG changeset patch # User Pierre-Yves David # Date 1454548754 0 # Node ID 7876ed4fceb729b32fbf3599fb0fcff21679fa96 # Parent 6079dcbfb7267ce154d2a9a9e1e1c25cbf25969f evolve: write our own custom evolvestate file Since for ever, we were using 'graftstate' to record the node currently being evolve and allow 'hg evolve --continue' we now move to our on 'evolvestate' file. This remove and issue with 'hg summary' listing interrupted evolve as graft. This also open the way for storing more data into that file and allow proper --abort and --continue of the whole evolve operation (and not just the last one). The whole thing is very hacky but at least there is some progress. Thanks goes to Shusen Liu for initiating this work. diff -r 6079dcbfb726 -r 7876ed4fceb7 README --- a/README Thu Feb 04 10:16:52 2016 +0000 +++ b/README Thu Feb 04 01:19:14 2016 +0000 @@ -64,6 +64,8 @@ - evolve: compatibility with Mercurial 3.7 - evolve: support merge with a single obsolete parent. - evolve: prevent added file to be marked as unknown if evolve fails (issue4966) +- evolve: stop relying on graftstate file for save evolve state + (for `hg evolve --continue`) 5.2.2 -- diff -r 6079dcbfb726 -r 7876ed4fceb7 hgext/evolve.py --- a/hgext/evolve.py Thu Feb 04 10:16:52 2016 +0000 +++ b/hgext/evolve.py Thu Feb 04 01:19:14 2016 +0000 @@ -67,6 +67,7 @@ import collections import socket import errno +import struct sha1re = re.compile(r'\b[0-9a-f]{6,40}\b') import mercurial @@ -116,6 +117,7 @@ command = cmdutil.command(cmdtable) _pack = struct.pack +_unpack = struct.unpack if gboptsmap is not None: memfilectx = context.memfilectx @@ -1651,8 +1653,19 @@ raise error.Abort('cannot specify both "--any" and "--continue"') if allopt: raise error.Abort('cannot specify both "--all" and "--continue"') - graftcmd = commands.table['graft'][0] - return graftcmd(ui, repo, old_obsolete=True, **{'continue': True}) + state = _evolvestateread(repo) + if state is None: + raise error.Abort('no evolve to continue') + orig = repo[state['current']] + # XXX This is a terrible terrible hack, please get rid of it. + repo.opener.write('graftstate', orig.hex() + '\n') + try: + graftcmd = commands.table['graft'][0] + ret = graftcmd(ui, repo, old_obsolete=True, **{'continue': True}) + _evolvestatedelete(repo) + return ret + finally: + util.unlinkpath(repo.join('graftstate'), ignoremissing=True) cmdutil.bailifchanged(repo) @@ -1796,7 +1809,7 @@ try: relocate(repo, orig, target, pctx, keepbranch) except MergeFailure: - repo.opener.write('graftstate', orig.hex() + '\n') + _evolvestatewrite(repo, {'current': orig.node()}) repo.ui.write_err(_('evolve failed!\n')) repo.ui.write_err( _('fix conflict and run "hg evolve --continue"' @@ -3736,6 +3749,84 @@ if oldbookmarks or destbookmarks: repo._bookmarks.recordchange(tr) +evolvestateversion = 0 + +@eh.uisetup +def setupevolveunfinished(ui): + data = ('evolvestate', True, False, _('evolve in progress'), + _("use 'hg evolve --continue' or 'hg update' to abort")) + cmdutil.unfinishedstates.append(data) + +@eh.wrapfunction(hg, 'clean') +def clean(orig, repo, *args, **kwargs): + ret = orig(repo, *args, **kwargs) + util.unlinkpath(repo.join('evolvestate'), ignoremissing=True) + return ret + +def _evolvestatewrite(repo, state): + # [version] + # [type][length][content] + # + # `version` is a 4 bytes integer (handled at higher level) + # `type` is a single character, `length` is a 4 byte integer, and + # `content` is an arbitrary byte sequence of length `length`. + f = repo.vfs('evolvestate', 'w') + try: + f.write(_pack('>I', evolvestateversion)) + current = state['current'] + key = 'C' # as in 'current' + format = '>sI%is' % len(current) + f.write(_pack(format, key, len(current), current)) + finally: + f.close() + +def _evolvestateread(repo): + try: + f = repo.vfs('evolvestate') + except IOError, err: + if err.errno != errno.ENOENT: + raise + return None + try: + versionblob = f.read(4) + if len(versionblob) < 4: + repo.ui.debug('ignoring corrupted evolvestte (file contains %i bits)' + % len(versionblob)) + return None + version = _unpack('>I', versionblob)[0] + if version != evolvestateversion: + raise error.Abort(_('unknown evolvestate version %i') + % version, hint=_('upgrade your evolve')) + records = [] + data = f.read() + off = 0 + end = len(data) + while off < end: + rtype = data[off] + off += 1 + length = _unpack('>I', data[off:(off + 4)])[0] + off += 4 + record = data[off:(off + length)] + off += length + if rtype == 't': + rtype, record = record[0], record[1:] + records.append((rtype, record)) + state = {} + for rtype, rdata in records: + if rtype == 'C': + state['current'] = rdata + elif rtype.lower(): + repo.ui.debug('ignore evolve state record type %s' % rtype) + else: + raise error.Abort(_('unknown evolvestate field type %r') + % rtype, hint=_('upgrade your evolve')) + return state + finally: + f.close() + +def _evolvestatedelete(repo): + util.unlinkpath(repo.join('evolvestate'), ignoremissing=True) + def _evolvemerge(repo, orig, dest, pctx, keepbranch): """Used by the evolve function to merge dest on top of pctx. return the same tuple as merge.graft"""