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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2f2b28facc6df4785adcfa9332d705bdb32562ef
4
- data.tar.gz: 3ce0f6380d3c87b031532f60dde25f472f181276
3
+ metadata.gz: daa907cac405078c8f5abb7ed8fbfc88287a697c
4
+ data.tar.gz: 47376faa0102f406245338f2b6bd06f39759db2d
5
5
  SHA512:
6
- metadata.gz: ba3c3784044e02474e25a028e32b962dd8d4322bcef7b5240e917cfa322f9c89165da9b82dc386e207c54f02793dfc7d40a8d123e535542ebdfd7fdf0e752463
7
- data.tar.gz: 7668bff93367a582932ee23e9474effe9c3ac55f39546a37dd831235bfa9fc95a518e8a5cbcf9aee7bd78c4cd82599a6b25e5b8b2bc4dc037f96d6e09fec56b5
6
+ metadata.gz: be9bf2ff037c1d694a77b7f85cca6fac75f90d0e3020120283a03a0e0d39e987e20a7c545451f8b04a5ac9a86b8f00887fff9150b01ab816bd7d46eb0b530d5a
7
+ data.tar.gz: 34aa7cf88a69a5547d5b790254f4567bc06b1071f066301285f19ec2353e4878de6bd708a9cc7281bd737341f99b6f1ad87f4207137a06a11ebf3ee6168abafe
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # GitFame
2
2
 
3
- Who did what in your project?
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 | percent |
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.percent` (String) Percent of total (loc/commits/files)
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
 
@@ -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
- }).pretty_puts
22
+ sort: opts[:sort],
23
+ whitespace: opts[:whitespace],
24
+ bytype: opts[:bytype],
25
+ exclude: opts[:exclude]
26
+ }).pretty_puts
@@ -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 = 0
10
- @raw_commits = 0
11
- @raw_files = 0
12
- args.keys.each { |name| instance_variable_set "@" + name.to_s, args[name] }
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 percent
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
@@ -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 = "loc"
11
- @progressbar = false
12
- args.keys.each { |name| instance_variable_set "@" + name.to_s, args[name] }
13
- @authors = {}
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
- table(authors, fields: [:name, :loc, :commits, :files, :percent])
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
@@ -1,3 +1,3 @@
1
1
  module GitFame
2
- VERSION = "1.0.0"
2
+ VERSION = "1.2.1"
3
3
  end
@@ -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 some percentage" do
27
- author.percent.should eq("12.6 / 32.9 / 43.8")
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) { GitFame::Author.new({raw_commits: 12345, raw_files: 6789, raw_loc: 1234})}
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.0.0
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: 2013-08-27 00:00:00.000000000 Z
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.0.0
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.