6 from optparse
import OptionParser
9 from git_stats
import parse
11 def prettyName(branch
):
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
22 name
= git
.describe("--all", branch
)
23 splitname
= name
.split('/')
29 """This class represents a metric
31 It is meant to store arbitrary data related to a specific metric.
40 res
= "Branch %s, dilution: %d." % \
41 (prettyName(self
.branch
), self
.dilution
)
45 def _bestMetric(metrics
):
46 """Finds the best metric from a set
48 Iterates the metrics and selects the ones with the lowest dilution.
49 All the returned metrics are guaranteed to have the same dilution.
52 metrics: The metrics to pick the best ones from.
54 Returns: A list with the best metrics.
59 # Find the best metric
60 for metric
in metrics
:
61 # Store the current value for easy access
62 dilution
= metric
.dilution
64 # First element, set the minimum to the current
68 # Worse than our best, not interesting
72 # Clear the old ones if we have a better minimum
81 def _calculateDilution(target
, start
, parent_lists
, global_memory
):
82 """Calculates the dilution for the specified commit.
85 target: The commit to calculate the dilution for.
86 start: The initial metric information.
87 parent_list: A dictionary containing { commit : parent } pairs.
101 # If we already checked this commit, don't add it again
102 if memory
.has_key(current
.commit
):
103 dilution
= memory
[current
.commit
]
104 if dilution
<= current
.dilution
:
107 if global_memory
.has_key(current
.commit
):
108 dilution
= global_memory
[current
.commit
]
109 if dilution
< current
.dilution
:
112 memory
[current
.commit
] = current
.dilution
113 global_memory
[current
.commit
] = current
.dilution
115 # We found what we are looking for
116 if target
== current
.commit
:
117 metrics
.append(current
)
120 parents
= parent_lists
[current
.commit
]
122 # Root commit, we can't go any further
126 for parent
in parents
:
127 if parent
== parents
[0]:
128 newdilution
= current
.dilution
130 newdilution
= current
.dilution
+ 1
134 next
.branch
= current
.branch
135 next
.dilution
= newdilution
141 min, champs
= _bestMetric(metrics
)
144 def debugPrint(debug
, text
):
145 """Only print the specified text if debug is on
147 debug: Whether debug is on.
148 text: The text to print
154 def belongsTo(commit
, debug
=False):
155 """Returns which branches the specified commit belongs to
158 commit: The commit to examine.
159 debug: Whether to return debug information as well.
161 Returns: A list of branches that the commit belongs to.
166 result
= git
.for_each_ref("refs/heads", "refs/remotes", format
="%(objectname)")
168 branches
= result
.split('\n')
170 if commit
in branches
:
171 debugPrint(debug
, "Easy as pie, that's a branch head!")
172 return [prettyName(commit
)]
179 debugPrint(debug
, "Retreiving branches that contain the commit...")
181 result
= git
.branch("-a", "--contains", commit
)
182 branches
= result
.split('\n')
186 for branch
in branches
:
187 branch_name
= branch
[2:]
188 branch_hash
= git
.rev_parse(branch_name
)
190 if branch_hash
in branch_map
.values():
193 branch_map
[branch_name
] = branch_hash
195 debugPrint(debug
, "Gathering information on the history...")
197 # First mine some data
198 args
= branch_map
.values()
199 result
= git
.rev_list("--parents", *args
)
200 data
= result
.split('\n')
203 splitline
= item
.split(' ')
206 if len(splitline
) == 1:
207 parent_lists
[first
] = []
208 elif splitline
[1] == '':
209 parent_lists
[first
] = []
211 parent_lists
[first
] = splitline
[1:]
213 debugPrint(debug
, "Done.\n")
215 debugPrint(debug
, "Checking branches now:")
219 for branch_name
, branch_hash
in branch_map
.iteritems():
220 debugPrint(debug
, branch_name
)
223 metric
.branch
= branch_hash
224 metric
.commit
= branch_hash
227 # Find the dilution for this branch
228 metric
= _calculateDilution(commit
, metric
, parent_lists
, memory
)
229 metrics
= metrics
+ metric
231 debugPrint(debug
, "Done.\n")
234 print("Listing found metrics:")
235 for metric
in metrics
:
238 min, champs
= _bestMetric(metrics
)
240 debugPrint(debug
, "Done.\n")
245 results
.append("The minimal dilution is: " + str(min))
247 # Loop over all the champs and get their name
248 for metric
in champs
:
249 results
.append(prettyName(metric
.branch
))
253 def branchcontains(branch
, commit
):
254 """returns whether the specified branch contains the specified commit.
261 whether the branch contains the commit.
265 arg
= branch
+ ".." + commit
266 result
= git
.rev_list(arg
)
269 # if there is a difference between these sets, the commit is not in the branch
272 # there is no difference between the two, thus the branch contains the commit
275 def branchList(commitFilter
, includeRemotes
=False):
276 """Returns all branches that contain the specified commit
279 commitFilter: The commit to filter by.
280 includeRemotes: Whether to search remote branches as well.
291 args
.append("--contains")
292 args
.append(commitFilter
)
294 result
= git
.branch(*args
)
296 branches
= result
.split('\n')
300 for branch
in branches
:
301 result
.append(branch
[2:])
306 """Dispatches branch related commands
309 progname
= os
.path
.basename(sys
.argv
[0]) + " branch"
311 parser
= OptionParser(option_class
=parse
.GitOption
, prog
=progname
)
315 "-b", "--belongs-to",
318 help="find out which branch the specified commit belongs to")
323 help="print debug information on the found metrics")
328 help="show only branches that contain the specified commit")
333 help="include remotes in the listing")
335 parser
.set_default("debug", False)
336 parser
.set_default("remotes", False)
338 (options
, args
) = parser
.parse_args(list(args
))
340 if options
.belongs_to
:
341 result
= belongsTo(options
.belongs_to
, options
.debug
)
343 result
= branchList(commitFilter
=options
.contains
,
344 includeRemotes
=options
.remotes
)
346 print("Matching branches:")
348 for branch
in result
: