diff --git a/hgext3rd/fbhistedit.py b/hgext3rd/fbhistedit.py --- a/hgext3rd/fbhistedit.py +++ b/hgext3rd/fbhistedit.py @@ -17,6 +17,8 @@ from mercurial import extensions from mercurial import hg from mercurial import lock +from mercurial import merge as mergemod +from mercurial import mergeutil from mercurial import node from mercurial import pycompat from mercurial import registrar @@ -123,7 +125,7 @@ def continuedirty(self): raise error.Abort(_('working copy has pending changes'), hint=_('amend, commit, or revert them and run histedit ' - '--continue, or abort with histedit --abort')) + '--continue/--retry, or abort with histedit --abort')) def continueclean(self): parentctxnode = self.state.parentctxnode @@ -142,7 +144,14 @@ return stop, execute, executerelative def extsetup(ui): - stop, execute, executerel = defineactions() + try: + extensions.find('histedit') + except KeyError: + ui.warn(_('fbhistedit: Please enable histedit extension as well\n')) + raise + + defineactions() + _extend_histedit(ui) if ui.config('experimental', 'histeditng'): rebase = extensions.find('rebase') @@ -160,6 +169,102 @@ rebase.cmdtable['rebase'] = tuple(newentry) +def _extend_histedit(ui): + histedit = extensions.find('histedit') + + _aliases, entry = cmdutil.findcmd('histedit', histedit.cmdtable) + options = entry[1] + options.append(('x', 'retry', False, + _('retry exec command that failed and try to continue'))) + options.append(('', 'show-plan', False, _('show remaining actions list'))) + + extensions.wrapfunction(histedit, '_histedit', _histedit) + +goalretry = 'retry' +goalshowplan = 'show-plan' +goalorig = 'orig' + +def _getgoal(opts): + if opts.get('retry'): + return goalretry + if opts.get('show_plan'): + return goalshowplan + return goalorig + +def _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs): + # TODO only abort if we try to histedit mq patches, not just + # blanket if mq patches are applied somewhere + mq = getattr(repo, 'mq', None) + if mq and mq.applied: + raise error.Abort(_('source has mq patches applied')) + + # basic argument incompatibility processing + outg = opts.get('outgoing') + editplan = opts.get('edit_plan') + abort = opts.get('abort') + + if goal == goalretry: + if any((outg, abort, revs, freeargs, rules, editplan)): + raise error.Abort(_('no arguments allowed with --retry')) + elif goal == goalshowplan: + if any((outg, abort, revs, freeargs, rules, editplan)): + raise error.Abort(_('no arguments allowed with --show-plan')) + elif goal == goalorig: + # We explicitly left the validation of arguments to orig + pass + +def _histedit(orig, ui, repo, state, *freeargs, **opts): + histedit = extensions.find('histedit') + + goal = _getgoal(opts) + revs = opts.get('rev', []) + rules = opts.get('commands', '') + state.keep = opts.get('keep', False) + + _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs) + + if goal == goalretry: + state.read() + state = bootstrapretry(ui, state, opts) + + histedit._continuehistedit(ui, repo, state) + histedit._finishhistedit(ui, repo, state) + elif goal == goalshowplan: + state.read() + showplan(ui, state) + else: + return orig(ui, repo, state, *freeargs, **opts) + +def bootstrapretry(ui, state, opts): + repo = state.repo + + ms = mergemod.mergestate.read(repo) + mergeutil.checkunresolved(ms) + + if not state.actions or state.actions[0].verb != 'exec': + msg = _("no exec in progress") + hint = _('If you want to continue a non-exec histedit command' + ' use "histedit --continue" instead.') + raise error.Abort(msg, hint=hint) + + if repo[None].dirty(missing=True): + raise error.Abort(_('working copy has pending changes'), + hint=_('amend, commit, or revert them and run histedit ' + '--retry, or abort with histedit --abort')) + + return state + +def showplan(ui, state): + if not state.actions: + msg = _("no histedit actions in progress") + hint = _('Did you meant to run histedit without "--show-plan"?') + raise error.Abort(msg, hint=hint) + + ui.write(_('histedit plan (call "histedit --continue/--retry" to resume it' + ' or "histedit --abort" to abort it):\n')) + for action in state.actions: + ui.write(' %s\n' % action.torule()) + def _rebase(orig, ui, repo, **opts): histedit = extensions.find('histedit') diff --git a/tests/test-fbhistedit-exec.t b/tests/test-fbhistedit-exec.t --- a/tests/test-fbhistedit-exec.t +++ b/tests/test-fbhistedit-exec.t @@ -118,25 +118,46 @@ > pick 055a42cdd887 d > pick e860deea161a e > exec exit 1 - > exec exit 1 + > exec exit 2 > pick 652413bf663e f - > exec exit 1 + > exec exit 3 > EOF 0 files updated, 0 files merged, 1 files removed, 0 files unresolved Command 'exit 1' failed with exit status 1 +retry should work + + $ hg histedit --retry + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + Command 'exit 1' failed with exit status 1 + [1] + continue should work $ hg histedit --continue 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - Command 'exit 1' failed with exit status 1 + Command 'exit 2' failed with exit status 2 + [1] + +retry after consecutive failed execs + + $ hg histedit --retry + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + Command 'exit 2' failed with exit status 2 [1] continue after consecutive failed execs $ hg histedit --continue 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - Command 'exit 1' failed with exit status 1 + Command 'exit 3' failed with exit status 3 + [1] + +retry after the last entry + + $ hg histedit --retry + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + Command 'exit 3' failed with exit status 3 [1] continue after the last entry @@ -156,7 +177,72 @@ | o cb9a9f314b8b a +retry should try to execute the command again and continue if succeeded + $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle + > pick 177f92b77385 c + > pick 055a42cdd887 d + > pick e860deea161a e + > exec exit 1 + > pick 652413bf663e f + > EOF + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + Command 'exit 1' failed with exit status 1 + + $ hg histedit --edit-plan --commands - 2>&1 << EOF| fixbundle + > exec echo "Called" + > exec exit 2 + > edit 652413bf663e f + > EOF + [1] + + $ hg histedit --retry + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + Called + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + Command 'exit 2' failed with exit status 2 + [1] + +retry should fail when working copy has pending changes + + $ echo "g" >> g + $ hg add g + $ hg histedit --retry + abort: working copy has pending changes + (amend, commit, or revert them and run histedit --retry, or abort with histedit --abort) + [255] + + $ hg revert -ar . + forgetting g + +retry should fail when used on non-exec histedit command + + $ hg histedit --continue + adding f + Editing (652413bf663e), you may commit or record as needed now. + (hg histedit --continue to resume) + [1] + + $ hg histedit --retry + abort: no exec in progress + (If you want to continue a non-exec histedit command use "histedit --continue" instead.) + [255] + $ hg histedit --abort + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ hg log --template '{node|short} {desc}' --graph + @ 652413bf663e f + | + o e860deea161a e + | + o 055a42cdd887 d + | + o 177f92b77385 c + | + o d2ae7f538514 b + | + o cb9a9f314b8b a + abort should work $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle diff --git a/tests/test-fbhistedit-show-plan.t b/tests/test-fbhistedit-show-plan.t new file mode 100644 --- /dev/null +++ b/tests/test-fbhistedit-show-plan.t @@ -0,0 +1,124 @@ + $ . "$TESTDIR/histedit-helpers.sh" + + $ cat >> $HGRCPATH < [extensions] + > histedit= + > fbhistedit=$TESTDIR/../hgext3rd/fbhistedit.py + > EOF + + $ initrepo () + > { + > hg init r + > cd r + > for x in a b c d e f ; do + > echo $x > $x + > hg add $x + > hg ci -m $x + > done + > } + + $ initrepo + +log before edit + + $ hg log --graph + @ changeset: 5:652413bf663e + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:e860deea161a + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +a failing command should drop us into the shell + + $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle + > pick 177f92b77385 c + > pick 055a42cdd887 d + > pick e860deea161a e + > exec exit 1 + > exec exit 2 + > pick 652413bf663e f + > exec exit 3 + > EOF + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + Command 'exit 1' failed with exit status 1 + +show-plan should work + + $ hg histedit --show-plan + histedit plan (call "histedit --continue/--retry" to resume it or "histedit --abort" to abort it): + exec exit 1 + exec exit 2 + pick 652413bf663e 5 f + exec exit 3 + +continue should work + + $ hg histedit --continue + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + Command 'exit 2' failed with exit status 2 + [1] + +show-plan after consecutive failed execs + + $ hg histedit --show-plan + histedit plan (call "histedit --continue/--retry" to resume it or "histedit --abort" to abort it): + exec exit 2 + pick 652413bf663e 5 f + exec exit 3 + +continue after consecutive failed execs + + $ hg histedit --continue + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + Command 'exit 3' failed with exit status 3 + [1] + +show-plan after the last entry + + $ hg histedit --show-plan + histedit plan (call "histedit --continue/--retry" to resume it or "histedit --abort" to abort it): + exec exit 3 + +continue after the last entry + + $ hg histedit --continue + + $ hg log --template '{node|short} {desc}' --graph + @ 652413bf663e f + | + o e860deea161a e + | + o 055a42cdd887 d + | + o 177f92b77385 c + | + o d2ae7f538514 b + | + o cb9a9f314b8b a +