示例#1
0
def restackonce(
    ui,
    repo,
    rev,
    rebaseopts=None,
    childrenonly=False,
    noconflict=None,
    noconflictmsg=None,
):
    """Rebase all descendants of precursors of rev onto rev, thereby
       stabilzing any non-obsolete descendants of those precursors.
       Takes in an optional dict of options for the rebase command.
       If childrenonly is True, only rebases direct children of precursors
       of rev rather than all descendants of those precursors.

       NOTE(phillco): This function shouldn't be used; prefer restack.restack
       or a custom rebase using `-d _destrestack(SRC)`.
    """
    # Get visible, non-obsolete descendants of precusors of rev.
    allpredecessors = repo.revs("predecessors(%d) - (%d)", rev, rev)
    fmt = "%s(%%ld) - %%ld - obsolete()" % ("children"
                                            if childrenonly else "descendants")
    descendants = repo.revs(fmt, allpredecessors, allpredecessors)

    # Nothing to do if there are no descendants.
    if not descendants:
        return

    # Overwrite source and destination, leave all other options.
    if rebaseopts is None:
        rebaseopts = {}
    rebaseopts["rev"] = descendants
    rebaseopts["dest"] = rev
    rebaseopts["noconflict"] = noconflict

    overrides = {
        # Explicitly disable revnum deprecation warnings. This is an internal
        # use of "rebase" that does not contain user-provided revsets.
        ("devel", "legacy.revnum"):
        ""
    }
    try:
        tweakdefaults = extensions.find("tweakdefaults")
    except KeyError:
        # No tweakdefaults extension -- skip this since there is no wrapper
        # to set the metadata.
        pass
    else:
        # We need to ensure that the 'operation' field in the obsmarker metadata
        # is always set to 'rebase', regardless of the current command so that
        # the restacked commits will appear as 'rebased' in smartlog.
        overrides[(tweakdefaults.globaldata,
                   tweakdefaults.createmarkersoperation)] = "rebase"

    if noconflictmsg:
        overrides[("rebase", "noconflictmsg")] = noconflictmsg

    # Perform rebase.
    with repo.ui.configoverride(overrides, "restack"):
        rebase.rebase(ui, repo, **rebaseopts)
示例#2
0
def debugbruterebase(ui, repo, source, dest):
    """for every non-empty subset of source, run rebase -r subset -d dest

    Print one line summary for each subset. Assume obsstore is enabled.
    """
    srevs = list(repo.revs(source))

    with repo.wlock(), repo.lock():
        repolen = len(repo)
        cl = repo.changelog
        newrevs = []

        def getdesc(rev):
            result = cl.changelogrevision(rev).description
            if rev in newrevs:
                result += "'"
            return result

        for i in xrange(1, 2**len(srevs)):
            subset = [rev for j, rev in enumerate(srevs) if i & (1 << j) != 0]
            spec = revsetlang.formatspec("%ld", subset)
            tr = repo.transaction("rebase")
            tr.report = lambda x: 0  # hide "transaction abort"

            oldnodes = set(repo.nodes("all()"))
            ui.pushbuffer()
            try:
                rebase.rebase(ui, repo, dest=dest, rev=[spec])
            except error.Abort as ex:
                summary = "ABORT: %s" % ex
            except Exception as ex:
                summary = "CRASH: %s" % ex
            else:
                # short summary about new nodes
                cl = repo.changelog
                descs = []
                newnodes = set(repo.nodes("all()"))
                newrevs = list(map(cl.rev, newnodes - oldnodes))
                for rev in sorted(newrevs):
                    desc = "%s:" % getdesc(rev)
                    for prev in cl.parentrevs(rev):
                        if prev > -1:
                            desc += getdesc(prev)
                    descs.append(desc)
                descs.sort()
                summary = " ".join(descs)
            ui.popbuffer()
            repo.localvfs.tryunlink("rebasestate")

            subsetdesc = "".join(getdesc(rev) for rev in subset)
            ui.write(("%s: %s\n") % (subsetdesc.rjust(len(srevs)), summary))
            tr.abort()
示例#3
0
def restack(ui, repo, **rebaseopts):
    """Repair a situation in which one or more commits in a stack
       have been obsoleted (thereby leaving their descendants in the stack
       orphaned) by finding any such commits and rebasing their descendants
       onto the latest version of each respective commit.

    """
    rebaseopts = rebaseopts.copy()

    with repo.wlock(), repo.lock():
        # Find drafts connected to the current stack via either changelog or
        # obsolete graph. Note: "draft() & ::." is optimized by D441.

        if not rebaseopts["rev"]:
            # 1. Connect drafts via changelog
            revs = list(repo.revs("(draft() & ::.)::"))
            if not revs:
                # "." is probably public. Check its direct children.
                revs = repo.revs("draft() & children(.)")
                if not revs:
                    ui.status(_("nothing to restack\n"))
                    return 1
            # 2. Connect revs via obsolete graph
            revs = list(
                repo.revs("successors(%ld)+predecessors(%ld)", revs, revs))
            # 3. Connect revs via changelog again to cover missing revs
            revs = list(repo.revs("draft() & ((draft() & %ld)::)", revs))

            rebaseopts["rev"] = [ctx.hex() for ctx in repo.set("%ld", revs)]

        rebaseopts["dest"] = "_destrestack(SRC)"

        rebase.rebase(ui, repo, **rebaseopts)

        # Ensure that we always end up on the latest version of the
        # current changeset. Usually, this will be taken care of
        # by the rebase operation. However, in some cases (such as
        # if we are on the precursor of the base changeset) the
        # rebase will not update to the latest version, so we need
        # to do this manually.
        successor = repo.revs("successors(.) - .").last()
        if successor is not None:
            commands.update(ui, repo, rev=repo[successor].hex())
示例#4
0
文件: split.py 项目: zerkella/eden
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()])