diff --git a/hgext/rebase.py b/hgext/rebase.py --- a/hgext/rebase.py +++ b/hgext/rebase.py @@ -1822,7 +1822,7 @@ def pullrebase(orig, ui, repo, *args, ** ui.debug('--update and --rebase are not compatible, ignoring ' 'the update flag\n') - cmdutil.checkunfinished(repo) + cmdutil.checkunfinished(repo, skipmerge=True) cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: ' 'please commit or shelve your changes first')) @@ -1950,6 +1950,6 @@ def uisetup(ui): entry[1].append(('t', 'tool', '', _("specify merge tool for rebase"))) cmdutil.summaryhooks.add('rebase', summaryhook) - statemod.addunfinished('rebase', fname='rebasestate') + statemod.addunfinished('rebase', fname='rebasestate', stopflag=True) cmdutil.afterresolvedstates.append( ['rebasestate', _('hg rebase --continue')]) diff --git a/hgext/strip.py b/hgext/strip.py --- a/hgext/strip.py +++ b/hgext/strip.py @@ -32,10 +32,12 @@ command = registrar.command(cmdtable) testedwith = 'ships-with-hg-core' def checklocalchanges(repo, force=False): - cmdutil.checkunfinished(repo) s = repo.status() if not force: + cmdutil.checkunfinished(repo) cmdutil.bailifchanged(repo) + else: + cmdutil.checkunfinished(repo, skipmerge=True) return s def _findupdatetarget(repo, nodes): diff --git a/hgext/transplant.py b/hgext/transplant.py --- a/hgext/transplant.py +++ b/hgext/transplant.py @@ -760,6 +760,8 @@ def kwtransplanted(context, mapping): def extsetup(ui): statemod.addunfinished ( 'transplant', fname='transplant/journal', clearable=True, + statushint=_('To continue: hg transplant --continue\n' + 'To abort: hg update'), cmdhint=_("use 'hg transplant --continue' or 'hg update' to abort") ) diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -623,15 +623,14 @@ def morestatus(repo, fm): statetuple = statemod.getrepostate(repo) label = 'status.morestatus' if statetuple: - state, statedetectionpredicate, helpfulmsg = statetuple + state, helpfulmsg = statetuple statemsg = _('The repository is in an unfinished *%s* state.') % state fm.plain('%s\n' % _commentlines(statemsg), label=label) conmsg = _conflictsmsg(repo) if conmsg: fm.plain('%s\n' % conmsg, label=label) if helpfulmsg: - helpmsg = helpfulmsg() - fm.plain('%s\n' % helpmsg, label=label) + fm.plain('%s\n' % _commentlines(helpfulmsg), label=label) def findpossible(cmd, table, strict=False): """ @@ -3260,7 +3259,7 @@ summaryhooks = util.hooks() summaryremotehooks = util.hooks() -def checkunfinished(repo, commit=False): +def checkunfinished(repo, commit=False, skipmerge=False): '''Look for an unfinished multistep operation, like graft, and abort if found. It's probably good to check this right before bailifchanged(). @@ -3268,13 +3267,15 @@ def checkunfinished(repo, commit=False): # Check for non-clearable states first, so things like rebase will take # precedence over update. for state in statemod._unfinishedstates: - if state._clearable or (commit and state._allowcommit): + if (state._clearable or (commit and state._allowcommit) or + state._reportonly): continue if state.isunfinished(repo): raise error.Abort(state.msg(), hint=state.hint()) for s in statemod._unfinishedstates: - if not s._clearable or (commit and s._allowcommit): + if (not s._clearable or (commit and s._allowcommit) or + (s._opname == 'merge' and skipmerge) or s._reportonly): continue if s.isunfinished(repo): raise error.Abort(s.msg(), hint=s.hint()) @@ -3284,10 +3285,14 @@ def clearunfinished(repo): that are clearable. ''' for state in statemod._unfinishedstates: + if state._reportonly: + continue if not state._clearable and state.isunfinished(repo): raise error.Abort(state.msg(), hint=state.hint()) for s in statemod._unfinishedstates: + if s._opname == 'merge' or state._reportonly: + continue if s._clearable and s.isunfinished(repo): util.unlink(repo.vfs.join(s._fname)) diff --git a/mercurial/state.py b/mercurial/state.py --- a/mercurial/state.py +++ b/mercurial/state.py @@ -98,7 +98,8 @@ class _statecheck(object): """ def __init__(self, opname, fname, clearable=False, allowcommit=False, - cmdmsg="", cmdhint=""): + reportonly=False, stopflag=False, cmdmsg="", cmdhint="", + statushint=""): """opname is the name the command or operation fname is the file name in which data should be stored in .hg directory. It is None for merge command. @@ -107,21 +108,47 @@ class _statecheck(object): state file. allowcommit boolean decides whether commit is allowed during interrupted state or not. + reportonly flag is used for operations like bisect where we just + need to detect the operation using 'hg status --verbose' + stopflag is a boolean that determines whether or not a command supports + --stop flag cmdmsg is used to pass a different status message in case standard message of the format "abort: cmdname in progress" is not desired. cmdhint is used to pass a different hint message in case standard - message of the format use 'hg cmdname --continue' or - 'hg cmdname --abort'" is not desired. + message of the format "To continue: hg cmdname --continue + To abort: hg cmdname --abort" is not desired. + statushint is used to pass a different status message in case standard + message of the format ('To continue: hg cmdname --continue' + 'To abort: hg cmdname --abort') is not desired """ self._opname = opname self._fname = fname self._clearable = clearable self._allowcommit = allowcommit self._cmdhint = cmdhint + self._statushint = statushint self._cmdmsg = cmdmsg + self._stopflag = stopflag + self._reportonly = reportonly + + def statusmsg(self): + """returns the hint message corresponding to the command for + hg status --verbose + """ + if not self._statushint: + hint = (_('To continue: hg %s --continue\n' + 'To abort: hg %s --abort') % (self._opname, + self._opname)) + if self._stopflag: + hint = hint + (_('\nTo stop: hg %s --stop') % + (self._opname)) + return hint + return self._statushint def hint(self): - """returns the hint message corresponding to the command""" + """returns the hint message corresponding to an interrupted + operation + """ if not self._cmdhint: return (_("use 'hg %s --continue' or 'hg %s --abort'") % (self._opname, self._opname)) @@ -134,8 +161,13 @@ class _statecheck(object): return self._cmdmsg def isunfinished(self, repo): - """determines whether a multi-step operation is in progress or not""" - return repo.vfs.exists(self._fname) + """determines whether a multi-step operation is in progress + or not + """ + if self._opname == 'merge': + return len(repo[None].parents()) > 1 + else: + return repo.vfs.exists(self._fname) # A list of statecheck objects for multistep operations like graft. _unfinishedstates = [] @@ -144,75 +176,40 @@ def addunfinished(opname, **kwargs): """this registers a new command or operation to unfinishedstates """ statecheckobj = _statecheck(opname, **kwargs) - _unfinishedstates.append(statecheckobj) + if opname == 'merge': + _unfinishedstates.append(statecheckobj) + else: + _unfinishedstates.insert(0, statecheckobj) addunfinished( - 'graft', fname='graftstate', clearable=True, - cmdhint=_("use 'hg graft --continue' or 'hg graft --stop' to stop") + 'graft', fname='graftstate', clearable=True, stopflag=True, + cmdhint=_("use 'hg graft --continue' or 'hg graft --stop' to stop"), ) addunfinished( 'update', fname='updatestate', clearable=True, cmdmsg=_('last update was interrupted'), - cmdhint=_("use 'hg update' to get a consistent checkout") + cmdhint=_("use 'hg update' to get a consistent checkout"), + statushint=_("To continue: hg update") ) - -def _commentlines(raw): - '''Surround lineswith a comment char and a new line''' - lines = raw.splitlines() - commentedlines = ['# %s' % line for line in lines] - return '\n'.join(commentedlines) + '\n' - -def _helpmessage(continuecmd, abortcmd): - msg = _('To continue: %s\n' - 'To abort: %s') % (continuecmd, abortcmd) - return _commentlines(msg) - -def _rebasemsg(): - return _helpmessage('hg rebase --continue', 'hg rebase --abort') - -def _histeditmsg(): - return _helpmessage('hg histedit --continue', 'hg histedit --abort') - -def _unshelvemsg(): - return _helpmessage('hg unshelve --continue', 'hg unshelve --abort') - -def _graftmsg(): - return _helpmessage('hg graft --continue', 'hg graft --abort') - -def _mergemsg(): - return _helpmessage('hg commit', 'hg merge --abort') - -def _bisectmsg(): - msg = _('To mark the changeset good: hg bisect --good\n' - 'To mark the changeset bad: hg bisect --bad\n' - 'To abort: hg bisect --reset\n') - return _commentlines(msg) - -def fileexistspredicate(filename): - return lambda repo: repo.vfs.exists(filename) - -def _mergepredicate(repo): - return len(repo[None].parents()) > 1 - -STATES = ( - # (state, predicate to detect states, helpful message function) - ('histedit', fileexistspredicate('histedit-state'), _histeditmsg), - ('bisect', fileexistspredicate('bisect.state'), _bisectmsg), - ('graft', fileexistspredicate('graftstate'), _graftmsg), - ('unshelve', fileexistspredicate('shelvedstate'), _unshelvemsg), - ('rebase', fileexistspredicate('rebasestate'), _rebasemsg), - # The merge state is part of a list that will be iterated over. - # They need to be last because some of the other unfinished states may also - # be in a merge or update state (eg. rebase, histedit, graft, etc). - # We want those to have priority. - ('merge', _mergepredicate, _mergemsg), +addunfinished( + 'bisect', fname='bisect.state', allowcommit=True, reportonly=True, + statushint=_('To mark the changeset good: hg bisect --good\n' + 'To mark the changeset bad: hg bisect --bad\n' + 'To abort: hg bisect --reset\n') +) +addunfinished( + 'merge', fname=None, clearable=True, allowcommit=True, + cmdmsg=_('outstanding uncommitted merge'), + statushint=_('To continue: hg commit\n' + 'To abort: hg merge --abort'), + cmdhint=_("use 'hg commit' or 'hg merge --abort'") ) def getrepostate(repo): # experimental config: commands.status.skipstates skip = set(repo.ui.configlist('commands', 'status.skipstates')) - for state, statedetectionpredicate, msgfn in STATES: - if state in skip: + for state in _unfinishedstates: + if state._opname in skip: continue - if statedetectionpredicate(repo): - return (state, statedetectionpredicate, msgfn) + if state.isunfinished(repo): + return (state._opname, state.statusmsg()) diff --git a/tests/test-graft.t b/tests/test-graft.t --- a/tests/test-graft.t +++ b/tests/test-graft.t @@ -281,6 +281,7 @@ Using status to get more context # To continue: hg graft --continue # To abort: hg graft --abort + # To stop: hg graft --stop Commit while interrupted should fail: diff --git a/tests/test-merge1.t b/tests/test-merge1.t --- a/tests/test-merge1.t +++ b/tests/test-merge1.t @@ -44,6 +44,13 @@ of the files in a commit we're updating commit: 1 unknown (interrupted update) update: 1 new changesets (update) phases: 2 draft +Detect interrupted update by hg status --verbose + $ hg status -v + ? b/nonempty + # The repository is in an unfinished *update* state. + + # To continue: hg update + $ rm b/nonempty diff --git a/tests/test-mq-qnew.t b/tests/test-mq-qnew.t --- a/tests/test-mq-qnew.t +++ b/tests/test-mq-qnew.t @@ -164,7 +164,8 @@ plain headers 0 files updated, 0 files merged, 0 files removed, 1 files unresolved use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon (no more unresolved files) - abort: cannot manage merge changesets + abort: outstanding uncommitted merge + (use 'hg commit' or 'hg merge --abort') $ rm -r sandbox hg headers @@ -243,7 +244,8 @@ hg headers 0 files updated, 0 files merged, 0 files removed, 1 files unresolved use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon (no more unresolved files) - abort: cannot manage merge changesets + abort: outstanding uncommitted merge + (use 'hg commit' or 'hg merge --abort') $ rm -r sandbox Test saving last-message.txt diff --git a/tests/test-rebase-conflicts.t b/tests/test-rebase-conflicts.t --- a/tests/test-rebase-conflicts.t +++ b/tests/test-rebase-conflicts.t @@ -82,6 +82,7 @@ Conflicting rebase: # To continue: hg rebase --continue # To abort: hg rebase --abort + # To stop: hg rebase --stop Try to continue without solving the conflict: diff --git a/tests/test-shelve.t b/tests/test-shelve.t --- a/tests/test-shelve.t +++ b/tests/test-shelve.t @@ -1153,7 +1153,8 @@ Abort unshelve while merging (issue5123) -- trying to pull in the shelve bits -- unshelve should abort otherwise, it'll eat my second parent. $ hg unshelve - abort: cannot unshelve while merging + abort: outstanding uncommitted merge + (use 'hg commit' or 'hg merge --abort') [255] $ cd .. diff --git a/tests/test-strip.t b/tests/test-strip.t --- a/tests/test-strip.t +++ b/tests/test-strip.t @@ -275,6 +275,7 @@ before strip of merge parent ##strip not allowed with merge in progress $ hg strip 4 abort: outstanding uncommitted merge + (use 'hg commit' or 'hg merge --abort') [255] ##strip allowed --force with merge in progress $ hg strip 4 --force diff --git a/tests/test-transplant.t b/tests/test-transplant.t --- a/tests/test-transplant.t +++ b/tests/test-transplant.t @@ -40,6 +40,7 @@ (branch merge, don't forget to commit) $ hg transplant 1 abort: outstanding uncommitted merge + (use 'hg commit' or 'hg merge --abort') [255] $ hg up -qC tip $ echo b0 > b1 @@ -461,7 +462,7 @@ transplant -c shouldn't use an old chang baz foo -test multiple revisions and --continue +test multiple revisions, --continue and hg status --verbose $ hg up -qC 0 $ echo bazbaz > baz @@ -481,6 +482,15 @@ test multiple revisions and --continue abort: transplant in progress (use 'hg transplant --continue' or 'hg update' to abort) [255] + $ hg status -v + A bar + ? baz.rej + ? foo.rej + # The repository is in an unfinished *transplant* state. + + # To continue: hg transplant --continue + # To abort: hg update + $ echo fixed > baz $ hg transplant --continue 9d6d6b5a8275 transplanted as d80c49962290