6 from optparse
import OptionParser
, OptionValueError
9 from git_stats
import parse
12 """Simple storage class containing stats on the activity in one file."""
21 return str(self
.count
) + ": +" + str(self
.added
) + " -" + str(self
.deleted
)
23 def activityInArea(log
, filterid
):
24 """Parses the specified file containing commit logs.
25 The output is filtered to contain only the results of the specified author.
26 The output is expected to be in the format described below:
29 [<lines added>\t<lines deleted>\t<path>]+
34 log: The log formatted as described above.
35 filterid: The id address to filter on.
38 A dictionary containing activity per path is returned.
39 Each path contains one Activity object with the aggregated results.
42 # Create a dict to store the activity stats per path
45 # Create a place to store the result in
50 # Parse all the lines in the file
54 # Split the line at the tab and store the data
55 splitline
= line
.split('\t')
57 length
= len(line
.lstrip())
59 # There is something on this line, but it contains no separator
60 if size
== 1 and length
> 0:
61 # Get the id address minus the newline
62 activity
.id.append(line
[:-1])
65 addpart
= splitline
[0]
66 deletepart
= splitline
[1]
74 activity
.added
= int(addpart
)
75 activity
.deleted
= int(deletepart
)
77 print("On line '" + str(i
) + "', could not convert number: " + str(e
))
79 path
= splitline
[2][:-1]
81 if not filterid
in activity
.id:
82 # Ignore other authors
87 # If we already encountered this path, update the count
88 if activityByPath
.has_key(path
):
89 known
= activityByPath
[path
]
90 result
.added
= known
.added
+ activity
.added
91 result
.deleted
= known
.deleted
+ activity
.deleted
92 result
.count
= known
.count
+ activity
.count
96 # Store it under it's path
97 activityByPath
[path
] = result
99 # Create a fresh activity to store the next round in
100 activity
= Activity()
102 print("Cannot parse line " + str(i
) + ".")
105 return activityByPath
107 def activityInFile(path
, id, start_from
, relative
):
108 """Shows the activity for the file in the current repo.
111 path: The path to filter on.
112 id: The id of the developer to show in the result.
113 startfrom: The commit to start logging from.
114 relative: Treat path as relative to the current working directory.
119 result
= git
.log(start_from
, "--", path
, pretty
="format:%" + id, with_keep_cwd
=relative
)
120 activity
= result
.split('\n')
124 for line
in activity
:
125 # Create an entry if there was none for this author yet
126 if not result
.has_key(line
):
133 def activity(id, field
, startFrom
):
134 """Shows the activity for the specified developer in the current repo.
137 id: The id of the developer, as specified by field.
138 field: The field to filter on.
139 startfrom: The commit to start logging from.
143 result
= git
.log("--numstat", startFrom
, "--", pretty
="format:%" + field
)
145 log
= result
.splitlines(True)
146 activity
= activityInArea(log
, id)
150 for key
, value
in activity
.iteritems():
151 result
.append(str(key
) + " = " + str(value
))
155 def aggregateActivity(idFilter
, field
, startFrom
):
156 """Aggregates the activity for all developers
159 idFilter: The id to filter on, if None all developers will be shown.
160 field: The field to filter on.
169 def _checkOptions(parser
, options
):
170 """Checks the specified options and uses the parser to indicate errors
173 parser: The parser to use to signal when the options are bad.
174 options: The options to check.
177 opts
= [options
.aggregate
, options
.developer
, options
.file]
179 if not parse
.isUnique(opts
):
180 parser
.error("Please choose exactly one mode")
185 parse
.check_file(value
=options
.file, relative
=options
.relative
)
186 except OptionValueError
, e
:
191 """Dispatches author related commands
194 progname
= os
.path
.basename(sys
.argv
[0]) + " author"
196 parser
= OptionParser(option_class
=parse
.GitOption
, prog
=progname
)
201 help="aggregate the results")
205 help="the id to filter on")
209 help="the file to filter on")
213 help="the one/two letter identifier specifying which field to use as id")
216 "-s", "--start-from",
219 help="the commit to start logging from")
224 help="paths are relative to the current directory")
226 parser
.set_default("id", "ae")
227 parser
.set_default("start_from", "HEAD")
228 parser
.set_default("relative", False)
230 (options
, args
) = parser
.parse_args(list(args
))
232 _checkOptions(parser
, options
)
234 if options
.aggregate
:
235 result
= aggregateActivity(options
.developer
, options
.id, options
.start_from
)
236 elif options
.developer
:
237 result
= activity(options
.developer
, options
.id, options
.start_from
)
239 activity_for_file
= activityInFile( options
.file,
246 for key
, value
in activity_for_file
.iteritems():
247 result
.append(key
+ ": " + str(value
))