def recordfunc(ui, repo, message, match, opts):
"""This is generic record driver.
Its job is to interactively filter local changes, and
accordingly prepare working directory into a state in which the
job can be delegated to a non-interactive commit command such as
'commit' or 'qrefresh'.
After the actual job is done by non-interactive command, the
working directory is restored to its original state.
In the end we'll record interesting changes, and everything else
will be left in place, so the user can continue working.
"""
cmdutil.checkunfinished(repo, commit=True)
merge = len(repo[None].parents()) > 1
if merge:
raise util.Abort(_("cannot partially commit a merge " '(use "hg commit" instead)'))
status = repo.status(match=match)
diffopts = opts.copy()
diffopts["nodates"] = True
diffopts["git"] = True
diffopts = patch.diffopts(ui, opts=diffopts)
chunks = patch.diff(repo, changes=status, opts=diffopts)
fp = cStringIO.StringIO()
fp.write("".join(chunks))
fp.seek(0)
# 1. filter patch, so we have intending-to apply subset of it
try:
chunks = filterpatch(ui, parsepatch(fp))
except patch.PatchError, err:
raise util.Abort(_("error parsing patch: %s") % err)
def checklocalchanges(repo, force=False, excsuffix=''):
cmdutil.checkunfinished(repo)
m, a, r, d = repo.status()[:4]
if not force:
if (m or a or r or d):
_("local changes found") # i18n tool detection
raise util.Abort(_("local changes found" + excsuffix))
if checksubstate(repo):
_("local changed subrepos found") # i18n tool detection
raise util.Abort(_("local changed subrepos found" + excsuffix))
return m, a, r, d
def shelvecmd(ui, repo, *pats, **opts):
'''save and set aside changes from the working directory
Shelving takes files that "hg status" reports as not clean, saves
the modifications to a bundle (a shelved change), and reverts the
files so that their state in the working directory becomes clean.
To restore these changes to the working directory, using "hg
unshelve"; this will work even if you switch to a different
commit.
When no files are specified, "hg shelve" saves all not-clean
files. If specific files or directories are named, only changes to
those files are shelved.
Each shelved change has a name that makes it easier to find later.
The name of a shelved change defaults to being based on the active
bookmark, or if there is no active bookmark, the current named
branch. To specify a different name, use ``--name``.
To see a list of existing shelved changes, use the ``--list``
option. For each shelved change, this will print its name, age,
and description; use ``--patch`` or ``--stat`` for more details.
To delete specific shelved changes, use ``--delete``. To delete
all shelved changes, use ``--cleanup``.
'''
cmdutil.checkunfinished(repo)
def checkopt(opt, incompatible):
if opts[opt]:
for i in incompatible.split():
if opts[i]:
raise util.Abort(_("options '--%s' and '--%s' may not be "
"used together") % (opt, i))
return True
if checkopt('cleanup', 'addremove delete list message name patch stat'):
if pats:
raise util.Abort(_("cannot specify names when using '--cleanup'"))
return cleanupcmd(ui, repo)
elif checkopt('delete', 'addremove cleanup list message name patch stat'):
return deletecmd(ui, repo, pats)
elif checkopt('list', 'addremove cleanup delete message name'):
return listcmd(ui, repo, pats, opts)
else:
for i in ('patch', 'stat'):
if opts[i]:
raise util.Abort(_("option '--%s' may not be "
"used when shelving a change") % (i,))
return createcmd(ui, repo, pats, opts)
def unshelve(ui, repo, *shelved, **opts):
"""restore a shelved change to the working directory
This command accepts an optional name of a shelved change to
restore. If none is given, the most recent shelved change is used.
If a shelved change is applied successfully, the bundle that
contains the shelved changes is deleted afterwards.
Since you can restore a shelved change on top of an arbitrary
commit, it is possible that unshelving will result in a conflict
between your changes and the commits you are unshelving onto. If
this occurs, you must resolve the conflict, then use
``--continue`` to complete the unshelve operation. (The bundle
will not be deleted until you successfully complete the unshelve.)
(Alternatively, you can use ``--abort`` to abandon an unshelve
that causes a conflict. This reverts the unshelved changes, and
does not delete the bundle.)
"""
abortf = opts['abort']
continuef = opts['continue']
if not abortf and not continuef:
cmdutil.checkunfinished(repo)
if abortf or continuef:
if abortf and continuef:
raise util.Abort(_('cannot use both abort and continue'))
if shelved:
raise util.Abort(_('cannot combine abort/continue with '
'naming a shelved change'))
try:
state = shelvedstate.load(repo)
except IOError, err:
if err.errno != errno.ENOENT:
raise
raise util.Abort(_('no unshelve operation underway'))
if abortf:
return unshelveabort(ui, repo, state, opts)
elif continuef:
return unshelvecontinue(ui, repo, state, opts)
#.........这里部分代码省略.........
if repo.pushsingle:
basenode = tipnode
# Given a base and tip node, find all changesets to review.
#
# A solution that works most of the time is to find all non-public
# ancestors of that node. This is our default.
#
# If basenode is specified, we stop the traversal when we encounter it.
#
# Note that we will still refuse to review a public changeset even with
# basenode. This decision is somewhat arbitrary and can be revisited later
# if there is an actual need to review public changesets.
nodes = [tipnode]
# Special case where basenode is the tip node.
if basenode and tipnode == basenode:
pass
else:
for node in repo[tipnode].ancestors():
ctx = repo[node]
if ctx.phase() == phases.public:
break
if basenode and ctx.node() == basenode:
nodes.insert(0, ctx.node())
break
nodes.insert(0, ctx.node())
# Filter out public nodes.
publicnodes = []
for node in nodes:
ctx = repo[node]
if ctx.phase() == phases.public:
publicnodes.append(node)
ui.status(_("(ignoring public changeset %s in review request)\n") % ctx.hex()[0:12])
nodes = [n for n in nodes if n not in publicnodes]
if not nodes:
raise util.Abort(
_("no non-public changesets left to review"),
hint=_("add or change the -r argument to include draft changesets"),
)
# We stop completely empty changesets prior to review.
for node in nodes:
ctx = repo[node]
if not ctx.files():
raise util.Abort(
_("cannot review empty changeset %s") % ctx.hex()[:12], hint=_("add files to or remove changeset")
)
# Ensure all reviewed changesets have commit IDs.
replacenodes = []
for node in nodes:
ctx = repo[node]
if "commitid" not in ctx.extra():
replacenodes.append(node)
def addcommitid(repo, ctx, revmap, copyfilectxfn):
parents = newparents(repo, ctx, revmap)
# Need to make a copy otherwise modification is made on original,
# which is just plain wrong.
extra = dict(ctx.extra())
assert "commitid" not in extra
extra["commitid"] = genid(repo)
memctx = context.memctx(
repo, parents, ctx.description(), ctx.files(), copyfilectxfn, user=ctx.user(), date=ctx.date(), extra=extra
)
return memctx
if replacenodes:
ui.status(_("(adding commit id to %d changesets)\n") % (len(replacenodes)))
nodemap = replacechangesets(repo, replacenodes, addcommitid, backuptopic="addcommitid")
# Since we're in the middle of an operation, update references
# to rewritten nodes.
nodes = [nodemap.get(node, node) for node in nodes]
pushop.revs = [nodemap.get(node, node) for node in pushop.revs]
pushop.reviewnodes = nodes
# Since we may rewrite changesets to contain review metadata after
# push, abort immediately if the working directory state is not
# compatible with rewriting. This prevents us from successfully
# pushing and failing to update commit metadata after the push. i.e.
# it prevents potential loss of metadata.
#
# There may be some scenarios where we don't rewrite after push.
# But coding that here would be complicated. And future server changes
# may change things like review request mapping, which may invalidate
# client assumptions. So always assume a rewrite is needed.
impactedrevs = list(repo.revs("%ln::", nodes))
if repo["."].rev() in impactedrevs:
cmdutil.checkunfinished(repo)
cmdutil.bailifchanged(repo)
return orig(pushop)
def histedit(ui, repo, *freeargs, **opts):
"""interactively edit changeset history
This command edits changesets between ANCESTOR and the parent of
the working directory.
With --outgoing, this edits changesets not found in the
destination repository. If URL of the destination is omitted, the
'default-push' (or 'default') path will be used.
For safety, this command is aborted, also if there are ambiguous
outgoing revisions which may confuse users: for example, there are
multiple branches containing outgoing revisions.
Use "min(outgoing() and ::.)" or similar revset specification
instead of --outgoing to specify edit target revision exactly in
such ambiguous situation. See :hg:`help revsets` for detail about
selecting revisions.
"""
# TODO only abort if we try and histedit mq patches, not just
# blanket if mq patches are applied somewhere
mq = getattr(repo, "mq", None)
if mq and mq.applied:
raise util.Abort(_("source has mq patches applied"))
# basic argument incompatibility processing
outg = opts.get("outgoing")
cont = opts.get("continue")
abort = opts.get("abort")
force = opts.get("force")
rules = opts.get("commands", "")
revs = opts.get("rev", [])
goal = "new" # This invocation goal, in new, continue, abort
if force and not outg:
raise util.Abort(_("--force only allowed with --outgoing"))
if cont:
if util.any((outg, abort, revs, freeargs, rules)):
raise util.Abort(_("no arguments allowed with --continue"))
goal = "continue"
elif abort:
if util.any((outg, revs, freeargs, rules)):
raise util.Abort(_("no arguments allowed with --abort"))
goal = "abort"
else:
if os.path.exists(os.path.join(repo.path, "histedit-state")):
raise util.Abort(_("history edit already in progress, try " "--continue or --abort"))
if outg:
if revs:
raise util.Abort(_("no revisions allowed with --outgoing"))
if len(freeargs) > 1:
raise util.Abort(_("only one repo argument allowed with --outgoing"))
else:
revs.extend(freeargs)
if len(revs) != 1:
raise util.Abort(_("histedit requires exactly one ancestor revision"))
if goal == "continue":
(parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
parentctx = repo[parentctxnode]
parentctx, repl = bootstrapcontinue(ui, repo, parentctx, rules, opts)
replacements.extend(repl)
elif goal == "abort":
(parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
mapping, tmpnodes, leafs, _ntm = processreplacement(repo, replacements)
ui.debug("restore wc to old parent %s\n" % node.short(topmost))
# check whether we should update away
parentnodes = [c.node() for c in repo[None].parents()]
for n in leafs | set([parentctxnode]):
if n in parentnodes:
hg.clean(repo, topmost)
break
else:
pass
cleanupnode(ui, repo, "created", tmpnodes)
cleanupnode(ui, repo, "temp", leafs)
os.unlink(os.path.join(repo.path, "histedit-state"))
return
else:
cmdutil.checkunfinished(repo)
cmdutil.bailifchanged(repo)
topmost, empty = repo.dirstate.parents()
if outg:
if freeargs:
remote = freeargs[0]
else:
remote = None
root = findoutgoing(ui, repo, remote, force, opts)
else:
root = revs[0]
root = scmutil.revsingle(repo, root).node()
keep = opts.get("keep", False)
revs = between(repo, root, topmost, keep)
if not revs:
raise util.Abort(_("%s is not an ancestor of working directory") % node.short(root))
ctxs = [repo[r] for r in revs]
if not rules:
rules = "\n".join([makedesc(c) for c in ctxs])
#.........这里部分代码省略.........
def _dotransplant(ui, repo, *revs, **opts):
def incwalk(repo, csets, match=util.always):
for node in csets:
if match(node):
yield node
def transplantwalk(repo, dest, heads, match=util.always):
'''Yield all nodes that are ancestors of a head but not ancestors
of dest.
If no heads are specified, the heads of repo will be used.'''
if not heads:
heads = repo.heads()
ancestors = []
ctx = repo[dest]
for head in heads:
ancestors.append(ctx.ancestor(repo[head]).node())
for node in repo.changelog.nodesbetween(ancestors, heads)[0]:
if match(node):
yield node
def checkopts(opts, revs):
if opts.get('continue'):
if opts.get('branch') or opts.get('all') or opts.get('merge'):
raise error.Abort(_('--continue is incompatible with '
'--branch, --all and --merge'))
return
if not (opts.get('source') or revs or
opts.get('merge') or opts.get('branch')):
raise error.Abort(_('no source URL, branch revision, or revision '
'list provided'))
if opts.get('all'):
if not opts.get('branch'):
raise error.Abort(_('--all requires a branch revision'))
if revs:
raise error.Abort(_('--all is incompatible with a '
'revision list'))
checkopts(opts, revs)
if not opts.get('log'):
# deprecated config: transplant.log
opts['log'] = ui.config('transplant', 'log')
if not opts.get('filter'):
# deprecated config: transplant.filter
opts['filter'] = ui.config('transplant', 'filter')
tp = transplanter(ui, repo, opts)
p1, p2 = repo.dirstate.parents()
if len(repo) > 0 and p1 == revlog.nullid:
raise error.Abort(_('no revision checked out'))
if opts.get('continue'):
if not tp.canresume():
raise error.Abort(_('no transplant to continue'))
else:
cmdutil.checkunfinished(repo)
if p2 != revlog.nullid:
raise error.Abort(_('outstanding uncommitted merges'))
m, a, r, d = repo.status()[:4]
if m or a or r or d:
raise error.Abort(_('outstanding local changes'))
sourcerepo = opts.get('source')
if sourcerepo:
peer = hg.peer(repo, opts, ui.expandpath(sourcerepo))
heads = map(peer.lookup, opts.get('branch', ()))
target = set(heads)
for r in revs:
try:
target.add(peer.lookup(r))
except error.RepoError:
pass
source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, peer,
onlyheads=sorted(target), force=True)
else:
source = repo
heads = map(source.lookup, opts.get('branch', ()))
cleanupfn = None
try:
if opts.get('continue'):
tp.resume(repo, source, opts)
return
tf = tp.transplantfilter(repo, source, p1)
if opts.get('prune'):
prune = set(source.lookup(r)
for r in scmutil.revrange(source, opts.get('prune')))
matchfn = lambda x: tf(x) and x not in prune
else:
matchfn = tf
merges = map(source.lookup, opts.get('merge', ()))
revmap = {}
if revs:
for r in scmutil.revrange(source, revs):
revmap[int(r)] = source.lookup(r)
elif opts.get('all') or not merges:
if source != repo:
alltransplants = incwalk(source, csets, match=matchfn)
else:
#.........这里部分代码省略.........
开发者ID:motlin,项目名称:cyg,代码行数:101,代码来源:transplant.py
示例10: split
def split(ui, repo, *revs, **opts):
"""split a changeset into smaller changesets
By default, split the current revision by prompting for all its hunks to be
redistributed into new changesets.
Use --rev to split a given changeset instead.
"""
tr = wlock = lock = None
newcommits = []
revarg = (list(revs) + opts.get('rev')) or ['.']
if len(revarg) != 1:
msg = _("more than one revset is given")
hnt = _("use either `hg split <rs>` or `hg split --rev <rs>`, not both")
raise error.Abort(msg, hint=hnt)
rev = scmutil.revsingle(repo, revarg[0])
if opts.get('no_rebase'):
torebase = ()
else:
torebase = repo.revs('descendants(%d) - (%d)', rev, rev)
try:
wlock = repo.wlock()
lock = repo.lock()
cmdutil.bailifchanged(repo)
if torebase:
cmdutil.checkunfinished(repo)
tr = repo.transaction('split')
ctx = repo[rev]
r = ctx.rev()
disallowunstable = not obsolete.isenabled(repo,
obsolete.allowunstableopt)
if disallowunstable:
# XXX We should check head revs
if repo.revs("(%d::) - %d", rev, rev):
raise error.Abort(_("cannot split commit: %s not a head") % ctx)
if len(ctx.parents()) > 1:
raise error.Abort(_("cannot split merge commits"))
prev = ctx.p1()
bmupdate = common.bookmarksupdater(repo, ctx.node(), tr)
bookactive = repo._activebookmark
if bookactive is not None:
repo.ui.status(_("(leaving bookmark %s)\n") % repo._activebookmark)
bookmarks.deactivate(repo)
hg.update(repo, prev)
commands.revert(ui, repo, rev=r, all=True)
def haschanges():
modified, added, removed, deleted = repo.status()[:4]
return modified or added or removed or deleted
msg = ("HG: This is the original pre-split commit message. "
"Edit it as appropriate.\n\n")
msg += ctx.description()
opts['message'] = msg
opts['edit'] = True
while haschanges():
pats = ()
cmdutil.dorecord(ui, repo, commands.commit, 'commit', False,
cmdutil.recordfilter, *pats, **opts)
# TODO: Does no seem like the best way to do this
# We should make dorecord return the newly created commit
newcommits.append(repo['.'])
if haschanges():
if ui.prompt('Done splitting? [yN]', default='n') == 'y':
commands.commit(ui, repo, **opts)
newcommits.append(repo['.'])
break
else:
ui.status(_("no more change to split\n"))
if newcommits:
tip = repo[newcommits[-1]]
bmupdate(tip.node())
if bookactive is not None:
bookmarks.activate(repo, bookactive)
obsolete.createmarkers(repo, [(repo[r], newcommits)],
operation='split')
if torebase:
top = repo.revs('allsuccessors(%d)', rev).last()
common.restackonce(ui, repo, top)
tr.close()
finally:
lockmod.release(tr, lock, wlock)
#.........这里部分代码省略.........
if srcf or basef or destf:
raise error.Abort(
_('abort and continue do not allow specifying revisions'))
if abortf and opts.get('tool', False):
ui.warn(_('tool option will be ignored\n'))
try:
(originalwd, target, state, skipped, collapsef, keepf,
keepbranchesf, external, activebookmark) = restorestatus(repo)
except error.RepoLookupError:
if abortf:
clearstatus(repo)
repo.ui.warn(_('rebase aborted (no revision is removed,'
' only broken state is cleared)\n'))
return 0
else:
msg = _('cannot continue inconsistent rebase')
hint = _('use "hg rebase --abort" to clear broken state')
raise error.Abort(msg, hint=hint)
if abortf:
return abort(repo, originalwd, target, state,
activebookmark=activebookmark)
else:
if srcf and basef:
raise error.Abort(_('cannot specify both a '
'source and a base'))
if revf and basef:
raise error.Abort(_('cannot specify both a '
'revision and a base'))
if revf and srcf:
raise error.Abort(_('cannot specify both a '
'revision and a source'))
cmdutil.checkunfinished(repo)
cmdutil.bailifchanged(repo)
if destf:
dest = scmutil.revsingle(repo, destf)
else:
dest = repo[_destrebase(repo)]
destf = str(dest)
if revf:
rebaseset = scmutil.revrange(repo, revf)
if not rebaseset:
ui.status(_('empty "rev" revision set - '
'nothing to rebase\n'))
return _nothingtorebase()
elif srcf:
src = scmutil.revrange(repo, [srcf])
if not src:
ui.status(_('empty "source" revision set - '
'nothing to rebase\n'))
return _nothingtorebase()
rebaseset = repo.revs('(%ld)::', src)
assert rebaseset
else:
base = scmutil.revrange(repo, [basef or '.'])
if not base:
ui.status(_('empty "base" revision set - '
"can't compute rebase set\n"))
return _nothingtorebase()
commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first()
if commonanc is not None:
rebaseset = repo.revs('(%d::(%ld) - %d)::',
commonanc, base, commonanc)
def replacechangesets(repo, oldnodes, createfn, backuptopic='replacing'):
"""Replace changesets with new versions.
This is a generic function used to perform history rewriting.
Given an iterable of input nodes, a function will be called which is
expected to produce a new changeset to replace the input node. The
function signature should be:
def createfn(repo, ctx, revmap, copyfilectxfn):
It is passed a repo, the changectx being rewritten, a map of old to new
revisions that have been changed so far, and a function that can be used
as the memctx callback for obtaining memfilectx when no file modifications
are to be performed (a common pattern). The function should return an
*uncommitted* memctx holding the new changeset info.
We currently restrict that the createfn callback must return a new
changeset and that no file changes may occur. Restricting file changes
satisfies the requirements this function was invented for and keeps the
implementation simple.
After the memctx is obtained, it is committed. Children changesets are
rebased automatically after all changesets have been rewritten.
After the old to new mapping is obtained, bookmarks are moved and old
changesets are made obsolete or stripped, depending on what is appropriate
for the repo configuration.
This function handles locking the repository and performing as many actions
in a transaction as possible.
Before any changes are made, we verify the state of the repo is sufficient
for transformation to occur and abort otherwise.
"""
if not oldnodes:
return {}
repo = repo.unfiltered()
# Validate function called properly.
for node in oldnodes:
if len(node) != 20:
raise util.Abort('replacechangesets expects 20 byte nodes')
uoldrevs = [repo[node].rev() for node in oldnodes]
oldrevs = sorted(uoldrevs)
if oldrevs != uoldrevs:
raise util.Abort('must pass oldnodes in changelog order')
# We may perform stripping and stripping inside a nested transaction
# is a recipe for disaster.
# currenttransaction was added in 3.3. Copy the implementation until we
# drop 3.2 compatibility.
if hasattr(repo, 'currenttransaction'):
intrans = repo.currenttransaction()
else:
if repo._transref and repo._transref().running():
intrans = True
else:
intrans = False
if intrans:
raise util.Abort('cannot call replacechangesets when a transaction '
'is active')
# The revisions impacted by the current operation. This is essentially
# all non-hidden children. We don't operate on hidden changesets because
# there is no point - they are hidden and deemed not important.
impactedrevs = list(repo.filtered('visible').revs('%ld::', oldrevs))
# If we'll need to update the working directory, don't do anything if there
# are uncommitted changes, as this could cause a giant mess (merge
# conflicts, etc). Note the comparison against impacted revs, as children
# of rewritten changesets will be rebased below.
dirstaterev = repo[repo.dirstate.p1()].rev()
if dirstaterev in impactedrevs:
cmdutil.checkunfinished(repo)
cmdutil.bailifchanged(repo)
obsenabled = False
if hasattr(obsolete, 'isenabled'):
obsenabled = obsolete.isenabled(repo, 'createmarkers')
else:
obsenabled = obsolete._enabled
def adjustphase(repo, tr, phase, node):
# transaction argument added in Mercurial 3.2.
try:
phases.advanceboundary(repo, tr, phase, [node])
phases.retractboundary(repo, tr, phase, [node])
except TypeError:
phases.advanceboundary(repo, phase, [node])
phases.retractboundary(repo, phase, [node])
nodemap = {}
wlock, lock, tr = None, None, None
try:
wlock = repo.wlock()
lock = repo.lock()
#.........这里部分代码省略.........
#.........这里部分代码省略.........
_('cannot use collapse with continue or abort'))
if srcf or basef or destf:
raise util.Abort(
_('abort and continue do not allow specifying revisions'))
if opts.get('tool', False):
ui.warn(_('tool option will be ignored\n'))
try:
(originalwd, target, state, skipped, collapsef, keepf,
keepbranchesf, external, activebookmark) = restorestatus(repo)
except error.RepoLookupError:
if abortf:
clearstatus(repo)
repo.ui.warn(_('rebase aborted (no revision is removed,'
' only broken state is cleared)\n'))
return 0
else:
msg = _('cannot continue inconsistent rebase')
hint = _('use "hg rebase --abort" to clear borken state')
raise util.Abort(msg, hint=hint)
if abortf:
return abort(repo, originalwd, target, state)
else:
if srcf and basef:
raise util.Abort(_('cannot specify both a '
'source and a base'))
if revf and basef:
raise util.Abort(_('cannot specify both a '
'revision and a base'))
if revf and srcf:
raise util.Abort(_('cannot specify both a '
'revision and a source'))
cmdutil.checkunfinished(repo)
cmdutil.bailifchanged(repo)
if not destf:
# Destination defaults to the latest revision in the
# current branch
branch = repo[None].branch()
dest = repo[branch]
else:
dest = scmutil.revsingle(repo, destf)
if revf:
rebaseset = scmutil.revrange(repo, revf)
elif srcf:
src = scmutil.revrange(repo, [srcf])
rebaseset = repo.revs('(%ld)::', src)
else:
base = scmutil.revrange(repo, [basef or '.'])
rebaseset = repo.revs(
'(children(ancestor(%ld, %d)) and ::(%ld))::',
base, dest, base)
if rebaseset:
root = min(rebaseset)
else:
root = None
if not rebaseset:
repo.ui.debug('base is ancestor of destination\n')
result = None
elif (not (keepf or obsolete._enabled)
and repo.revs('first(children(%ld) - %ld)',
rebaseset, rebaseset)):
raise util.Abort(
def _dounshelve(ui, repo, *shelved, **opts):
abortf = opts.get('abort')
continuef = opts.get('continue')
if not abortf and not continuef:
cmdutil.checkunfinished(repo)
shelved = list(shelved)
if opts.get("name"):
shelved.append(opts["name"])
if abortf or continuef:
if abortf and continuef:
raise error.Abort(_('cannot use both abort and continue'))
if shelved:
raise error.Abort(_('cannot combine abort/continue with '
'naming a shelved change'))
if abortf and opts.get('tool', False):
ui.warn(_('tool option will be ignored\n'))
try:
state = shelvedstate.load(repo)
if opts.get('keep') is None:
opts['keep'] = state.keep
except IOError as err:
if err.errno != errno.ENOENT:
raise
cmdutil.wrongtooltocontinue(repo, _('unshelve'))
except error.CorruptedState as err:
ui.debug(str(err) + '\n')
if continuef:
msg = _('corrupted shelved state file')
hint = _('please run hg unshelve --abort to abort unshelve '
'operation')
raise error.Abort(msg, hint=hint)
elif abortf:
msg = _('could not read shelved state file, your working copy '
'may be in an unexpected state\nplease update to some '
'commit\n')
ui.warn(msg)
shelvedstate.clear(repo)
return
if abortf:
return unshelveabort(ui, repo, state, opts)
elif continuef:
return unshelvecontinue(ui, repo, state, opts)
elif len(shelved) > 1:
raise error.Abort(_('can only unshelve one change at a time'))
elif not shelved:
shelved = listshelves(repo)
if not shelved:
raise error.Abort(_('no shelved changes to apply!'))
basename = util.split(shelved[0][1])[1]
ui.status(_("unshelving change '%s'\n") % basename)
else:
basename = shelved[0]
if not shelvedfile(repo, basename, patchextension).exists():
raise error.Abort(_("shelved change '%s' not found") % basename)
lock = tr = None
obsshelve = True
obsshelvedfile = shelvedfile(repo, basename, 'oshelve')
if not obsshelvedfile.exists():
# although we can unshelve a obs-based shelve technically,
# this particular shelve was created using a traditional way
obsshelve = False
ui.note(_("falling back to traditional unshelve since "
"shelve was traditional"))
try:
lock = repo.lock()
tr = repo.transaction('unshelve', report=lambda x: None)
oldtiprev = len(repo)
pctx = repo['.']
tmpwctx = pctx
# The goal is to have a commit structure like so:
# ...-> pctx -> tmpwctx -> shelvectx
# where tmpwctx is an optional commit with the user's pending changes
# and shelvectx is the unshelved changes. Then we merge it all down
# to the original pctx.
activebookmark = _backupactivebookmark(repo)
tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts,
tmpwctx)
repo, shelvectx = _unshelverestorecommit(ui, repo, basename, obsshelve)
_checkunshelveuntrackedproblems(ui, repo, shelvectx)
branchtorestore = ''
if shelvectx.branch() != shelvectx.p1().branch():
branchtorestore = shelvectx.branch()
rebaseconfigoverrides = {('ui', 'forcemerge'): opts.get('tool', ''),
('experimental', 'rebaseskipobsolete'): 'off'}
with ui.configoverride(rebaseconfigoverrides, 'unshelve'):
shelvectx = _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev,
basename, pctx, tmpwctx,
shelvectx, branchtorestore,
activebookmark, obsshelve)
mergefiles(ui, repo, pctx, shelvectx)
restorebranch(ui, repo, branchtorestore)
_forgetunknownfiles(repo, shelvectx, addedbefore)
#.........这里部分代码省略.........
def createcmd(ui, repo, pats, opts):
"""subcommand that creates a new shelve"""
with repo.wlock():
cmdutil.checkunfinished(repo)
return _docreatecmd(ui, repo, pats, opts)
def _moverelative(ui, repo, args, opts, reverse=False):
"""Update to a changeset relative to the current changeset.
Implements both `hg previous` and `hg next`.
Takes in a list of positional arguments and a dict of command line
options. (See help for `hg previous` and `hg next` to see which
arguments and flags are supported.)
Moves forward through history by default -- the behavior of `hg next`.
Setting reverse=True will change the behavior to that of `hg previous`.
"""
# Parse positional argument.
try:
n = int(args[0]) if args else 1
except ValueError:
raise error.Abort(_("argument must be an integer"))
if n <= 0:
return
if ui.configbool('fbamend', 'alwaysnewest'):
opts['newest'] = True
# Check that the given combination of arguments is valid.
if args:
if opts.get('bookmark', False):
raise error.Abort(_("cannot use both number and --bookmark"))
if opts.get('top', False):
raise error.Abort(_("cannot use both number and --top"))
if opts.get('bottom', False):
raise error.Abort(_("cannot use both number and --bottom"))
if opts.get('bookmark', False):
if opts.get('top', False):
raise error.Abort(_("cannot use both --top and --bookmark"))
if opts.get('bottom', False):
raise error.Abort(_("cannot use both --bottom and --bookmark"))
if opts.get('towards', False) and opts.get('top', False):
raise error.Abort(_("cannot use both --top and --towards"))
if opts.get('merge', False) and opts.get('rebase', False):
raise error.Abort(_("cannot use both --merge and --rebase"))
# Check if there is an outstanding operation or uncommited changes.
cmdutil.checkunfinished(repo)
if not opts.get('clean', False) and not opts.get('merge', False):
try:
cmdutil.bailifchanged(repo)
except error.Abort as e:
e.hint = _("use --clean to discard uncommitted changes "
"or --merge to bring them along")
raise
# If we have both --clean and --rebase, we need to discard any outstanding
# changes now before we attempt to perform any rebases.
if opts.get('clean') and opts.get('rebase'):
commands.update(ui, repo, rev=repo['.'].rev(), clean=True)
with repo.wlock(), repo.lock():
# Record the active bookmark, if any.
bookmark = repo._activebookmark
noactivate = opts.get('no_activate_bookmark', False)
movebookmark = opts.get('move_bookmark', False)
with repo.transaction('moverelative') as tr:
# Find the desired changeset. May potentially perform rebase.
try:
target = _findtarget(ui, repo, n, opts, reverse)
except error.InterventionRequired:
# Rebase failed. Need to manually close transaction to allow
# `hg rebase --continue` to work correctly.
tr.close()
raise
# Move the active bookmark if neccesary. Needs to happen before
# we update to avoid getting a 'leaving bookmark X' message.
if movebookmark and bookmark is not None:
_setbookmark(repo, tr, bookmark, target)
# Update to the target changeset.
commands.update(ui, repo, rev=target,
clean=opts.get('clean', False))
# Print out the changeset we landed on.
_showchangesets(ui, repo, revs=[target])
# Activate the bookmark on the new changeset.
if not noactivate and not movebookmark:
_activate(ui, repo, target)
请发表评论