git-health-check 0.0.1

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.
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: []