##// END OF EJS Templates
scripts: handle running with pending deleted files...
scripts: handle running with pending deleted files Don't fail on files in manifest missing in working directory.

File last commit:

r7314:6834e126 default
r8548:0a84ef07 default
Show More
graphmod.py
178 lines | 6.3 KiB | text/x-python | PythonLexer
# -*- coding: utf-8 -*-
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Modified mercurial DAG graph functions that re-uses VCS structure
It allows to have a shared codebase for DAG generation for hg and git repos
"""
nullrev = -1
def _first_known_ancestors(parentrev_func, minrev, knownrevs, head):
"""
Return the apparent parents of the head revision in a filtered DAG.
This is like calling plain parentrev_func, except that if the parent has
been filtered (isn't in knownrevs), recurse on its parents.
For ancestors that are unknown in knownrevs, the revision number is negated.
(This means that a Mercurial revision can have more than 2 "parents".)
parentrev_func defines the full DAG.
knownrevs contains the subset we care about. minrev is lower bound on knownrevs.
"""
pending = set([head])
seen = set()
ancestors = set()
while pending:
r = pending.pop()
if r < minrev:
if r > nullrev: # ignore -1 returned from parentrev_func
ancestors.add(-head) # fake unique unknown parent for this rev
elif r in knownrevs:
ancestors.add(r)
elif r not in seen:
pending.update(parentrev_func(r))
seen.add(r)
return ancestors
def graph_data(repo, revs):
"""Return a DAG with colored edge information for revs
For each DAG node this function emits tuples::
((col, color), [(col, nextcol, color)])
with the following new elements:
- Tuple (col, color) with column and color index for the current node
- A list of tuples indicating the edges between the current node and its
parents.
"""
dag = _dagwalker(repo, revs)
return list(_colored(repo, dag))
def _dagwalker(repo, revs):
"""Iterate over revs, yielding revs (highest first) and parents to show in the graph."""
if not revs:
return
if repo.alias == 'hg':
parentrev_func = repo._repo.changelog.parentrevs
elif repo.alias == 'git':
def parentrev_func(rev):
return [x.revision for x in repo[rev].parents]
minrev = revs[-1] # assuming sorted with highest revision numbers first
knownrevs = set(revs)
acache = {}
for rev in revs:
parents = set(parentrev_func(rev)) - set([nullrev])
dagparents = parents & knownrevs
# Calculate indirect parents
for p in parents - dagparents:
ancestors = acache.get(p)
if ancestors is None:
ancestors = acache[p] = _first_known_ancestors(parentrev_func, minrev, knownrevs, p)
dagparents.update(ancestors)
yield (rev, sorted(dagparents))
def _colored(repo, dag):
"""annotates a DAG with colored edge information
For each DAG node this function emits tuples::
((col, color), [(col, nextcol, color)])
with the following new elements:
- Tuple (col, color) with column and color index for the current node
- A list of tuples indicating the edges between the current node and its
parents.
"""
branch_cache = {}
def branch(rev):
"""Return branch for rev, using cache for efficiency.
For Mercurial, always return the named branch name (which may be 'default').
For Git, return a branch name for branch heads, otherwise None."""
if rev not in branch_cache:
branch_cache[rev] = repo[rev].branch
return branch_cache[rev]
row = [] # the ancestor revision that each column is tracking
colors = {} # color number for revisions - set by descendants
obs = {} # obsolete flag for revisions - set by descendants
newcolor = 1 # the next available color
for (rev, dagparents) in dag:
# Compute row and nextrow
if rev not in row:
row.append(rev) # new head
colors[rev] = newcolor
obs[rev] = int(repo[rev].obsolete)
newcolor += 1
col = row.index(rev)
addparents = [p for p in dagparents if p not in row]
# Add unknown parents to nextrow
tmprow = row[:]
tmprow[col:col + 1] = reversed(addparents) # highest revs first (to the right), dead ends last (to the left)
# Filter old fake parents but keep new ones
nextrow = []
for r in tmprow:
if r >= 0 or r in dagparents:
nextrow.append(r)
else:
colors.pop(r)
obs.pop(r)
# Let color and obs for this rev be "inherited" by the first "parent"
color = colors.pop(rev)
if addparents:
b = branch(rev)
searching = True
for p in reversed(addparents):
obs[p] = int(repo[p].obsolete) if p >= 0 else 0
if searching and branch(abs(p)) in [b, None]:
# This is the first parent on the same branch - inherit the color
colors[p] = color
searching = False # make sure we don't give the color away twice
else:
colors[p] = newcolor
newcolor += 1
# Add edges to the graph
edges = []
for ecol, ep in enumerate(row):
if ep in nextrow:
edges.append((ecol, nextrow.index(ep), colors[ep], obs[ep]))
elif ep == rev:
for p in dagparents:
edges.append((ecol, nextrow.index(p), colors[p], obs[p]))
# Yield and move on
closing = int(repo[rev].closesbranch)
obsolete = int(repo[rev].obsolete)
bumped = int(repo[rev].bumped)
divergent = int(repo[rev].divergent)
extinct = int(repo[rev].extinct)
unstable = int(repo[rev].unstable)
yield ((col, color), edges, closing, obsolete, bumped, divergent, extinct, unstable)
row = nextrow