gitstats-rb 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.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +674 -0
  4. data/README.md +74 -0
  5. data/bin/gitstats +200 -0
  6. data/gitstats-rb.gemspec +24 -0
  7. data/lib/gitstats.rb +18 -0
  8. data/lib/gitstats/author.rb +37 -0
  9. data/lib/gitstats/git.rb +165 -0
  10. data/lib/gitstats/renderer.rb +30 -0
  11. data/lib/gitstats/renderer/gnuplot.rb +150 -0
  12. data/lib/gitstats/renderer/haml.rb +72 -0
  13. data/lib/gitstats/renderer/sass.rb +33 -0
  14. data/lib/gitstats/statgen.rb +119 -0
  15. data/lib/gitstats/stats.rb +12 -0
  16. data/lib/gitstats/stats/commit.rb +63 -0
  17. data/lib/gitstats/stats/commit/author.rb +17 -0
  18. data/lib/gitstats/stats/commit/time.rb +109 -0
  19. data/lib/gitstats/stats/file.rb +15 -0
  20. data/lib/gitstats/stats/file/filetype.rb +14 -0
  21. data/lib/gitstats/yearmonth.rb +39 -0
  22. data/template/gnuplot/activity.haml +3 -0
  23. data/template/gnuplot/asc.gif +0 -0
  24. data/template/gnuplot/authors.haml +2 -0
  25. data/template/gnuplot/bg.gif +0 -0
  26. data/template/gnuplot/commits_per_hour.plot +14 -0
  27. data/template/gnuplot/commits_per_month.plot +15 -0
  28. data/template/gnuplot/commits_per_wday.plot +15 -0
  29. data/template/gnuplot/commits_per_year.plot +11 -0
  30. data/template/gnuplot/commits_per_yearmonth.plot +11 -0
  31. data/template/gnuplot/desc.gif +0 -0
  32. data/template/gnuplot/filechanges_by_yearmonth.plot +17 -0
  33. data/template/gnuplot/files.haml +2 -0
  34. data/template/gnuplot/files_by_yearmonth.plot +11 -0
  35. data/template/gnuplot/helpers/block.rb +26 -0
  36. data/template/gnuplot/helpers/config.rb +12 -0
  37. data/template/gnuplot/helpers/names.rb +79 -0
  38. data/template/gnuplot/helpers/utils.rb +16 -0
  39. data/template/gnuplot/index.haml +2 -0
  40. data/template/gnuplot/jqplot.canvasAxisLabelRenderer.min.js +57 -0
  41. data/template/gnuplot/jqplot.canvasAxisTickRenderer.min.js +57 -0
  42. data/template/gnuplot/jqplot.canvasTextRenderer.min.js +57 -0
  43. data/template/gnuplot/jqplot.cursor.min.js +57 -0
  44. data/template/gnuplot/jqplot.dateAxisRenderer.min.js +57 -0
  45. data/template/gnuplot/jquery.jqplot.min.css +1 -0
  46. data/template/gnuplot/jquery.jqplot.min.js +57 -0
  47. data/template/gnuplot/jquery.js +4 -0
  48. data/template/gnuplot/jquery.tablesorter.js +4 -0
  49. data/template/gnuplot/lastweeks.plot +14 -0
  50. data/template/gnuplot/layouts/default.haml +117 -0
  51. data/template/gnuplot/linechanges_by_yearmonth.plot +17 -0
  52. data/template/gnuplot/lines.haml +2 -0
  53. data/template/gnuplot/lines_by_yearmonth.plot +11 -0
  54. data/template/gnuplot/partials/authors.haml +30 -0
  55. data/template/gnuplot/partials/blockheader.haml +2 -0
  56. data/template/gnuplot/partials/blocktoc.haml +7 -0
  57. data/template/gnuplot/partials/commitchart.haml +60 -0
  58. data/template/gnuplot/partials/commits_per_month.haml +27 -0
  59. data/template/gnuplot/partials/commits_per_year.haml +27 -0
  60. data/template/gnuplot/partials/commits_per_yearmonth.haml +27 -0
  61. data/template/gnuplot/partials/day_of_week.haml +28 -0
  62. data/template/gnuplot/partials/filechanges_by_yearmonth.haml +3 -0
  63. data/template/gnuplot/partials/files_by_yearmonth.haml +3 -0
  64. data/template/gnuplot/partials/fileschart.haml +86 -0
  65. data/template/gnuplot/partials/filetypes.haml +23 -0
  66. data/template/gnuplot/partials/general.haml +34 -0
  67. data/template/gnuplot/partials/hour_of_day.haml +28 -0
  68. data/template/gnuplot/partials/hour_of_week.haml +24 -0
  69. data/template/gnuplot/partials/lastweeks.haml +24 -0
  70. data/template/gnuplot/partials/linechanges_by_yearmonth.haml +3 -0
  71. data/template/gnuplot/partials/lines_by_yearmonth.haml +3 -0
  72. data/template/gnuplot/partials/lineschart.haml +86 -0
  73. data/template/gnuplot/partials/repos.haml +13 -0
  74. data/template/gnuplot/partials/top_authors_of_year.haml +30 -0
  75. data/template/gnuplot/partials/top_authors_of_yearmonth.haml +30 -0
  76. data/template/gnuplot/style.scss +132 -0
  77. data/template/highcharts/activity.haml +5 -0
  78. data/template/highcharts/authors.haml +3 -0
  79. data/template/highcharts/bootstrap-theme.min.css +10 -0
  80. data/template/highcharts/bootstrap.min.css +10 -0
  81. data/template/highcharts/bootstrap.min.js +11 -0
  82. data/template/highcharts/exporting.js +23 -0
  83. data/template/highcharts/glyphicons-halflings-regular.eot +0 -0
  84. data/template/highcharts/glyphicons-halflings-regular.svg +229 -0
  85. data/template/highcharts/glyphicons-halflings-regular.ttf +0 -0
  86. data/template/highcharts/glyphicons-halflings-regular.woff +0 -0
  87. data/template/highcharts/heatmap.js +23 -0
  88. data/template/highcharts/helpers/block.rb +27 -0
  89. data/template/highcharts/helpers/names.rb +62 -0
  90. data/template/highcharts/helpers/params.rb +35 -0
  91. data/template/highcharts/highcharts.js +308 -0
  92. data/template/highcharts/index.haml +2 -0
  93. data/template/highcharts/jquery-1.11.1.min.js +4 -0
  94. data/template/highcharts/jquery.dataTables.min.css +1 -0
  95. data/template/highcharts/jquery.dataTables.min.js +164 -0
  96. data/template/highcharts/layouts/default.haml +54 -0
  97. data/template/highcharts/partials/activity_per_day.haml +65 -0
  98. data/template/highcharts/partials/activity_per_hour.haml +42 -0
  99. data/template/highcharts/partials/activity_per_month.haml +47 -0
  100. data/template/highcharts/partials/activity_per_week_last_year.haml +47 -0
  101. data/template/highcharts/partials/activity_per_weekday.haml +44 -0
  102. data/template/highcharts/partials/activity_per_weekday_and_hour.haml +20 -0
  103. data/template/highcharts/partials/blocktoc.haml +4 -0
  104. data/template/highcharts/partials/commits_per_author.haml +64 -0
  105. data/template/highcharts/partials/commits_per_author_detailed.haml +92 -0
  106. data/template/highcharts/partials/general.haml +33 -0
  107. data/template/highcharts/partials/globalstats.haml +41 -0
  108. data/template/highcharts/partials/repos.haml +12 -0
  109. data/template/highcharts/set_highcharts.js +323 -0
  110. data/template/highcharts/style.scss +148 -0
  111. metadata +227 -0
@@ -0,0 +1,74 @@
1
+ # gitstats-rb
2
+
3
+ gitstats-rb is a clone of [gitstats](http://gitstats.sourceforge.net) written in
4
+ ruby. It's written to support templates and should be easily extendable.
5
+
6
+ ## Installation
7
+
8
+ gem install gitstats-rb
9
+
10
+ ## Getting started
11
+
12
+ The basic usage is quite simple. Just run `gitstats` with the git directory as
13
+ parameter. If you want to generate stats of more than one repository just list
14
+ them one after another. Note that this will generate only one statistic but
15
+ consider the commits from all repositories. Additionally, you can also specify a
16
+ name and a ref for each repository via the following format:
17
+ `<name>:<path to repository>:<ref>`.
18
+
19
+ For example:
20
+
21
+ gitstats gitstats-rb:.:master
22
+
23
+ For more options please read sections about caching below and run `gitstats -h`.
24
+
25
+ ## Caching
26
+
27
+ gitstats-rb implements two types of caches but one of them is just useful when
28
+ developing new statistic classes. The one useful for the end-user is the
29
+ stats-cache and the other one (for the devs) is the commit-cache.
30
+
31
+ ### stats-cache
32
+
33
+ The stats-cache caches the statistic objects used internally. This drastically
34
+ improves the speed of incremental updates because only the new commits have to
35
+ be taken into account. To activate this cache pass the `-c` command line flag.
36
+ By default this creates the stats-cache file in the output directory. If you
37
+ want to use another file you can specify it by using `--statcache <filename>`.
38
+
39
+ Please note that this cache can only be used when working on the same
40
+ repositories as used in the previous run. Identification is done using the
41
+ repository name or, if not specified, using the given (not relative!) path.
42
+
43
+ ### commit-cache
44
+
45
+ As already mentioned this cache is only useful if you want to develope new
46
+ statistic classes. It works by caching the internal commit objects to a per
47
+ repository file that can be reread when running again. This is especially useful
48
+ if you experiment with big repositories where this cache is about twice as fast
49
+ as the `git log` command used internally.
50
+
51
+ This cache can be activated with the `-C` command line flag. By default the
52
+ commit-cache is written into the output directory (`./stats` by default). If you
53
+ want to use another directory you can specify it by using
54
+ `--commitcache <directory name>`.
55
+
56
+ ## Examples
57
+
58
+ * gitstats-rb: [highcharts](http://nioshd.github.io/gitstats-rb/gitstats-rb-highcharts/),
59
+ [gnuplot](http://nioshd.github.io/gitstats-rb/gitstats-rb-gnuplot/)
60
+ * Linux: [highcharts](http://nioshd.github.io/gitstats-rb/linux-highcharts/),
61
+ [gnuplot](http://nioshd.github.io/gitstats-rb/linux-gnuplot/)
62
+
63
+ ## Dependencies
64
+
65
+ * [HAML](http://haml-lang.com)
66
+ * [SASS](http://sass-lang.com)
67
+ * [Compass](http://compass-style.org)
68
+ * [Compass-blueprint](http://compass-blueprint.org/)
69
+ * [Gnuplot (GEM)](http://rubygems.org/gems/gnuplot)
70
+ * [Gnuplot binary](http://www.gnuplot.info/)
71
+
72
+ ## License
73
+
74
+ The project is licensed under GPLv3. The details can be found in the `LICENSE` file.
@@ -0,0 +1,200 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'fileutils'
5
+ require 'date'
6
+ require 'rubygems'
7
+
8
+ require 'haml'
9
+ require 'sass'
10
+ require 'compass'
11
+ require 'compass-blueprint'
12
+ require 'gnuplot'
13
+
14
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
15
+
16
+ require 'gitstats'
17
+
18
+ $options = {
19
+ :out => 'stats',
20
+ :template => nil,
21
+ :verbose => false,
22
+ :debug => false,
23
+ :quiet => false,
24
+ :cache => false,
25
+ :statcache => nil,
26
+ :commitcache => nil,
27
+ :commitcache_dir => nil,
28
+ :future => true,
29
+ :maxage => 0,
30
+ :withmail => false,
31
+ }
32
+
33
+ parser = OptionParser.new do |opts|
34
+ opts.banner = 'Usage: gitstats.rb [options] <[name1:]gitdir1[:ref1]> [<[name2:]gitdir2[:ref2]> ...]'
35
+
36
+ opts.on('-o', '--out=arg', 'output directory') do |arg|
37
+ $options[:out] = arg
38
+ end
39
+
40
+ opts.on('-t', '--template=arg', 'template name (e.g., highcharts, gnuplot) or directory') do |arg|
41
+ $options[:template] = arg
42
+ end
43
+
44
+ opts.on('-c', '--[no-]cache', 'use the statcache file') do |arg|
45
+ $options[:cache] = arg
46
+ end
47
+
48
+ opts.on('-C', '--[no-]commitcache', 'use the commit cache') do |arg|
49
+ $options[:commitcache] = arg
50
+ end
51
+
52
+ opts.on('-s', '--statcache=arg', 'statcache file to use') do |arg|
53
+ $options[:statcache] = arg
54
+ end
55
+
56
+ opts.on('--commitcache=arg', 'commit cache directory to use') do |arg|
57
+ $options[:commitcache_dir] = arg
58
+ end
59
+
60
+ opts.on('--[no-]future', 'count future commits') do |arg|
61
+ $options[:future] = arg
62
+ end
63
+
64
+ opts.on('-m', '--max-age=arg', Integer, 'set max age of commit in days') do |arg|
65
+ $options[:maxage] = arg
66
+ end
67
+
68
+ opts.on('--[no-]mail', 'include mail in author names') do |arg|
69
+ $options[:withmail] = arg
70
+ end
71
+
72
+ opts.on('-v', '--[no-]verbose', 'verbose mode') do |arg|
73
+ $options[:verbose] = arg
74
+ end
75
+
76
+ opts.on('-q', '--[no-]quiet', 'quiet mode') do |arg|
77
+ $options[:quiet] = arg
78
+ end
79
+
80
+ opts.on('-d', '--[no-]debug', 'print debug messages') do |arg|
81
+ $options[:debug] = arg
82
+ end
83
+
84
+ opts.on_tail('-h', '--help', 'this help') do
85
+ puts opts
86
+ exit 0
87
+ end
88
+ end
89
+
90
+ parser.parse!
91
+
92
+ if $options[:quiet] && $options[:verbose]
93
+ STDERR.puts 'cannot specify --quiet and --verbose at the same time!'
94
+ exit 1
95
+ end
96
+
97
+ $options[:statcache] = File.join($options[:out], '.statcache') if $options[:statcache].nil?
98
+ $options[:commitcache_dir] = $options[:out] if $options[:commitcache_dir].nil?
99
+
100
+ # template selection
101
+ # -> use the packaged highcharts template when nothing has been specified
102
+ # -> use the specified template when it exists
103
+ # -> use an packaged alternative template when the names match
104
+ templateRoot = File.expand_path(File.join(File.dirname(__FILE__), '..', 'template'))
105
+ $options[:template] = File.join(templateRoot, 'highcharts') if $options[:template].nil?
106
+
107
+ unless File.directory?($options[:template])
108
+ template = File.join(templateRoot, $options[:template])
109
+ $options[:template] = template if File.directory?(template)
110
+ end
111
+
112
+ puts "Determined Template: #{$options[:template]}" if $options[:verbose]
113
+ unless File.directory?($options[:template])
114
+ STDERR.puts "Template (#{$options[:template]}) could not be found!"
115
+ exit 1
116
+ end
117
+
118
+
119
+ FileUtils.mkdir_p($options[:out])
120
+
121
+ stat = nil
122
+ cache_loaded = false
123
+ if $options[:cache]
124
+ begin
125
+ puts 'trying to load cache ...' unless $options[:quiet]
126
+ stat = Marshal::load(IO::readlines($options[:statcache]).join(''))
127
+ stat.clear_repos
128
+ cache_loaded = true
129
+ rescue
130
+ end
131
+ end
132
+
133
+ if stat.nil?
134
+ if ARGV.empty?
135
+ puts parser
136
+ exit 1
137
+ end
138
+
139
+ stat = StatGen.new
140
+ end
141
+
142
+ if cache_loaded
143
+ if stat.include_mail != $options[:withmail]
144
+ puts 'cannot change --[no-]mail option when using statcache'
145
+ exit 1
146
+ end
147
+
148
+ if stat.future != $options[:future]
149
+ puts 'cannot change --[no-]future option when using statcache'
150
+ exit 1
151
+ end
152
+
153
+ if stat.maxage != $options[:maxage] * 24 * 60 * 60
154
+ puts 'cannot change --max-age option when using statcache'
155
+ exit 1
156
+ end
157
+ end
158
+
159
+ Author::include_mail = $options[:withmail]
160
+ stat.include_mail = $options[:withmail]
161
+ stat.verbose = $options[:verbose]
162
+ stat.debug = $options[:debug]
163
+ stat.quiet = $options[:quiet]
164
+ stat.future = $options[:future]
165
+ stat.maxage = $options[:maxage] * 24 * 60 * 60
166
+ stat.commitcache = $options[:commitcache] ? $options[:commitcache_dir] : nil
167
+
168
+ ARGV.each do |path|
169
+ name, path, ref = path.split(':')
170
+ path ||= name
171
+ ref ||= 'HEAD'
172
+ stat << [name, File.expand_path(path), ref]
173
+ end
174
+
175
+ if cache_loaded
176
+ unless stat.check_repostate
177
+ puts 'cannot use cache when working on different repositories!'
178
+ exit 1
179
+ end
180
+ end
181
+
182
+ puts 'fetching statistics ...' unless $options[:quiet]
183
+ begin
184
+ stat.calc
185
+ ensure
186
+ if $options[:cache]
187
+ puts 'writing cache ...' unless $options[:quiet]
188
+ cache = Marshal::dump(stat)
189
+ FileUtils.mkdir_p(File.dirname($options[:statcache]))
190
+ File.new($options[:statcache], 'w').write(cache)
191
+ end
192
+ end
193
+
194
+ # exit early if there has been no commits
195
+ exit 0 if stat.general_stats.commits == 0
196
+
197
+ puts 'rendering ...' unless $options[:quiet]
198
+ renderer = Renderer.new($options[:template], $options[:out], $options[:verbose])
199
+ renderer.render(stat)
200
+
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'gitstats-rb'
5
+ s.version = '2.0.0'
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = ['Mario Werner', 'Christoph Plank']
8
+ s.email = ['mario.werner@iaik.tugraz.at', 'chrisistuff@gmail.com']
9
+ s.homepage = 'https://github.com/niosHD/gitstats-rb'
10
+ s.summary = %q{Generates statistics of git repositories}
11
+ s.description = %q{Generates statistics of git repositories like http://gitstats.sourceforge.net but with a more extendable and flexible backend system}
12
+ s.has_rdoc = false
13
+ s.license = 'GPL-3.0'
14
+
15
+ s.add_dependency 'haml', '~> 5.0'
16
+ s.add_dependency 'sass', '~> 3.4'
17
+ s.add_dependency 'compass', '~> 1.0'
18
+ s.add_dependency 'compass-blueprint', '~> 1.0'
19
+ s.add_dependency 'gnuplot', '~> 2.6'
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ['lib']
24
+ end
@@ -0,0 +1,18 @@
1
+ require 'gitstats/author'
2
+ require 'gitstats/yearmonth'
3
+ require 'gitstats/git'
4
+
5
+ require 'gitstats/stats'
6
+ require 'gitstats/stats/commit'
7
+ require 'gitstats/stats/commit/author'
8
+ require 'gitstats/stats/commit/time'
9
+ require 'gitstats/stats/file'
10
+ require 'gitstats/stats/file/filetype'
11
+
12
+ require 'gitstats/statgen'
13
+
14
+ require 'gitstats/renderer'
15
+ require 'gitstats/renderer/haml'
16
+ require 'gitstats/renderer/sass'
17
+ require 'gitstats/renderer/gnuplot'
18
+
@@ -0,0 +1,37 @@
1
+ class Author
2
+ include Comparable
3
+
4
+ def self.include_mail=(include_mail)
5
+ @@include_mail = include_mail
6
+ end
7
+
8
+ def self.include_mail
9
+ @@include_mail ||= false
10
+ @@include_mail
11
+ end
12
+
13
+ attr_reader :name
14
+ attr_reader :email
15
+
16
+ def initialize(name, email)
17
+ @name = name
18
+ @email = email
19
+ end
20
+
21
+ def to_s
22
+ if self.class.include_mail
23
+ "#{name} <#{email}>"
24
+ else
25
+ name
26
+ end
27
+ end
28
+
29
+ def eql?(b)
30
+ to_s.hash == b.to_s.hash
31
+ end
32
+
33
+ def hash
34
+ to_s.hash
35
+ end
36
+ end
37
+
@@ -0,0 +1,165 @@
1
+ class Git
2
+ attr_reader :name
3
+ attr_reader :base
4
+ attr_reader :ref
5
+
6
+ def initialize(name, base, ref = 'HEAD', debug = false, cachefile = nil)
7
+ @name = name
8
+ @base = base
9
+ @ref = ref
10
+ @debug = debug
11
+ @cachefile = cachefile
12
+ end
13
+
14
+ def open_cache
15
+ FileUtils.mkdir_p(File.dirname(@cachefile))
16
+ @cache = File.new(@cachefile, 'a')
17
+ end
18
+
19
+ def close_cache
20
+ unless @cache.nil?
21
+ @cache.close
22
+ @cache = nil
23
+ end
24
+ end
25
+
26
+ def write_cache(commit)
27
+ obj = Marshal.dump(commit)
28
+ raise "Object too large" if obj.size > 65535
29
+
30
+ str = ((obj.size >> 8) & 0xff).chr
31
+ str += (obj.size & 0xff).chr
32
+ str += obj
33
+
34
+ @cache.write(str)
35
+ @cache.flush
36
+ end
37
+
38
+ def read_cache
39
+ f = File.new(@cachefile)
40
+ while(!f.eof?)
41
+ tmp = f.read(2)
42
+ len = (tmp[0] << 8) + tmp[1]
43
+ obj = f.read(len)
44
+ raise "Read short object" if obj.size != len
45
+ yield Marshal.load(obj)
46
+ end
47
+ f.close
48
+ end
49
+
50
+ def get_commits(last = nil, &block)
51
+ if last.nil?
52
+ range = @ref
53
+ unless @cachefile.nil?
54
+ begin
55
+ read_cache do |commit|
56
+ block.call(commit)
57
+ last = commit
58
+ end
59
+ range = "#{last[:hash]}..#{@ref}"
60
+ rescue
61
+ end
62
+ end
63
+ else
64
+ range = "#{last}..#{@ref}"
65
+ end
66
+
67
+ open_cache unless @cachefile.nil?
68
+
69
+ commit = nil
70
+ sh("git log --reverse --summary --numstat --encoding=utf-8 --pretty=format:\"HEADER: %at %ai %H %T %aN <%aE>\" #{range}") do |line|
71
+ # My `git log` sometimes returns non utf-8 strings...
72
+ # The workaround tries to recover from such scenarios.
73
+ unless line.valid_encoding?
74
+ line = line.force_encoding('ISO-8859-15').encode!('UTF-8', :invalid => :replace, :undef => :replace, :replace => "")
75
+ end
76
+
77
+ if line =~ /^HEADER:/
78
+ unless commit.nil?
79
+ write_cache(commit) unless @cachefile.nil?
80
+ block.call(commit)
81
+ end
82
+
83
+ parts = line.split(' ', 8)
84
+ parts.shift
85
+
86
+ commit = Hash.new
87
+ commit[:time] = Time.at(parts[0].to_i)
88
+ commit[:timezone] = parts[3]
89
+ commit[:hash] = parts[4]
90
+ commit[:tree] = parts[5]
91
+ name = nil
92
+ email = ''
93
+ match = /^(.+) <(.+)>$/.match(parts[6])
94
+ if match.nil?
95
+ name = parts[6]
96
+ else
97
+ name, email = match.captures
98
+ end
99
+ commit[:author] = Author.new(name, email)
100
+ commit[:files_added] = 0
101
+ commit[:files_deleted] = 0
102
+ commit[:lines_added] = 0
103
+ commit[:lines_deleted] = 0
104
+ elsif line == ''
105
+ write_cache(commit) unless @cachefile.nil?
106
+ block.call(commit)
107
+ commit = nil
108
+ elsif line =~ /^ /
109
+ if line =~ /^ create/
110
+ commit[:files_added] += 1
111
+ elsif line =~ /^ delete/
112
+ commit[:files_deleted] += 1
113
+ end
114
+ else
115
+ match = /^(\d+)\s+(\d+)/.match(line)
116
+ unless match.nil?
117
+ added, deleted = match.captures
118
+ commit[:lines_added] += added.to_i
119
+ commit[:lines_deleted] += deleted.to_i
120
+ end
121
+ end
122
+ end
123
+
124
+ unless commit.nil?
125
+ write_cache(commit) unless @cachefile.nil?
126
+ block.call(commit)
127
+ end
128
+
129
+ ensure
130
+ close_cache unless @cachefile.nil?
131
+ end
132
+
133
+ def get_files(ref = nil, &block)
134
+ ref ||= @ref
135
+
136
+ sh("git ls-tree -r -l #{ref}").split(/\n/).each do |line|
137
+ parts = line.split(/\s+/, 5)
138
+ next if parts[1] != 'blob'
139
+
140
+ file = Hash.new
141
+ file[:hash] = parts[2]
142
+ file[:size] = parts[3].to_i
143
+ file[:name] = parts[4]
144
+
145
+ block.call(file)
146
+ end
147
+ end
148
+
149
+ private
150
+ def sh(cmd, &block)
151
+ puts cmd if @debug
152
+ Dir.chdir(@base) do
153
+ if block.nil?
154
+ `#{cmd}`
155
+ else
156
+ IO.popen(cmd) do |io|
157
+ io.each_line do |line|
158
+ block.call(line.chomp)
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+