This moves cmdutil.unfinishedstates, checkunfinished(),clearunfinished()
to state.py. the already existing users of this module are updated accordingly.
Test results remain unchanged.
( )
durin42 | |
martinvonz |
hg-reviewers |
This moves cmdutil.unfinishedstates, checkunfinished(),clearunfinished()
to state.py. the already existing users of this module are updated accordingly.
Test results remain unchanged.
Lint Skipped |
Unit Tests Skipped |
Thanks for working on this!
I think this patch does too much at once. At least the help message change should be moved to a separate patch. Ideally, the other changes would also be split up. Perhaps something like this:
You don't have to follow those steps exactly, of course. You know the code better, so maybe some of what I suggested doesn't make sense.
hgext/rebase.py | ||
---|---|---|
1951 | Pass at least the boolean arguments as named arguments (clearable=False etc) |
@martinvonz I have completed steps 1 to 5 as you stated in the sequence above in statecheck.py. For the purpose of showing that it works without flaw, I redirected the API calls to the new API for both STATES and unfinished state and removed those from cmdutil.py. All that remains is a minor bug that I am facing with hg bisect. I will clear that soon enough. Do you want me to send the API integration and tests as a separate patch and API as another?
In D6484#94131, @taapas1128 wrote:@martinvonz I have completed steps 1 to 5 as you stated in the sequence above in statecheck.py. For the purpose of showing that it works without flaw, I redirected the API calls to the new API for both STATES and unfinished state and removed those from cmdutil.py. All that remains is a minor bug that I am facing with hg bisect. I will clear that soon enough. Do you want me to send the API integration and tests as a separate patch and API as another?
I think @martinvonz wants to say that we should split the whole work into nice small commits. This will help us to review it better and understand the whole change much better. The 1 to 5 are like, one commit should do one of those things.
I also agree with him, what do you think?
@martinvonz have a look at this stack now . It is strictly according to your guidelines.
Thanks for splitting this out! Sorry about being so particular about splitting up the patch. I think it's an important part of writing patches that others will review. Hopefully it wasn't just frustrating and you can also see some benefit with it. The benefit is mostly to reviewers and future code archeologists and not to the author, so it can feel like wasting time. Remember that more people will read the patches than the people writing them (which is just you).
@martinvonz At first I didn't seem to realize why the patches need to be split that way but later I realized that I wasn't looking from the perspective of a reviewer but was just getting the job done as an author. Thanks for making me work this out :)
hgext/absorb.py | ||
---|---|---|
1012 | I wonder if we should keep the functions in cmdutil.py and let them delegate to the new ones. That way we won't have to update all the callers. I think cmdutil sounds like a more natural place for the callers to looks for this function. That's also where bailifchanged() is, so it it makes sense that these two similar functions are in the same place. | |
hgext/strip.py | ||
51–52 | Delete (looks like a bad rebase) |
Path | Packages | |||
---|---|---|---|---|
M | hgext/absorb.py (3 lines) | |||
M | hgext/fix.py (3 lines) | |||
M | hgext/histedit.py (10 lines) | |||
M | hgext/phabricator.py (3 lines) | |||
M | hgext/rebase.py (13 lines) | |||
M | hgext/record.py (3 lines) | |||
M | hgext/shelve.py (11 lines) | |||
M | hgext/strip.py (3 lines) | |||
M | hgext/transplant.py (9 lines) | |||
M | mercurial/cmdutil.py (103 lines) | |||
M | mercurial/commands.py (15 lines) | |||
A | M | mercurial/statecheck.py (131 lines) | ||
M | tests/test-absorb-unfinished.t (3 lines) | |||
M | tests/test-bisect.t (6 lines) | |||
M | tests/test-conflict.t (5 lines) | |||
M | tests/test-fix.t (3 lines) | |||
M | tests/test-graft.t (10 lines) | |||
M | tests/test-histedit-arguments.t (3 lines) | |||
M | tests/test-histedit-edit.t (9 lines) | |||
M | tests/test-histedit-fold.t (6 lines) | |||
M | tests/test-histedit-no-change.t (3 lines) | |||
M | tests/test-qrecord.t (5 lines) | |||
M | tests/test-rebase-abort.t (15 lines) | |||
M | tests/test-rebase-conflicts.t (5 lines) | |||
M | tests/test-rebase-obsolete.t (3 lines) | |||
M | tests/test-rebase-pull.t (3 lines) | |||
M | tests/test-shelve.t (15 lines) | |||
M | tests/test-transplant.t (3 lines) |
Commit | Parents | Author | Summary | Date |
---|---|---|---|---|
e8a8ce182e99 | a0c5e06e9b1a | Taapas Agrawal | May 31 2019, 8:12 AM |
Status | Author | Revision | |
---|---|---|---|
Closed | taapas1128 | ||
Closed | taapas1128 | ||
Closed | taapas1128 | ||
Closed | taapas1128 | ||
Closed | taapas1128 | ||
Closed | taapas1128 | ||
Closed | taapas1128 | ||
Closed | taapas1128 |
copies, | copies, | ||||
error, | error, | ||||
mdiff, | mdiff, | ||||
merge, | merge, | ||||
obsolete, | obsolete, | ||||
pycompat, | pycompat, | ||||
registrar, | registrar, | ||||
scmutil, | scmutil, | ||||
statecheck as statecheckmod, | |||||
util, | util, | ||||
worker, | worker, | ||||
) | ) | ||||
# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for | # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for | ||||
# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should | # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should | ||||
# be specifying the version(s) of Mercurial they are tested with, or | # be specifying the version(s) of Mercurial they are tested with, or | ||||
# leave the attribute unspecified. | # leave the attribute unspecified. | ||||
return workqueue, numitems | return workqueue, numitems | ||||
def getrevstofix(ui, repo, opts): | def getrevstofix(ui, repo, opts): | ||||
"""Returns the set of revision numbers that should be fixed""" | """Returns the set of revision numbers that should be fixed""" | ||||
revs = set(scmutil.revrange(repo, opts['rev'])) | revs = set(scmutil.revrange(repo, opts['rev'])) | ||||
for rev in revs: | for rev in revs: | ||||
checkfixablectx(ui, repo, repo[rev]) | checkfixablectx(ui, repo, repo[rev]) | ||||
if revs: | if revs: | ||||
cmdutil.checkunfinished(repo) | statecheckmod.checkunfinished(repo) | ||||
checknodescendants(repo, revs) | checknodescendants(repo, revs) | ||||
if opts.get('working_dir'): | if opts.get('working_dir'): | ||||
revs.add(wdirrev) | revs.add(wdirrev) | ||||
if list(merge.mergestate.read(repo).unresolved()): | if list(merge.mergestate.read(repo).unresolved()): | ||||
raise error.Abort('unresolved conflicts', hint="use 'hg resolve'") | raise error.Abort('unresolved conflicts', hint="use 'hg resolve'") | ||||
if not revs: | if not revs: | ||||
raise error.Abort( | raise error.Abort( | ||||
'no changesets specified', hint='use --rev or --working-dir') | 'no changesets specified', hint='use --rev or --working-dir') |
mergeutil, | mergeutil, | ||||
node, | node, | ||||
obsolete, | obsolete, | ||||
pycompat, | pycompat, | ||||
registrar, | registrar, | ||||
repair, | repair, | ||||
scmutil, | scmutil, | ||||
state as statemod, | state as statemod, | ||||
statecheck as statecheckmod, | |||||
util, | util, | ||||
) | ) | ||||
from mercurial.utils import ( | from mercurial.utils import ( | ||||
dateutil, | dateutil, | ||||
stringutil, | stringutil, | ||||
) | ) | ||||
pickle = util.pickle | pickle = util.pickle | ||||
raise error.Abort(_("Python curses library required")) | raise error.Abort(_("Python curses library required")) | ||||
# disable color | # disable color | ||||
ui._colormode = None | ui._colormode = None | ||||
try: | try: | ||||
keep = opts.get('keep') | keep = opts.get('keep') | ||||
revs = opts.get('rev', [])[:] | revs = opts.get('rev', [])[:] | ||||
cmdutil.checkunfinished(repo) | statecheckmod.checkunfinished(repo) | ||||
cmdutil.bailifchanged(repo) | cmdutil.bailifchanged(repo) | ||||
if os.path.exists(os.path.join(repo.path, 'histedit-state')): | if os.path.exists(os.path.join(repo.path, 'histedit-state')): | ||||
raise error.Abort(_('history edit already in progress, try ' | raise error.Abort(_('history edit already in progress, try ' | ||||
'--continue or --abort')) | '--continue or --abort')) | ||||
revs.extend(freeargs) | revs.extend(freeargs) | ||||
if not revs: | if not revs: | ||||
defaultrev = destutil.desthistedit(ui, repo) | defaultrev = destutil.desthistedit(ui, repo) | ||||
state.actions = actions | state.actions = actions | ||||
state.write() | state.write() | ||||
def _newhistedit(ui, repo, state, revs, freeargs, opts): | def _newhistedit(ui, repo, state, revs, freeargs, opts): | ||||
outg = opts.get('outgoing') | outg = opts.get('outgoing') | ||||
rules = opts.get('commands', '') | rules = opts.get('commands', '') | ||||
force = opts.get('force') | force = opts.get('force') | ||||
cmdutil.checkunfinished(repo) | statecheckmod.checkunfinished(repo) | ||||
cmdutil.bailifchanged(repo) | cmdutil.bailifchanged(repo) | ||||
topmost = repo.dirstate.p1() | topmost = repo.dirstate.p1() | ||||
if outg: | if outg: | ||||
if freeargs: | if freeargs: | ||||
remote = freeargs[0] | remote = freeargs[0] | ||||
else: | else: | ||||
remote = None | remote = None | ||||
if state.actions: | if state.actions: | ||||
# i18n: column positioning for "hg summary" | # i18n: column positioning for "hg summary" | ||||
ui.write(_('hist: %s (histedit --continue)\n') % | ui.write(_('hist: %s (histedit --continue)\n') % | ||||
(ui.label(_('%d remaining'), 'histedit.remaining') % | (ui.label(_('%d remaining'), 'histedit.remaining') % | ||||
len(state.actions))) | len(state.actions))) | ||||
def extsetup(ui): | def extsetup(ui): | ||||
cmdutil.summaryhooks.add('histedit', summaryhook) | cmdutil.summaryhooks.add('histedit', summaryhook) | ||||
cmdutil.unfinishedstates.append( | statecheckmod.unfinishedstates.append(statecheckmod.statecheck('histedit', | ||||
['histedit-state', False, True, _('histedit in progress'), | 'histedit-state', False, True, False)) | ||||
_("use 'hg histedit --continue' or 'hg histedit --abort'")]) | |||||
cmdutil.afterresolvedstates.append( | cmdutil.afterresolvedstates.append( | ||||
['histedit-state', _('hg histedit --continue')]) | ['histedit-state', _('hg histedit --continue')]) |
obsutil, | obsutil, | ||||
parser, | parser, | ||||
patch, | patch, | ||||
phases, | phases, | ||||
pycompat, | pycompat, | ||||
registrar, | registrar, | ||||
scmutil, | scmutil, | ||||
smartset, | smartset, | ||||
statecheck as statecheckmod, | |||||
tags, | tags, | ||||
templatefilters, | templatefilters, | ||||
templateutil, | templateutil, | ||||
url as urlmod, | url as urlmod, | ||||
util, | util, | ||||
) | ) | ||||
from mercurial.utils import ( | from mercurial.utils import ( | ||||
procutil, | procutil, | ||||
""" | """ | ||||
opts = pycompat.byteskwargs(opts) | opts = pycompat.byteskwargs(opts) | ||||
revs = list(revs) + opts.get(b'rev', []) | revs = list(revs) + opts.get(b'rev', []) | ||||
revs = scmutil.revrange(repo, revs) | revs = scmutil.revrange(repo, revs) | ||||
if not revs: | if not revs: | ||||
raise error.Abort(_(b'phabsend requires at least one changeset')) | raise error.Abort(_(b'phabsend requires at least one changeset')) | ||||
if opts.get(b'amend'): | if opts.get(b'amend'): | ||||
cmdutil.checkunfinished(repo) | statecheckmod.checkunfinished(repo) | ||||
# {newnode: (oldnode, olddiff, olddrev} | # {newnode: (oldnode, olddiff, olddrev} | ||||
oldmap = getoldnodedrevmap(repo, [repo[r].node() for r in revs]) | oldmap = getoldnodedrevmap(repo, [repo[r].node() for r in revs]) | ||||
confirm = ui.configbool(b'phabsend', b'confirm') | confirm = ui.configbool(b'phabsend', b'confirm') | ||||
confirm |= bool(opts.get(b'confirm')) | confirm |= bool(opts.get(b'confirm')) | ||||
if confirm: | if confirm: | ||||
confirmed = _confirmbeforesend(repo, revs, oldmap) | confirmed = _confirmbeforesend(repo, revs, oldmap) |
from mercurial.i18n import _ | from mercurial.i18n import _ | ||||
from mercurial import ( | from mercurial import ( | ||||
cmdutil, | cmdutil, | ||||
commands, | commands, | ||||
error, | error, | ||||
extensions, | extensions, | ||||
registrar, | registrar, | ||||
statecheck as statecheckmod | |||||
) | ) | ||||
cmdtable = {} | cmdtable = {} | ||||
command = registrar.command(cmdtable) | command = registrar.command(cmdtable) | ||||
# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for | # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for | ||||
# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should | # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should | ||||
# be specifying the version(s) of Mercurial they are tested with, or | # be specifying the version(s) of Mercurial they are tested with, or | ||||
# leave the attribute unspecified. | # leave the attribute unspecified. | ||||
repo.mq.checkpatchname(patch) | repo.mq.checkpatchname(patch) | ||||
def committomq(ui, repo, *pats, **opts): | def committomq(ui, repo, *pats, **opts): | ||||
opts[r'checkname'] = False | opts[r'checkname'] = False | ||||
mq.new(ui, repo, patch, *pats, **opts) | mq.new(ui, repo, patch, *pats, **opts) | ||||
overrides = {('experimental', 'crecord'): False} | overrides = {('experimental', 'crecord'): False} | ||||
with ui.configoverride(overrides, 'record'): | with ui.configoverride(overrides, 'record'): | ||||
cmdutil.checkunfinished(repo) | statecheckmod.checkunfinished(repo) | ||||
cmdutil.dorecord(ui, repo, committomq, cmdsuggest, False, | cmdutil.dorecord(ui, repo, committomq, cmdsuggest, False, | ||||
cmdutil.recordfilter, *pats, **opts) | cmdutil.recordfilter, *pats, **opts) | ||||
def qnew(origfn, ui, repo, patch, *args, **opts): | def qnew(origfn, ui, repo, patch, *args, **opts): | ||||
if opts[r'interactive']: | if opts[r'interactive']: | ||||
return _qrecord(None, ui, repo, patch, *args, **opts) | return _qrecord(None, ui, repo, patch, *args, **opts) | ||||
return origfn(ui, repo, patch, *args, **opts) | return origfn(ui, repo, patch, *args, **opts) | ||||
merge, | merge, | ||||
node as nodemod, | node as nodemod, | ||||
patch, | patch, | ||||
phases, | phases, | ||||
pycompat, | pycompat, | ||||
registrar, | registrar, | ||||
repair, | repair, | ||||
scmutil, | scmutil, | ||||
statecheck as statecheckmod, | |||||
templatefilters, | templatefilters, | ||||
util, | util, | ||||
vfs as vfsmod, | vfs as vfsmod, | ||||
) | ) | ||||
from . import ( | from . import ( | ||||
rebase, | rebase, | ||||
) | ) | ||||
if phases.supportinternal(repo): | if phases.supportinternal(repo): | ||||
tr.close() | tr.close() | ||||
else: | else: | ||||
_aborttransaction(repo, tr) | _aborttransaction(repo, tr) | ||||
def createcmd(ui, repo, pats, opts): | def createcmd(ui, repo, pats, opts): | ||||
"""subcommand that creates a new shelve""" | """subcommand that creates a new shelve""" | ||||
with repo.wlock(): | with repo.wlock(): | ||||
cmdutil.checkunfinished(repo) | statecheckmod.checkunfinished(repo) | ||||
return _docreatecmd(ui, repo, pats, opts) | return _docreatecmd(ui, repo, pats, opts) | ||||
def _docreatecmd(ui, repo, pats, opts): | def _docreatecmd(ui, repo, pats, opts): | ||||
wctx = repo[None] | wctx = repo[None] | ||||
parents = wctx.parents() | parents = wctx.parents() | ||||
if len(parents) > 1: | if len(parents) > 1: | ||||
raise error.Abort(_('cannot shelve while merging')) | raise error.Abort(_('cannot shelve while merging')) | ||||
parent = parents[0] | parent = parents[0] | ||||
with repo.wlock(): | with repo.wlock(): | ||||
return _dounshelve(ui, repo, *shelved, **opts) | return _dounshelve(ui, repo, *shelved, **opts) | ||||
def _dounshelve(ui, repo, *shelved, **opts): | def _dounshelve(ui, repo, *shelved, **opts): | ||||
opts = pycompat.byteskwargs(opts) | opts = pycompat.byteskwargs(opts) | ||||
abortf = opts.get('abort') | abortf = opts.get('abort') | ||||
continuef = opts.get('continue') | continuef = opts.get('continue') | ||||
if not abortf and not continuef: | if not abortf and not continuef: | ||||
cmdutil.checkunfinished(repo) | statecheckmod.checkunfinished(repo) | ||||
shelved = list(shelved) | shelved = list(shelved) | ||||
if opts.get("name"): | if opts.get("name"): | ||||
shelved.append(opts["name"]) | shelved.append(opts["name"]) | ||||
if abortf or continuef: | if abortf or continuef: | ||||
if abortf and continuef: | if abortf and continuef: | ||||
raise error.Abort(_('cannot use both abort and continue')) | raise error.Abort(_('cannot use both abort and continue')) | ||||
if shelved: | if shelved: | ||||
elif checkopt('list'): | elif checkopt('list'): | ||||
return listcmd(ui, repo, pats, opts) | return listcmd(ui, repo, pats, opts) | ||||
elif checkopt('patch') or checkopt('stat'): | elif checkopt('patch') or checkopt('stat'): | ||||
return patchcmds(ui, repo, pats, opts) | return patchcmds(ui, repo, pats, opts) | ||||
else: | else: | ||||
return createcmd(ui, repo, pats, opts) | return createcmd(ui, repo, pats, opts) | ||||
def extsetup(ui): | def extsetup(ui): | ||||
cmdutil.unfinishedstates.append( | statecheckmod.unfinishedstates.append(statecheckmod.statecheck('unshelve', | ||||
[shelvedstate._filename, False, False, | shelvedstate._filename, False, False, False)) | ||||
_('unshelve already in progress'), | |||||
_("use 'hg unshelve --continue' or 'hg unshelve --abort'")]) | |||||
cmdutil.afterresolvedstates.append( | cmdutil.afterresolvedstates.append( | ||||
[shelvedstate._filename, _('hg unshelve --continue')]) | [shelvedstate._filename, _('hg unshelve --continue')]) |
node as nodemod, | node as nodemod, | ||||
patch, | patch, | ||||
pycompat, | pycompat, | ||||
registrar, | registrar, | ||||
revlog, | revlog, | ||||
revset, | revset, | ||||
scmutil, | scmutil, | ||||
smartset, | smartset, | ||||
statecheck as statecheckmod, | |||||
util, | util, | ||||
vfs as vfsmod, | vfs as vfsmod, | ||||
) | ) | ||||
from mercurial.utils import ( | from mercurial.utils import ( | ||||
procutil, | procutil, | ||||
stringutil, | stringutil, | ||||
) | ) | ||||
p1 = repo.dirstate.p1() | p1 = repo.dirstate.p1() | ||||
if len(repo) > 0 and p1 == revlog.nullid: | if len(repo) > 0 and p1 == revlog.nullid: | ||||
raise error.Abort(_('no revision checked out')) | raise error.Abort(_('no revision checked out')) | ||||
if opts.get('continue'): | if opts.get('continue'): | ||||
if not tp.canresume(): | if not tp.canresume(): | ||||
raise error.Abort(_('no transplant to continue')) | raise error.Abort(_('no transplant to continue')) | ||||
else: | else: | ||||
cmdutil.checkunfinished(repo) | statecheckmod.checkunfinished(repo) | ||||
cmdutil.bailifchanged(repo) | cmdutil.bailifchanged(repo) | ||||
sourcerepo = opts.get('source') | sourcerepo = opts.get('source') | ||||
if sourcerepo: | if sourcerepo: | ||||
peer = hg.peer(repo, opts, ui.expandpath(sourcerepo)) | peer = hg.peer(repo, opts, ui.expandpath(sourcerepo)) | ||||
heads = pycompat.maplist(peer.lookup, opts.get('branch', ())) | heads = pycompat.maplist(peer.lookup, opts.get('branch', ())) | ||||
target = set(heads) | target = set(heads) | ||||
for r in revs: | for r in revs: | ||||
def kwtransplanted(context, mapping): | def kwtransplanted(context, mapping): | ||||
"""String. The node identifier of the transplanted | """String. The node identifier of the transplanted | ||||
changeset if any.""" | changeset if any.""" | ||||
ctx = context.resource(mapping, 'ctx') | ctx = context.resource(mapping, 'ctx') | ||||
n = ctx.extra().get('transplant_source') | n = ctx.extra().get('transplant_source') | ||||
return n and nodemod.hex(n) or '' | return n and nodemod.hex(n) or '' | ||||
def extsetup(ui): | def extsetup(ui): | ||||
cmdutil.unfinishedstates.append( | statecheckmod.unfinishedstates.append(statecheckmod.statecheck('transplant', | ||||
['transplant/journal', True, False, _('transplant in progress'), | 'transplant/journal', True, False, False)) | ||||
_("use 'hg transplant --continue' or 'hg update' to abort")]) | |||||
# tell hggettext to extract docstrings from these functions: | # tell hggettext to extract docstrings from these functions: | ||||
i18nfunctions = [revsettransplanted, kwtransplanted] | i18nfunctions = [revsettransplanted, kwtransplanted] |
patch, | patch, | ||||
pathutil, | pathutil, | ||||
phases, | phases, | ||||
pycompat, | pycompat, | ||||
revlog, | revlog, | ||||
rewriteutil, | rewriteutil, | ||||
scmutil, | scmutil, | ||||
smartset, | smartset, | ||||
statecheck as statecheckmod, | |||||
subrepoutil, | subrepoutil, | ||||
templatekw, | templatekw, | ||||
templater, | templater, | ||||
util, | util, | ||||
vfs as vfsmod, | vfs as vfsmod, | ||||
) | ) | ||||
from .utils import ( | from .utils import ( | ||||
After the actual job is done by non-interactive command, the | After the actual job is done by non-interactive command, the | ||||
working directory is restored to its original state. | working directory is restored to its original state. | ||||
In the end we'll record interesting changes, and everything else | In the end we'll record interesting changes, and everything else | ||||
will be left in place, so the user can continue working. | will be left in place, so the user can continue working. | ||||
""" | """ | ||||
checkunfinished(repo, commit=True) | statecheckmod.checkunfinished(repo, commit=True) | ||||
wctx = repo[None] | wctx = repo[None] | ||||
merge = len(wctx.parents()) > 1 | merge = len(wctx.parents()) > 1 | ||||
if merge: | if merge: | ||||
raise error.Abort(_('cannot partially commit a merge ' | raise error.Abort(_('cannot partially commit a merge ' | ||||
'(use "hg commit" instead)')) | '(use "hg commit" instead)')) | ||||
status = repo.status(match=match) | status = repo.status(match=match) | ||||
%s | %s | ||||
To mark files as resolved: hg resolve --mark FILE''') % mergeliststr | To mark files as resolved: hg resolve --mark FILE''') % mergeliststr | ||||
else: | else: | ||||
msg = _('No unresolved merge conflicts.') | msg = _('No unresolved merge conflicts.') | ||||
return _commentlines(msg) | return _commentlines(msg) | ||||
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), | |||||
) | |||||
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: | |||||
continue | |||||
if statedetectionpredicate(repo): | |||||
return (state, statedetectionpredicate, msgfn) | |||||
def morestatus(repo, fm): | def morestatus(repo, fm): | ||||
statetuple = _getrepostate(repo) | statetuple = statecheckmod._getrepostate(repo) | ||||
label = 'status.morestatus' | label = 'status.morestatus' | ||||
if statetuple: | if statetuple: | ||||
state, statedetectionpredicate, helpfulmsg = statetuple | state, statedetectionpredicate, helpfulmsg = statetuple | ||||
statemsg = _('The repository is in an unfinished *%s* state.') % state | statemsg = _('The repository is in an unfinished *%s* state.') % state | ||||
fm.plain('%s\n' % _commentlines(statemsg), label=label) | fm.plain('%s\n' % _commentlines(statemsg), label=label) | ||||
conmsg = _conflictsmsg(repo) | conmsg = _conflictsmsg(repo) | ||||
if conmsg: | if conmsg: | ||||
fm.plain('%s\n' % conmsg, label=label) | fm.plain('%s\n' % conmsg, label=label) | ||||
if helpfulmsg: | if helpfulmsg: | ||||
helpmsg = helpfulmsg() | fm.plain('%s\n' % helpfulmsg, label=label) | ||||
fm.plain('%s\n' % helpmsg, label=label) | |||||
def findpossible(cmd, table, strict=False): | def findpossible(cmd, table, strict=False): | ||||
""" | """ | ||||
Return cmd -> (aliases, command table entry) | Return cmd -> (aliases, command table entry) | ||||
for each matching command. | for each matching command. | ||||
Return debug commands (or their aliases) only if no normal command matches. | Return debug commands (or their aliases) only if no normal command matches. | ||||
""" | """ | ||||
choice = {} | choice = {} | ||||
# functions should return tuple of booleans below, if 'changes' is None: | # functions should return tuple of booleans below, if 'changes' is None: | ||||
# (whether-incomings-are-needed, whether-outgoings-are-needed) | # (whether-incomings-are-needed, whether-outgoings-are-needed) | ||||
# | # | ||||
# otherwise, 'changes' is a tuple of tuples below: | # otherwise, 'changes' is a tuple of tuples below: | ||||
# - (sourceurl, sourcebranch, sourcepeer, incoming) | # - (sourceurl, sourcebranch, sourcepeer, incoming) | ||||
# - (desturl, destbranch, destpeer, outgoing) | # - (desturl, destbranch, destpeer, outgoing) | ||||
summaryremotehooks = util.hooks() | summaryremotehooks = util.hooks() | ||||
# A list of state files kept by multistep operations like graft. | |||||
# Since graft cannot be aborted, it is considered 'clearable' by update. | |||||
# note: bisect is intentionally excluded | |||||
# (state file, clearable, allowcommit, error, hint) | |||||
unfinishedstates = [ | |||||
('graftstate', True, False, _('graft in progress'), | |||||
_("use 'hg graft --continue' or 'hg graft --stop' to stop")), | |||||
('updatestate', True, False, _('last update was interrupted'), | |||||
_("use 'hg update' to get a consistent checkout")) | |||||
] | |||||
def checkunfinished(repo, commit=False): | |||||
'''Look for an unfinished multistep operation, like graft, and abort | |||||
if found. It's probably good to check this right before | |||||
bailifchanged(). | |||||
''' | |||||
# Check for non-clearable states first, so things like rebase will take | |||||
# precedence over update. | |||||
for f, clearable, allowcommit, msg, hint in unfinishedstates: | |||||
if clearable or (commit and allowcommit): | |||||
continue | |||||
if repo.vfs.exists(f): | |||||
raise error.Abort(msg, hint=hint) | |||||
for f, clearable, allowcommit, msg, hint in unfinishedstates: | |||||
if not clearable or (commit and allowcommit): | |||||
continue | |||||
if repo.vfs.exists(f): | |||||
raise error.Abort(msg, hint=hint) | |||||
def clearunfinished(repo): | |||||
'''Check for unfinished operations (as above), and clear the ones | |||||
that are clearable. | |||||
''' | |||||
for f, clearable, allowcommit, msg, hint in unfinishedstates: | |||||
if not clearable and repo.vfs.exists(f): | |||||
raise error.Abort(msg, hint=hint) | |||||
for f, clearable, allowcommit, msg, hint in unfinishedstates: | |||||
if clearable and repo.vfs.exists(f): | |||||
util.unlink(repo.vfs.join(f)) | |||||
afterresolvedstates = [ | afterresolvedstates = [ | ||||
('graftstate', | ('graftstate', | ||||
_('hg graft --continue')), | _('hg graft --continue')), | ||||
] | ] | ||||
def howtocontinue(repo): | def howtocontinue(repo): | ||||
'''Check for an unfinished operation and return the command to finish | '''Check for an unfinished operation and return the command to finish |
rcutil, | rcutil, | ||||
registrar, | registrar, | ||||
repair, | repair, | ||||
revsetlang, | revsetlang, | ||||
rewriteutil, | rewriteutil, | ||||
scmutil, | scmutil, | ||||
server, | server, | ||||
state as statemod, | state as statemod, | ||||
statecheck as statecheckmod, | |||||
streamclone, | streamclone, | ||||
tags as tagsmod, | tags as tagsmod, | ||||
ui as uimod, | ui as uimod, | ||||
util, | util, | ||||
verify as verifymod, | verify as verifymod, | ||||
wireprotoserver, | wireprotoserver, | ||||
) | ) | ||||
from .utils import ( | from .utils import ( | ||||
if not rev: | if not rev: | ||||
raise error.Abort(_("please specify a revision to backout")) | raise error.Abort(_("please specify a revision to backout")) | ||||
date = opts.get('date') | date = opts.get('date') | ||||
if date: | if date: | ||||
opts['date'] = dateutil.parsedate(date) | opts['date'] = dateutil.parsedate(date) | ||||
cmdutil.checkunfinished(repo) | statecheckmod.checkunfinished(repo) | ||||
cmdutil.bailifchanged(repo) | cmdutil.bailifchanged(repo) | ||||
node = scmutil.revsingle(repo, rev).node() | node = scmutil.revsingle(repo, rev).node() | ||||
op1, op2 = repo.dirstate.parents() | op1, op2 = repo.dirstate.parents() | ||||
if not repo.changelog.isancestor(node, op1): | if not repo.changelog.isancestor(node, op1): | ||||
raise error.Abort(_('cannot backout change that is not an ancestor')) | raise error.Abort(_('cannot backout change that is not an ancestor')) | ||||
p1, p2 = repo.changelog.parents(node) | p1, p2 = repo.changelog.parents(node) | ||||
hbisect.save_state(repo, state) | hbisect.save_state(repo, state) | ||||
if not (state['good'] and state['bad']): | if not (state['good'] and state['bad']): | ||||
return | return | ||||
def mayupdate(repo, node, show_stats=True): | def mayupdate(repo, node, show_stats=True): | ||||
"""common used update sequence""" | """common used update sequence""" | ||||
if noupdate: | if noupdate: | ||||
return | return | ||||
cmdutil.checkunfinished(repo) | statecheckmod.checkunfinished(repo) | ||||
cmdutil.bailifchanged(repo) | cmdutil.bailifchanged(repo) | ||||
return hg.clean(repo, node, show_stats=show_stats) | return hg.clean(repo, node, show_stats=show_stats) | ||||
displayer = logcmdutil.changesetdisplayer(ui, repo, {}) | displayer = logcmdutil.changesetdisplayer(ui, repo, {}) | ||||
if command: | if command: | ||||
changesets = 1 | changesets = 1 | ||||
if noupdate: | if noupdate: | ||||
opts = pycompat.byteskwargs(opts) | opts = pycompat.byteskwargs(opts) | ||||
if opts.get('subrepos'): | if opts.get('subrepos'): | ||||
if opts.get('amend'): | if opts.get('amend'): | ||||
raise error.Abort(_('cannot amend with --subrepos')) | raise error.Abort(_('cannot amend with --subrepos')) | ||||
# Let --subrepos on the command line override config setting. | # Let --subrepos on the command line override config setting. | ||||
ui.setconfig('ui', 'commitsubrepos', True, 'commit') | ui.setconfig('ui', 'commitsubrepos', True, 'commit') | ||||
cmdutil.checkunfinished(repo, commit=True) | statecheckmod.checkunfinished(repo, commit=True) | ||||
branch = repo[None].branch() | branch = repo[None].branch() | ||||
bheads = repo.branchheads(branch) | bheads = repo.branchheads(branch) | ||||
extra = {} | extra = {} | ||||
if opts.get('close_branch'): | if opts.get('close_branch'): | ||||
extra['close'] = '1' | extra['close'] = '1' | ||||
# Currently histedit gets confused if an amend happens while histedit | # Currently histedit gets confused if an amend happens while histedit | ||||
# is in progress. Since we have a checkunfinished command, we are | # is in progress. Since we have a checkunfinished command, we are | ||||
# temporarily honoring it. | # temporarily honoring it. | ||||
# | # | ||||
# Note: eventually this guard will be removed. Please do not expect | # Note: eventually this guard will be removed. Please do not expect | ||||
# this behavior to remain. | # this behavior to remain. | ||||
if not obsolete.isenabled(repo, obsolete.createmarkersopt): | if not obsolete.isenabled(repo, obsolete.createmarkersopt): | ||||
cmdutil.checkunfinished(repo) | statecheckmod.checkunfinished(repo) | ||||
node = cmdutil.amend(ui, repo, old, extra, pats, opts) | node = cmdutil.amend(ui, repo, old, extra, pats, opts) | ||||
if node == old.node(): | if node == old.node(): | ||||
ui.status(_("nothing changed\n")) | ui.status(_("nothing changed\n")) | ||||
return 1 | return 1 | ||||
else: | else: | ||||
def commitfunc(ui, repo, message, match, opts): | def commitfunc(ui, repo, message, match, opts): | ||||
overrides = {} | overrides = {} | ||||
opts['no_commit'] = statedata.get('no_commit') | opts['no_commit'] = statedata.get('no_commit') | ||||
nodes = statedata['nodes'] | nodes = statedata['nodes'] | ||||
revs = [repo[node].rev() for node in nodes] | revs = [repo[node].rev() for node in nodes] | ||||
else: | else: | ||||
cmdutil.wrongtooltocontinue(repo, _('graft')) | cmdutil.wrongtooltocontinue(repo, _('graft')) | ||||
else: | else: | ||||
if not revs: | if not revs: | ||||
raise error.Abort(_('no revisions specified')) | raise error.Abort(_('no revisions specified')) | ||||
cmdutil.checkunfinished(repo) | statecheckmod.checkunfinished(repo) | ||||
cmdutil.bailifchanged(repo) | cmdutil.bailifchanged(repo) | ||||
revs = scmutil.revrange(repo, revs) | revs = scmutil.revrange(repo, revs) | ||||
skipped = set() | skipped = set() | ||||
if basectx is None: | if basectx is None: | ||||
# check for merges | # check for merges | ||||
for rev in repo.revs('%ld and merge()', revs): | for rev in repo.revs('%ld and merge()', revs): | ||||
ui.warn(_('skipping ungraftable merge revision %d\n') % rev) | ui.warn(_('skipping ungraftable merge revision %d\n') % rev) | ||||
raise error.Abort(_('cannot use --exact with --prefix')) | raise error.Abort(_('cannot use --exact with --prefix')) | ||||
base = opts["base"] | base = opts["base"] | ||||
msgs = [] | msgs = [] | ||||
ret = 0 | ret = 0 | ||||
with repo.wlock(): | with repo.wlock(): | ||||
if update: | if update: | ||||
cmdutil.checkunfinished(repo) | statecheckmod.checkunfinished(repo) | ||||
if (exact or not opts.get('force')): | if (exact or not opts.get('force')): | ||||
cmdutil.bailifchanged(repo) | cmdutil.bailifchanged(repo) | ||||
if not opts.get('no_commit'): | if not opts.get('no_commit'): | ||||
lock = repo.lock | lock = repo.lock | ||||
tr = lambda: repo.transaction('import') | tr = lambda: repo.transaction('import') | ||||
dsguard = util.nullcontextmanager | dsguard = util.nullcontextmanager | ||||
else: | else: | ||||
updatecheck = None | updatecheck = None | ||||
if check: | if check: | ||||
updatecheck = 'abort' | updatecheck = 'abort' | ||||
elif merge: | elif merge: | ||||
updatecheck = 'none' | updatecheck = 'none' | ||||
with repo.wlock(): | with repo.wlock(): | ||||
cmdutil.clearunfinished(repo) | statecheckmod.clearunfinished(repo) | ||||
if date: | if date: | ||||
rev = cmdutil.finddate(ui, repo, date) | rev = cmdutil.finddate(ui, repo, date) | ||||
# if we defined a bookmark, we have to remember the original name | # if we defined a bookmark, we have to remember the original name | ||||
brev = rev | brev = rev | ||||
if rev: | if rev: | ||||
repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn') | repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn') |
from __future__ import absolute_import | |||||
from .i18n import _ | |||||
from . import ( | |||||
error, | |||||
util, | |||||
) | |||||
class statecheck(object): | |||||
"""a utility class that will to deal with multistep operations | |||||
like graft, histedit, bisect, update etc and check whether such commands | |||||
are in an unfinished conditition of not and return appropriate message | |||||
and hint. | |||||
It also has the ability to register and determine the states of any new | |||||
multistep operation or multistep command extension. | |||||
""" | |||||
def __init__(self, cmdname, fname, clearable, allowcommit, stopflag): | |||||
"""cmdname is the name the command | |||||
fname is the file name in which data should be stored in .hg directory. | |||||
It is None for merge command. | |||||
clearable boolean determines whether or not interrupted states can be | |||||
cleared by running `hg update -C .` | |||||
allowcommit boolean decides whether commit is allowed during interrupted | |||||
state or not. | |||||
stopflag is a boolean that determines whether or not a command supports | |||||
--stop flag | |||||
""" | |||||
self._cmdname = cmdname | |||||
self._fname = fname | |||||
self._clearable = clearable | |||||
self._allowcommit = allowcommit | |||||
self._stopflag = stopflag | |||||
def _hintmessage(self): | |||||
"""returns the hint message corresponding to the command""" | |||||
if self._cmdname == 'bisect': | |||||
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') | |||||
elif self._cmdname == 'update': | |||||
msg = _("use 'hg update' to get a consistent checkout") | |||||
else: | |||||
msg = (_('To continue: hg %s --continue\n' | |||||
'To abort: hg %s --abort') % (self._cmdname, | |||||
self._cmdname)) | |||||
if self._stopflag: | |||||
msg = msg + (_('\nTo stop: hg %s --stop') % | |||||
(self._cmdname)) | |||||
return msg | |||||
def _statusmessage(self): | |||||
"""returns the status message corresponding to the command""" | |||||
if self._cmdname == 'update': | |||||
hint = _('last update was interrupted') | |||||
else: | |||||
hint = _('%s in progress') % (self._cmdname) | |||||
return hint | |||||
def _mergepredicate(self, repo): | |||||
"""determines is a merge is in progress or not""" | |||||
return len(repo[None].parents()) > 1 | |||||
unfinishedstates=[] | |||||
unfinishedstates.append(statecheck('graft', 'graftstate', True, False, True)) | |||||
unfinishedstates.append(statecheck('update', 'updatestate', True, False, False)) | |||||
unfinishedstates.append(statecheck('merge', 'None', True, False, False)) | |||||
def checkunfinished(repo, commit=False): | |||||
'''Look for an unfinished multistep operation, like graft, and abort | |||||
if found. It's probably good to check this right before | |||||
bailifchanged(). | |||||
''' | |||||
# Check for non-clearable states first, so things like rebase will take | |||||
# precedence over update. | |||||
for state in unfinishedstates: | |||||
if (state._clearable or (commit and state._allowcommit) | |||||
or state._cmdname=='merge'): | |||||
continue | |||||
if repo.vfs.exists(state._fname): | |||||
raise error.Abort(state._statusmessage(), hint=state._hintmessage()) | |||||
for s in unfinishedstates: | |||||
if (not s._clearable or (commit and s._allowcommit) | |||||
or s._cmdname=='merge'): | |||||
continue | |||||
if repo.vfs.exists(s._fname): | |||||
raise error.Abort(s._statusmessage(), hint=s._hintmessage()) | |||||
def _getrepostate(repo): | |||||
# experimental config: commands.status.skipstates | |||||
skip = set(repo.ui.configlist('commands', 'status.skipstates')) | |||||
for state in unfinishedstates: | |||||
if state._cmdname in skip or state._clearable: | |||||
continue | |||||
if state._cmdname == 'merge' and state._mergepredicate(repo): | |||||
return (state._cmdname, state._mergepredicate(repo), | |||||
state._hintmessage()) | |||||
elif repo.vfs.exists(state._fname): | |||||
return (state._cmdname, repo.vfs.exists(state._fname), | |||||
state._hintmessage()) | |||||
for state in unfinishedstates: | |||||
if state._cmdname in skip or not state._clearable: | |||||
continue | |||||
if state._cmdname == 'merge' and state._mergepredicate(repo): | |||||
return (state._cmdname, state._mergepredicate(repo), | |||||
state._hintmessage()) | |||||
elif repo.vfs.exists(state._fname): | |||||
return (state._cmdname, repo.vfs.exists(state._fname), | |||||
state._hintmessage()) | |||||
def clearunfinished(repo): | |||||
'''Check for unfinished operations (as above), and clear the ones | |||||
that are clearable. | |||||
''' | |||||
for state in unfinishedstates: | |||||
if not state._clearable and repo.vfs.exists(state._fname): | |||||
raise error.Abort(state._statusmessage(), hint=state._hintmessage()) | |||||
for s in unfinishedstates: | |||||
if s._clearable and repo.vfs.exists(s._fname): | |||||
util.unlink(repo.vfs.join(s._fname)) |
rebasing 1:c3b6dc0e177a "foo 2" (tip) | rebasing 1:c3b6dc0e177a "foo 2" (tip) | ||||
merging foo.whole | merging foo.whole | ||||
warning: conflicts while merging foo.whole! (edit, then use 'hg resolve --mark') | warning: conflicts while merging foo.whole! (edit, then use 'hg resolve --mark') | ||||
unresolved conflicts (see hg resolve, then hg rebase --continue) | unresolved conflicts (see hg resolve, then hg rebase --continue) | ||||
[1] | [1] | ||||
$ hg --config extensions.rebase= absorb | $ hg --config extensions.rebase= absorb | ||||
abort: rebase in progress | abort: rebase in progress | ||||
(use 'hg rebase --continue' or 'hg rebase --abort') | (To continue: hg rebase --continue | ||||
To abort: hg rebase --abort) | |||||
[255] | [255] | ||||
$ hg up -C | $ hg up -C | ||||
0 files updated, 0 files merged, 0 files removed, 0 files unresolved | 0 files updated, 0 files merged, 0 files removed, 0 files unresolved | ||||
bisect test | bisect test | ||||
$ hg bisect -r | $ hg bisect -r | ||||
$ hg bisect -b | $ hg bisect -b | ||||
$ hg status -v | $ hg status -v | ||||
# The repository is in an unfinished *bisect* state. | |||||
# To mark the changeset good: hg bisect --good | |||||
# To mark the changeset bad: hg bisect --bad | |||||
# To abort: hg bisect --reset | |||||
$ hg status -v --config commands.status.skipstates=bisect | $ hg status -v --config commands.status.skipstates=bisect | ||||
$ hg summary | $ hg summary | ||||
parent: 31:58c80a7c8a40 tip | parent: 31:58c80a7c8a40 tip | ||||
msg 31 | msg 31 | ||||
branch: default | branch: default | ||||
commit: (clean) | commit: (clean) | ||||
update: (current) | update: (current) | ||||
phases: 32 draft | phases: 32 draft |
# The repository is in an unfinished *merge* state. | # The repository is in an unfinished *merge* state. | ||||
# Unresolved merge conflicts: | # Unresolved merge conflicts: | ||||
# | # | ||||
# a | # a | ||||
# | # | ||||
# To mark files as resolved: hg resolve --mark FILE | # To mark files as resolved: hg resolve --mark FILE | ||||
# To continue: hg commit | To continue: hg merge --continue | ||||
# To abort: hg merge --abort | To abort: hg merge --abort | ||||
$ hg status -Tjson | $ hg status -Tjson | ||||
[ | [ | ||||
{ | { | ||||
"path": "a", | "path": "a", | ||||
"status": "M" | "status": "M" | ||||
}, | }, | ||||
{ | { | ||||
"path": "a.orig", | "path": "a.orig", |
$ hg --config extensions.rebase= fix --working-dir | $ hg --config extensions.rebase= fix --working-dir | ||||
abort: unresolved conflicts | abort: unresolved conflicts | ||||
(use 'hg resolve') | (use 'hg resolve') | ||||
[255] | [255] | ||||
$ hg --config extensions.rebase= fix -r . | $ hg --config extensions.rebase= fix -r . | ||||
abort: rebase in progress | abort: rebase in progress | ||||
(use 'hg rebase --continue' or 'hg rebase --abort') | (To continue: hg rebase --continue | ||||
To abort: hg rebase --abort) | |||||
[255] | [255] | ||||
When fixing a file that was renamed, we should diff against the source of the | When fixing a file that was renamed, we should diff against the source of the | ||||
rename for incremental fixing and we should correctly reproduce the rename in | rename for incremental fixing and we should correctly reproduce the rename in | ||||
the replacement revision. | the replacement revision. | ||||
$ hg init fixrenamecommit | $ hg init fixrenamecommit | ||||
$ cd fixrenamecommit | $ cd fixrenamecommit |
# The repository is in an unfinished *graft* state. | # The repository is in an unfinished *graft* state. | ||||
# Unresolved merge conflicts: | # Unresolved merge conflicts: | ||||
# | # | ||||
# e | # e | ||||
# | # | ||||
# To mark files as resolved: hg resolve --mark FILE | # To mark files as resolved: hg resolve --mark FILE | ||||
# To continue: hg graft --continue | To continue: hg graft --continue | ||||
# To abort: hg graft --abort | To abort: hg graft --abort | ||||
To stop: hg graft --stop | |||||
Commit while interrupted should fail: | Commit while interrupted should fail: | ||||
$ hg ci -m 'commit interrupted graft' | $ hg ci -m 'commit interrupted graft' | ||||
abort: graft in progress | abort: graft in progress | ||||
(use 'hg graft --continue' or 'hg graft --stop' to stop) | (To continue: hg graft --continue | ||||
To abort: hg graft --abort | |||||
To stop: hg graft --stop) | |||||
[255] | [255] | ||||
Abort the graft and try committing: | Abort the graft and try committing: | ||||
$ hg up -C . | $ hg up -C . | ||||
2 files updated, 0 files merged, 0 files removed, 0 files unresolved | 2 files updated, 0 files merged, 0 files removed, 0 files unresolved | ||||
$ echo c >> e | $ echo c >> e | ||||
$ hg ci -mtest | $ hg ci -mtest |
Editing (6f2f0241f119), you may commit or record as needed now. | Editing (6f2f0241f119), you may commit or record as needed now. | ||||
(hg histedit --continue to resume) | (hg histedit --continue to resume) | ||||
[1] | [1] | ||||
$ hg resolve -m --all | $ hg resolve -m --all | ||||
(no more unresolved files) | (no more unresolved files) | ||||
continue: hg histedit --continue | continue: hg histedit --continue | ||||
$ hg commit --amend -m 'reject this fold' | $ hg commit --amend -m 'reject this fold' | ||||
abort: histedit in progress | abort: histedit in progress | ||||
(use 'hg histedit --continue' or 'hg histedit --abort') | (To continue: hg histedit --continue | ||||
To abort: hg histedit --abort) | |||||
[255] | [255] | ||||
With markers enabled, histedit does not get confused, and | With markers enabled, histedit does not get confused, and | ||||
amend should not be blocked by the ongoing histedit. | amend should not be blocked by the ongoing histedit. | ||||
$ cat >>$HGRCPATH <<EOF | $ cat >>$HGRCPATH <<EOF | ||||
> [experimental] | > [experimental] | ||||
> evolution.createmarkers=True | > evolution.createmarkers=True |
> EOF | > EOF | ||||
0 files updated, 0 files merged, 3 files removed, 0 files unresolved | 0 files updated, 0 files merged, 3 files removed, 0 files unresolved | ||||
Editing (e860deea161a), you may commit or record as needed now. | Editing (e860deea161a), you may commit or record as needed now. | ||||
(hg histedit --continue to resume) | (hg histedit --continue to resume) | ||||
try to update and get an error | try to update and get an error | ||||
$ hg update tip | $ hg update tip | ||||
abort: histedit in progress | abort: histedit in progress | ||||
(use 'hg histedit --continue' or 'hg histedit --abort') | (To continue: hg histedit --continue | ||||
To abort: hg histedit --abort) | |||||
[255] | [255] | ||||
edit the plan via the editor | edit the plan via the editor | ||||
$ cat >> $TESTTMP/editplan.sh <<EOF | $ cat >> $TESTTMP/editplan.sh <<EOF | ||||
> cat > \$1 <<EOF2 | > cat > \$1 <<EOF2 | ||||
> drop e860deea161a e | > drop e860deea161a e | ||||
> drop 652413bf663e f | > drop 652413bf663e f | ||||
> drop 3c6a8ed2ebe8 g | > drop 3c6a8ed2ebe8 g | ||||
strip-backup/177f92b77385-0ebe6a8f-histedit.hg | strip-backup/177f92b77385-0ebe6a8f-histedit.hg | ||||
Go at a random point and try to continue | Go at a random point and try to continue | ||||
$ hg id -n | $ hg id -n | ||||
3+ | 3+ | ||||
$ hg up 0 | $ hg up 0 | ||||
abort: histedit in progress | abort: histedit in progress | ||||
(use 'hg histedit --continue' or 'hg histedit --abort') | (To continue: hg histedit --continue | ||||
To abort: hg histedit --abort) | |||||
[255] | [255] | ||||
Try to delete necessary commit | Try to delete necessary commit | ||||
$ hg strip -r 652413b | $ hg strip -r 652413b | ||||
abort: histedit in progress, can't strip 652413bf663e | abort: histedit in progress, can't strip 652413bf663e | ||||
[255] | [255] | ||||
commit, then edit the revision | commit, then edit the revision | ||||
$ hg ci -m 'wat' | $ hg ci -m 'wat' | ||||
created new head | created new head | ||||
$ echo a > e | $ echo a > e | ||||
qnew should fail while we're in the middle of the edit step | qnew should fail while we're in the middle of the edit step | ||||
$ hg --config extensions.mq= qnew please-fail | $ hg --config extensions.mq= qnew please-fail | ||||
abort: histedit in progress | abort: histedit in progress | ||||
(use 'hg histedit --continue' or 'hg histedit --abort') | (To continue: hg histedit --continue | ||||
To abort: hg histedit --abort) | |||||
[255] | [255] | ||||
$ HGEDITOR='echo foobaz > ' hg histedit --continue 2>&1 | fixbundle | $ HGEDITOR='echo foobaz > ' hg histedit --continue 2>&1 | fixbundle | ||||
$ hg log --graph | $ hg log --graph | ||||
@ changeset: 6:b5f70786f9b0 | @ changeset: 6:b5f70786f9b0 | ||||
| tag: tip | | tag: tip | ||||
| user: test | | user: test | ||||
| date: Thu Jan 01 00:00:00 1970 +0000 | | date: Thu Jan 01 00:00:00 1970 +0000 |
# The repository is in an unfinished *histedit* state. | # The repository is in an unfinished *histedit* state. | ||||
# Unresolved merge conflicts: | # Unresolved merge conflicts: | ||||
# | # | ||||
# file | # file | ||||
# | # | ||||
# To mark files as resolved: hg resolve --mark FILE | # To mark files as resolved: hg resolve --mark FILE | ||||
# To continue: hg histedit --continue | To continue: hg histedit --continue | ||||
# To abort: hg histedit --abort | To abort: hg histedit --abort | ||||
To stop: hg histedit --stop | |||||
$ hg resolve -l | $ hg resolve -l | ||||
U file | U file | ||||
$ hg revert -r 'p1()' file | $ hg revert -r 'p1()' file | ||||
$ hg resolve --mark file | $ hg resolve --mark file | ||||
(no more unresolved files) | (no more unresolved files) | ||||
continue: hg histedit --continue | continue: hg histedit --continue | ||||
$ hg histedit --continue | $ hg histedit --continue | ||||
251d831eeec5: empty changeset | 251d831eeec5: empty changeset |
o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b" | o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b" | ||||
| | | | ||||
o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a" | o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a" | ||||
abort editing session, after first forcibly updating away | abort editing session, after first forcibly updating away | ||||
$ hg up 0 | $ hg up 0 | ||||
abort: histedit in progress | abort: histedit in progress | ||||
(use 'hg histedit --continue' or 'hg histedit --abort') | (To continue: hg histedit --continue | ||||
To abort: hg histedit --abort) | |||||
[255] | [255] | ||||
$ mv .hg/histedit-state .hg/histedit-state-ignore | $ mv .hg/histedit-state .hg/histedit-state-ignore | ||||
$ hg up 0 | $ hg up 0 | ||||
0 files updated, 0 files merged, 3 files removed, 0 files unresolved | 0 files updated, 0 files merged, 3 files removed, 0 files unresolved | ||||
$ mv .hg/histedit-state-ignore .hg/histedit-state | $ mv .hg/histedit-state-ignore .hg/histedit-state | ||||
$ hg sum | $ hg sum | ||||
parent: 0:cb9a9f314b8b | parent: 0:cb9a9f314b8b | ||||
a | a |
> y | > y | ||||
> y | > y | ||||
> n | > n | ||||
> y | > y | ||||
> y | > y | ||||
> n | > n | ||||
> EOF | > EOF | ||||
abort: histedit in progress | abort: histedit in progress | ||||
(use 'hg histedit --continue' or 'hg histedit --abort') | (To continue: hg histedit --continue | ||||
To abort: hg histedit --abort) | |||||
[255] | [255] |
$ hg up -C 0 # user does other stuff in the repo | $ hg up -C 0 # user does other stuff in the repo | ||||
0 files updated, 0 files merged, 1 files removed, 0 files unresolved | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved | ||||
$ mv rebasestate .hg/ # user upgrades to 2.7 | $ mv rebasestate .hg/ # user upgrades to 2.7 | ||||
$ echo new > a | $ echo new > a | ||||
$ hg up 1 # user gets an error saying to run hg rebase --abort | $ hg up 1 # user gets an error saying to run hg rebase --abort | ||||
abort: rebase in progress | abort: rebase in progress | ||||
(use 'hg rebase --continue' or 'hg rebase --abort') | (To continue: hg rebase --continue | ||||
To abort: hg rebase --abort) | |||||
[255] | [255] | ||||
$ cat a | $ cat a | ||||
new | new | ||||
$ hg rebase --abort | $ hg rebase --abort | ||||
rebase aborted | rebase aborted | ||||
$ cat a | $ cat a | ||||
new | new | ||||
.hg/merge/state | .hg/merge/state | ||||
.hg/rebasestate | .hg/rebasestate | ||||
.hg/undo.backup.dirstate | .hg/undo.backup.dirstate | ||||
.hg/undo.dirstate | .hg/undo.dirstate | ||||
.hg/updatestate | .hg/updatestate | ||||
$ hg rebase -s 3 -d tip | $ hg rebase -s 3 -d tip | ||||
abort: rebase in progress | abort: rebase in progress | ||||
(use 'hg rebase --continue' or 'hg rebase --abort') | (To continue: hg rebase --continue | ||||
To abort: hg rebase --abort) | |||||
[255] | [255] | ||||
$ hg up . | $ hg up . | ||||
abort: rebase in progress | abort: rebase in progress | ||||
(use 'hg rebase --continue' or 'hg rebase --abort') | (To continue: hg rebase --continue | ||||
To abort: hg rebase --abort) | |||||
[255] | [255] | ||||
$ hg up -C . | $ hg up -C . | ||||
abort: rebase in progress | abort: rebase in progress | ||||
(use 'hg rebase --continue' or 'hg rebase --abort') | (To continue: hg rebase --continue | ||||
To abort: hg rebase --abort) | |||||
[255] | [255] | ||||
$ hg graft 3 | $ hg graft 3 | ||||
abort: rebase in progress | abort: rebase in progress | ||||
(use 'hg rebase --continue' or 'hg rebase --abort') | (To continue: hg rebase --continue | ||||
To abort: hg rebase --abort) | |||||
[255] | [255] | ||||
$ hg rebase --abort | $ hg rebase --abort | ||||
saved backup bundle to $TESTTMP/interrupted/.hg/strip-backup/3d8812cf300d-93041a90-backup.hg | saved backup bundle to $TESTTMP/interrupted/.hg/strip-backup/3d8812cf300d-93041a90-backup.hg | ||||
rebase aborted | rebase aborted | ||||
$ hg log -G --template "{rev} {desc} {bookmarks}" | $ hg log -G --template "{rev} {desc} {bookmarks}" | ||||
o 6 no-a | o 6 no-a | ||||
| | | |
# The repository is in an unfinished *rebase* state. | # The repository is in an unfinished *rebase* state. | ||||
# Unresolved merge conflicts: | # Unresolved merge conflicts: | ||||
# | # | ||||
# common | # common | ||||
# | # | ||||
# To mark files as resolved: hg resolve --mark FILE | # To mark files as resolved: hg resolve --mark FILE | ||||
# To continue: hg rebase --continue | To continue: hg rebase --continue | ||||
# To abort: hg rebase --abort | To abort: hg rebase --abort | ||||
Try to continue without solving the conflict: | Try to continue without solving the conflict: | ||||
$ hg rebase --continue | $ hg rebase --continue | ||||
abort: unresolved merge conflicts (see 'hg help resolve') | abort: unresolved merge conflicts (see 'hg help resolve') | ||||
[255] | [255] | ||||
Conclude rebase: | Conclude rebase: |
unresolved conflicts (see hg resolve, then hg rebase --continue) | unresolved conflicts (see hg resolve, then hg rebase --continue) | ||||
[1] | [1] | ||||
$ hg rebase --stop --dry-run | $ hg rebase --stop --dry-run | ||||
abort: cannot specify both --dry-run and --stop | abort: cannot specify both --dry-run and --stop | ||||
[255] | [255] | ||||
$ hg rebase -s 3 -d 5 | $ hg rebase -s 3 -d 5 | ||||
abort: rebase in progress | abort: rebase in progress | ||||
(use 'hg rebase --continue' or 'hg rebase --abort') | (To continue: hg rebase --continue | ||||
To abort: hg rebase --abort) | |||||
[255] | [255] | ||||
$ hg rebase --stop --continue | $ hg rebase --stop --continue | ||||
abort: cannot use --stop with --continue | abort: cannot use --stop with --continue | ||||
[255] | [255] | ||||
Test --stop moves bookmarks of original revisions to new rebased nodes: | Test --stop moves bookmarks of original revisions to new rebased nodes: | ||||
====================================================================== | ====================================================================== | ||||
$ cd .. | $ cd .. |
$ hg histedit . -q --commands - << EOF | $ hg histedit . -q --commands - << EOF | ||||
> edit d80cc2da061e histedit: generate unfinished state | > edit d80cc2da061e histedit: generate unfinished state | ||||
> EOF | > EOF | ||||
Editing (d80cc2da061e), you may commit or record as needed now. | Editing (d80cc2da061e), you may commit or record as needed now. | ||||
(hg histedit --continue to resume) | (hg histedit --continue to resume) | ||||
[1] | [1] | ||||
$ hg pull --rebase | $ hg pull --rebase | ||||
abort: histedit in progress | abort: histedit in progress | ||||
(use 'hg histedit --continue' or 'hg histedit --abort') | (To continue: hg histedit --continue | ||||
To abort: hg histedit --abort) | |||||
[255] | [255] | ||||
$ hg histedit --abort --quiet | $ hg histedit --abort --quiet | ||||
Abort pull early with pending uncommitted merge: | Abort pull early with pending uncommitted merge: | ||||
$ cd .. | $ cd .. | ||||
$ hg clone --noupdate c d | $ hg clone --noupdate c d | ||||
$ cd d | $ cd d |
# The repository is in an unfinished *unshelve* state. | # The repository is in an unfinished *unshelve* state. | ||||
# Unresolved merge conflicts: | # Unresolved merge conflicts: | ||||
# | # | ||||
# a/a | # a/a | ||||
# | # | ||||
# To mark files as resolved: hg resolve --mark FILE | # To mark files as resolved: hg resolve --mark FILE | ||||
# To continue: hg unshelve --continue | To continue: hg unshelve --continue | ||||
# To abort: hg unshelve --abort | To abort: hg unshelve --abort | ||||
ensure that we have a merge with unresolved conflicts | ensure that we have a merge with unresolved conflicts | ||||
#if phasebased | #if phasebased | ||||
$ hg heads -q --template '{rev}\n' | $ hg heads -q --template '{rev}\n' | ||||
8 | 8 | ||||
5 | 5 | ||||
$ hg parents -q --template '{rev}\n' | $ hg parents -q --template '{rev}\n' | ||||
rename to b.rename/b | rename to b.rename/b | ||||
diff --git a/c b/c.copy | diff --git a/c b/c.copy | ||||
copy from c | copy from c | ||||
copy to c.copy | copy to c.copy | ||||
$ hg resolve -l | $ hg resolve -l | ||||
U a/a | U a/a | ||||
$ hg shelve | $ hg shelve | ||||
abort: unshelve already in progress | abort: unshelve in progress | ||||
(use 'hg unshelve --continue' or 'hg unshelve --abort') | (To continue: hg unshelve --continue | ||||
To abort: hg unshelve --abort) | |||||
[255] | [255] | ||||
abort the unshelve and be happy | abort the unshelve and be happy | ||||
$ hg status | $ hg status | ||||
M a/a | M a/a | ||||
M b.rename/b | M b.rename/b | ||||
M c.copy | M c.copy | ||||
[255] | [255] | ||||
$ hg revert -r . a/a | $ hg revert -r . a/a | ||||
$ hg resolve -m a/a | $ hg resolve -m a/a | ||||
(no more unresolved files) | (no more unresolved files) | ||||
continue: hg unshelve --continue | continue: hg unshelve --continue | ||||
$ hg commit -m 'commit while unshelve in progress' | $ hg commit -m 'commit while unshelve in progress' | ||||
abort: unshelve already in progress | abort: unshelve in progress | ||||
(use 'hg unshelve --continue' or 'hg unshelve --abort') | (To continue: hg unshelve --continue | ||||
To abort: hg unshelve --abort) | |||||
[255] | [255] | ||||
$ hg graft --continue | $ hg graft --continue | ||||
abort: no graft in progress | abort: no graft in progress | ||||
(continue: hg unshelve --continue) | (continue: hg unshelve --continue) | ||||
[255] | [255] | ||||
$ hg unshelve -c | $ hg unshelve -c | ||||
unshelve of 'default' complete | unshelve of 'default' complete |
patching file baz | patching file baz | ||||
Hunk #1 FAILED at 0 | Hunk #1 FAILED at 0 | ||||
1 out of 1 hunks FAILED -- saving rejects to file baz.rej | 1 out of 1 hunks FAILED -- saving rejects to file baz.rej | ||||
patch failed to apply | patch failed to apply | ||||
abort: fix up the working directory and run hg transplant --continue | abort: fix up the working directory and run hg transplant --continue | ||||
[255] | [255] | ||||
$ hg transplant 1:3 | $ hg transplant 1:3 | ||||
abort: transplant in progress | abort: transplant in progress | ||||
(use 'hg transplant --continue' or 'hg update' to abort) | (To continue: hg transplant --continue | ||||
To abort: hg transplant --abort) | |||||
[255] | [255] | ||||
$ echo fixed > baz | $ echo fixed > baz | ||||
$ hg transplant --continue | $ hg transplant --continue | ||||
9d6d6b5a8275 transplanted as d80c49962290 | 9d6d6b5a8275 transplanted as d80c49962290 | ||||
applying 1dab759070cf | applying 1dab759070cf | ||||
1dab759070cf transplanted to aa0ffe6bd5ae | 1dab759070cf transplanted to aa0ffe6bd5ae | ||||
$ cd .. | $ cd .. |
I wonder if we should keep the functions in cmdutil.py and let them delegate to the new ones. That way we won't have to update all the callers. I think cmdutil sounds like a more natural place for the callers to looks for this function. That's also where bailifchanged() is, so it it makes sense that these two similar functions are in the same place.