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
):
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.
104 current
= stack
.pop()
106 # If we already checked this commit, don't add it again
107 if memory
.has_key(current
.commit
):
108 dilution
= memory
[current
.commit
]
109 if dilution
<= current
.dilution
:
112 if global_memory
.has_key(current
.commit
):
113 dilution
= global_memory
[current
.commit
]
114 if dilution
< current
.dilution
:
117 memory
[current
.commit
] = current
.dilution
118 global_memory
[current
.commit
] = current
.dilution
120 # We found what we are looking for
121 if target
== current
.commit
:
122 metrics
.append(current
)
125 parents
= parent_lists
[current
.commit
]
127 # Root commit, we can't go any further
131 for parent
in parents
:
132 if parent
== parents
[0]:
133 newdilution
= current
.dilution
135 newdilution
= current
.dilution
+ 1
139 next
.branch
= current
.branch
140 next
.dilution
= newdilution
146 min, champs
= _bestMetric(metrics
)
149 def debugPrint(debug
, text
):
150 """Only print the specified text if debug is on
152 debug: Whether debug is on.
153 text: The text to print
159 def getBranchMap(branches
, cut_off_prefix
=True):
167 for branch
in branches
:
171 branch_hash
= git
.rev_parse(branch
)
173 if branch_hash
in branch_map
.values():
176 branch_map
[branch
] = branch_hash
180 def getParentList(commits
):
186 # First mine some data
187 result
= git
.rev_list("--parents", *commits
)
188 data
= result
.split('\n')
193 splitline
= item
.split(' ')
196 if len(splitline
) == 1:
197 parent_list
[first
] = []
198 elif splitline
[1] == '':
199 parent_list
[first
] = []
201 parent_list
[first
] = splitline
[1:]
205 def belongsTo(commit
, branch_map
, parent_list
, pretty_names
={}, debug
=False):
206 """Returns which branches the specified commit belongs to
209 commit: The commit to examine.
210 branch_map: A dictionary of all refs and their hashes.
211 parent_list: A dictionary with tuples of refs and their rev-lists.
212 debug: Whether to return debug information as well.
214 Returns: A list of branches that the commit belongs to.
219 if commit
in branch_map
:
220 debugPrint(debug
, "Easy as pie, that's a branch head!")
221 name
= branch_map
[commit
]
226 debugPrint(debug
, "Checking branches now:")
230 for branch_name
, branch_hash
in branch_map
.iteritems():
231 debugPrint(debug
, branch_name
)
234 metric
.branch
= branch_hash
235 metric
.commit
= branch_hash
238 # Find the dilution for this branch
239 metric
= _calculateDilution(commit
, metric
, parent_list
, memory
)
240 metrics
= metrics
+ metric
242 debugPrint(debug
, "Done.\n")
245 print("Listing found metrics:")
246 for metric
in metrics
:
249 min, champs
= _bestMetric(metrics
)
251 debugPrint(debug
, "Done.\n")
256 results
.append("The minimal dilution is: " + str(min))
258 # Loop over all the champs and get their name
259 for metric
in champs
:
260 results
.append(prettyName(metric
.branch
, pretty_names
))
264 def branchcontains(branch
, commit
):
265 """returns whether the specified branch contains the specified commit.
272 whether the branch contains the commit.
276 arg
= branch
+ ".." + commit
277 result
= git
.rev_list(arg
)
280 # if there is a difference between these sets, the commit is not in the branch
283 # there is no difference between the two, thus the branch contains the commit
286 def branchList(commitFilter
, includeRemotes
=False):
287 """Returns all branches that contain the specified commit
290 commitFilter: The commit to filter by.
291 includeRemotes: Whether to search remote branches as well.
302 args
.append("--contains")
303 args
.append(commitFilter
)
305 result
= git
.branch(*args
)
307 branches
= result
.split('\n')
311 for branch
in branches
:
312 result
.append(branch
[2:])
317 """Dispatches branch related commands
320 progname
= os
.path
.basename(sys
.argv
[0]) + " branch"
322 parser
= OptionParser(option_class
=parse
.GitOption
, prog
=progname
)
326 "-b", "--belongs-to",
329 help="find out which branch the specified commit belongs to")
334 help="print debug information on the found metrics")
339 help="show only branches that contain the specified commit")
344 help="include remotes in the listing")
346 parser
.set_default("debug", False)
347 parser
.set_default("remotes", False)
349 (options
, args
) = parser
.parse_args(list(args
))
351 if options
.belongs_to
:
354 result
= git
.branch("-a", "--contains", options
.belongs_to
)
355 branches
= result
.split('\n')
357 branch_map
= getBranchMap(branches
)
358 parent_list
= getParentList(branch_map
.values())
360 result
= belongsTo( options
.belongs_to
,
365 result
= branchList(commitFilter
=options
.contains
,
366 includeRemotes
=options
.remotes
)
368 print("Matching branches:")
370 for branch
in result
: