--- 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"""