[PATCH 1 of 3 evolve-ext-V2] evolve: add ordering of the revisions for evolve --rev
Pierre-Yves David
pierre-yves.david at ens-lyon.org
Thu Jun 4 12:00:51 CDT 2015
On 06/04/2015 09:54 AM, Laurent Charignon wrote:
>
>> On Jun 4, 2015, at 9:19 AM, Pierre-Yves David <pierre-yves.david at ens-lyon.org> wrote:
>>
>>
>>
>> On 06/03/2015 02:04 PM, Laurent Charignon wrote:
>>> # HG changeset patch
>>> # User Laurent Charignon <lcharignon at fb.com>
>>> # Date 1433283749 25200
>>> # Tue Jun 02 15:22:29 2015 -0700
>>> # Node ID 16e32f9b706fa06a5a3c88e8a26139bfd128f4bf
>>> # Parent b4a62d6f03534de6e65962c0e92cadea4b6cbad1
>>> evolve: add ordering of the revisions for evolve --rev
>>>
>>> When running evolve --rev we want to process the revisions in an optimal fashion
>>> to solve the maximum amount of trouble in the minimum number of steps.
>>> This patch adds a step to evolve --rev to order the revision before solving
>>> the troubles. A simple test is added to cover a basic case.
>>
>> We are getting there. I will take a version with fixed docstring, see below (and discuss).
>>
>>> diff --git a/hgext/evolve.py b/hgext/evolve.py
>>> --- a/hgext/evolve.py
>>> +++ b/hgext/evolve.py
>>> @@ -28,6 +28,7 @@
>>> from StringIO import StringIO
>>> import struct
>>> import re
>>> +import collections
>>> import socket
>>> import errno
>>> sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
>>> @@ -1232,6 +1233,69 @@
>>> if repo['.'] != startnode:
>>> ui.status(_('working directory is now at %s\n') % repo['.'])
>>>
>>> +def _singlesuccessor(repo, p):
>>
>> This function is non trivial and need a docstring explaining what is its purpose (returning the one unique latest successors of 'p' or die trying.)
>>
>>> + if not p.obsolete():
>>> + return p
>>> + obs = repo[p]
>>> + ui = repo.ui
>>> + newer = obsolete.successorssets(repo, obs.node())
>>> + # search of a parent which is not killed
>>> + while not newer or newer == [()]:
>>> + ui.debug("stabilize target %s is plain dead,"
>>> + " trying to stabilize on its parent\n" %
>>> + obs)
>>> + obs = obs.parents()[0]
>>> + newer = obsolete.successorssets(repo, obs.node())
>>> + if len(newer) > 1:
>>> + raise util.Abort(_("conflict rewriting. can't choose destination\n"))
>>> + return repo[newer[0][0]].rev()
>>> +
>>> +def orderrevs(repo, revs):
>>> + """ Compute an ordering to solve instability for the given revs
>>> +
>>> + - Takes revs a list of troubled revisions
>>> + - Returns the same revisions ordered to solve their instability
>>> +
>>> + First we compute dependencies between the revs.
>>> + For each troubled revision we keep track of what instability if any should
>>> + be resolved in order to resolve it.
>>> +
>>> + Then we 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 build
>>> + """
>>
>> That docstring is full of good inforation, however it a bit too low level for a docstring. Most of it should be moved as inline comment (#). The goal of the docstring is to carry the 'contract' of the function, its high level goal. The current content is details about the implementation.
>>
>> What this function do is something along the line of
>>
>> - return revision from the bottom to the top of the -final- stack that the stabilization process will produce.
>>
>> And this important because:
>>
>> - This ensure we can stabilize each revision on its final destination. (or phrased the other way. final destination is always available when we process a revision)
>
> Ok
>
>>
>>> + # 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)
>>> +
>>> + # Step 1: Build the dependency graph
>>> + for r in revs:
>>> + dependencies[r] = set()
>>> + for p in repo[r].parents():
>>> + succ = _singlesuccessor(repo, p)
>>> + if succ in revs:
>>> + dependencies[r].add(succ)
>>> + rdependencies[succ].add(r)
>>> +
>>> + # Step 2: Build the ordering
>>> + solvablerevs = collections.deque([ r for r in sorted(dependencies.keys())\
>>> + if len(dependencies[r]) == 0 ])
>>
>> Small nits:
>> - you could use a heap, they are sorted by definition
>> - You may want to use a stack instead this would give some of the 'one branch after the other' property for free.
>
> I think that it is the best option, let's go for that
I think I would be happy to take this patch as is (with fixed docstring)
and take code refactor in another patch. To make the topic move forward.
>> - you can use dependencies.items() to get both key value in the same go
>
> Yes but it makes the sorting code more complicated if we don't go with the heap.
does it?
[key for key, value in sorted(dependencies.keys()) if value]
(other nit, not space inside (), [] amd {})
>> - Python style recommendation is to use `if not dep[r]` instead of checking len to 0
>
> Ok
>
>>
>>> + ordering = []
>>> + while solvablerevs:
>>> + rev = solvablerevs.popleft()
>>> + for dependent in rdependencies[rev]:
>>> + dependencies[dependent].remove(rev)
>>> + if len(dependencies[dependent]) == 0:
>>
>> (ditto, small nits, regarding len usage here)
>>
>>> + solvablerevs.append(dependent)
>>> + ordering.append(rev)
>>
>> V2 looks simpler and more efficient, nice job.
>>
>> --
>> Pierre-Yves David
>
--
Pierre-Yves David
More information about the Mercurial-devel
mailing list