hgext3rd/topic/evolvebits.py
author Sushil khanchi <sushilkhanchi97@gmail.com>
Fri, 21 Sep 2018 15:52:53 +0530
changeset 4122 4eb3877540f1
parent 3697 6aff4bb3970d
child 4814 48b30ff742cb
permissions -rw-r--r--
evovle: remove redundancy in evolve output Copying the discription of this redundancy issue given by Pierre Yves David: When running `hg evolve` to stabilize orphan changeset output about the currently stabilized changeset is issued. For example: $ hg evolve move:[3] a3 atop:[4] a2 working directory is now at 7c5649f73d11 This output can become quite repetitive when orphan are stabilized atop each other. For example: $ hg evolve --all move:[8] dansk 2! atop:[10] dansk! merging main-file-1 move:[9] dansk 3! atop:[11] dansk 2! In this case it would be smoother to issue: $ hg evolve --all move:[8] dansk 2! atop:[10] dansk! merging main-file-1 move:[9] dansk 3! Since we are moving "dansk 3!" atop the changeset we just stabilized. When adding this be careful that we still want to issue the "atop" message in various cases: 1. first changesets in a stack 2. when the orphan is not stabilized atop previous one 3. when using hg evolve --continue to resume an evolution So, I have made the changes which also respect above listed three points. And changes in tests/test-evovle*.t reflecting the changed behavior.

import collections

from mercurial import (
    obsutil,
)

# Copied from evolve 081605c2e9b6

def _orderrevs(repo, revs):
    """Compute an ordering to solve instability for the given revs

    revs is a list of unstable revisions.

    Returns the same revisions ordered to solve their instability from the
    bottom to the top of the stack that the stabilization process will produce
    eventually.

    This ensures the minimal number of stabilizations, as we can stabilize each
    revision on its final stabilized destination.
    """
    # Step 1: Build the dependency graph
    dependencies, rdependencies = builddependencies(repo, revs)
    # Step 2: Build the ordering
    # Remove the revisions with no dependency(A) and add them to the ordering.
    # Removing these revisions leads to new revisions with no dependency (the
    # one depending on A) that we can remove from the dependency graph and add
    # to the ordering. We progress in a similar fashion until the ordering is
    # built
    solvablerevs = [r for r in sorted(dependencies.keys())
                    if not dependencies[r]]
    ordering = []
    while solvablerevs:
        rev = solvablerevs.pop()
        for dependent in rdependencies[rev]:
            dependencies[dependent].remove(rev)
            if not dependencies[dependent]:
                solvablerevs.append(dependent)
        del dependencies[rev]
        ordering.append(rev)

    ordering.extend(sorted(dependencies))
    return ordering

def builddependencies(repo, revs):
    """returns dependency graphs giving an order to solve instability of revs
    (see _orderrevs for more information on usage)"""

    # For each troubled revision we keep track of what instability if any should
    # be resolved in order to resolve it. Example:
    # dependencies = {3: [6], 6:[]}
    # Means that: 6 has no dependency, 3 depends on 6 to be solved
    dependencies = {}
    # rdependencies is the inverted dict of dependencies
    rdependencies = collections.defaultdict(set)

    for r in revs:
        dependencies[r] = set()
        for p in repo[r].parents():
            try:
                succ = _singlesuccessor(repo, p)
            except MultipleSuccessorsError as exc:
                dependencies[r] = exc.successorssets
                continue
            if succ in revs:
                dependencies[r].add(succ)
                rdependencies[succ].add(r)
    return dependencies, rdependencies

def _singlesuccessor(repo, p):
    """returns p (as rev) if not obsolete or its unique latest successors

    fail if there are no such successor"""

    if not p.obsolete():
        return p.rev()
    obs = repo[p]
    ui = repo.ui
    newer = obsutil.successorssets(repo, obs.node())
    # search of a parent which is not killed
    while not newer:
        ui.debug("stabilize target %s is plain dead,"
                 " trying to stabilize on its parent\n" %
                 obs)
        obs = obs.parents()[0]
        newer = obsutil.successorssets(repo, obs.node())
    if 1 < len(newer):
        # divergence case
        # we should pick as arbitrary one
        raise MultipleSuccessorsError(newer)
    elif 1 < len(newer[0]):
        splitheads = list(repo.revs('heads(%ln::%ln)', newer[0], newer[0]))
        if 1 < len(splitheads):
            # split case, See if we can make sense of it.
            raise MultipleSuccessorsError(newer)
        return splitheads[0]

    return repo[newer[0][0]].rev()

class MultipleSuccessorsError(RuntimeError):
    """Exception raised by _singlesuccessor when multiple successor sets exists

    The object contains the list of successorssets in its 'successorssets'
    attribute to call to easily recover.
    """

    def __init__(self, successorssets):
        self.successorssets = successorssets