3 # Create a graph of patch flow into the mainline.
9 # The various types of commit we understand.
12 def __init__(self
, id, parent
):
21 def __init__(self
, id, parent
):
22 Commit
.__init
__(self
, id, parent
)
24 self
.internal
= 1 # Two branches within a repo?
25 self
.parents
= [ parent
]
27 def addparent(self
, parentid
):
28 self
.parents
.append(parentid
)
30 def addtree(self
, tree
):
35 # Trees: where the commits come from.
38 def __init__(self
, name
, url
):
44 def addcommit(self
, id):
45 self
.commits
.append(id)
47 def addinput(self
, tree
):
48 if tree
not in self
.inputs
:
49 self
.inputs
.append(tree
)
50 # print '%s -> %s' % (tree.name, self.name)
52 Mainline
= Tree('Mainline',
53 'git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git')
54 KnownTrees
= { Mainline
.url
: Mainline
}
56 def NormalizeURL(url
):
59 if url
== '../net-2.6/':
60 url
= 'git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6'
61 url
= url
.replace('master.kernel.org:', 'git://git.kernel.org')
62 if url
[-18:] == 'torvalds/linux-2.6':
64 if url
[:8] == '/pub/scm':
65 url
= 'git://git.kernel.org' + url
69 url
= NormalizeURL(url
)
71 return KnownTrees
[url
]
74 KnownTrees
[url
] = tree
78 # We track which tree every commit belongs to.
82 def __init__ (self
, tree
, priority
, path
):
84 self
.priority
= priority
87 def AddCommitTree(id, entry
):
88 # print 'add: ', id, '[',
89 # for tree in entry.path:
93 oldentry
= CommitTrees
[id]
94 if entry
.priority
< oldentry
.priority
:
95 CommitTrees
[id] = entry
97 CommitTrees
[id] = entry
100 def LookupCommitTree(id):
102 return CommitTrees
[id]
104 print 'Unfound commit %s' % (id)
105 return CTEntry (Mainline
, 0, [])
108 # Input handling with one-line pushback.
119 return Input
.readline()
126 # Pull in a commit and see what it is.
130 # Skip junk up to the next commit.
136 m
= patterns
.Pcommit
.match(line
)
141 # Look at the commit and see how many parents we have.
143 ids
= m
.group(1).split()
145 if len(CommitTrees
.values()) > 0:
146 print 'No-Parent commit:', ids
[0]
148 print 'Did you run git with --parents?'
151 if len(ids
) == 2: # Simple commit
152 return Commit(ids
[0], ids
[1])
154 # OK, we have a merge.
156 merge
= Merge(ids
[0], ids
[1])
160 # We need to figure out what kind of merge it is, so read through the
161 # descriptive text to the merge line.
166 print 'EOF looking for merge line'
169 # Maybe it's an external merge?
171 m
= patterns
.PExtMerge
.match(line
)
173 merge
.addtree(LookupTree(m
.group(2)))
176 # OK, maybe it's internal
178 if patterns
.PIntMerge
.match(line
) or patterns
.PIntMerge2
.match(line
):
179 #print 'Internal:', line[:-1]
182 m
= patterns
.Pcommit
.match(line
)
184 print 'Hit next commit (%s) looking for merge line' % (m
.group(1))
189 # Print out a tree and its inputs
191 def PrintTree(tree
, indent
= ''):
192 print '%s%4d %s' % (indent
, len(tree
.commits
), tree
.name
)
193 for input in tree
.inputs
:
194 PrintTree(input, indent
+ ' ')
197 # Let's try to build a data structure giving the patch flows.
200 def __init__(self
, tree
):
206 rootnode
= FlowNode(Mainline
)
207 for centry
in CommitTrees
.values():
208 FillFlowPath(centry
.path
, rootnode
)
211 def FillFlowPath(path
, node
):
215 next
, rest
= path
[0], path
[1:]
217 nextnode
= node
.inputs
[next
.name
]
219 nextnode
= node
.inputs
[next
.name
] = FlowNode(next
)
220 return FillFlowPath(rest
, nextnode
)
222 def PrintFlowTree(ftree
, indent
= ''):
223 print '%s%3d %s' % (indent
, ftree
.commits
, ftree
.tree
.name
)
224 for input in ftree
.inputs
.values():
225 PrintFlowTree(input, indent
+ ' ')
228 # Something for graphviz
230 GVHeader
= '''digraph "runtree" {
231 graph [ label = "Patch flow into the mainline",
235 node [shape = polygon,
245 global MainlineCommits
246 MainlineCommits
= ftree
.commits
247 gvf
= open('runtree.gv', 'w')
249 inputs
= ftree
.inputs
.values()
252 GVPrintNode(gvf
, input, 'Mainline')
255 def GVNodeName(treename
):
256 sname
= treename
.split('/')
257 if treename
.find('kernel.org') >= 0:
258 return '%s/%s' % (sname
[-2], sname
[-1])
259 sep
= treename
.find ('://')
261 return treename
[sep
+3:]
265 return n2
.commits
- n1
.commits
267 def GVPrintNode(gvf
, node
, parent
):
268 name
= GVNodeName(node
.tree
.name
)
269 gvf
.write ('"%s" -> "%s" [taillabel = "%d", labelfontsize = 8' % (name
, parent
, node
.commits
))
270 gvf
.write (', arrowsize = 0.5')
271 if MainlineCommits
/node
.commits
< 20:
272 gvf
.write(', color = red')
273 elif MainlineCommits
/node
.commits
< 100:
274 gvf
.write(', color = orange');
276 inputs
= node
.inputs
.values()
280 GVPrintNode(gvf
, input, name
)
289 entry
= LookupCommitTree(commit
.id)
291 priority
= entry
.priority
292 tree
.addcommit(commit
.id)
294 # For regular commits, just remember the tree involved
296 if not commit
.ismerge
:
297 AddCommitTree(commit
.parent
, entry
)
299 # For merges we have to deal with all the parents.
302 AddCommitTree(commit
.parents
[0], CTEntry (tree
, priority
, entry
.path
))
304 for p
in commit
.parents
[1:]:
305 path
= entry
.path
+ [tree
]
306 AddCommitTree(p
, CTEntry (tree
, priority
, entry
.path
))
308 for p
in commit
.parents
[1:]:
309 path
= entry
.path
+ [commit
.tree
]
310 AddCommitTree(p
, CTEntry (commit
.tree
, priority
+ 1, path
))
311 if commit
.tree
is not Mainline
:
312 tree
.addinput(commit
.tree
)
316 ftree
= BuildFlowTree()
319 print '%d commits total, %d trees' % (MainlineCommits
, len (KnownTrees
.keys()))