git_fame 1.0.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +20 -6
- data/bin/git-fame +8 -2
- data/lib/git_fame/author.rb +19 -8
- data/lib/git_fame/base.rb +59 -13
- data/lib/git_fame/version.rb +1 -1
- data/spec/git_fame_spec.rb +26 -3
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: daa907cac405078c8f5abb7ed8fbfc88287a697c
|
4
|
+
data.tar.gz: 47376faa0102f406245338f2b6bd06f39759db2d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be9bf2ff037c1d694a77b7f85cca6fac75f90d0e3020120283a03a0e0d39e987e20a7c545451f8b04a5ac9a86b8f00887fff9150b01ab816bd7d46eb0b530d5a
|
7
|
+
data.tar.gz: 34aa7cf88a69a5547d5b790254f4567bc06b1071f066301285f19ec2353e4878de6bd708a9cc7281bd737341f99b6f1ad87f4207137a06a11ebf3ee6168abafe
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# GitFame
|
2
2
|
|
3
|
-
|
3
|
+
Pretty-print collaborators sorted by contributions.
|
4
4
|
|
5
5
|
## Output
|
6
6
|
|
@@ -10,7 +10,7 @@ Total number of lines: 63,132
|
|
10
10
|
Total number of commits: 4,330
|
11
11
|
|
12
12
|
+------------------------+--------+---------+-------+--------------------+
|
13
|
-
| name | loc | commits | files |
|
13
|
+
| name | loc | commits | files | distribution |
|
14
14
|
+------------------------+--------+---------+-------+--------------------+
|
15
15
|
| Johan Sørensen | 22,272 | 1,814 | 414 | 35.3 / 41.9 / 20.2 |
|
16
16
|
| Marius Mathiesen | 10,387 | 502 | 229 | 16.5 / 11.6 / 11.2 |
|
@@ -44,9 +44,12 @@ Run `git fame` to generate output as above.
|
|
44
44
|
|
45
45
|
#### Options
|
46
46
|
|
47
|
+
- `git fame --bytype` Should a breakout of line counts by file type be output? Default is 'false'
|
48
|
+
- `git fame --exclude=paths/to/files,paths/to/other/files` Comma separated paths to exclude from the counts. Default is none.
|
47
49
|
- `git fame --order=loc` Order table by `loc`. Available options are: `loc`, `commits` and `files`. Default is `loc`.
|
48
|
-
- `git fame --repository=/path/to/repo` Git repository to be used. Default is the current folder.
|
49
50
|
- `git fame --progressbar=1` Should a progressbar be visible during the calculation? Default is `1`.
|
51
|
+
- `git fame --whitespace` Ignore whitespace changes when blaming files. Default is `false`.
|
52
|
+
- `git fame --repository=/path/to/repo` Git repository to be used. Default is the current folder.
|
50
53
|
|
51
54
|
### Class
|
52
55
|
|
@@ -57,12 +60,18 @@ Want to work with the data before printing it?
|
|
57
60
|
- **repository** (String) Path to repository.
|
58
61
|
- **sort** (String) What should #authors be sorted by? Available options are: `loc`, `commits` and `files`. Default is `loc`.
|
59
62
|
- **progressbar** (Boolean) Should a progressbar be shown during the calculation? Default is `false`.
|
63
|
+
- **whitespace** (Boolean) Ignore whitespace changes when blaming files. Default is `false`.
|
64
|
+
- **bytype** (Boolean) Should a breakout of line counts by file type be output? Default is 'false'
|
65
|
+
- **exclude** (String) Comma separated paths to exclude from the counts. Default is none.
|
60
66
|
|
61
67
|
``` ruby
|
62
68
|
repository = GitFame::Base.new({
|
63
|
-
sort: "loc",
|
69
|
+
sort: "loc",
|
64
70
|
repository: "/tmp/repo",
|
65
|
-
progressbar: false
|
71
|
+
progressbar: false,
|
72
|
+
whitespace: false
|
73
|
+
bytype: false,
|
74
|
+
exclude: "vendor, public/assets"
|
66
75
|
})
|
67
76
|
```
|
68
77
|
|
@@ -88,10 +97,15 @@ repository = GitFame::Base.new({
|
|
88
97
|
- `author.commits` (String) Number of commits.
|
89
98
|
- `author.files` (String) Number of files changed.
|
90
99
|
- Non formated
|
91
|
-
- `author.
|
100
|
+
- `author.distribution` (String) Distribution (in %) between users (loc/commits/files)
|
92
101
|
- `author.raw_loc` (Fixnum) Number of lines.
|
93
102
|
- `author.raw_commits` (Fixnum) Number of commits.
|
94
103
|
- `author.raw_files` (Fixnum) Number of files changed.
|
104
|
+
- `author.file_type_counts` (Array) File types (k) and loc (v)
|
105
|
+
|
106
|
+
#### A note about authors found
|
107
|
+
|
108
|
+
The list of authors may include what you perceive to be duplicate people. If a git user's configured name or email address change over time, the person will appear multiple times in this list (and your repo's git history). Git allows you to configure this using the .mailmap feature. See ````git shortlog --help```` for more information.
|
95
109
|
|
96
110
|
## Contributing
|
97
111
|
|
data/bin/git-fame
CHANGED
@@ -9,6 +9,9 @@ opts = Trollop::options do
|
|
9
9
|
opt :repository, "What git repository should be used?", default: "."
|
10
10
|
opt :sort, "What should the printed table be sorted by? #{sort.join(", ")}", default: "loc", type: String
|
11
11
|
opt :progressbar, "Show progressbar during calculation", default: 1, type: Integer
|
12
|
+
opt :whitespace, "Ignore whitespace changes", default: false
|
13
|
+
opt :bytype, "Group line counts by file extension (i.e. .rb, .erb, .yml)", default: false
|
14
|
+
opt :exclude, "Paths to exclude (comma separated)", default: "", type: String
|
12
15
|
end
|
13
16
|
|
14
17
|
Trollop::die :repository, "is not a git repository" unless GitFame::Base.git_repository?(opts[:repository])
|
@@ -16,5 +19,8 @@ Trollop::die :sort, "must be one of the following; #{sort.join(", ")}" unless so
|
|
16
19
|
GitFame::Base.new({
|
17
20
|
repository: opts[:repository],
|
18
21
|
progressbar: opts[:progressbar] == 1,
|
19
|
-
sort: opts[:sort]
|
20
|
-
|
22
|
+
sort: opts[:sort],
|
23
|
+
whitespace: opts[:whitespace],
|
24
|
+
bytype: opts[:bytype],
|
25
|
+
exclude: opts[:exclude]
|
26
|
+
}).pretty_puts
|
data/lib/git_fame/author.rb
CHANGED
@@ -1,22 +1,25 @@
|
|
1
1
|
module GitFame
|
2
2
|
class Author
|
3
3
|
include GitFame::Helper
|
4
|
-
attr_accessor :name, :raw_files, :raw_commits, :raw_loc, :files_list
|
4
|
+
attr_accessor :name, :raw_files, :raw_commits, :raw_loc, :files_list, :file_type_counts
|
5
5
|
#
|
6
6
|
# @args Hash
|
7
7
|
#
|
8
8
|
def initialize(args = {})
|
9
|
-
@raw_loc
|
10
|
-
@raw_commits
|
11
|
-
@raw_files
|
12
|
-
|
9
|
+
@raw_loc = 0
|
10
|
+
@raw_commits = 0
|
11
|
+
@raw_files = 0
|
12
|
+
@file_type_counts = Hash.new(0)
|
13
|
+
args.keys.each do |name|
|
14
|
+
instance_variable_set "@" + name.to_s, args[name]
|
15
|
+
end
|
13
16
|
end
|
14
17
|
|
15
18
|
#
|
16
|
-
# @return String Percent of total
|
17
19
|
# @format loc / commits / files
|
20
|
+
# @return String Distribution (in %) between users
|
18
21
|
#
|
19
|
-
def
|
22
|
+
def distribution
|
20
23
|
"%.1f / %.1f / %.1f" % [:loc, :commits, :files].
|
21
24
|
map{ |w| (send("raw_#{w}") / @parent.send(w).to_f) * 100 }
|
22
25
|
end
|
@@ -26,5 +29,13 @@ module GitFame
|
|
26
29
|
number_with_delimiter(send("raw_#{method}"))
|
27
30
|
end
|
28
31
|
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Intended to catch file type counts
|
35
|
+
#
|
36
|
+
def method_missing(m, *args, &block)
|
37
|
+
file_type_counts[m.to_s]
|
38
|
+
end
|
39
|
+
|
29
40
|
end
|
30
|
-
end
|
41
|
+
end
|
data/lib/git_fame/base.rb
CHANGED
@@ -5,13 +5,21 @@ module GitFame
|
|
5
5
|
#
|
6
6
|
# @args[:repository] String Absolute path to git repository
|
7
7
|
# @args[:sort] String What should #authors be sorted by?
|
8
|
+
# @args[:bytype] Boolean Should counts be grouped by file extension?
|
9
|
+
# @args[:exclude] String Comma-separated list of paths in the repo which should be excluded
|
8
10
|
#
|
9
11
|
def initialize(args)
|
10
|
-
@sort
|
11
|
-
@progressbar
|
12
|
-
|
13
|
-
@
|
12
|
+
@sort = "loc"
|
13
|
+
@progressbar = false
|
14
|
+
@whitespace = false
|
15
|
+
@bytype = false
|
16
|
+
@exclude = ""
|
17
|
+
@authors = {}
|
14
18
|
@file_authors = Hash.new { |h,k| h[k] = {} }
|
19
|
+
args.keys.each do |name|
|
20
|
+
instance_variable_set "@" + name.to_s, args[name]
|
21
|
+
end
|
22
|
+
convert_exclude_paths_to_array
|
15
23
|
end
|
16
24
|
|
17
25
|
#
|
@@ -23,7 +31,10 @@ module GitFame
|
|
23
31
|
puts "\nTotal number of files: #{number_with_delimiter(files)}"
|
24
32
|
puts "Total number of lines: #{number_with_delimiter(loc)}"
|
25
33
|
puts "Total number of commits: #{number_with_delimiter(commits)}\n"
|
26
|
-
|
34
|
+
|
35
|
+
fields = [:name, :loc, :commits, :files, :distribution]
|
36
|
+
fields << populate.instance_variable_get("@file_extensions").uniq.sort if @bytype
|
37
|
+
table(authors, fields: fields.flatten)
|
27
38
|
end
|
28
39
|
|
29
40
|
#
|
@@ -33,6 +44,13 @@ module GitFame
|
|
33
44
|
populate.instance_variable_get("@files").count
|
34
45
|
end
|
35
46
|
|
47
|
+
#
|
48
|
+
# @return Array list of repo files processed
|
49
|
+
#
|
50
|
+
def file_list
|
51
|
+
populate.instance_variable_get("@files")
|
52
|
+
end
|
53
|
+
|
36
54
|
#
|
37
55
|
# @return Fixnum Total number of commits
|
38
56
|
#
|
@@ -52,9 +70,9 @@ module GitFame
|
|
52
70
|
#
|
53
71
|
def authors
|
54
72
|
authors = populate.instance_variable_get("@authors").values
|
55
|
-
@sort ? authors.sort_by do |author|
|
73
|
+
@sort ? authors.sort_by do |author|
|
56
74
|
if @sort == "name"
|
57
|
-
author.send(@sort)
|
75
|
+
author.send(@sort)
|
58
76
|
else
|
59
77
|
-1 * author.send("raw_#{@sort}")
|
60
78
|
end
|
@@ -106,35 +124,44 @@ module GitFame
|
|
106
124
|
def populate
|
107
125
|
@_pop ||= lambda {
|
108
126
|
@files = execute("git ls-files").split("\n")
|
127
|
+
@file_extensions = []
|
128
|
+
remove_excluded_files
|
109
129
|
progressbar = SilentProgressbar.new("Blame", @files.count, @progressbar)
|
130
|
+
blame_opts = @whitespace ? "-w" : ""
|
110
131
|
@files.each do |file|
|
111
132
|
progressbar.inc
|
133
|
+
if @bytype
|
134
|
+
file_extension = File.extname(file).sub(/\A\./,"")
|
135
|
+
file_extension = "unknown" if file_extension.empty?
|
136
|
+
end
|
112
137
|
if type = Mimer.identify(File.join(@repository, file)) and not type.mime_type.match(/binary/)
|
138
|
+
@file_extensions << file_extension # only count extensions that aren't binary!
|
113
139
|
begin
|
114
|
-
execute("git blame '#{file}' --line-porcelain").scan(/^author (.+)$/).each do |author|
|
140
|
+
execute("git blame '#{file}' #{blame_opts} --line-porcelain").scan(/^author (.+)$/).each do |author|
|
115
141
|
fetch(author.first).raw_loc += 1
|
116
142
|
@file_authors[author.first][file] ||= 1
|
143
|
+
fetch(author.first).file_type_counts[file_extension] += 1 if @bytype
|
117
144
|
end
|
118
145
|
rescue ArgumentError; end # Encoding error
|
119
146
|
end
|
120
147
|
end
|
121
148
|
|
122
|
-
execute("git shortlog -se").split("\n").map do |l|
|
149
|
+
execute("git shortlog -se").split("\n").map do |l|
|
123
150
|
_, commits, u = l.match(%r{^\s*(\d+)\s+(.+?)\s+<.+?>}).to_a
|
124
151
|
user = fetch(u)
|
125
152
|
# Has this user been updated before?
|
126
153
|
if user.raw_commits.zero?
|
127
154
|
update(u, {
|
128
|
-
raw_commits: commits.to_i,
|
129
|
-
raw_files: @file_authors[u].keys.count,
|
155
|
+
raw_commits: commits.to_i,
|
156
|
+
raw_files: @file_authors[u].keys.count,
|
130
157
|
files_list: @file_authors[u].keys
|
131
158
|
})
|
132
159
|
else
|
133
160
|
# Calculate the number of files edited by users
|
134
161
|
files = (user.files_list + @file_authors[u].keys).uniq
|
135
162
|
update(u, {
|
136
|
-
raw_commits: commits.to_i + user.raw_commits,
|
137
|
-
raw_files: files.count,
|
163
|
+
raw_commits: commits.to_i + user.raw_commits,
|
164
|
+
raw_files: files.count,
|
138
165
|
files_list: files
|
139
166
|
})
|
140
167
|
end
|
@@ -145,5 +172,24 @@ module GitFame
|
|
145
172
|
}.call
|
146
173
|
return self
|
147
174
|
end
|
175
|
+
|
176
|
+
#
|
177
|
+
# Converts @exclude argument to an array and removes leading slash
|
178
|
+
#
|
179
|
+
def convert_exclude_paths_to_array
|
180
|
+
@exclude = @exclude.split(",").map{|path| path.strip.sub(/\A\//, "") }
|
181
|
+
end
|
182
|
+
|
183
|
+
#
|
184
|
+
# Removes files matching paths in @exclude from @files instance variable
|
185
|
+
#
|
186
|
+
def remove_excluded_files
|
187
|
+
return if @exclude.empty?
|
188
|
+
@files = @files.map do |path|
|
189
|
+
next if path =~ /\A(#{@exclude.join("|")})/
|
190
|
+
path
|
191
|
+
end.compact
|
192
|
+
end
|
193
|
+
|
148
194
|
end
|
149
195
|
end
|
data/lib/git_fame/version.rb
CHANGED
data/spec/git_fame_spec.rb
CHANGED
@@ -23,12 +23,19 @@ describe GitFame::Base do
|
|
23
23
|
author.raw_files.should eq(7)
|
24
24
|
end
|
25
25
|
|
26
|
-
it "should have
|
27
|
-
author.
|
26
|
+
it "should have a distribution" do
|
27
|
+
author.distribution.should eq("12.6 / 32.9 / 43.8")
|
28
28
|
end
|
29
29
|
end
|
30
30
|
describe "format" do
|
31
|
-
let(:author)
|
31
|
+
let(:author) do
|
32
|
+
GitFame::Author.new({
|
33
|
+
raw_commits: 12345,
|
34
|
+
raw_files: 6789,
|
35
|
+
raw_loc: 1234
|
36
|
+
})
|
37
|
+
end
|
38
|
+
|
32
39
|
it "should format #commits" do
|
33
40
|
author.commits.should eq("12,345")
|
34
41
|
end
|
@@ -68,6 +75,22 @@ describe GitFame::Base do
|
|
68
75
|
end
|
69
76
|
end
|
70
77
|
|
78
|
+
describe "#command_line_arguments" do
|
79
|
+
let(:subject) { GitFame::Base.new({repository: @repository, exclude: "lib", bytype: true }) }
|
80
|
+
it "should exclude the lib folder" do
|
81
|
+
subject.file_list.include?("lib/gash.rb").should be_false
|
82
|
+
end
|
83
|
+
|
84
|
+
let(:author) { subject.authors.first }
|
85
|
+
it "should break out counts by file type" do
|
86
|
+
author.file_type_counts["rdoc"].should eq(23)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should output zero for file types the author hasn't touched" do
|
90
|
+
author.file_type_counts["derp"].should eq(0)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
71
94
|
describe "#pretty_print" do
|
72
95
|
it "should print" do
|
73
96
|
lambda {
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git_fame
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Linus Oleander
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-02-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: progressbar
|
@@ -124,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
124
|
version: '0'
|
125
125
|
requirements: []
|
126
126
|
rubyforge_project:
|
127
|
-
rubygems_version: 2.
|
127
|
+
rubygems_version: 2.1.8
|
128
128
|
signing_key:
|
129
129
|
specification_version: 4
|
130
130
|
summary: 'Generates some awesome stats from git-blame A Ruby wrapper for git-blame.
|