Beispiel #1
0
def _docreatecmd(ui, repo, pats, opts):
    wctx = repo[None]
    parents = wctx.parents()
    if len(parents) > 1:
        raise error.Abort(_("cannot shelve while merging"))
    parent = parents[0]
    origbranch = wctx.branch()

    if parent.node() != nodemod.nullid:
        desc = "shelve changes to: %s" % parent.description().split("\n", 1)[0]
    else:
        desc = "(changes in empty repository)"

    if not opts.get("message"):
        opts["message"] = desc

    activebookmark = None
    try:
        with repo.lock(), repo.transaction("commit", report=None):

            interactive = opts.get("interactive", False)
            includeunknown = opts.get(
                "unknown", False) and not opts.get("addremove", False)

            name = getshelvename(repo, parent, opts)
            activebookmark = _backupactivebookmark(repo)
            extra = {}
            if includeunknown:
                _includeunknownfiles(repo, pats, opts, extra)

            if _iswctxonnewbranch(repo) and not _isbareshelve(pats, opts):
                # In non-bare shelve we don't store newly created branch
                # at bundled commit
                repo.dirstate.setbranch(repo["."].branch())

            commitfunc = getcommitfunc(extra, interactive, editor=True)
            if not interactive:
                node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
            else:
                node = cmdutil.dorecord(ui, repo, commitfunc, None, False,
                                        cmdutil.recordfilter, *pats, **opts)
            if not node:
                _nothingtoshelvemessaging(ui, repo, pats, opts)
                return 1

            _hidenodes(repo, [node])
    except (KeyboardInterrupt, Exception):
        if activebookmark:
            bookmarks.activate(repo, activebookmark)
        raise

    _shelvecreatedcommit(ui, repo, node, name)

    if ui.formatted:
        desc = util.ellipsis(desc, ui.termwidth())
    ui.status(_("shelved as %s\n") % name)

    # current wc parent may be already obsolete because
    # it might have been created previously and shelve just
    # reuses it
    try:
        hg.update(repo, parent.node())
    except (KeyboardInterrupt, Exception):
        # failed to update to the original revision, which has left us on the
        # (hidden) shelve commit.  Move directly to the original commit by
        # updating the dirstate parents.
        repo.setparents(parent.node())
        raise
    finally:
        if origbranch != repo["."].branch() and not _isbareshelve(pats, opts):
            repo.dirstate.setbranch(origbranch)
        if activebookmark:
            bookmarks.activate(repo, activebookmark)
Beispiel #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()])
Beispiel #3
0
def amend(ui, repo, *pats, **opts):
    """save pending changes to the current commit

    Replaces your current commit with a new commit that contains the contents
    of the original commit, plus any pending changes.

    By default, all pending changes (in other words, those reported by 'hg
    status') are committed. To commit only some of your changes,
    you can:

    - Specify an exact list of files for which you want changes committed.

    - Use the -I or -X flags to pattern match file names to exclude or
      include by using a fileset. See 'hg help filesets' for more
      information.

    - Specify the --interactive flag to open a UI that will enable you
      to select individual insertions or deletions.

    By default, hg amend reuses your existing commit message and does not
    prompt you for changes. To change your commit message, you can:

    - Specify --edit / -e to open your configured editor to update the
      existing commit message.

    - Specify --message / -m to replace the entire commit message, including
      any commit template fields, with a string that you specify.

    .. note::

       Specifying -m overwrites all information in the commit message,
       including information specified as part of a pre-loaded commit
       template. For example, any information associating this commit with
       a code review system will be lost and might result in breakages.

    When you amend a commit that has descendants, those descendants are
    rebased on top of the amended version of the commit, unless doing so
    would result in merge conflicts. If this happens, run 'hg restack'
    to manually trigger the rebase so that you can go through the merge
    conflict resolution process.
    """
    # 'rebase' is a tristate option: None=auto, True=force, False=disable
    rebase = opts.get("rebase")
    to = opts.get("to")

    if rebase and _histediting(repo):
        # if a histedit is in flight, it's dangerous to remove old commits
        hint = _("during histedit, use amend without --rebase")
        raise error.Abort("histedit in progress", hint=hint)

    badflags = [flag for flag in ["rebase", "fixup"] if opts.get(flag, None)]
    if opts.get("interactive") and badflags:
        raise error.Abort(
            _("--interactive and --%s are mutually exclusive") % badflags[0])

    fixup = opts.get("fixup")

    badtoflags = [
        "rebase",
        "fixup",
        "addremove",
        "edit",
        "interactive",
        "include",
        "exclude",
        "message",
        "logfile",
        "date",
        "user",
        "no-move-detection",
        "stack",
        "template",
    ]

    if to and any(opts.get(flag, None) for flag in badtoflags):
        raise error.Abort(_("--to cannot be used with any other options"))

    if fixup:
        ui.warn(
            _("warning: --fixup is deprecated and WILL BE REMOVED. use 'hg restack' instead.\n"
              ))
        fixupamend(ui, repo)
        return

    if to:
        amendtocommit(ui, repo, to)
        return

    old = repo["."]
    if old.phase() == phases.public:
        raise error.Abort(_("cannot amend public changesets"))
    if len(repo[None].parents()) > 1:
        raise error.Abort(_("cannot amend while merging"))

    haschildren = len(old.children()) > 0

    opts["message"] = cmdutil.logmessage(repo, opts)
    # Avoid further processing of any logfile. If such a file existed, its
    # contents have been copied into opts['message'] by logmessage
    opts["logfile"] = ""

    if not opts.get("noeditmessage") and not opts.get("message"):
        opts["message"] = old.description()

    commitdate = opts.get("date")
    if not commitdate:
        if ui.config("amend", "date") == "implicitupdate":
            commitdate = "now"
        else:
            commitdate = old.date()

    oldbookmarks = old.bookmarks()
    tr = None
    wlock = None
    lock = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()

        if opts.get("interactive"):
            # Strip the interactive flag to avoid infinite recursive loop
            opts.pop("interactive")
            cmdutil.dorecord(ui, repo, amend, None, False,
                             cmdutil.recordfilter, *pats, **opts)
            return

        else:
            node = cmdutil.amend(ui, repo, old, {}, pats, opts)

        if node == old.node():
            ui.status(_("nothing changed\n"))
            return 1

        conf = ui.config("amend", "autorestack", RESTACK_DEFAULT)
        noconflict = None

        # RESTACK_NO_CONFLICT requires IMM.
        if conf == RESTACK_NO_CONFLICT and not ui.config(
                "rebase", "experimental.inmemory", False):
            conf = RESTACK_DEFAULT

        # If they explicitly disabled the old behavior, disable the new behavior
        # too, for now.
        # internal config: commands.amend.autorebase
        if ui.configbool("commands", "amend.autorebase") is False:
            # In the future we'll add a nag message here.
            conf = RESTACK_NEVER

        if conf not in RESTACK_VALUES:
            ui.warn(
                _('invalid amend.autorestack config of "%s"; falling back to %s\n'
                  ) % (conf, RESTACK_DEFAULT))
            conf = RESTACK_DEFAULT

        if haschildren and rebase is None and not _histediting(repo):
            if conf == RESTACK_ALWAYS:
                rebase = True
            elif conf == RESTACK_NO_CONFLICT:
                if repo[None].dirty():
                    # For now, only restack if the WC is clean (t31742174).
                    ui.status(
                        _("not restacking because working copy is dirty\n"))
                    rebase = False
                else:
                    # internal config: amend.autorestackmsg
                    msg = ui.config(
                        "amend",
                        "autorestackmsg",
                        _("restacking children automatically (unless they conflict)"
                          ),
                    )
                    if msg:
                        ui.status("%s\n" % msg)
                    rebase = True
                    noconflict = True
            elif conf == RESTACK_ONLY_TRIVIAL:
                newcommit = repo[node]
                # If the rebase did not change the manifest and the
                # working copy is clean, force the children to be
                # restacked.
                rebase = (old.manifestnode() == newcommit.manifestnode()
                          and not repo[None].dirty())
                if rebase:
                    hintutil.trigger("amend-autorebase")
            else:
                rebase = False

        if haschildren and not rebase and not _histediting(repo):
            hintutil.trigger("amend-restack", old.node())

        changes = []
        # move old bookmarks to new node
        for bm in oldbookmarks:
            changes.append((bm, node))

        tr = repo.transaction("fixupamend")
        repo._bookmarks.applychanges(repo, tr, changes)
        tr.close()

        if rebase and haschildren:
            noconflictmsg = _(
                "restacking would create conflicts (%s in %s), so you must run it manually\n(run `hg restack` manually to restack this commit's children)"
            )
            revs = [
                c.hex() for c in repo.set("(%n::)-%n", old.node(), old.node())
            ]
            with ui.configoverride({
                ("rebase", "noconflictmsg"): noconflictmsg
            }):
                # Note: this has effects on linearizing (old:: - old). That can
                # fail. If that fails, it might make sense to try a plain
                # rebase -s (old:: - old) -d new.
                restack.restack(ui, repo, rev=revs, noconflict=noconflict)

        showtemplate(ui, repo, repo[node], **opts)
    finally:
        lockmod.release(wlock, lock, tr)