git_fame 1.7.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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