git_fame 1.7.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,83 @@
1
+ # Extracted from the dolt gem. Currently not maintained.
2
+ # Source: https://docs.omniref.com/ruby/gems/libdolt/0.33.14/symbols/Dolt::Git::Blame::PorcelainParser
3
+ module GitFame
4
+ class BlameParser
5
+ def initialize(output)
6
+ @output = output
7
+ @commits = {}
8
+ end
9
+ def parse
10
+ lines = @output.split("\n")
11
+ chunks = []
12
+ while lines.length > 0
13
+ chunk = extract_header(lines)
14
+ affected_lines = extract_lines(lines, chunk[:num_lines])
15
+ if chunks.last && chunk[:oid] == chunks.last[:oid]
16
+ chunks.last[:lines].concat(affected_lines)
17
+ else
18
+ chunk[:lines] = affected_lines
19
+ chunks << chunk
20
+ end
21
+ end
22
+ chunks
23
+ rescue Exception => error
24
+ raise Error, "Failed parsing Procelain: #{error.message}"
25
+ end
26
+
27
+ def is_header?(line)
28
+ line =~ /^[0-9a-f]{40} \d+ \d+ \d+$/
29
+ end
30
+
31
+ def extract_header(lines)
32
+ header = lines.shift
33
+ pieces = header.scan(/^([0-9a-f]{40}) (\d+) (\d+) (\d+)$/).first
34
+ header = { :oid => pieces.first, :num_lines => pieces[3].to_i }
35
+ if lines.first =~ /^author/
36
+ header[:author] = extract_hash(lines, :author)
37
+ header[:committer] = extract_hash(lines, :committer)
38
+ header[:summary] = extract(lines, "summary")
39
+ header[:boundary] = extract(lines, "boundary")
40
+ throwaway = lines.shift until throwaway =~ /^filename/
41
+ @commits[header[:oid]] = header
42
+ else
43
+ header[:author] = @commits[header[:oid]][:author]
44
+ header[:committer] = @commits[header[:oid]][:committer]
45
+ header[:summary] = @commits[header[:oid]][:summary]
46
+ end
47
+ header
48
+ end
49
+
50
+ def extract_lines(lines, num)
51
+ extracted = []
52
+ num.times do
53
+ if extracted.length > 0
54
+ line = lines.shift # Header for next line
55
+ end
56
+ content = lines.shift # Actual content
57
+ next unless content
58
+ extracted.push(content[1..content.length]) # 8 spaces padding
59
+ end
60
+ extracted
61
+ end
62
+
63
+ def extract_hash(lines, type)
64
+ {
65
+ :name => extract(lines, "#{type}"),
66
+ :mail => extract(lines, "#{type}-mail").gsub(/[<>]/, ""),
67
+ :time => (Time.at(extract(lines, "#{type}-time").to_i).utc +
68
+ Time.zone_offset(extract(lines, "#{type}-tz")))
69
+ }
70
+ end
71
+ def extract(lines, thing)
72
+ if thing == "boundary"
73
+ if (line = lines.shift) == thing
74
+ return true
75
+ else
76
+ return ! lines.unshift(line)
77
+ end
78
+ end
79
+
80
+ lines.shift.split("#{thing} ")[1]
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,27 @@
1
+ class CommitRange < Struct.new(:data, :current_branch)
2
+ SHORT_LENGTH = 7
3
+
4
+ def to_s(short = false)
5
+ @_to_s ||= range.map do |commit|
6
+ short ? shorten(commit) : commit
7
+ end.join("..")
8
+ end
9
+
10
+ def is_range?
11
+ data.is_a?(Array)
12
+ end
13
+
14
+ def range
15
+ is_range? ? data : [data]
16
+ end
17
+
18
+ private
19
+
20
+ def branch?(commit)
21
+ current_branch == commit
22
+ end
23
+
24
+ def shorten(commit)
25
+ branch?(commit) ? commit : commit[0..SHORT_LENGTH]
26
+ end
27
+ end
@@ -1,4 +1,6 @@
1
1
  module GitFame
2
- class BranchNotFound < StandardError
2
+ class Error < StandardError
3
+ end
4
+ class NothingFound < StandardError
3
5
  end
4
6
  end
@@ -1,11 +1,18 @@
1
- class SilentProgressbar < ProgressBar
1
+ require "ruby-progressbar"
2
+
3
+ class SilentProgressbar < ProgressBar::Base
2
4
  #
3
5
  # @name String Name for progressbar
4
6
  # @steps Fixnum Total number of steps
5
7
  # @active Should progressbar be visible?
6
8
  #
7
9
  def initialize(name, steps, active = false)
8
- out = active ? $stdout : File.new("/dev/null", "w")
9
- super(name, steps, out)
10
+ output = active ? $stdout : File.new("/dev/null", "w")
11
+ super({
12
+ title: name,
13
+ total: steps,
14
+ output: output,
15
+ smoothing: 0.6
16
+ })
10
17
  end
11
18
  end
@@ -1,3 +1,3 @@
1
1
  module GitFame
2
- VERSION = "1.7.2"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -19,19 +19,21 @@ describe "bin/git-fame" do
19
19
  "--sort=name",
20
20
  "--sort=commits",
21
21
  "--sort=loc",
22
- "--sort=files",
23
- "--progressbar=0",
24
- "--progressbar=1",
22
+ "--hide-progressbar",
25
23
  "--whitespace",
26
- "--bytype",
24
+ "--by-type",
27
25
  "--include=hello",
28
26
  "--exclude=hello",
29
27
  "--extension=rb,ln",
30
28
  "--branch=master",
31
29
  "--format=csv",
32
30
  "--format=pretty",
31
+ "--before=2010-01-01",
32
+ "--after=1980-01-01",
33
33
  "--version",
34
- "--help"
34
+ "--help",
35
+ "--verbose",
36
+ "--everything"
35
37
  ].each do |option|
36
38
  it "should support #{option}" do
37
39
  run(option).should be_a_succees
@@ -41,4 +43,65 @@ describe "bin/git-fame" do
41
43
  it "should sort by loc by default" do
42
44
  run("--sort=loc", "--progressbar=0").first.should eq(run("--progressbar=0").first)
43
45
  end
44
- end
46
+
47
+ context "dates" do
48
+ it "should fail on invalid before date" do
49
+ res = run("--before='---'")
50
+ res.should_not be_a_succees
51
+ res.first.should eq("Error: '---' is not a valid date\n")
52
+ end
53
+
54
+ it "should fail on invalid after date" do
55
+ res = run("--after='---'")
56
+ res.should_not be_a_succees
57
+ res.should include_output("Error: '---' is not a valid date\n")
58
+ end
59
+
60
+ it "should not print stack trace on invalid dates (--after)" do
61
+ res = run("--after='---'")
62
+ res.should_not be_a_succees
63
+ res.should_not include_output("GitFame::Error")
64
+ end
65
+
66
+ it "should not print stack trace on invalid dates (--before)" do
67
+ res = run("--before='---'")
68
+ res.should_not be_a_succees
69
+ res.should_not include_output("GitFame::Error")
70
+ end
71
+
72
+ it "should not print stack trace on out of span (--before)" do
73
+ res = run("--before='1910-01-01'")
74
+ res.should_not be_a_succees
75
+ res.should_not include_output("GitFame::Error")
76
+ end
77
+
78
+ it "should not print stack trace on out of span (--after)" do
79
+ res = run("--after='2100-01-01'")
80
+ res.should_not be_a_succees
81
+ res.should_not include_output("GitFame::Error")
82
+ end
83
+ end
84
+
85
+ context "sort" do
86
+ it "should fail on non existing option" do
87
+ run("--sort=-----").should_not be_a_succees
88
+ end
89
+
90
+ results = []
91
+ GitFame::SORT.each do |option|
92
+ it "should be able to sort by #{option}" do
93
+ out = run("--sort=#{option}")
94
+ out.should be_a_succees
95
+ # TODO: Not a impl. problem
96
+ # Just not enough data
97
+ unless option == "name"
98
+ results.push(out.first)
99
+ end
100
+ end
101
+ end
102
+
103
+ it "#{GitFame::SORT.join(", ")} should not output the same thing" do
104
+ results.uniq.sort.should eq(results.sort)
105
+ end
106
+ end
107
+ end
@@ -1,33 +1,39 @@
1
1
  describe GitFame::Base do
2
- let(:subject) { GitFame::Base.new({repository: repository}) }
2
+ let(:subject) { GitFame::Base.new({ repository: repository }) }
3
+
3
4
  describe "#authors" do
4
5
  it "should have a list of authors" do
5
6
  subject.should have(3).authors
6
7
  end
7
8
 
8
9
  describe "author" do
9
- require "pp"
10
- let(:author) { subject.authors.last }
10
+ let(:author) do
11
+ subject.authors.find do |author|
12
+ author.name.include?("Magnus Holm")
13
+ end
14
+ end
15
+
11
16
  it "should have a bunch of commits" do
12
- author.raw_commits.should eq(23)
17
+ author.raw(:commits).should eq(41)
13
18
  end
14
19
 
15
20
  it "should respond to name" do
16
- author.name.should eq("Linus Oleander")
21
+ author.name.should eq("Magnus Holm")
17
22
  end
18
23
 
19
24
  it "should have a number of locs" do
20
- author.raw_loc.should eq(136)
25
+ author.raw(:loc).should eq(579)
21
26
  end
22
27
 
23
28
  it "should have a number of files" do
24
- author.raw_files.should eq(7)
29
+ author.raw(:files).should eq(4)
25
30
  end
26
31
 
27
32
  it "should have a distribution" do
28
- author.distribution.should eq("12.6 / 32.9 / 43.8")
33
+ author.distribution.should eq("53.9 / 58.6 / 25.0")
29
34
  end
30
35
  end
36
+
31
37
  describe "format" do
32
38
  let(:author) do
33
39
  GitFame::Author.new({
@@ -55,7 +61,7 @@ describe GitFame::Base do
55
61
  it "should respond to #loc, #commits and #files" do
56
62
  subject.files.should eq(16)
57
63
  subject.commits.should eq(70)
58
- subject.loc.should eq(1082)
64
+ subject.loc.should eq(1075)
59
65
  end
60
66
  end
61
67
 
@@ -74,6 +80,7 @@ describe GitFame::Base do
74
80
  repository: repository,
75
81
  sort: "commits"
76
82
  }).authors
83
+
77
84
  authors.map(&:name).
78
85
  should eq(["Magnus Holm", "Linus Oleander", "7rans"])
79
86
  end
@@ -86,29 +93,55 @@ describe GitFame::Base do
86
93
  authors.map(&:name).
87
94
  should eq(["7rans", "Linus Oleander", "Magnus Holm"])
88
95
  end
96
+
97
+ it "should be able to sort #authors by loc" do
98
+ authors = GitFame::Base.new({
99
+ repository: repository,
100
+ sort: "loc"
101
+ }).authors
102
+ authors.map(&:name).
103
+ should eq(["Magnus Holm", "7rans", "Linus Oleander"])
104
+ end
89
105
  end
90
106
 
91
- describe "#command_line_arguments" do
107
+ describe "exclude" do
92
108
  let(:subject) do
93
109
  GitFame::Base.new({
94
110
  repository: repository,
95
- exclude: "lib",
96
- bytype: true,
97
- extensions: "rb,rdoc"
111
+ exclude: "lib/*"
98
112
  })
99
113
  end
100
114
 
101
115
  it "should exclude the lib folder" do
102
- subject.file_list.include?("lib/gash.rb").should be_falsey
116
+ subject.file_list.map(&:path).should_not include("lib/gash.rb")
103
117
  end
104
118
 
105
119
  it "should exclude non rb or rdoc files" do
106
- subject.file_list.include?("HISTORY").should be_falsey
120
+ subject.file_list.map(&:path).should include("HISTORY")
121
+ end
122
+
123
+ it "should exclude non matching paths" do
124
+ subject.file_list.map(&:path).should include("spec/gash_spec.rb")
125
+ end
126
+ end
127
+
128
+ describe "types" do
129
+ let(:subject) do
130
+ GitFame::Base.new({
131
+ repository: repository,
132
+ by_type: true,
133
+ extensions: "rb,rdoc"
134
+ })
135
+ end
136
+
137
+ let(:author) do
138
+ subject.authors.find do |author|
139
+ author.name == "7rans"
140
+ end
107
141
  end
108
142
 
109
- let(:author) { subject.authors.find { |author| author.name == "7rans" } }
110
143
  it "should break out counts by file type" do
111
- author.file_type_counts["rdoc"].should eq(23)
144
+ author.file_type_counts["rdoc"].should eq(1)
112
145
  end
113
146
 
114
147
  it "should output zero for file types the author hasn't touched" do
@@ -116,6 +149,43 @@ describe GitFame::Base do
116
149
  end
117
150
  end
118
151
 
152
+ describe "include" do
153
+ let(:subject) do
154
+ GitFame::Base.new({
155
+ repository: repository,
156
+ include: "lib/*,spec/*"
157
+ })
158
+ end
159
+
160
+ it "should exclude the lib folder" do
161
+ subject.file_list.map(&:path).should include("lib/gash.rb")
162
+ end
163
+
164
+ it "should exclude non rb or rdoc files" do
165
+ subject.file_list.map(&:path).should_not include("HISTORY")
166
+ end
167
+
168
+ it "should exclude non matching paths" do
169
+ subject.file_list.map(&:path).should include("spec/gash_spec.rb")
170
+ end
171
+ end
172
+
173
+ context "glob" do
174
+ it "should exclude" do
175
+ GitFame::Base.new({
176
+ repository: repository,
177
+ exclude: "lib/*.rb"
178
+ }).file_list.map(&:path).should_not include("lib/gash.rb")
179
+ end
180
+
181
+ it "should include" do
182
+ GitFame::Base.new({
183
+ repository: repository,
184
+ include: "lib/*.rb"
185
+ }).file_list.map(&:path).should include("lib/gash.rb")
186
+ end
187
+ end
188
+
119
189
  describe "#pretty_print" do
120
190
  it "should print" do
121
191
  lambda {
@@ -132,22 +202,21 @@ describe GitFame::Base do
132
202
  end
133
203
 
134
204
  it "should be equal to" do
135
- subject.to_csv.should eq("name,loc,commits,files,distribution\n" \
136
- "Magnus Holm,586,41,4,54.2 / 58.6 / 25.0\n" \
137
- "7rans,360,6,10,33.3 / 8.6 / 62.5\n" \
138
- "Linus Oleander,136,23,7,12.6 / 32.9 / 43.8\n")
205
+ subject.to_csv.should eq([
206
+ "name,loc,commits,files,distribution\n",
207
+ "Magnus Holm,579,41,4,53.9 / 58.6 / 25.0\n",
208
+ "7rans,360,6,10,33.5 / 8.6 / 62.5\n",
209
+ "Linus Oleander,136,23,7,12.7 / 32.9 / 43.8\n",
210
+ ].join)
139
211
  end
140
212
  end
141
213
 
142
- describe "branches" do
214
+ describe "branches", :this do
143
215
  it "should handle existing branches" do
144
- authors = GitFame::Base.new({
216
+ GitFame::Base.new({
145
217
  repository: repository,
146
- branch: "0.1.0"
218
+ branch: "master"
147
219
  }).authors
148
-
149
- authors.count.should eq(1)
150
- authors.first.name.should eq("Magnus Holm")
151
220
  end
152
221
 
153
222
  it "should raise an error if branch doesn't exist" do
@@ -156,7 +225,7 @@ describe GitFame::Base do
156
225
  repository: repository,
157
226
  branch: "-----"
158
227
  }).authors
159
- }.to raise_error(GitFame::BranchNotFound)
228
+ }.to raise_error(GitFame::Error)
160
229
  end
161
230
 
162
231
  it "should not raise on empty branch (use fallback)" do
@@ -171,4 +240,225 @@ describe GitFame::Base do
171
240
  }).authors.should_not be_empty
172
241
  end
173
242
  end
243
+
244
+ describe "after", :this do
245
+ it "should raise error if 'after' is to far in the future" do
246
+ lambda do
247
+ GitFame::Base.new({
248
+ repository: repository,
249
+ after: "2020-01-01"
250
+ }).commits
251
+ end.should raise_error(GitFame::Error)
252
+ end
253
+
254
+ it "should handle same day as HEAD" do
255
+ GitFame::Base.new({
256
+ repository: repository,
257
+ after: "2012-05-23"
258
+ }).commits.should eq(1)
259
+ end
260
+
261
+ it "should handle an out of scope 'after' date" do
262
+ GitFame::Base.new({
263
+ repository: repository,
264
+ after: "2000-01-01"
265
+ }).commits.should eq(70)
266
+ end
267
+ end
268
+
269
+ describe "before", :this do
270
+ it "should raise error if 'before' is to far back in history" do
271
+ lambda do
272
+ GitFame::Base.new({
273
+ repository: repository,
274
+ before: "1972-01-01"
275
+ }).commits
276
+ end.should raise_error(GitFame::Error)
277
+ end
278
+
279
+ it "should handle same day as last commit" do
280
+ GitFame::Base.new({
281
+ repository: repository,
282
+ before: "2008-08-31"
283
+ }).commits.should eq(1)
284
+ end
285
+
286
+ it "should handle an out of scope 'before' date" do
287
+ GitFame::Base.new({
288
+ repository: repository,
289
+ before: "2050-01-01"
290
+ }).commits.should eq(70)
291
+ end
292
+
293
+ it "should handle same day as last commit" do
294
+ GitFame::Base.new({
295
+ repository: repository,
296
+ before: "2008-08-31"
297
+ }).commits.should eq(1)
298
+ end
299
+
300
+ it "should validate before date" do
301
+ lambda do
302
+ GitFame::Base.new({
303
+ repository: repository,
304
+ before: "----"
305
+ })
306
+ end.should raise_error(GitFame::Error)
307
+ end
308
+
309
+ it "should validate before date" do
310
+ lambda do
311
+ GitFame::Base.new({
312
+ repository: repository,
313
+ after: "----"
314
+ })
315
+ end.should raise_error(GitFame::Error)
316
+ end
317
+ end
318
+
319
+ describe "span", :this do
320
+ it "should handle spans as inclusive" do
321
+ GitFame::Base.new({
322
+ repository: repository,
323
+ after: "2008-09-01",
324
+ before: "2008-09-03"
325
+ }).commits.should eq(4)
326
+ end
327
+
328
+ it "should should possible a wide date span (include all)" do
329
+ GitFame::Base.new({
330
+ repository: repository,
331
+ after: "2000-01-01",
332
+ before: "2020-01-01"
333
+ }).commits.should eq(70)
334
+ end
335
+
336
+ it "should handle a too early 'after'" do
337
+ GitFame::Base.new({
338
+ repository: repository,
339
+ after: "2000-01-01",
340
+ before: "2008-08-31"
341
+ }).commits.should eq(1)
342
+ end
343
+
344
+ it "should catch empty commit span" do
345
+ lambda do
346
+ GitFame::Base.new({
347
+ repository: repository,
348
+ after: "2010-04-10",
349
+ before: "2010-04-11"
350
+ }).commits
351
+ end.should raise_error(GitFame::Error)
352
+ end
353
+
354
+ it "should handle a too late 'before'" do
355
+ GitFame::Base.new({
356
+ repository: repository,
357
+ after: "2012-02-29",
358
+ before: "2030-01-01"
359
+ }).commits.should eq(4)
360
+ end
361
+
362
+ it "should handle the after date same date as init commit" do
363
+ GitFame::Base.new({
364
+ repository: repository,
365
+ after: "2008-08-31",
366
+ before: "2008-09-03"
367
+ }).commits.should eq(5)
368
+ end
369
+
370
+ it "should handle an existing before with an old after" do
371
+ GitFame::Base.new({
372
+ repository: repository,
373
+ after: "2000-08-31",
374
+ before: "2008-09-03"
375
+ }).commits.should eq(5)
376
+ end
377
+
378
+ it "should handle an existing after with an old before" do
379
+ GitFame::Base.new({
380
+ repository: repository,
381
+ after: "2012-05-23",
382
+ before: "2020-01-01"
383
+ }).commits.should eq(1)
384
+ end
385
+
386
+ it "should raise an error if after > before" do
387
+ lambda do
388
+ GitFame::Base.new({
389
+ repository: repository,
390
+ after: "2020-01-01",
391
+ before: "2000-01-01"
392
+ }).commits
393
+ end.should raise_error(GitFame::Error)
394
+ end
395
+
396
+ it "should raise error if set too high" do
397
+ lambda do
398
+ GitFame::Base.new({
399
+ repository: repository,
400
+ after: "2030-01-01",
401
+ before: "2050-01-01"
402
+ }).commits
403
+ end.should raise_error(GitFame::Error)
404
+ end
405
+
406
+ it "should raise error if set too low" do
407
+ lambda do
408
+ GitFame::Base.new({
409
+ repository: repository,
410
+ after: "1990-01-01",
411
+ before: "2000-01-01"
412
+ }).commits
413
+ end.should raise_error(GitFame::Error)
414
+ end
415
+
416
+ it "should handle same day" do
417
+ GitFame::Base.new({
418
+ repository: repository,
419
+ after: "2012-02-29",
420
+ before: "2012-02-29"
421
+ }).commits.should eq(3)
422
+ end
423
+
424
+ it "should handle same day (HEAD)" do
425
+ GitFame::Base.new({
426
+ repository: repository,
427
+ after: "2012-05-23",
428
+ before: "2012-05-23"
429
+ }).commits.should eq(1)
430
+ end
431
+
432
+ it "should handle same day (last commit)" do
433
+ GitFame::Base.new({
434
+ repository: repository,
435
+ after: "2008-08-31",
436
+ before: "2008-08-31"
437
+ }).commits.should eq(1)
438
+ end
439
+
440
+ it "should handle a non existent 'after' date" do
441
+ GitFame::Base.new({
442
+ repository: repository,
443
+ after: "2008-09-02",
444
+ before: "2008-09-04"
445
+ }).commits.should eq(4)
446
+ end
447
+
448
+ it "should handle a non existent 'before' date" do
449
+ GitFame::Base.new({
450
+ repository: repository,
451
+ after: "2008-09-04",
452
+ before: "2008-09-05"
453
+ }).commits.should eq(3)
454
+ end
455
+
456
+ it "should handle both non existent 'before' and 'after'" do
457
+ GitFame::Base.new({
458
+ repository: repository,
459
+ after: "2008-09-02",
460
+ before: "2008-09-05"
461
+ }).commits.should eq(4)
462
+ end
463
+ end
174
464
  end