comparison mercurial/commands.py @ 38453:5cdfc20bfd5f

graft: introduce --abort flag to abort interrupted graft This patch introduces a new --abort flag to `hg graft` command which aborts an interrupted graft and rollbacks to the state before graft. The behavior when some of grafted changeset get's published while interrupted graft or we have new descendants on grafted changesets is same as that of rebase which is warn the user, don't strip and abort the abort the graft. Tests are added for the new flag. .. feature:: `hg graft` now has a `--abort` flag which aborts the interrupted graft and rollbacks to state before the graft. Differential Revision: https://phab.mercurial-scm.org/D3754
author Pulkit Goyal <7895pulkit@gmail.com>
date Fri, 25 May 2018 18:16:38 +0530
parents afb7e15392b9
children 854c2ccc800e
comparison
equal deleted inserted replaced
38452:afb7e15392b9 38453:5cdfc20bfd5f
47 patch, 47 patch,
48 phases, 48 phases,
49 pycompat, 49 pycompat,
50 rcutil, 50 rcutil,
51 registrar, 51 registrar,
52 repair,
52 revsetlang, 53 revsetlang,
53 rewriteutil, 54 rewriteutil,
54 scmutil, 55 scmutil,
55 server, 56 server,
56 state as statemod, 57 state as statemod,
2105 @command( 2106 @command(
2106 'graft', 2107 'graft',
2107 [('r', 'rev', [], _('revisions to graft'), _('REV')), 2108 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2108 ('c', 'continue', False, _('resume interrupted graft')), 2109 ('c', 'continue', False, _('resume interrupted graft')),
2109 ('', 'stop', False, _('stop interrupted graft')), 2110 ('', 'stop', False, _('stop interrupted graft')),
2111 ('', 'abort', False, _('abort interrupted graft')),
2110 ('e', 'edit', False, _('invoke editor on commit messages')), 2112 ('e', 'edit', False, _('invoke editor on commit messages')),
2111 ('', 'log', None, _('append graft info to log message')), 2113 ('', 'log', None, _('append graft info to log message')),
2112 ('f', 'force', False, _('force graft')), 2114 ('f', 'force', False, _('force graft')),
2113 ('D', 'currentdate', False, 2115 ('D', 'currentdate', False,
2114 _('record the current date as commit date')), 2116 _('record the current date as commit date')),
2202 2204
2203 if opts.get('stop'): 2205 if opts.get('stop'):
2204 if opts.get('continue'): 2206 if opts.get('continue'):
2205 raise error.Abort(_("cannot use '--continue' and " 2207 raise error.Abort(_("cannot use '--continue' and "
2206 "'--stop' together")) 2208 "'--stop' together"))
2209 if opts.get('abort'):
2210 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2211
2207 if any((opts.get('edit'), opts.get('log'), opts.get('user'), 2212 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2208 opts.get('date'), opts.get('currentdate'), 2213 opts.get('date'), opts.get('currentdate'),
2209 opts.get('currentuser'), opts.get('rev'))): 2214 opts.get('currentuser'), opts.get('rev'))):
2210 raise error.Abort(_("cannot specify any other flag with '--stop'")) 2215 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2211 return _stopgraft(ui, repo, graftstate) 2216 return _stopgraft(ui, repo, graftstate)
2217 elif opts.get('abort'):
2218 if opts.get('continue'):
2219 raise error.Abort(_("cannot use '--continue' and "
2220 "'--abort' together"))
2221 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2222 opts.get('date'), opts.get('currentdate'),
2223 opts.get('currentuser'), opts.get('rev'))):
2224 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2225
2226 return _abortgraft(ui, repo, graftstate)
2212 elif opts.get('continue'): 2227 elif opts.get('continue'):
2213 cont = True 2228 cont = True
2214 if revs: 2229 if revs:
2215 raise error.Abort(_("can't specify --continue and revisions")) 2230 raise error.Abort(_("can't specify --continue and revisions"))
2216 # read in unfinished revisions 2231 # read in unfinished revisions
2371 2386
2372 # remove state when we complete successfully 2387 # remove state when we complete successfully
2373 if not opts.get('dry_run'): 2388 if not opts.get('dry_run'):
2374 graftstate.delete() 2389 graftstate.delete()
2375 2390
2391 return 0
2392
2393 def _abortgraft(ui, repo, graftstate):
2394 """abort the interrupted graft and rollbacks to the state before interrupted
2395 graft"""
2396 if not graftstate.exists():
2397 raise error.Abort(_("no interrupted graft to abort"))
2398 statedata = _readgraftstate(repo, graftstate)
2399 newnodes = statedata.get('newnodes')
2400 if newnodes is None:
2401 # and old graft state which does not have all the data required to abort
2402 # the graft
2403 raise error.Abort(_("cannot abort using an old graftstate"))
2404
2405 # changeset from which graft operation was started
2406 startctx = None
2407 if len(newnodes) > 0:
2408 startctx = repo[newnodes[0]].p1()
2409 else:
2410 startctx = repo['.']
2411 # whether to strip or not
2412 cleanup = False
2413 if newnodes:
2414 newnodes = [repo[r].rev() for r in newnodes]
2415 cleanup = True
2416 # checking that none of the newnodes turned public or is public
2417 immutable = [c for c in newnodes if not repo[c].mutable()]
2418 if immutable:
2419 repo.ui.warn(_("cannot clean up public changesets %s\n")
2420 % ', '.join(bytes(repo[r]) for r in immutable),
2421 hint=_("see 'hg help phases' for details"))
2422 cleanup = False
2423
2424 # checking that no new nodes are created on top of grafted revs
2425 desc = set(repo.changelog.descendants(newnodes))
2426 if desc - set(newnodes):
2427 repo.ui.warn(_("new changesets detected on destination "
2428 "branch, can't strip\n"))
2429 cleanup = False
2430
2431 if cleanup:
2432 with repo.wlock(), repo.lock():
2433 hg.updaterepo(repo, startctx.node(), True)
2434 # stripping the new nodes created
2435 strippoints = [c.node() for c in repo.set("roots(%ld)",
2436 newnodes)]
2437 repair.strip(repo.ui, repo, strippoints, backup=False)
2438
2439 if not cleanup:
2440 # we don't update to the startnode if we can't strip
2441 startctx = repo['.']
2442 hg.updaterepo(repo, startctx.node(), True)
2443
2444 ui.status(_("graft aborted\n"))
2445 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2446 graftstate.delete()
2376 return 0 2447 return 0
2377 2448
2378 def _readgraftstate(repo, graftstate): 2449 def _readgraftstate(repo, graftstate):
2379 """read the graft state file and return a dict of the data stored in it""" 2450 """read the graft state file and return a dict of the data stored in it"""
2380 try: 2451 try: