gitstats: Check for empty diffs
[git-stats.git] / src / git_stats / bug.py
blob040f905a450c345ea71cd9f4ff0ee0f8f7a320c6
1 #!/usr/bin/env python
3 import os
4 import sys
6 from optparse import OptionParser, OptionValueError
7 from git import Repo
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
15 class CommitInfo:
16 """Contains information about a specific commit
17 """
19 def __init__(self, commit):
20 """Initializes with the specified commit
21 """
23 self.commit = commit
24 self.branches = []
25 self.reverts = []
26 self.msg_matches = ""
27 self.diff_matches = ""
29 def isBugfix():
30 """Checks if this commit is a bugfix
32 Returns: Whether branches, reverts, msg_matches or diff_matches is set.
33 """
35 return self.branches \
36 or self.reverts \
37 or self.msg_matches \
38 or self.diff_matches
40 def __str__(self):
41 """Returns a string representation of this object
42 """
44 result = []
46 for branch in self.branches:
47 result.append("In branch: " + branch)
49 for commit in self.reverts:
50 result.append("Reverts commit: " + commit)
52 if self.msg_matches:
53 result.append("Message matches: '" + self.msg_matches + "'.")
55 if self.diff_matches:
56 result.append("Diff matches: '" + self.diff_matches + "'.")
58 res = self.commit + ":"
60 for line in result:
61 res += "\n" + line
63 return res
65 def aggregateType(options):
66 """
67 """
69 git = Repo(".").git
70 result = git.rev_list(options.start_from)
71 commits = result.split('\n')
73 kwargs = extractKwargs(options)
75 result = []
77 for commit in commits:
78 stats = determineType(commit, **kwargs)
79 result.append(stats)
81 return result
83 def determineType(target,
84 debug=False,
85 branchFilter=None,
86 msgFilter=None,
87 diffFilter=None):
88 """Determines the type of the specified commit
90 Args:
91 commit: The commit to determine the type of.
92 debug: Whether to include debug information.
93 branchFilter: Type=fix if a commit belongs to any of these branches.
94 msgFilter: Type=fix if the commit log msg matches this filter.
95 diffFilter: Type=fix if the commit diff matches this filter.
96 """
98 branches = branch.belongsTo(target)
99 reverts = diff.findReverts(target)
100 msg_matches = msgFilter and commit.commitmsgMatches(target, msgFilter)
101 diff_matches = diffFilter and commit.commitdiffMatches(target, diffFilter)
103 result = CommitInfo(target)
105 if branchFilter in branches:
106 match = set(branchFilter).intersection(set(branches))
107 result.branches = match
109 if reverts:
110 result.reverts = reverts
112 if msg_matches:
113 result.msg_matches = msgFilter
115 if diff_matches:
116 result.diff_matches = diffFilter
118 return result
120 def extractKwargs(options):
121 """Extracts kwargs and returns the result
123 The kwargs are extracted from the specified options and
124 from the 'config' file.
127 opts = config.read()
129 kwargs = {}
131 kwargs["debug"] = options.debug or opts.get("debug", False)
132 kwargs["msgFilter"] = options.message_filter or opts.get("msgFilter", None)
133 kwargs["diffFilter"] = options.diff_filter or opts.get("diffFilter", None)
134 kwargs["branchFilter"] = options.branch_filter or opts.get("branchFilter", None)
136 return kwargs
138 def _checkOptions(parser, options):
139 """Checks the specified options and uses the parser to indicate errors
141 Args:
142 parser: The parser to use to signal when the options are bad.
143 options: The options to check.
146 opts = [options.aggregate, options.type]
148 if not parse.isUnique(opts):
149 parser.error("Please choose exactly one mode")
151 def dispatch(*args):
152 """Dispatches index related commands
155 progname = os.path.basename(sys.argv[0]) + " bug"
157 parser = OptionParser(option_class=parse.GitOption, prog=progname)
159 parser.add_option(
160 "-d", "--debug",
161 action="store_true",
162 help="show debug information")
164 parser.add_option(
165 "-a", "--aggregate",
166 action="store_true",
167 help="aggregate bug information of all commits")
169 parser.add_option(
170 "-t", "--type",
171 type="commit",
172 help="shows which type the specified commit is")
174 parser.add_option(
175 "-s", "--start-from",
176 type="commit",
177 metavar="COMMIT",
178 help="the commit to start from")
180 parser.add_option(
181 "-m", "--message-filter",
182 help="mark the commit as a fix if it's log matches this filter")
184 parser.add_option(
185 "-f", "--diff-filter",
186 help="mark the commit as a fix if it's diff matches this filter")
188 parser.add_option(
189 "-b", "--branch-filter",
190 help="mark the commit as a fix if it belongs to this branch")
192 # Default to True for now, until there is another option
193 parser.set_default("debug", False)
194 parser.set_default("aggregate", False)
195 parser.set_default("start_from", "HEAD")
197 (options, args) = parser.parse_args(list(args))
199 _checkOptions(parser, options)
201 if options.aggregate:
202 result = aggregateType(options)
203 elif options.type:
204 kwargs = extractKwargs(options)
205 stats = determineType(options.type, **kwargs)
206 result = [str(stats)]
208 for line in result:
209 print(line)