# HG changeset patch
# User Peer Sommerlund <p...@users.sourceforge.net>
# Date 1251705048 -7200
logview: Add branch_grapher

Add a revision grapher that shows branches instead of 
ancestory. The multiple merge pattern will show as two 
swimlanes, instead of a long curtain. This also means that 
it is not always possible to show the correct parent of a 
changeset, although the correct branch will be visible.

diff -r 74b2963abd9b -r 52ad7538a583 hggtk/logview/revgraph.py
--- a/hggtk/logview/revgraph.py
+++ b/hggtk/logview/revgraph.py
@@ -145,6 +145,189 @@
         curr_rev -= 1
 
 
+class BranchGrapher:
+    """Incremental branch grapher
+
+    This generator function produces a graph that uses loose ends to keep
+    focus on branches. All revisions in the range are shown, but not all edges.
+    The function identifies branches and may use loose ends if an edge links
+    two branches.
+    """
+    
+    def __init__(self, repo, start_rev, stop_rev):
+        assert start_rev >= stop_rev
+        self.repo = repo
+        
+        #
+        #  Iterator content
+        #  Variables that keep track of the iterator
+        #
+        
+        # The current revision to process
+        self.curr_rev = start_rev
+        
+        # Process all revs from start_rev to and including stop_rev
+        self.stop_rev = stop_rev
+
+        # List current branches. For each branch the next rev is listed
+        # The order of branches determine their columns
+        self.curr_branches = [start_rev]
+        
+        # For each branch, tell the next version that will show up
+        self.next_in_branch = {}
+        
+        #
+        #  Graph variables
+        #  These hold information related to the graph. Most are computed 
lazily.
+        #
+        
+        # Map rev to next rev in branch = first parent of rev. 
+        # The parent of last rev in a branch is undefined, 
+        # even if the revsion has a parent rev.
+        self.parent_of = {}
+        
+        # Map rev to newest rev in branch. This identifies the branch that the 
rev
+        # is part of
+        self.branch4rev = {}
+        
+        # Last revision in branch
+        self.branch_tail = {}
+        
+        # Map branch-id (head-rev of branch) to color
+        self.color4branch = {}
+        
+        # Next colour used. for branches
+        self.nextcolor = 0
+
+    def _get_parents(self, rev):
+        return [x for x in self.repo.changelog.parentrevs(rev) if x != nullrev]
+        
+    def _covered_rev(self, rev):
+        """True if rev is inside the revision range for the iterator"""
+        return self.stop_rev <= rev
+        
+    def _new_branch(self, branch_head):
+        """Mark all unknown revisions in range that are direct ancestors
+        of branch_head as part of the same branch. Stops when stop_rev
+        is passed or a known revision is found"""
+        assert self._covered_rev(branch_head)
+        assert not branch_head in self.branch4rev
+        self.color4branch[branch_head] = self.nextcolor
+        self.nextcolor += 1
+        self.next_in_branch[branch_head] = branch_head
+        rev = branch_head
+        while not rev in self.branch4rev:
+            # TODO consider lazy evaluation here
+            if not self._covered_rev(rev):
+                # rev is outside visible range, so we don't know tail location
+                self.branch_tail[branch_head] = 0 # Prev revs wasn't tail
+                return
+            self.branch4rev[rev] = branch_head
+            self.branch_tail[branch_head] = rev
+            parents = self._get_parents(rev)
+            if not parents:
+                # All revisions have been exhausted (rev = 0)
+                self.parent_of[rev] = None
+                return
+            self.parent_of[rev] = parents[0]
+            rev = parents[0]
+
+    def _get_rev_branch(self, rev):
+        """Find revision branch or create a new branch"""
+        branch = self.branch4rev.get(rev)
+        if branch is None:
+            branch = rev
+            self._new_branch(branch)
+        assert rev in self.branch4rev
+        assert branch in self.branch_tail
+        return branch
+        
+    def _compute_next_branches(self):
+        """Compute next row of branches"""
+        next_branches = self.curr_branches[:]
+        # Find branch used by current revision
+        curr_branch = self._get_rev_branch(self.curr_rev)
+        self.next_in_branch[curr_branch] = self.parent_of[self.curr_rev]
+        branch_index = self.curr_branches.index(curr_branch)
+        # Insert branches if parents point to new branches
+        new_parents = 0
+        for parent in self._get_parents(self.curr_rev):
+            branch = self._get_rev_branch(parent)
+            if not branch in next_branches:
+                new_parents += 1
+                next_branches.insert(branch_index + new_parents, branch)
+        # Delete branch if last revision
+        if self.curr_rev == self.branch_tail[curr_branch]:
+            del next_branches[branch_index]
+        # Return result
+        return next_branches
+    
+    def _rev_color(self, rev):
+        """Map a revision to a color"""
+        return self.color4branch[self.branch4rev[rev]]
+
+    def _compute_lines(self, parents, next_branches):
+        # Compute lines (from CUR to NEXT branch row)
+        lines = []
+        curr_rev_branch = self.branch4rev[self.curr_rev]
+        for curr_column, branch in enumerate(self.curr_branches):
+            if branch == curr_rev_branch:
+                # Add lines from current branch to parents
+                for par_rev in parents:
+                    par_branch = self._get_rev_branch(par_rev)
+                    color = self.color4branch[par_branch]
+                    next_column = next_branches.index(par_branch)
+                    line_type = type_PLAIN
+                    if par_rev != self.next_in_branch.get(par_branch):
+                        line_type = type_LOOSE_LOW
+                    lines.append( (curr_column, next_column, color, line_type) 
)
+            else:
+                # Continue unrelated branch
+                color = self.color4branch[branch]
+                next_column = next_branches.index(branch)
+                lines.append( (curr_column, next_column, color, type_PLAIN) )
+        return lines
+        
+    def more(self):
+        return self.curr_rev >= self.stop_rev
+        
+    def next(self):
+        """Perform one iteration of the branch grapher"""
+        
+        # Compute revision (on CUR branch row)
+        rev = self.curr_rev
+        rev_branch = self._get_rev_branch(rev)
+        if rev_branch not in self.curr_branches:
+            # New head
+            self.curr_branches.append(rev_branch)
+        
+        # Compute parents (indicates the branches on NEXT branch row that 
curr_rev links to)
+        parents = self._get_parents(rev)
+               # BUG: __get_parents is not defined - why?
+        
+        # Compute lines (from CUR to NEXT branch row)
+        next_branches = self._compute_next_branches()
+        lines = self._compute_lines(parents, next_branches)
+        
+        # Compute node info (for CUR branch row)
+        rev_column = self.curr_branches.index(rev_branch)
+        rev_color = self.color4branch[rev_branch]
+        node = (rev_column, rev_color)
+        
+        # Next loop
+        self.curr_branches = next_branches
+        self.curr_rev -= 1
+        
+        # Return result
+        # TODO: Refactor parents field away - it is apparently not used 
anywhere
+        return (rev, node, lines, parents)
+    
+def branch_grapher(repo, start_rev, stop_rev):
+    grapher = BranchGrapher(repo, start_rev, stop_rev)
+    while grapher.more():
+        yield grapher.next()            
+
+
 def filelog_grapher(repo, path):
     '''
     Graph the ancestry of a single file (log).  Deletions show

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with 
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
Tortoisehg-develop mailing list
Tortoisehg-develop@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tortoisehg-develop

Reply via email to