6 from optparse
import OptionParser
, OptionValueError
9 from git_stats
import branch
10 from git_stats
import commit
11 from git_stats
import config
12 from git_stats
import diff
13 from git_stats
import parse
16 """Contains information about a specific commit
19 def __init__(self
, commit
):
20 """Initializes with the specified commit
27 self
.diff_matches
= ""
30 """Checks if this commit is a bugfix
32 Returns: Whether branches, reverts, msg_matches or diff_matches is set.
35 return self
.branches \
41 """Returns a string representation of this object
46 for branch
in self
.branches
:
47 result
.append("In branch: " + branch
)
49 for commit
in self
.reverts
:
50 result
.append("Reverts commit: " + commit
)
53 result
.append("Message matches: '" + self
.msg_matches
+ "'.")
56 result
.append("Diff matches: '" + self
.diff_matches
+ "'.")
58 res
= self
.commit
+ ":"
69 branch_map: A dictionary of all refs and their hashes.
70 parent_list: A dictionary with tuples of refs and their rev-lists.
71 raw_diffs: A dictionary with commits and their raw diffs.
72 parsed_diffs: A dictionary with commits and their parsed diffs.
73 touched_files: A dictionary with files and the commits that touched them.
74 pretty_names: A dictionary with commits and their names
78 """Initializes the memory to an empty state
83 self
.pretty_names
= {}
85 self
.parsed_diffs
= {}
86 self
.touched_files
= {}
88 result
= git
.for_each_ref("refs/heads",
92 branches
= result
.split('\n')
93 self
.branch_map
= branch
.getBranchMap(branches
, cut_off_prefix
=False)
94 self
.parent_list
= branch
.getParentList(self
.branch_map
.values())
96 for name
, hash in self
.branch_map
.iteritems():
97 # Skip the leading 'refs/[heads|remotes]' prefix
98 pos
= name
.find('/', 5)
101 self
.pretty_names
[hash] = name
108 """Creates a new Options instance.
113 def aggregateType(options
):
114 """Uses the specified options to get type information for many commits
118 result
= git
.rev_list(options
.start_from
)
119 commits
= result
.split('\n')
121 options
= extractOptions(options
)
123 # Unless overridden by the user ignore parents for large repositories
124 if len(commits
) > 1000 and options
.ignore_parents
== None:
125 options
.ignore_parents
= True
132 commits
= commits
[:options
.limit
]
134 for commit
in commits
:
138 stats
= determineType(commit
, memory
, options
)
143 def determineType(target
, memory
, options
):
144 """Determines the type of the specified commit
147 commit: The commit to determine the type of.
148 memory: A class containing various storage containers.
149 options: A dictionary containing options.
152 branches
= branch
.belongsTo(target
,
155 pretty_names
=memory
.pretty_names
,
156 ignore_parents
=options
.ignore_parents
)
158 reverts
= diff
.findReverts( target
,
159 raw_diffs
=memory
.raw_diffs
,
160 parsed_diffs
=memory
.parsed_diffs
,
161 touched_files
=memory
.touched_files
)
163 msg_matches
= options
.msg_filter
and \
164 commit
.commitmsgMatches(target
, options
.msg_filter
)
166 diff_matches
= options
.diff_filter
and \
167 commit
.commitdiffMatches( target
,
169 raw_diffs
=memory
.raw_diffs
)
171 result
= CommitInfo(target
)
173 filter_branches
= options
.branch_filter
.split(':')
174 match
= set(filter_branches
).intersection(set(branches
))
177 result
.branches
= match
180 result
.reverts
= reverts
183 result
.msg_matches
= options
.msg_filter
186 result
.diff_matches
= options
.diff_filter
190 def extractOptions(options
):
191 """Extracts options and returns the result
193 The options are extracted from the specified options and
194 from the 'config' file.
201 options_list
= ["branch_filter",
208 for opt
in options_list
:
209 if getattr(options
, opt
, None) == None:
210 val
= opts
.get(opt
, None)
212 val
= getattr(options
, opt
)
214 setattr(result
, opt
, val
)
218 def _checkOptions(parser
, options
):
219 """Checks the specified options and uses the parser to indicate errors
222 parser: The parser to use to signal when the options are bad.
223 options: The options to check.
226 opts
= [options
.aggregate
, options
.type]
228 if not parse
.isUnique(opts
):
229 parser
.error("Please choose exactly one mode")
232 """Dispatches index related commands
235 progname
= os
.path
.basename(sys
.argv
[0]) + " bug"
237 parser
= OptionParser(option_class
=parse
.GitOption
, prog
=progname
)
242 help="show debug information")
247 help="aggregate bug information of all commits")
252 help="shows which type the specified commit is")
255 "-s", "--start-from",
258 help="the commit to start from")
261 "-m", "--message-filter",
262 help="mark the commit as a fix if it's log matches this filter")
265 "-f", "--diff-filter",
266 help="mark the commit as a fix if it's diff matches this filter")
269 "-b", "--branch-filter",
270 help="mark the commit as a fix if it belongs to this branch")
276 help="limit the commits checked to the most recent N ones")
279 "-i", "--ignore-parents",
281 help="whether to enable parentage ignoring")
283 # Default to True for now, until there is another option
284 parser
.set_default("debug", False)
285 parser
.set_default("aggregate", False)
286 parser
.set_default("start_from", "HEAD")
287 parser
.set_default("limit", 10)
288 parser
.set_default("ignore_parents", None)
290 (options
, args
) = parser
.parse_args(list(args
))
292 _checkOptions(parser
, options
)
294 if options
.aggregate
:
295 result
= aggregateType(options
)
297 kwargs
= extractOptions(options
)
299 stats
= determineType(options
.type, memory
, kwargs
)
300 result
= [str(stats
)]