mercurial/ancestor.py
author Jim Hague <jim.hague@acm.org>
Thu, 01 Mar 2012 15:27:24 +0000
changeset 16223 ac4fd3238ead
parent 14494 1ffeeb91c55d
child 17970 0b03454abae7
permissions -rw-r--r--
bugzilla: allow change comment to mark bugs fixed Add a second regular expression used when scanning change comments. Bugs matched by this new regular expression have the bug comments and optionally hours updated as with the first regular expression, but they are also marked as fixed. The bug status and resolution to set to mark a bug as fixed can be configured. By default status is set to RESOLVED and resolution to FIXED, the default Bugzilla settings. For example, a change comment containing 'Fixes 1234 h1.5' will be added to bug 1234, the bug will have its working time increased by 1.65 hours, and the bug will be marked RESOLVED/FIXED. Change comments may contain both bug update and fix instructions. If the same bug ID occurs in both, the last instruction found takes precedence. The patch adds new bug states 'bug_status' and 'resolution' and actions to update them to the XMLRPC and XMLRPC/email access methods. XMLRPC does not support marking bugs as fixed when used with Bugzilla versions prior to 4.0. When used with an earlier Bugzilla version, a warning is issued and only comment and hours updated.

# ancestor.py - generic DAG ancestor algorithm for mercurial
#
# Copyright 2006 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

import heapq

def ancestor(a, b, pfunc):
    """
    Returns the common ancestor of a and b that is furthest from a
    root (as measured by longest path) or None if no ancestor is
    found. If there are multiple common ancestors at the same
    distance, the first one found is returned.

    pfunc must return a list of parent vertices for a given vertex
    """

    if a == b:
        return a

    a, b = sorted([a, b])

    # find depth from root of all ancestors
    # depth is stored as a negative for heapq
    parentcache = {}
    visit = [a, b]
    depth = {}
    while visit:
        vertex = visit[-1]
        pl = pfunc(vertex)
        parentcache[vertex] = pl
        if not pl:
            depth[vertex] = 0
            visit.pop()
        else:
            for p in pl:
                if p == a or p == b: # did we find a or b as a parent?
                    return p # we're done
                if p not in depth:
                    visit.append(p)
            if visit[-1] == vertex:
                # -(maximum distance of parents + 1)
                depth[vertex] = min([depth[p] for p in pl]) - 1
                visit.pop()

    # traverse ancestors in order of decreasing distance from root
    def ancestors(vertex):
        h = [(depth[vertex], vertex)]
        seen = set()
        while h:
            d, n = heapq.heappop(h)
            if n not in seen:
                seen.add(n)
                yield (d, n)
                for p in parentcache[n]:
                    heapq.heappush(h, (depth[p], p))

    def generations(vertex):
        sg, s = None, set()
        for g, v in ancestors(vertex):
            if g != sg:
                if sg:
                    yield sg, s
                sg, s = g, set((v,))
            else:
                s.add(v)
        yield sg, s

    x = generations(a)
    y = generations(b)
    gx = x.next()
    gy = y.next()

    # increment each ancestor list until it is closer to root than
    # the other, or they match
    try:
        while True:
            if gx[0] == gy[0]:
                for v in gx[1]:
                    if v in gy[1]:
                        return v
                gy = y.next()
                gx = x.next()
            elif gx[0] > gy[0]:
                gy = y.next()
            else:
                gx = x.next()
    except StopIteration:
        return None