Mercurial > evolve
changeset 1597:7876ed4fceb7
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.
author | Pierre-Yves David <pierre-yves.david@fb.com> |
---|---|
date | Thu, 04 Feb 2016 01:19:14 +0000 |
parents | 6079dcbfb726 |
children | 2a08ef812b84 |
files | README hgext/evolve.py |
diffstat | 2 files changed, 96 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- 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 --
--- 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"""