6 from optparse
import OptionParser
9 from git_stats
import parse
11 def prettyName(branch
, pretty_names
={}):
12 """Looks up the name of a branch and returns it
14 Returns only the last part of the branchname.
17 branch: The branch to print
18 pretty_names: A dictionary with commits and their names
21 if not pretty_names
.has_key(branch
):
23 name
= git
.describe("--all", branch
)
24 splitname
= name
.split('/')
26 pretty_names
[branch
] = name
28 name
= pretty_names
[branch
]
33 """This class represents a metric
35 It is meant to store arbitrary data related to a specific metric.
44 res
= "Branch %s, dilution: %d." % \
45 (prettyName(self
.branch
), self
.dilution
)
49 def _bestMetric(metrics
):
50 """Finds the best metric from a set
52 Iterates the metrics and selects the ones with the lowest dilution.
53 All the returned metrics are guaranteed to have the same dilution.
56 metrics: The metrics to pick the best ones from.
58 Returns: A list with the best metrics.
63 # Find the best metric
64 for metric
in metrics
:
65 # Store the current value for easy access
66 dilution
= metric
.dilution
68 # First element, set the minimum to the current
72 # Worse than our best, not interesting
76 # Clear the old ones if we have a better minimum
85 def _calculateDilution(target
, start
, parent_lists
, global_memory
, ignore
=[]):
86 """Calculates the dilution for the specified commit.
89 target: The commit to calculate the dilution for.
90 start: The initial metric information.
91 parent_list: A dictionary with commits and their parents.
92 global_memory: A dictionary with all commits and their parents.
93 ignore: The parent commits of the target commit which should be ignored.
105 current
= stack
.pop()
107 # We found what we are looking for
108 if target
== current
.commit
:
109 metrics
.append(current
)
112 # Ignore commits that are past the target
113 if current
.commit
in ignore
:
116 # If we already checked this commit, don't add it again
117 if memory
.has_key(current
.commit
):
118 dilution
= memory
[current
.commit
]
119 if dilution
<= current
.dilution
:
122 if global_memory
.has_key(current
.commit
):
123 dilution
= global_memory
[current
.commit
]
124 if dilution
< current
.dilution
:
127 memory
[current
.commit
] = current
.dilution
128 global_memory
[current
.commit
] = current
.dilution
130 parents
= parent_lists
[current
.commit
]
132 # Root commit, we can't go any further
136 for parent
in parents
:
137 if parent
== parents
[0]:
138 newdilution
= current
.dilution
140 newdilution
= current
.dilution
+ 1
144 next
.branch
= current
.branch
145 next
.dilution
= newdilution
151 min, champs
= _bestMetric(metrics
)
154 def debugPrint(debug
, text
):
155 """Only print the specified text if debug is on
157 debug: Whether debug is on.
158 text: The text to print
164 def getBranchMap(branches
, cut_off_prefix
=True):
172 for branch
in branches
:
176 branch_hash
= git
.rev_parse(branch
)
178 if branch_hash
in branch_map
.values():
181 branch_map
[branch
] = branch_hash
185 def getParentList(commits
):
191 # First mine some data
192 result
= git
.rev_list("--parents", *commits
)
193 data
= result
.split('\n')
198 splitline
= item
.split(' ')
201 if len(splitline
) == 1:
202 parent_list
[first
] = []
203 elif splitline
[1] == '':
204 parent_list
[first
] = []
206 parent_list
[first
] = splitline
[1:]
210 def belongsTo(commit
,
215 ignore_parents
=False):
216 """Returns which branches the specified commit belongs to
219 commit: The commit to examine.
220 branch_map: A dictionary of all refs and their hashes.
221 parent_list: A dictionary with tuples of refs and their rev-lists.
222 pretty_names: A dictionary with commits and their names
223 debug: Whether to return debug information as well.
225 Returns: A list of branches that the commit belongs to.
230 if commit
in branch_map
:
231 debugPrint(debug
, "Easy as pie, that's a branch head!")
232 name
= branch_map
[commit
]
238 result
= git
.rev_list(commit
)
239 ignore
= set(result
.split('\n'))
243 debugPrint(debug
, "Checking branches now:")
247 for branch_name
, branch_hash
in branch_map
.iteritems():
248 debugPrint(debug
, branch_name
)
251 metric
.branch
= branch_hash
252 metric
.commit
= branch_hash
255 # Find the dilution for this branch
256 metric
= _calculateDilution(commit
, metric
, parent_list
, memory
, ignore
)
257 metrics
= metrics
+ metric
259 debugPrint(debug
, "Done.\n")
262 print("Listing found metrics:")
263 for metric
in metrics
:
266 min, champs
= _bestMetric(metrics
)
268 debugPrint(debug
, "Done.\n")
273 results
.append("The minimal dilution is: " + str(min))
275 # Loop over all the champs and get their name
276 for metric
in champs
:
277 results
.append(prettyName(metric
.branch
, pretty_names
))
281 def branchcontains(branch
, commit
):
282 """returns whether the specified branch contains the specified commit.
289 whether the branch contains the commit.
293 arg
= branch
+ ".." + commit
294 result
= git
.rev_list(arg
)
297 # if there is a difference between these sets, the commit is not in the branch
300 # there is no difference between the two, thus the branch contains the commit
303 def branchList(commitFilter
, includeRemotes
=False):
304 """Returns all branches that contain the specified commit
307 commitFilter: The commit to filter by.
308 includeRemotes: Whether to search remote branches as well.
319 args
.append("--contains")
320 args
.append(commitFilter
)
322 result
= git
.branch(*args
)
324 branches
= result
.split('\n')
328 for branch
in branches
:
329 result
.append(branch
[2:])
334 """Dispatches branch related commands
337 progname
= os
.path
.basename(sys
.argv
[0]) + " branch"
339 parser
= OptionParser(option_class
=parse
.GitOption
, prog
=progname
)
343 "-b", "--belongs-to",
346 help="find out which branch the specified commit belongs to")
351 help="print debug information on the found metrics")
356 help="show only branches that contain the specified commit")
361 help="include remotes in the listing")
363 parser
.set_default("debug", False)
364 parser
.set_default("remotes", False)
366 (options
, args
) = parser
.parse_args(list(args
))
368 if options
.belongs_to
:
371 result
= git
.branch("-a", "--contains", options
.belongs_to
)
372 branches
= result
.split('\n')
374 branch_map
= getBranchMap(branches
)
375 parent_list
= getParentList(branch_map
.values())
377 result
= belongsTo( options
.belongs_to
,
382 result
= branchList(commitFilter
=options
.contains
,
383 includeRemotes
=options
.remotes
)
385 print("Matching branches:")
387 for branch
in result
: