~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to graph.py

  • Committer: Aaron Bentley
  • Date: 2006-07-24 15:18:40 UTC
  • Revision ID: abentley@panoramicfeedback.com-20060724151840-cf9270c1afbdfa27
Add runtime ignores for shelf

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005 Aaron Bentley
2
 
# <aaron@aaronbentley.com>
 
2
# <aaron.bentley@utoronto.ca>
3
3
#
4
4
#    This program is free software; you can redistribute it and/or modify
5
5
#    it under the terms of the GNU General Public License as published by
20
20
from bzrlib.branch import Branch
21
21
from bzrlib.errors import BzrCommandError, NoCommonRoot, NoSuchRevision
22
22
from bzrlib.graph import node_distances, select_farthest
23
 
from bzrlib.revision import combined_graph, revision_graph, NULL_REVISION
 
23
from bzrlib.revision import combined_graph, revision_graph
24
24
from bzrlib.revision import MultipleRevisionSources
25
25
import bzrlib.errors
26
26
import re
56
56
    for me, my_parents in ancestors.iteritems():
57
57
        if me in skip:
58
58
            continue
59
 
        new_ancestors[me] = {}
 
59
        new_ancestors[me] = {} 
60
60
        for parent in my_parents:
61
 
            new_parent = parent
 
61
            new_parent = parent 
62
62
            distance = 0
63
63
            while can_skip(new_parent, descendants, ancestors):
64
64
                if new_parent in exceptions:
69
69
                new_parent = list(ancestors[new_parent])[0]
70
70
                distance += 1
71
71
            new_ancestors[me][new_parent] = distance
72
 
    return new_ancestors
 
72
    return new_ancestors    
73
73
 
74
74
def get_rev_info(rev_id, source):
75
75
    """Return the committer, message, and date of a revision."""
77
77
    message = None
78
78
    date = None
79
79
    if rev_id == 'null:':
80
 
        return None, 'Null Revision', None, None
 
80
        return None, 'Null Revision', None
81
81
    try:
82
82
        rev = source.get_revision(rev_id)
83
83
    except NoSuchRevision:
84
84
        try:
85
85
            committer = '-'.join(rev_id.split('-')[:-2]).strip(' ')
86
86
            if committer == '':
87
 
                return None, None, None, None
 
87
                return None, None, None
88
88
        except ValueError:
89
 
            return None, None, None, None
 
89
            return None, None, None
90
90
    else:
91
91
        committer = short_committer(rev.committer)
92
92
        if rev.message is not None:
93
93
            message = rev.message.split('\n')[0]
94
94
        gmtime = time.gmtime(rev.timestamp + (rev.timezone or 0))
95
95
        date = time.strftime('%Y/%m/%d', gmtime)
96
 
        nick = rev.properties.get('branch-nick')
97
96
    if '@' in committer:
98
97
        try:
99
98
            committer = mail_map[committer]
103
102
        committer = committer_alias[committer]
104
103
    except KeyError:
105
104
        pass
106
 
    return committer, message, nick, date
 
105
    return committer, message, date
107
106
 
108
107
class Grapher(object):
109
 
 
110
108
    def __init__(self, branch, other_branch=None):
111
109
        object.__init__(self)
112
110
        self.branch = branch
113
111
        self.other_branch = other_branch
 
112
        revision_a = self.branch.last_revision()
114
113
        if other_branch is not None:
115
 
            other_repo = other_branch.repository
 
114
            branch.fetch(other_branch)
116
115
            revision_b = self.other_branch.last_revision()
 
116
            try:
 
117
                self.root, self.ancestors, self.descendants, self.common = \
 
118
                    combined_graph(revision_a, revision_b,
 
119
                                   self.branch.repository)
 
120
            except bzrlib.errors.NoCommonRoot:
 
121
                raise bzrlib.errors.NoCommonAncestor(revision_a, revision_b)
117
122
        else:
118
 
            other_repo = None
119
 
            revision_b = None
120
 
        self.graph = self.branch.repository.get_graph(other_repo)
121
 
        revision_a = self.branch.last_revision()
122
 
        self.scan_graph(revision_a, revision_b)
 
123
            self.root, self.ancestors, self.descendants = \
 
124
                revision_graph(revision_a, branch.repository)
 
125
            self.common = []
 
126
 
123
127
        self.n_history = branch.revision_history()
124
 
        self.n_revnos = branch.get_revision_id_to_revno_map()
125
 
        self.distances = node_distances(self.descendants, self.ancestors,
 
128
        self.distances = node_distances(self.descendants, self.ancestors, 
126
129
                                        self.root)
127
130
        if other_branch is not None:
128
131
            self.base = select_farthest(self.distances, self.common)
129
 
            self.m_history = other_branch.revision_history()
130
 
            self.m_revnos = other_branch.get_revision_id_to_revno_map()
131
 
            self.new_base = self.graph.find_unique_lca(revision_a,
132
 
                                                       revision_b)
133
 
            self.lcas = self.graph.find_lca(revision_a, revision_b)
 
132
            self.m_history = other_branch.revision_history() 
134
133
        else:
135
134
            self.base = None
136
 
            self.new_base = None
137
 
            self.lcas = set()
138
135
            self.m_history = []
139
 
            self.m_revnos = {}
140
 
 
141
 
    def scan_graph(self, revision_a, revision_b):
142
 
        a_ancestors = dict(self.graph.iter_ancestry([revision_a]))
143
 
        self.ancestors = a_ancestors
144
 
        self.root = NULL_REVISION
145
 
        if revision_b is not None:
146
 
            b_ancestors = dict(self.graph.iter_ancestry([revision_b]))
147
 
            self.common = set(a_ancestors.keys())
148
 
            self.common.intersection_update(b_ancestors)
149
 
            self.ancestors.update(b_ancestors)
150
 
        else:
151
 
            self.common = []
152
 
            revision_b = None
153
 
        self.descendants = {}
154
 
        ghosts = set()
155
 
        for revision, parents in self.ancestors.iteritems():
156
 
            self.descendants.setdefault(revision, [])
157
 
            if parents is None:
158
 
                ghosts.add(revision)
159
 
                parents = [NULL_REVISION]
160
 
            for parent in parents:
161
 
                self.descendants.setdefault(parent, []).append(revision)
162
 
        for ghost in ghosts:
163
 
            self.ancestors[ghost] = [NULL_REVISION]
164
 
 
165
 
    @staticmethod
166
 
    def _get_revno_str(prefix, revno_map, revision_id):
167
 
        try:
168
 
            revno = revno_map[revision_id]
169
 
        except KeyError:
170
 
            return None
171
 
        return '%s%s' % (prefix, '.'.join(str(n) for n in revno))
172
136
 
173
137
    def dot_node(self, node, num):
174
138
        try:
180
144
        except ValueError:
181
145
            m_rev = None
182
146
        if (n_rev, m_rev) == (None, None):
183
 
            name = self._get_revno_str('r', self.n_revnos, node)
184
 
            if name is None:
185
 
                name = self._get_revno_str('R', self.m_revnos, node)
186
 
            if name is None:
187
 
                name = node[-5:]
 
147
            name = node[-5:]
188
148
            cluster = None
189
149
        elif n_rev == m_rev:
190
150
            name = "rR%d" % n_rev
210
170
            assert m_rev is not None
211
171
            cluster = "other_history"
212
172
            color = "#ff0000"
213
 
        if node in self.lcas:
214
 
            color = "#9933cc"
215
173
        if node == self.base:
216
 
            color = "#669933"
217
 
            if node == self.new_base:
218
 
                color = "#33ff33"
219
 
        if node == self.new_base:
220
 
            color = '#33cc99'
 
174
            color = "#33ff99"
221
175
 
222
176
        label = [name]
223
 
        committer, message, nick, date = get_rev_info(node,
224
 
                                                      self.branch.repository)
 
177
        committer, message, date = get_rev_info(node, self.branch.repository)
225
178
        if committer is not None:
226
179
            label.append(committer)
227
180
 
228
 
        if nick is not None:
229
 
            label.append(nick)
230
 
 
231
181
        if date is not None:
232
182
            label.append(date)
233
183
 
237
187
        else:
238
188
            rank = None
239
189
 
240
 
        d_node = Node("n%d" % num, color=color, label="\\n".join(label),
 
190
        d_node = Node("n%d" % num, color=color, label="\\n".join(label), 
241
191
                    rev_id=node, cluster=cluster, message=message,
242
192
                    date=date)
243
193
        d_node.rank = rank
246
196
            d_node.node_style.append('dotted')
247
197
 
248
198
        return d_node
249
 
 
250
 
    def get_relations(self, collapse=False, max_distance=None):
 
199
        
 
200
    def get_relations(self, collapse=False):
251
201
        dot_nodes = {}
252
202
        node_relations = []
253
203
        num = 0
254
204
        if collapse:
255
 
            exceptions = self.lcas.union([self.base, self.new_base])
256
 
            visible_ancestors = compact_ancestors(self.descendants,
257
 
                                                  self.ancestors,
258
 
                                                  exceptions)
 
205
            visible_ancestors = compact_ancestors(self.descendants, 
 
206
                                                  self.ancestors, (self.base,))
259
207
        else:
260
 
            visible_ancestors = {}
261
 
            for revision, parents in self.ancestors.iteritems():
262
 
                visible_ancestors[revision] = dict((p, 0) for p in parents)
263
 
        if max_distance is not None:
264
 
            min_distance = max(self.distances.values()) - max_distance
265
 
            visible_ancestors = dict((n, p) for n, p in
266
 
                                     visible_ancestors.iteritems() if
267
 
                                     self.distances[n] >= min_distance)
 
208
            visible_ancestors = self.ancestors
268
209
        for node, parents in visible_ancestors.iteritems():
269
210
            if node not in dot_nodes:
270
211
                dot_nodes[node] = self.dot_node(node, num)
271
212
                num += 1
272
 
            for parent, skipped in parents.iteritems():
 
213
            if visible_ancestors is self.ancestors:
 
214
                parent_iter = ((f, 0) for f in parents)
 
215
            else:
 
216
                parent_iter = (f for f in parents.iteritems())
 
217
            for parent, skipped in parent_iter:
273
218
                if parent not in dot_nodes:
274
219
                    dot_nodes[parent] = self.dot_node(parent, num)
275
220
                    num += 1
281
226
 
282
227
 
283
228
def write_ancestry_file(branch, filename, collapse=True, antialias=True,
284
 
                        merge_branch=None, ranking="forced", max_distance=None):
 
229
                        merge_branch=None, ranking="forced"):
285
230
    b = Branch.open_containing(branch)[0]
286
231
    if merge_branch is not None:
287
232
        m = Branch.open_containing(merge_branch)[0]
293
238
            m.lock_read()
294
239
        try:
295
240
            grapher = Grapher(b, m)
296
 
            relations = grapher.get_relations(collapse, max_distance)
 
241
            relations = grapher.get_relations(collapse)
297
242
        finally:
298
243
            if m is not None:
299
244
                m.unlock()
305
250
    done = False
306
251
    if ext not in RSVG_OUTPUT_TYPES:
307
252
        antialias = False
308
 
    if antialias:
 
253
    if antialias: 
309
254
        output = list(output)
310
255
        try:
311
256
            invoke_dot_aa(output, filename, ext)
323
268
            done = True
324
269
        except NoDot, e:
325
270
            raise BzrCommandError("Can't find 'dot'.  Please ensure Graphviz"\
326
 
                " is installed correctly.")
 
271
                " is installed correctly, or use --noantialias")
327
272
    elif ext == 'dot' and not done:
328
273
        my_file = file(filename, 'wb')
329
274
        for fragment in output:
333
278
            invoke_dot_html(output, filename)
334
279
        except NoDot, e:
335
280
            raise BzrCommandError("Can't find 'dot'.  Please ensure Graphviz"\
336
 
                " is installed correctly.")
 
281
                " is installed correctly, or use --noantialias")
337
282
    elif not done:
338
283
        print "Unknown file extension: %s" % ext
 
284