git-health-check 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
19
+
20
+ Gemfile.lock
21
+ healthcheck
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ben Snape
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # Git Health Check
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'git-health-check'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install git-health-check
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
30
+
31
+ # To Do:
32
+
33
+ 1. add timings
34
+ 2. add CLI arguments
35
+ 3. improve logic
36
+ 4. improve reporting
37
+ 5. use Thor instead of OptionParser
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/ghc ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'git-health-check'
4
+
5
+ exit GitHealthCheck::Cli::Application.new(ARGV).execute
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'git-health-check/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "git-health-check"
8
+ gem.version = GitHealthCheck::VERSION
9
+ gem.authors = ["Ben Snape"]
10
+ gem.email = ["bsnape@gmail.com"]
11
+ gem.description = %q{Git Health Check}
12
+ gem.summary = %q{Git Health Check}
13
+ gem.homepage = "http://wwww.bensnape.com"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency("ruport")
21
+ gem.add_development_dependency("rake")
22
+
23
+ end
@@ -0,0 +1,11 @@
1
+ require "git-health-check/version"
2
+ require 'git-health-check/history'
3
+ require 'git-health-check/working_copy'
4
+ require 'git-health-check/report'
5
+ require 'git-health-check/git_lib'
6
+ require 'git-health-check/packfile'
7
+ require 'git-health-check/cli/git_health_check_command'
8
+ require 'git-health-check/cli/application'
9
+ require 'git-health-check/cli/options'
10
+ require 'git-health-check/cli/version_command'
11
+ require 'git-health-check/cli/help_command'
@@ -0,0 +1,35 @@
1
+ module GitHealthCheck
2
+ module Cli
3
+
4
+ class Application
5
+
6
+ SUCCESS = 0
7
+ ERROR = 1
8
+
9
+ def initialize(argv)
10
+ @options = GitHealthCheck::Cli::Options.new(argv)
11
+ @status = SUCCESS
12
+ end
13
+
14
+ def execute
15
+ begin
16
+ cmd = @options.parse
17
+ cmd.execute(self)
18
+ rescue Exception => error
19
+ $stderr.puts "Error: #{error}"
20
+ @status = ERROR
21
+ end
22
+ @status
23
+ end
24
+
25
+ def output(text)
26
+ print text
27
+ end
28
+
29
+ def report_success
30
+ @status
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ require 'ruport'
2
+
3
+ module GitHealthCheck
4
+ module Cli
5
+
6
+ class GitHealthCheckCommand
7
+
8
+ def initialize(parser, threshold = 0.5)
9
+ @threshold = threshold
10
+ @parser = parser
11
+ @repository = Dir.pwd
12
+ end
13
+
14
+ def execute(view)
15
+ history = GitHealthCheck::History.new(@repository, 'HEAD', @threshold)
16
+ working_copy = GitHealthCheck::WorkingCopy.new @repository
17
+ packfile = GitHealthCheck::Packfile.new(@repository)
18
+ packfile.packfile_stats
19
+
20
+ working_copy_output = working_copy.fast_find_in_working_copy
21
+ history_output = history.search
22
+
23
+ working_copy_report = Table("object sha", "size (KB)", "compressed (KB)", "path")
24
+ history_report = Table("object sha", "size (MB)", "path", "commit details", "author")
25
+
26
+ working_copy_output.each { |sha, (size, csize, path)| working_copy_report << [sha, size, csize, path] }
27
+ history_output.each { |sha, size, path, where, who| history_report << [sha, size, path, where, who] }
28
+
29
+ report = GitHealthCheck::Report.new(working_copy_report.to_html, history_report.to_html, packfile)
30
+ report.create
31
+
32
+ view.report_success
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,16 @@
1
+ module GitHealthCheck
2
+ module Cli
3
+
4
+ class HelpCommand < GitHealthCheckCommand
5
+
6
+ def initialize(parser)
7
+ @parser = parser
8
+ end
9
+
10
+ def execute(view)
11
+ view.output(@parser.to_s)
12
+ view.report_success
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,50 @@
1
+ require 'optparse'
2
+
3
+ module GitHealthCheck
4
+ module Cli
5
+
6
+ class Options
7
+
8
+ def initialize(argv)
9
+ @argv = argv
10
+ @parser = OptionParser.new
11
+ @command_class = GitHealthCheckCommand
12
+ set_parser_options
13
+ end
14
+
15
+ def banner
16
+ return <<EOB
17
+ Usage: ghc [option]
18
+
19
+ Output metrics for your Git repository.
20
+
21
+ Examples:
22
+ ghc
23
+ ghc -t 10
24
+ ghc -t 0.2
25
+
26
+ EOB
27
+ end
28
+
29
+ def set_parser_options
30
+ @parser.banner = banner
31
+
32
+ @parser.separator "Options:"
33
+
34
+ @parser.on("-v", "--version", "Displays the gem version") { @command_class = VersionCommand }
35
+
36
+ @parser.on("-h", "--help", "Displays this help message") { @command_class = HelpCommand }
37
+
38
+ @parser.on("-t", "--threshold THRESHOLD", Float, "Specify history size threshold in mB (default 0.5)") do |n|
39
+ @threshold = n
40
+ end
41
+ end
42
+
43
+ def parse
44
+ @parser.parse!(@argv)
45
+ @threshold ? @command_class.new(@parser, @threshold) : @command_class.new(@parser)
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,12 @@
1
+ module GitHealthCheck
2
+ module Cli
3
+
4
+ class VersionCommand < GitHealthCheckCommand
5
+
6
+ def execute(view)
7
+ view.output("Git-Health-Check Version: #{GitHealthCheck::VERSION}\n")
8
+ view.report_success
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,42 @@
1
+ module GitHealthCheck
2
+ class GitLib
3
+
4
+ def initialize(repository_path)
5
+ Dir.chdir repository_path
6
+ end
7
+
8
+ def get_commit_author(commit_sha)
9
+ `git log #{commit_sha} -n 1 | grep Author | cut -f 2- -d ' '`.chomp
10
+ end
11
+
12
+ def get_commit_details(commit_sha)
13
+ `git show -s #{commit_sha} --format='%h: %cr'`.chomp
14
+ end
15
+
16
+ def get_pack_number
17
+ `ls .git/objects/pack/pack-*.idx 2> /dev/null | wc -l`.to_i
18
+ end
19
+
20
+ def get_status
21
+ `git status`
22
+ end
23
+
24
+ def filesystem_check
25
+ `git fsck --full --strict`
26
+ end
27
+
28
+ def garbage_collection
29
+ `git gc`
30
+ end
31
+
32
+ def repack
33
+ `git repack -a -d -f --depth=250 --window=250` # better than gc --aggressive
34
+ # http://metalinguist.wordpress.com/2007/12/06/the-woes-of-git-gc-aggressive-and-how-git-deltas-work/
35
+ end
36
+
37
+ def count_objects
38
+ `git count-objects -v`
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,41 @@
1
+ # Thanks to mislav
2
+ #
3
+ # http://stackoverflow.com/questions/298314/find-files-in-git-repo-over-x-megabytes-that-dont-exist-in-head
4
+ module GitHealthCheck
5
+
6
+ class History
7
+
8
+ MEGABYTE = 1000 ** 2
9
+
10
+ def initialize(repository, head = 'HEAD', threshold = 0.1)
11
+ @head = head
12
+ @bytes_threshold = threshold.to_f * MEGABYTE
13
+ Dir.chdir repository
14
+ @git_lib = GitHealthCheck::GitLib.new repository
15
+ end
16
+
17
+ def search
18
+
19
+ big_files = {}
20
+
21
+ # list commit objects in chronological order
22
+ IO.popen("git rev-list #@head", 'r') do |rev_list|
23
+ rev_list.each_line do |commit|
24
+ # list contents of the tree object
25
+ `git ls-tree -zrl #{commit.chomp!}`.split("\0").each do |object|
26
+ bits, type, sha, size, path = object.split(/\s+/, 5)
27
+ size = size.to_i
28
+ big_files[sha] = [path, size, commit] if size >= @bytes_threshold
29
+ end
30
+ end
31
+ end
32
+
33
+ big_files.map do |sha, (path, size, commit_sha)|
34
+ where = @git_lib.get_commit_details commit_sha
35
+ who = @git_lib.get_commit_author commit_sha
36
+ [sha, size.to_f / MEGABYTE, path, where, who]
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,25 @@
1
+ module GitHealthCheck
2
+ class Packfile
3
+
4
+ attr_reader :count
5
+ attr_reader :size
6
+ attr_reader :in_pack
7
+ attr_reader :pack_count
8
+ attr_reader :size_of_pack
9
+ attr_reader :prune_packable
10
+ attr_reader :garbage
11
+
12
+ def initialize(repository)
13
+ @repository = repository
14
+ @git_lib = GitHealthCheck::GitLib.new repository
15
+ end
16
+
17
+ def packfile_stats
18
+ stats = @git_lib.count_objects
19
+ @count, @size, @in_pack, @pack_count, @size_of_pack, @prune_packable, @garbage = stats.split("\n").map do |stat|
20
+ stat.scan /\d+/
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ require 'erb'
2
+
3
+ module GitHealthCheck
4
+
5
+ class Report
6
+
7
+ def initialize(working_copy, history, packfile)
8
+ @working_copy = working_copy
9
+ @history = history
10
+ @packfile = packfile
11
+ @report_directory = Dir.pwd + "/healthcheck"
12
+ @repository = Dir.pwd
13
+ end
14
+
15
+ def get_binding
16
+ binding
17
+ end
18
+
19
+ def get_template
20
+ File.read(File.dirname(__FILE__) + "/report/report.erb")
21
+ end
22
+
23
+ def create
24
+ rhtml = ERB.new(get_template)
25
+ output = rhtml.result(get_binding)
26
+
27
+ Dir.mkdir @report_directory unless File.directory? @report_directory
28
+
29
+ File.open(@report_directory + "/report.html", "w+") do |f|
30
+ f.write output
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,104 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Git Health Check</title>
5
+
6
+ <style type="text/css">
7
+ html {
8
+ }
9
+
10
+ body {
11
+ background: none repeat scroll 0 0 #FFFFFF;
12
+ color: #000000;
13
+ font-family: tahoma, verdana, arial, sans-serif;
14
+ font-size: 80%;
15
+ line-height: 1.5em;
16
+ margin: 10px;
17
+ padding: 0;
18
+ }
19
+
20
+ a {
21
+ color: #0254D0;
22
+ text-decoration: none;
23
+ }
24
+
25
+ h1, h2, h3 {
26
+ color: #000000;
27
+ letter-spacing: 0;
28
+ }
29
+
30
+ h1 {
31
+ font-size: 24px;
32
+ }
33
+
34
+ h2 {
35
+ font-size: 20px;
36
+ margin: 10px 27px 15px 0;
37
+ padding: 20px 0 2px;
38
+ }
39
+
40
+ h3 {
41
+ font-size: 16px;
42
+ }
43
+
44
+ #history {
45
+ padding: 0 0 0 20px;
46
+ }
47
+
48
+ #working-copy {
49
+ padding: 0 0 0 20px;
50
+ }
51
+ </style>
52
+ </head>
53
+ <body>
54
+ <h1>Git Health Check Report Summary for <%= @repository %></h1>
55
+
56
+ <hr>
57
+
58
+ <div id="packfile-stats">
59
+ <h2>Packfile Statistics</h2>
60
+ <ul>
61
+ <li><strong>Number of <a href="http://git-scm.com/book/en/Git-Internals-Git-Objects">objects</a> in
62
+ pack:</strong> <%= @packfile.pack_count[0] %></li>
63
+
64
+ <li><strong>Total packfile size: </strong><%= @packfile.size_of_pack[0] %>
65
+ <% if @packfile.size_of_pack[0].to_i >= 1073741824 %>
66
+ <%= 'gb' %>
67
+ <% elsif @packfile.size_of_pack[0].to_i >= 1048576 %>
68
+ <%= 'mb' %>
69
+ <% else %>
70
+ <%= 'kB' %>
71
+ <% end %>
72
+ </li>
73
+ </ul>
74
+ </div>
75
+
76
+ <div id="repository-size">
77
+
78
+ <h2>Repository Size</h2>
79
+
80
+ <div id="working-copy">
81
+
82
+ <h3>Working Copy</h3>
83
+
84
+ <p>This metric inspects your repository's <a href="http://git-scm.com/book/en/Git-Internals-Packfiles">packfile</a>
85
+ contents via its index to identify the <em>n</em> (default 10) largest objects - and consequently the
86
+ <strong>largest files</strong> - in your working copy.</p> <br>
87
+
88
+ <p><%= @working_copy %></p>
89
+
90
+ </div>
91
+
92
+ <div id="history">
93
+ <h3>History</h3>
94
+
95
+ <p>This metric thoroughly inspects your repository's history and identifies the largest files over a configurable
96
+ threshold (defaults at 0.5 MB) that have been committed in the past <strong>but are
97
+ no longer part of the working copy</strong>.</p> <br>
98
+
99
+ <p><%= @history %></p>
100
+ </div>
101
+ </div>
102
+
103
+ </body>
104
+ </html>
@@ -0,0 +1,3 @@
1
+ module GitHealthCheck
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,40 @@
1
+ module GitHealthCheck
2
+
3
+ class WorkingCopy
4
+
5
+ def initialize(repository)
6
+ @repository = repository
7
+ @git_lib = GitHealthCheck::GitLib.new repository
8
+ end
9
+
10
+ def fast_find_in_working_copy(number=10)
11
+ raise 'no packs found' if @git_lib.get_pack_number == 0
12
+ objects = `git verify-pack -v .git/objects/pack/pack-*.idx | grep -E 'tree|blob' | sort -k4nr`
13
+
14
+ objects = objects.split("\n")
15
+
16
+ test = {}
17
+
18
+ objects.each do |object|
19
+ break if test.size == number
20
+ sha = object.match(/^\w+/)[0]
21
+ #path = `git rev-list --all --objects | grep #{sha} | cut -f 2 -d ' '`.chomp
22
+ path = `git rev-list --all --objects | grep #{sha}`.match(/\w+\s(.*)/)[1]
23
+ # an empty path means you need to do a garbage collection
24
+ next unless File.exist?("#@repository/#{path}")
25
+
26
+ sha, type, size, size_in_pack, offset = object.split
27
+
28
+ # convert from byte to kilobyte
29
+ size = (size.to_f / 1024).round 2
30
+ size_in_pack = (size_in_pack.to_f / 1024).round 2
31
+
32
+ test[sha] = [size, size_in_pack, path]
33
+ end
34
+
35
+ test
36
+ end
37
+
38
+
39
+ end
40
+ end
data/test_client.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'git-health-check'
2
+
3
+ ARGV << '-t'
4
+ ARGV << '10'
5
+
6
+ GitHealthCheck::Cli::Application.new(ARGV).execute
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: git-health-check
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ben Snape
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ruport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Git Health Check
47
+ email:
48
+ - bsnape@gmail.com
49
+ executables:
50
+ - ghc
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - LICENSE.txt
57
+ - README.md
58
+ - Rakefile
59
+ - bin/ghc
60
+ - git-health-check.gemspec
61
+ - lib/git-health-check.rb
62
+ - lib/git-health-check/cli/application.rb
63
+ - lib/git-health-check/cli/git_health_check_command.rb
64
+ - lib/git-health-check/cli/help_command.rb
65
+ - lib/git-health-check/cli/options.rb
66
+ - lib/git-health-check/cli/version_command.rb
67
+ - lib/git-health-check/git_lib.rb
68
+ - lib/git-health-check/history.rb
69
+ - lib/git-health-check/packfile.rb
70
+ - lib/git-health-check/report.rb
71
+ - lib/git-health-check/report/report.erb
72
+ - lib/git-health-check/version.rb
73
+ - lib/git-health-check/working_copy.rb
74
+ - test_client.rb
75
+ homepage: http://wwww.bensnape.com
76
+ licenses: []
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 1.8.24
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: Git Health Check
99
+ test_files: []