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 | ||
---|---|---|
1953 | 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 (6 lines) | |||
M | hgext/phabricator.py (3 lines) | |||
M | hgext/rebase.py (8 lines) | |||
M | hgext/record.py (3 lines) | |||
M | hgext/shelve.py (8 lines) | |||
M | hgext/strip.py (3 lines) | |||
M | hgext/transplant.py (5 lines) | |||
M | mercurial/cmdutil.py (44 lines) | |||
M | mercurial/commands.py (15 lines) | |||
M | mercurial/state.py (43 lines) |
Commit | Parents | Author | Summary | Date |
---|---|---|---|---|
Taapas Agrawal | Jun 8 2019, 2:13 PM |
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, | ||||
state as statemod, | |||||
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) | statemod.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') |
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) | statemod.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) | statemod.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( | statemod.unfinishedstates.append( | ||||
['histedit-state', False, True, _('histedit in progress'), | ['histedit-state', False, True, _('histedit in progress'), | ||||
_("use 'hg histedit --continue' or 'hg histedit --abort'")]) | _("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, | ||||
state as statemod, | |||||
tags, | tags, | ||||
templateutil, | templateutil, | ||||
url as urlmod, | url as urlmod, | ||||
util, | util, | ||||
) | ) | ||||
from mercurial.utils import ( | from mercurial.utils import ( | ||||
procutil, | procutil, | ||||
stringutil, | stringutil, | ||||
""" | """ | ||||
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) | statemod.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, | ||||
state as statemod, | |||||
) | ) | ||||
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) | statemod.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, | ||||
state as statemod, | |||||
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) | statemod.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) | statemod.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( | statemod.unfinishedstates.append( | ||||
[shelvedstate._filename, False, False, | [shelvedstate._filename, False, False, | ||||
_('unshelve already in progress'), | _('unshelve already in progress'), | ||||
_("use 'hg unshelve --continue' or 'hg unshelve --abort'")]) | _("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, | ||||
state as statemod, | |||||
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) | statemod.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( | statemod.unfinishedstates.append( | ||||
['transplant/journal', True, False, _('transplant in progress'), | ['transplant/journal', True, False, _('transplant in progress'), | ||||
_("use 'hg transplant --continue' or 'hg update' to abort")]) | _("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, | ||||
state as statemod, | |||||
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) | statemod.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) | ||||
# 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 | ||||
it. | it. |
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) | statemod.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) | statemod.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) | statemod.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) | statemod.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) | statemod.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) | statemod.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) | statemod.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') | ||||
ctx = scmutil.revsingle(repo, rev, default=None) | ctx = scmutil.revsingle(repo, rev, default=None) |
.hg/ directory. | .hg/ directory. | ||||
We store the data on disk in cbor, for which we use the CBOR format to encode | We store the data on disk in cbor, for which we use the CBOR format to encode | ||||
the data. | the data. | ||||
""" | """ | ||||
from __future__ import absolute_import | from __future__ import absolute_import | ||||
from .i18n import _ | |||||
from . import ( | from . import ( | ||||
error, | error, | ||||
util, | util, | ||||
) | ) | ||||
from .utils import ( | from .utils import ( | ||||
cborutil, | cborutil, | ||||
) | ) | ||||
def delete(self): | def delete(self): | ||||
"""drop the state file if exists""" | """drop the state file if exists""" | ||||
util.unlinkpath(self._repo.vfs.join(self.fname), ignoremissing=True) | util.unlinkpath(self._repo.vfs.join(self.fname), ignoremissing=True) | ||||
def exists(self): | def exists(self): | ||||
"""check whether the state file exists or not""" | """check whether the state file exists or not""" | ||||
return self._repo.vfs.exists(self.fname) | return self._repo.vfs.exists(self.fname) | ||||
# 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)) |
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.