git-heatmap 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e448107c9a8fa78501a4777c7ede303263328c407f87756720c6413f1ed5f337
4
+ data.tar.gz: c159c572f28d1690465974d6a2cac16f5a87067f16d9357e001887217a763a8e
5
+ SHA512:
6
+ metadata.gz: 850b32bba0b3a514945cbfed0bb3376432087f7d0d6a452235665b7c552a74755ef389ab6ffeedec23bb1f382d73c7f7e107a6d8d81a1edd098157d667c90849
7
+ data.tar.gz: 509bdb4755f34586cb3c0224e077bcff734e46f500f3da37f1ea6e1f27f53ecb7aa2167cc5344909899084a9ae03fa07dd2c945c02ba3527f16b78b90c896774
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.1
7
+ before_install: gem install bundler -v 1.17.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in git-heatmap.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Git::Heatmap
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/git/heatmap`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'git-heatmap'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install git-heatmap
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/git-heatmap.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/git-heatmap ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative '../lib/git/heatmap'
24
+
25
+ begin
26
+ Git::Heatmap::Command.call
27
+ rescue Interrupt
28
+ end
@@ -0,0 +1,29 @@
1
+ require_relative "lib/git/heatmap/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "git-heatmap"
5
+ spec.version = Git::Heatmap::VERSION
6
+ spec.authors = ["Samuel Williams"]
7
+ spec.email = ["samuel.williams@oriontransfer.co.nz"]
8
+
9
+ spec.summary = "Generate heatmap style visualisations based on git history."
10
+ spec.homepage = "https://github.com/ioquatix/git-heatmap"
11
+
12
+ # Specify which files should be added to the gem when it is released.
13
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
14
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
15
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ end
17
+
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "console"
22
+ spec.add_dependency "rugged"
23
+ spec.add_dependency "trenni"
24
+ spec.add_dependency "samovar", "~> 2.0"
25
+
26
+ spec.add_development_dependency "bundler"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rspec", "~> 3.0"
29
+ end
@@ -0,0 +1,22 @@
1
+ # Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative "heatmap/version"
22
+ require_relative "heatmap/command"
@@ -0,0 +1,73 @@
1
+ # Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'command/generate'
22
+
23
+ require_relative 'version'
24
+
25
+ require 'samovar'
26
+ require 'console'
27
+
28
+ module Git
29
+ module Heatmap
30
+ module Command
31
+ def self.call(*args)
32
+ Top.call(*args)
33
+ end
34
+
35
+ class Top < Samovar::Command
36
+ self.description = "A git visualisation generator."
37
+
38
+ options do
39
+ option '--verbose | --quiet', "Verbosity of output for debugging.", key: :logging
40
+ option '-v/--version', "Print out the application version."
41
+ end
42
+
43
+ nested :command, {
44
+ 'generate' => Generate,
45
+ }, default: 'generate'
46
+
47
+ def verbose?
48
+ @options[:logging] == :verbose
49
+ end
50
+
51
+ def quiet?
52
+ @options[:logging] == :quiet
53
+ end
54
+
55
+ def call
56
+ if verbose?
57
+ Console.logger.debug!
58
+ elsif quiet?
59
+ Console.logger.warn!
60
+ else
61
+ Console.logger.info!
62
+ end
63
+
64
+ if @options[:version]
65
+ puts "#{self.name} v#{VERSION}"
66
+ else
67
+ @command.call
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,88 @@
1
+ # Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'samovar'
22
+ require 'rugged'
23
+
24
+ require_relative '../period'
25
+ require_relative '../templates/series'
26
+
27
+ require 'irb'
28
+
29
+ module Git
30
+ module Heatmap
31
+ module Command
32
+ class Generate < Samovar::Command
33
+ self.description = "Generate the visualisation."
34
+
35
+ PERIODS = {
36
+ "hourly" => Hourly,
37
+ "daily" => Daily,
38
+ "weekly" => Weekly,
39
+ "monthly" => Monthly,
40
+ "quarterly" => Quarterly,
41
+ "yearly" => Yearly
42
+ }
43
+
44
+ options do
45
+ option "--template <path>", "The template file to use.", default: "series"
46
+ option "--period <name>", "The period to use, one of: #{PERIODS.keys.join(', ')}.", default: "daily"
47
+ option "--depth <integer>", "Specify the depth of aggregation (depth of top level directories).", type: Integer, default: 2
48
+
49
+ option "--output <path>", "The output path to use."
50
+ end
51
+
52
+ many :paths, "One or more repositories to visualise."
53
+
54
+ def period
55
+ PERIODS[@options[:period]].new
56
+ end
57
+
58
+ def title
59
+ @paths.map do |path|
60
+ File.basename(File.expand_path(path))
61
+ end.join(', ')
62
+ end
63
+
64
+ def output_path
65
+ @options.fetch(:output) do
66
+ "#{title} #{@options[:period]}.html"
67
+ end
68
+ end
69
+
70
+ def template(commits)
71
+ Templates::Series.new(commits, title: self.title)
72
+ end
73
+
74
+ def call
75
+ commits = Commits.new(filter: self.period, depth: @options[:depth])
76
+
77
+ @paths.each do |path|
78
+ repository = Rugged::Repository.discover(path)
79
+
80
+ commits.add(repository)
81
+ end
82
+
83
+ File.write(output_path, template(commits).call)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,159 @@
1
+ # Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'set'
22
+
23
+ module Git
24
+ module Heatmap
25
+ class Aggregate
26
+ def initialize(filter)
27
+ @filter = filter
28
+
29
+ @commits = {}
30
+
31
+ @periods = {}
32
+ @earliest_commit_at = nil
33
+ @latest_commit_at = nil
34
+
35
+ @maximum = 0
36
+ end
37
+
38
+ attr :commits
39
+ attr :periods
40
+
41
+ attr :earliest_commit_at
42
+ attr :latest_commit_at
43
+
44
+ def size
45
+ @commits.size
46
+ end
47
+
48
+ attr :maximum
49
+
50
+ def << commit
51
+ return if @commits.include?(commit.oid)
52
+
53
+ @commits[commit.oid] = commit
54
+
55
+ author = commit.author
56
+ time = author[:time]
57
+ key = @filter.key(time)
58
+ commits = (@periods[key] ||= [])
59
+ commits << commit
60
+
61
+ if commits.size > @maximum
62
+ @maximum = commits.size
63
+ end
64
+
65
+ if @earliest_commit_at.nil? or time < @earliest_commit_at
66
+ @earliest_commit_at = time
67
+ end
68
+
69
+ if @latest_commit_at.nil? or time > @latest_commit_at
70
+ @latest_commit_at = time
71
+ end
72
+ end
73
+ end
74
+
75
+ class Commits
76
+ def initialize(filter: Weekly.new, depth: 4)
77
+ @filter = filter
78
+
79
+ @authors = Set.new
80
+ @directories = Hash.new{|h,k| h[k] = Aggregate.new(filter)}
81
+
82
+ @depth = depth
83
+
84
+ @earliest_commit_at = nil
85
+ @latest_commit_at = nil
86
+
87
+ @maximum = nil
88
+ end
89
+
90
+ attr :filter
91
+
92
+ attr :earliest_commit_at
93
+ attr :latest_commit_at
94
+
95
+ def each_period(&block)
96
+ @filter.between(@earliest_commit_at, @latest_commit_at, &block)
97
+ end
98
+
99
+ def maximum
100
+ @maximum ||= @directories.each_value.max_by(&:maximum).maximum
101
+ end
102
+
103
+ attr :authors
104
+ attr :directories
105
+
106
+ def each_directory
107
+ @directories.keys.sort.each do |key|
108
+ yield key, @directories[key]
109
+ end
110
+ end
111
+
112
+ def << commit
113
+ @maximum = nil
114
+
115
+ if parent = commit.parents.first
116
+ # Documentation seems to imply this shouldn't be needed.
117
+ diff = commit.diff(commit.parents.first.tree)
118
+ else
119
+ diff = commit.diff
120
+ end
121
+
122
+ author = commit.author
123
+ time = author[:time]
124
+ @authors << author[:name]
125
+
126
+ if @earliest_commit_at.nil? or time < @earliest_commit_at
127
+ @earliest_commit_at = time
128
+ end
129
+
130
+ if @latest_commit_at.nil? or time > @latest_commit_at
131
+ @latest_commit_at = time
132
+ end
133
+
134
+ diff.each_patch do |patch|
135
+ delta = patch.delta
136
+ path = delta.new_file[:path]
137
+
138
+ parts = path.split(File::SEPARATOR)
139
+ parts.unshift(File.basename(commit.tree.repo.workdir))
140
+ parts.pop # Remove file name
141
+ root = parts[0...@depth]
142
+
143
+ @directories[root] << commit
144
+ end
145
+ end
146
+
147
+ def add(repository)
148
+ walker = Rugged::Walker.new(repository)
149
+
150
+ walker.sorting(Rugged::SORT_DATE)
151
+ walker.push(repository.head.target.oid)
152
+
153
+ walker.each do |commit|
154
+ self << commit
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end