Ejemplo n.º 1
0
def drop(ui, repo, *revs, **opts):
    """drop changeset from stack
    """
    if not rebasemod:
        raise error.Abort(_("required extensions not detected"))

    cmdutil.checkunfinished(repo)
    cmdutil.bailifchanged(repo)

    revs = scmutil.revrange(repo, list(revs) + opts.get("rev"))
    if not revs:
        raise error.Abort(_("no revision to drop was provided"))

    # currently drop supports dropping only one changeset at a time
    if len(revs) > 1:
        raise error.Abort(_("only one revision can be dropped at a time"))

    revid = revs.first()
    changectx = repo[revid]
    if changectx.phase() == phases.public:
        raise error.Abort(_("public changeset which landed cannot be dropped"))

    parents = repo.revs("parents(%s)", revid)
    if len(parents) > 1:
        raise error.Abort(_("merge changeset cannot be dropped"))
    elif len(parents) == 0:
        raise error.Abort(_("root changeset cannot be dropped"))

    _showrev(ui, repo, revid)

    descendants = repo.revs("(%d::) - %d", revid, revid)
    parent = parents.first()
    with repo.wlock():
        with repo.lock():
            with repo.transaction("drop"):
                if len(descendants) > 0:
                    try:
                        rebasemod.rebase(ui,
                                         repo,
                                         dest=str(parent),
                                         rev=descendants)
                    except error.InterventionRequired:
                        ui.warn(
                            _("conflict occurred during drop: " +
                              "please fix it by running " +
                              "'hg rebase --continue', " +
                              "and then re-run 'hg drop'\n"))
                        raise
                    scmutil.cleanupnodes(repo, [changectx.node()], "drop")
Ejemplo n.º 2
0
def split(ui, repo, *revs, **opts):
    """split a changeset into smaller changesets

    Prompt for hunks to be selected until exhausted. Each selection of hunks
    will form a separate changeset, in order from parent to child: the first
    selection will form the first changeset, the second selection will form
    the second changeset, and so on.

    Operates on the current revision by default. Use --rev to split a given
    changeset instead.
    """
    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 = list(
            map(hex, repo.nodes("descendants(%d) - (%d)", rev, rev)))

    with repo.wlock(), repo.lock():
        cmdutil.bailifchanged(repo)
        if torebase:
            cmdutil.checkunfinished(repo)
        ctx = repo[rev]
        r = ctx.hex()
        allowunstable = visibility.tracking(repo) or obsolete.isenabled(
            repo, obsolete.allowunstableopt)
        if not allowunstable:
            # 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())
        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

        # We need to detect the case where the user selects all remaining
        # changes, as that will end the split.  That's the commit we want to
        # mark as the result of the split.  To do this, wrap the recordfilter
        # function and compare the output to see if it contains all the
        # originalchunks.
        shouldrecordmutation = [False]

        def mutinfo(extra):
            if shouldrecordmutation[0]:
                return mutation.record(
                    repo,
                    extra,
                    [ctx.node()],
                    "split",
                    splitting=[c.node() for c in newcommits],
                )

        def recordfilter(ui, originalchunks, operation=None):
            chunks, newopts = cmdutil.recordfilter(ui, originalchunks,
                                                   operation)
            if cmdutil.comparechunks(chunks, originalchunks):
                shouldrecordmutation[0] = True
            return chunks, newopts

        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
        opts["_commitmutinfofunc"] = mutinfo
        try:
            while haschanges():
                pats = ()
                with repo.transaction("split"):
                    cmdutil.dorecord(ui, repo, commands.commit, "commit",
                                     False, 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":
                        shouldrecordmutation[0] = True
                        with repo.transaction("split"):
                            commands.commit(ui, repo, **opts)
                        newcommits.append(repo["."])
                        break
                else:
                    ui.status(_("no more change to split\n"))
        except Exception:
            # Rollback everything
            hg.updaterepo(repo, r, True)  # overwrite=True
            if newcommits:
                visibility.remove(repo, [c.node() for c in newcommits])

            if bookactive is not None:
                bookmarks.activate(repo, bookactive)
            raise

        if newcommits:
            phabdiffs = {}
            for c in newcommits:
                phabdiff = diffprops.parserevfromcommitmsg(
                    repo[c].description())
                if phabdiff:
                    phabdiffs.setdefault(phabdiff, []).append(c)
            if any(len(commits) > 1 for commits in phabdiffs.values()):
                hintutil.trigger("split-phabricator",
                                 ui.config("split", "phabricatoradvice"))

            tip = repo[newcommits[-1]]
            with repo.transaction("post-split"):
                bmupdate(tip.node())
                if bookactive is not None:
                    bookmarks.activate(repo, bookactive)
                if obsolete.isenabled(repo, obsolete.createmarkersopt):
                    obsolete.createmarkers(repo, [(repo[r], newcommits)],
                                           operation="split")
            if torebase:
                rebaseopts = {"dest": "_destrestack(SRC)", "rev": torebase}
                rebase.rebase(ui, repo, **rebaseopts)
            unfi = repo
            with repo.transaction("post-split-hide"):
                visibility.remove(repo, [unfi[r].node()])
Ejemplo n.º 3
0
def createcmd(ui, repo, pats, opts):
    """subcommand that creates a new shelve"""
    with repo.wlock():
        cmdutil.checkunfinished(repo)
        return _docreatecmd(ui, repo, pats, opts)
Ejemplo n.º 4
0
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)

        if obsshelve:
            _obsoleteredundantnodes(repo, tr, pctx, shelvectx, tmpwctx)

        shelvedstate.clear(repo)
        _finishunshelve(repo, oldtiprev, tr, activebookmark, obsshelve)
        unshelvecleanup(ui, repo, basename, opts)
    finally:
        if tr:
            tr.release()
        lockmod.release(lock)
Ejemplo n.º 5
0
def redo(ui, repo, *args, **opts):
    """undo the last undo

    Reverse the effects of an :hg:`undo` operation.

    You can run :hg:`redo` multiple times to undo a series of :hg:`undo`
    commands. Alternatively, you can explicitly specify the number of
    :hg:`undo` commands to undo by providing a number as a positional argument.

    Specify --preview to see a graphical display that shows what your smartlog
    will look like after you run the command.

    For an interactive interface, run :hg:`undo --interactive`. This command
    enables you to visually step backwards and forwards in the undo history.
    Run :hg:`help undo` for more information.

    """
    shiftedindex = _computerelative(repo, 0)
    preview = opts.get("preview")

    branch = ""
    reverseindex = 0
    redocount = 0
    done = False
    while not done:
        # we step back the linear undo log
        # redoes cancel out undoes, if we have one more undo, we should undo
        # there, otherwise we continue looking
        # we are careful to not redo past absolute undoes (bc we loose undoredo
        # log info)
        # if we run into something that isn't undo or redo, we Abort (including
        # gaps in the log)
        # we extract the --index arguments out of undoes to make sure we update
        # the undoredo index correctly
        nodedict = _readindex(repo, reverseindex)
        commandstr = _readnode(repo, "command.i", nodedict["command"])
        commandlist = commandstr.split("\0")

        if "True" == nodedict["unfinished"]:
            # don't want to redo to an interupted state
            reverseindex += 1
        elif commandlist[0] == "undo":
            undoopts = {}
            fancyopts.fancyopts(
                commandlist,
                cmdtable["undo"][1] + commands.globalopts,
                undoopts,
                gnu=True,
            )
            if redocount == 0:
                # want to go to state before the undo (not after)
                toshift = undoopts["step"]
                shiftedindex -= toshift
                reverseindex += 1
                branch = undoopts.get("branch")
                done = True
            else:
                if undoopts["absolute"]:
                    raise error.Abort(_("can't redo past absolute undo"))
                reverseindex += 1
                redocount -= 1
        elif commandlist[0] == "redo":
            redocount += 1
            reverseindex += 1
        else:
            raise error.Abort(_("nothing to redo"))

    if preview:
        _preview(ui, repo, reverseindex)
        return

    with repo.wlock(), repo.lock(), repo.transaction("redo"):
        cmdutil.checkunfinished(repo)
        cmdutil.bailifchanged(repo)
        repo = repo.unfiltered()
        _undoto(ui, repo, reverseindex)
        # update undredo by removing what the given undo added
        _logundoredoindex(repo, shiftedindex, branch)
Ejemplo n.º 6
0
def undo(ui, repo, *args, **opts):
    """undo the last local command

    Reverse the effects of the last local command. A local command is one that
    changed the currently checked out commit, that modified the contents of
    local commits, or that changed local bookmarks. Examples of local commands
    include :hg:`checkout`, :hg:`commit`, :hg:`amend`, and :hg:`rebase`.

    You cannot use :hg:`undo` to undo uncommited changes in the working copy,
    or changes to remote bookmarks.

    You can run :hg:`undo` multiple times to undo a series of local commands.
    Alternatively, you can explicitly specify the number of local commands to
    undo using --step. This number can also be specified as a positional
    argument.

    To undo the effects of :hg:`undo`, run :hg:`redo`. Run :hg:`help redo` for
    more information.

    Include --keep to preserve the state of the working copy. For example,
    specify --keep when running :hg:`undo` to reverse the effects of an
    :hg:`commit` or :hg:`amend` operation while still preserving changes
    in the working copy. These changes will appear as pending changes.

    Specify --preview to see a graphical display that shows what your smartlog
    will look like after you run the command. Specify --interactive for an
    interactive version of this preview in which you can step backwards and
    forwards in the undo history.

    .. note::

       :hg:`undo` cannot be used with non-local commands, or with commands
       that are read-only. :hg:`undo` will skip over these commands in the
       undo history.

       For hybrid commands that result in both local and remote changes,
       :hg:`undo` will undo the local changes, but not the remote changes.
       For example, `hg pull --rebase` might move remote/master and also
       rebase local commits. In this situation, :hg:`undo` will revert the
       rebase, but not the change to remote/master.

    .. container:: verbose

        Branch limits the scope of an undo to a group of local (draft)
        changectxs, identified by any one member of this group.
    """
    reverseindex = opts.get("step")
    relativeundo = not opts.get("absolute")
    keep = opts.get("keep")
    branch = opts.get("branch")
    preview = opts.get("preview")
    interactive = opts.get("interactive")
    if interactive and interactiveui is None:
        raise error.Abort(_("interactive ui is not supported on Windows"))
    if interactive:
        preview = True

    repo = repo.unfiltered()

    if branch and reverseindex != 1 and reverseindex != -1:
        raise error.Abort(_("--branch with --index not supported"))
    if relativeundo:
        try:
            reverseindex = _computerelative(repo,
                                            reverseindex,
                                            absolute=not relativeundo,
                                            branch=branch)
        except IndexError:
            raise error.Abort(
                _("cannot undo this far - undo extension was not"
                  " enabled"))

    if branch and preview:
        raise error.Abort(_("--branch with --preview not supported"))

    if interactive:
        cmdutil.checkunfinished(repo)
        cmdutil.bailifchanged(repo)

        class undopreview(interactiveui.viewframe):
            def render(self):
                ui = self.ui
                ui.pushbuffer()
                return_code = _preview(ui, self.repo, self.index)
                if return_code == 1:
                    if self.index < 0:
                        self.index += 1
                        repo.ui.status(_("Already at newest repo state\a\n"))
                    elif self.index > 0:
                        self.index -= 1
                        repo.ui.status(_("Already at oldest repo state\a\n"))
                    _preview(ui, self.repo, self.index)
                text = ui.config(
                    "undo",
                    "interactivehelptext",
                    "legend: red - to hide; green - to revive\n",
                )
                repo.ui.status(text)
                repo.ui.status(
                    _("<-: newer  "
                      "->: older  "
                      "q: abort  "
                      "enter: confirm\n"))
                return ui.popbuffer()

            def rightarrow(self):
                self.index += 1

            def leftarrow(self):
                self.index -= 1

            def enter(self):
                del opts["preview"]
                del opts["interactive"]
                opts["absolute"] = "absolute"
                opts["step"] = self.index
                undo(ui, repo, *args, **opts)
                return

        viewobj = undopreview(ui, repo, reverseindex)
        interactiveui.view(viewobj)
        return
    elif preview:
        _preview(ui, repo, reverseindex)
        return

    with repo.wlock(), repo.lock(), repo.transaction("undo"):
        cmdutil.checkunfinished(repo)
        cmdutil.bailifchanged(repo)
        if not (opts.get("force") or _gapcheck(ui, repo, reverseindex)):
            raise error.Abort(
                _("attempted risky undo across"
                  " missing history"))
        _undoto(ui, repo, reverseindex, keep=keep, branch=branch)

        # store undo data
        # for absolute undos, think of this as a reset
        # for relative undos, think of this as an update
        _logundoredoindex(repo, reverseindex, branch)
Ejemplo n.º 7
0
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("amend", "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["."].hex(), 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 necessary. 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=hex(target),
                clean=opts.get("clean", False),
                merge=opts.get("merge", False),
            )

            # Print out the changeset we landed on.
            _showchangesets(ui, repo, nodes=[target])

            # Activate the bookmark on the new changeset.
            if not noactivate and not movebookmark:
                _activate(ui, repo, target)
Ejemplo n.º 8
0
def chistedit(ui, repo, *freeargs, **opts):
    """Provides a ncurses interface to histedit. Press ? in chistedit mode
    to see an extensive help. Requires python-curses to be installed."""

    if curses is None:
        raise error.Abort(_("Python curses library required"))

    # disable color
    ui._colormode = None

    try:
        keep = opts.get("keep")
        revs = opts.get("rev", [])[:]
        cmdutil.checkunfinished(repo)
        cmdutil.bailifchanged(repo)

        if os.path.exists(os.path.join(repo.path, "histedit-state")):
            raise error.Abort(
                _("history edit already in progress, try "
                  "--continue or --abort"))
        revs.extend(freeargs)
        if not revs:
            defaultrev = destutil.desthistedit(ui, repo)
            if defaultrev is not None:
                revs.append(defaultrev)
        if len(revs) != 1:
            raise error.Abort(
                _("histedit requires exactly one ancestor revision"))

        rr = list(repo.set("roots(%ld)", scmutil.revrange(repo, revs)))
        if len(rr) != 1:
            raise error.Abort(
                _("The specified revisions must have "
                  "exactly one common root"))
        root = rr[0].node()

        topmost, empty = repo.dirstate.parents()
        revs = histedit.between(repo, root, topmost, keep)
        if not revs:
            raise error.Abort(
                _("%s is not an ancestor of working directory") %
                node.short(root))

        ctxs = []
        for i, r in enumerate(revs):
            ctxs.append(histeditrule(repo[r], i))
        rc = curses.wrapper(functools.partial(main, repo, ctxs))
        curses.echo()
        curses.endwin()
        if rc is False:
            ui.write(_("chistedit aborted\n"))
            return 0
        if type(rc) is list:
            ui.status(_("running histedit\n"))
            rules = makecommands(rc)
            filename = repo.localvfs.join("chistedit")
            with open(filename, "w+") as fp:
                for r in rules:
                    fp.write(r)
            opts["commands"] = filename
            return histedit.histedit(ui, repo, *freeargs, **opts)
    except KeyboardInterrupt:
        pass
    return -1