inimit 0.0.1 → 0.0.2
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/Gemfile +1 -0
- data/VERSION +1 -1
- data/bin/inimit +1 -1
- data/inimit.gemspec +79 -0
- data/lib/inimit.rb +128 -40
- metadata +3 -2
data/Gemfile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/bin/inimit
CHANGED
data/inimit.gemspec
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "inimit"
|
8
|
+
s.version = "0.0.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["AlphaHydrae"]
|
12
|
+
s.date = "2012-09-12"
|
13
|
+
s.description = "Compares files in two directories using SHA1."
|
14
|
+
s.email = "hydrae.alpha@gmail.com"
|
15
|
+
s.executables = ["inimit"]
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"LICENSE.txt",
|
18
|
+
"README.md"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".rspec",
|
22
|
+
".rvmrc",
|
23
|
+
".screenrc",
|
24
|
+
".travis.yml",
|
25
|
+
"Gemfile",
|
26
|
+
"Gemfile.lock",
|
27
|
+
"LICENSE.txt",
|
28
|
+
"README.md",
|
29
|
+
"Rakefile",
|
30
|
+
"VERSION",
|
31
|
+
"bin/inimit",
|
32
|
+
"inimit.gemspec",
|
33
|
+
"lib/inimit.rb",
|
34
|
+
"spec/helper.rb",
|
35
|
+
"spec/version_spec.rb"
|
36
|
+
]
|
37
|
+
s.homepage = "http://github.com/AlphaHydrae/inimit"
|
38
|
+
s.licenses = ["MIT"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubygems_version = "1.8.24"
|
41
|
+
s.summary = "Recursive file comparison command."
|
42
|
+
|
43
|
+
if s.respond_to? :specification_version then
|
44
|
+
s.specification_version = 3
|
45
|
+
|
46
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
+
s.add_runtime_dependency(%q<paint>, ["~> 0.8.5"])
|
48
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<rake>, [">= 0"])
|
50
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
51
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<gemcutter>, [">= 0"])
|
53
|
+
s.add_development_dependency(%q<gem-release>, [">= 0"])
|
54
|
+
s.add_development_dependency(%q<rake-version>, [">= 0"])
|
55
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
56
|
+
else
|
57
|
+
s.add_dependency(%q<paint>, ["~> 0.8.5"])
|
58
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
59
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
60
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
61
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
62
|
+
s.add_dependency(%q<gemcutter>, [">= 0"])
|
63
|
+
s.add_dependency(%q<gem-release>, [">= 0"])
|
64
|
+
s.add_dependency(%q<rake-version>, [">= 0"])
|
65
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
66
|
+
end
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<paint>, ["~> 0.8.5"])
|
69
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
70
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
71
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
72
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
73
|
+
s.add_dependency(%q<gemcutter>, [">= 0"])
|
74
|
+
s.add_dependency(%q<gem-release>, [">= 0"])
|
75
|
+
s.add_dependency(%q<rake-version>, [">= 0"])
|
76
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
data/lib/inimit.rb
CHANGED
@@ -1,83 +1,171 @@
|
|
1
1
|
require 'find'
|
2
|
-
require 'digest
|
2
|
+
require 'digest'
|
3
3
|
require 'paint'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
# TODO: lenient option to only fail on mismatch
|
4
7
|
|
5
8
|
module Inimit
|
6
|
-
VERSION = '0.0.
|
9
|
+
VERSION = '0.0.2'
|
7
10
|
|
8
|
-
|
11
|
+
class Opts < OptionParser
|
12
|
+
def to_s
|
13
|
+
"#{banner}\nOptions:\n#{summarize ''}"
|
14
|
+
end
|
15
|
+
end
|
9
16
|
|
10
|
-
|
17
|
+
def self.run *args
|
18
|
+
|
19
|
+
options = {
|
20
|
+
:verbose => 0,
|
21
|
+
:mismatch_only => false
|
22
|
+
}
|
23
|
+
Opts.new do |opts|
|
24
|
+
opts.banner = "#{Paint[opts.program_name, :bold]} recursively compares files.\n\nUsage:\n inimit [OPTION]... [SOURCE] TARGET\n"
|
25
|
+
opts.version = VERSION
|
26
|
+
opts.on('-d', '--digest ALGORITHM', 'Use ALGORITHM to hash file contents (available: md5, sha1, sha256, sha384, sha512)') do |digest|
|
27
|
+
options[:digest] = digest
|
28
|
+
end
|
29
|
+
opts.on('-m', '--mismatch', 'Only show files when missing or the content does not match') do
|
30
|
+
options[:mismatch_only] = true
|
31
|
+
end
|
32
|
+
opts.on('-v', '--verbose', 'Increase verbosity (once: show hash prefixes, twice: show full hashes)') do
|
33
|
+
options[:verbose] += 1
|
34
|
+
end
|
35
|
+
opts.on('-h', '--help', 'Show this help and exit'){ puts opts; exit 0 }
|
36
|
+
opts.on('-u', '--usage', 'Show this help and exit'){ puts opts; exit 0 }
|
37
|
+
end.parse! args
|
38
|
+
|
39
|
+
# check number of args
|
40
|
+
self.abort "A target directory must be given" if args.length < 1
|
41
|
+
self.abort "Only one source and target must be given (got #{args.length} arguments)" if args.length > 2
|
42
|
+
|
43
|
+
source, target = case args.length
|
44
|
+
when 1
|
45
|
+
[ '.', args.shift ]
|
46
|
+
when 2
|
47
|
+
[ args.shift, args.shift ]
|
48
|
+
end
|
11
49
|
|
12
|
-
|
13
|
-
|
14
|
-
|
50
|
+
if !File.exists?(source)
|
51
|
+
self.abort "Unknown file or directory #{source}"
|
52
|
+
elsif !File.file?(source) and !File.directory?(source)
|
53
|
+
self.abort "Source #{source} is neither a file nor a directory"
|
54
|
+
elsif File.file?(source) and !File.file?(target)
|
55
|
+
self.abort "Source #{source} is a file, target #{target} is not"
|
56
|
+
elsif File.directory?(source) and !File.directory?(target)
|
57
|
+
self.abort "Source #{source} is a directory, target #{target} is not"
|
58
|
+
end
|
59
|
+
|
60
|
+
if File.directory? source
|
61
|
+
self.compare source, target, options
|
62
|
+
else
|
63
|
+
self.compare_files source, target, options
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.compare source, target, options = {}
|
15
68
|
|
16
69
|
source, target = File.expand_path(source), File.expand_path(target)
|
17
70
|
|
18
71
|
puts
|
19
|
-
puts "Comparing files in #{source} to #{target}
|
20
|
-
puts
|
72
|
+
puts "Comparing files in #{source} to #{target}."
|
73
|
+
puts unless options[:mismatch_only]
|
21
74
|
|
22
75
|
Dir.chdir source
|
23
|
-
unknown =
|
24
|
-
different = []
|
76
|
+
total, match, unknown, mismatch = 0, 0, 0, 0
|
25
77
|
|
26
78
|
Find.find '.' do |file|
|
27
79
|
|
28
80
|
# skip directories
|
29
81
|
next unless File.file? file
|
30
|
-
|
31
|
-
print "- #{file.sub /^\.\//, ''}..."
|
82
|
+
total += 1
|
32
83
|
|
33
84
|
source_file = File.join source, file
|
34
85
|
target_file = File.join target, file
|
35
86
|
|
36
87
|
if !File.file? target_file
|
37
|
-
unknown
|
88
|
+
unknown += 1
|
38
89
|
puts Paint[" not found", :yellow]
|
39
90
|
next
|
40
91
|
end
|
41
92
|
|
42
|
-
source_sha1 = self.hash source_file
|
43
|
-
target_sha1 = self.hash target_file
|
93
|
+
source_sha1 = self.hash source_file, options
|
94
|
+
target_sha1 = self.hash target_file, options
|
44
95
|
|
45
96
|
if source_sha1 == target_sha1
|
46
|
-
|
97
|
+
match += 1
|
98
|
+
unless options[:mismatch_only]
|
99
|
+
print Paint[self.hash_notice(source_sha1, target_sha1, options), :green]
|
100
|
+
puts " #{file.sub /^\.\//, ''}..."
|
101
|
+
end
|
47
102
|
else
|
48
|
-
|
49
|
-
|
103
|
+
mismatch += 1
|
104
|
+
print Paint[self.hash_notice(source_sha1, target_sha1, options), :red]
|
105
|
+
puts " #{file.sub /^\.\//, ''}..."
|
50
106
|
end
|
51
107
|
end
|
52
108
|
|
53
|
-
|
109
|
+
puts
|
110
|
+
|
111
|
+
notice = "Checked #{total} files: "
|
112
|
+
notice << Paint["#{match} identical", :green]
|
113
|
+
notice << ", "
|
114
|
+
notice << Paint["#{unknown} not found", :yellow]
|
115
|
+
notice << ", "
|
116
|
+
notice << Paint["#{mismatch} different", :red]
|
117
|
+
|
118
|
+
puts notice
|
119
|
+
puts
|
120
|
+
|
121
|
+
exit 1 if unknown >= 1 or mismatch >= 1
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.compare_files source, target, options = {}
|
125
|
+
|
126
|
+
source_sha1, target_sha1 = self.hash(source, options), self.hash(target, options)
|
127
|
+
|
128
|
+
puts
|
129
|
+
|
130
|
+
if source_sha1 == target_sha1
|
131
|
+
msg = "Files are identical"
|
132
|
+
msg << " (#{self.hash_notice source_sha1, target_sha1, options})" if options[:verbose] >= 1
|
133
|
+
msg << "."
|
134
|
+
puts Paint[msg, :green]
|
54
135
|
puts
|
55
|
-
|
136
|
+
else
|
137
|
+
msg = "The contents of the files do not match"
|
138
|
+
msg << " (#{self.hash_notice source_sha1, target_sha1, options})" if options[:verbose] >= 1
|
139
|
+
msg << "."
|
140
|
+
puts Paint[msg, :red]
|
56
141
|
puts
|
57
|
-
|
142
|
+
exit 1
|
58
143
|
end
|
144
|
+
end
|
59
145
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
146
|
+
def self.hash_notice h1, h2, options = {}
|
147
|
+
if h1 == h2
|
148
|
+
case options[:verbose]
|
149
|
+
when 1; "#{h1[0,7]} == #{h2[0,7]}"
|
150
|
+
when 2; "#{h1} == #{h2}"
|
151
|
+
else; "identical"
|
65
152
|
end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
puts Paint["The following files in #{source} do not have the same content in #{target}:", :red, :bold]
|
71
|
-
different.each do |h|
|
72
|
-
puts "- #{h[:file]}"
|
153
|
+
else
|
154
|
+
case options[:verbose]
|
155
|
+
when 1..2; "#{h1} != #{h2}"
|
156
|
+
else; "different"
|
73
157
|
end
|
74
158
|
end
|
75
|
-
|
76
|
-
puts
|
77
159
|
end
|
78
160
|
|
79
|
-
def self.hash file
|
80
|
-
dig =
|
161
|
+
def self.hash file, options = {}
|
162
|
+
dig = case options[:digest]
|
163
|
+
when 'md5'; Digest::MD5.new
|
164
|
+
when 'sha256'; Digest::SHA256.new
|
165
|
+
when 'sha384'; Digest::SHA384.new
|
166
|
+
when 'sha512'; Digest::SHA512.new
|
167
|
+
else; Digest::SHA1.new
|
168
|
+
end
|
81
169
|
File.open file, 'rb' do |io|
|
82
170
|
dig.update io.readpartial(4096) while !io.eof
|
83
171
|
end
|
@@ -85,7 +173,7 @@ module Inimit
|
|
85
173
|
end
|
86
174
|
|
87
175
|
def self.abort msg
|
88
|
-
warn msg
|
89
|
-
exit
|
176
|
+
warn Paint[msg, :yellow]
|
177
|
+
exit 2
|
90
178
|
end
|
91
179
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inimit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: paint
|
@@ -175,6 +175,7 @@ files:
|
|
175
175
|
- Rakefile
|
176
176
|
- VERSION
|
177
177
|
- bin/inimit
|
178
|
+
- inimit.gemspec
|
178
179
|
- lib/inimit.rb
|
179
180
|
- spec/helper.rb
|
180
181
|
- spec/version_spec.rb
|