treedisha 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/LICENSE +22 -0
- data/README.md +20 -0
- data/lib/treedisha.rb +15 -0
- data/lib/treedisha/comparator.rb +102 -0
- data/lib/treedisha/filesystem.rb +20 -0
- data/spec/lib/comparator_spec.rb +97 -0
- data/spec/lib/filesystem_spec.rb +14 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/fixtures_helper.rb +10 -0
- data/spec/support/test_dir_helper.rb +5 -0
- metadata +78 -0
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2010 dpree
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Treedisha
|
2
|
+
|
3
|
+
diffing filesystem-trees using sha1-checksums
|
4
|
+
|
5
|
+
## Description
|
6
|
+
|
7
|
+
Treedisha is a little ruby gem for comparing two file trees.
|
8
|
+
like git, it uses sha1-checksums to differentiate files from each other and it also doesn't rely on directories. (actually, i was using a wrapper around git for the same job, before i wrote this)
|
9
|
+
|
10
|
+
after comparing the two file trees, it should provide the following information:
|
11
|
+
|
12
|
+
* files that weren't changed
|
13
|
+
* files that were moved (from path => to path)
|
14
|
+
* files that were modified (from sha1 => to sha1)
|
15
|
+
* files that were created
|
16
|
+
* files that were deleted
|
17
|
+
|
18
|
+
## License
|
19
|
+
|
20
|
+
Treedisha is covered by the MIT License. See LICENSE for more information.
|
data/lib/treedisha.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# stdlib
|
2
|
+
require "digest/sha1"
|
3
|
+
|
4
|
+
require File.join(File.dirname(__FILE__), "treedisha/filesystem")
|
5
|
+
require File.join(File.dirname(__FILE__), "treedisha/comparator")
|
6
|
+
|
7
|
+
module Treedisha
|
8
|
+
def self.list(root_path)
|
9
|
+
Treedisha::Filesystem.all_files_with_sha1(root_path)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.compare(list_after, list_before)
|
13
|
+
Treedisha::Comparator.new(list_after, list_before)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Treedisha
|
2
|
+
class Comparator
|
3
|
+
|
4
|
+
attr_reader :untouched
|
5
|
+
attr_reader :created, :deleted
|
6
|
+
attr_reader :moved_old_locations, :moved_new_locations
|
7
|
+
attr_reader :modified_new_content, :modified_old_content
|
8
|
+
attr_reader :modified, :moved
|
9
|
+
|
10
|
+
def initialize(new_tree, old_tree)
|
11
|
+
@new_tree = new_tree
|
12
|
+
@old_tree = old_tree
|
13
|
+
|
14
|
+
compare!
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def compare!
|
20
|
+
@untouched = @new_tree & @old_tree
|
21
|
+
|
22
|
+
new_tree_without_shared = @new_tree - untouched
|
23
|
+
old_tree_without_shared = @old_tree - untouched
|
24
|
+
|
25
|
+
@moved_new_locations, created_or_modified_new_content = diff(new_tree_without_shared, old_tree_without_shared)
|
26
|
+
@moved_old_locations, deleted_or_modified_old_content = diff(old_tree_without_shared, new_tree_without_shared)
|
27
|
+
|
28
|
+
@moved = merge_moved_by_checksum(moved_new_locations, moved_old_locations)
|
29
|
+
|
30
|
+
@modified_new_content, @created = diff_paths(created_or_modified_new_content, deleted_or_modified_old_content)
|
31
|
+
@modified_old_content, @deleted = diff_paths(deleted_or_modified_old_content, created_or_modified_new_content)
|
32
|
+
|
33
|
+
@modified = merge_modified_by_path(modified_new_content, modified_old_content)
|
34
|
+
end
|
35
|
+
|
36
|
+
def diff(base_list_without_shared, search_list_without_shared)
|
37
|
+
different_paths_by_hash = []
|
38
|
+
different_hashes = []
|
39
|
+
|
40
|
+
base_list_without_shared.each do |base_hash, base_path|
|
41
|
+
found_pair_by_hash = search_list_without_shared.assoc(base_hash)
|
42
|
+
|
43
|
+
# shared hash but different path
|
44
|
+
if found_pair_by_hash
|
45
|
+
different_paths_by_hash << [base_hash, base_path]
|
46
|
+
|
47
|
+
# different hash
|
48
|
+
else
|
49
|
+
different_hashes << [base_hash, base_path]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
[different_paths_by_hash, different_hashes]
|
53
|
+
end
|
54
|
+
|
55
|
+
def diff_paths(base_list, search_list)
|
56
|
+
shared_paths = []
|
57
|
+
different_paths = []
|
58
|
+
|
59
|
+
base_list.each do |base_hash, base_path|
|
60
|
+
found_pair_by_path = search_list.rassoc(base_path)
|
61
|
+
|
62
|
+
# shared path
|
63
|
+
if found_pair_by_path
|
64
|
+
shared_paths << [base_hash, base_path]
|
65
|
+
|
66
|
+
# different path
|
67
|
+
else
|
68
|
+
different_paths << [base_hash, base_path]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
[shared_paths, different_paths]
|
73
|
+
end
|
74
|
+
|
75
|
+
def merge_moved_by_checksum(new_list, old_list)
|
76
|
+
moved = {}
|
77
|
+
new_list.each do |new_checksum, new_path|
|
78
|
+
if moved[new_checksum]
|
79
|
+
moved[new_checksum][:new_paths] << new_path
|
80
|
+
else
|
81
|
+
found_pairs_by_checksum = old_list.find_all{|old_checksum, old_path| new_checksum == old_checksum}
|
82
|
+
moved[new_checksum] = {
|
83
|
+
:new_paths => [new_path],
|
84
|
+
:old_paths => found_pairs_by_checksum.map{|a,b| b}
|
85
|
+
}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
moved
|
89
|
+
end
|
90
|
+
|
91
|
+
def merge_modified_by_path(new_list, old_list)
|
92
|
+
modified = {}
|
93
|
+
new_list.each do |new_checksum, new_path|
|
94
|
+
modified[new_path] = {:new_checksum => new_checksum}
|
95
|
+
end
|
96
|
+
old_list.each do |old_checksum, old_path|
|
97
|
+
modified[old_path][:old_checksum] = old_checksum
|
98
|
+
end
|
99
|
+
modified
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Treedisha
|
2
|
+
module Filesystem
|
3
|
+
|
4
|
+
def self.all_files(root_path)
|
5
|
+
Dir.glob("#{root_path}/**/*").find_all do |f|
|
6
|
+
!File.directory? f
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.all_files_with_sha1(root_path)
|
11
|
+
all_files(root_path).map do |f|
|
12
|
+
[sha1_for_file(f), f]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.sha1_for_file(file)
|
17
|
+
Digest::SHA1.file(file).to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Treedisha::Comparator do
|
4
|
+
before(:each) do
|
5
|
+
new_tree = FixturesHelper.read_json_file("comparator_a/state_after.json")
|
6
|
+
old_tree = FixturesHelper.read_json_file("comparator_a/state_before.json")
|
7
|
+
|
8
|
+
@comparator = Treedisha::Comparator.new(new_tree, old_tree)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should throw no error" do
|
12
|
+
new_tree = FixturesHelper.read_json_file("comparator_b/state_after.json")
|
13
|
+
old_tree = FixturesHelper.read_json_file("comparator_b/state_before.json")
|
14
|
+
|
15
|
+
comparator = Treedisha::Comparator.new(new_tree, old_tree)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be possible find unmodified files" do
|
19
|
+
@comparator.untouched.size.should == 3
|
20
|
+
@comparator.untouched.each do |hash,path|
|
21
|
+
path.should =~ /untouched/
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should be possible find moved files and their new location" do
|
26
|
+
@comparator.moved_new_locations.size.should == 2
|
27
|
+
@comparator.moved_new_locations.each do |hash, path|
|
28
|
+
path.should =~ /file_moved_new_location/
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should be possible find moved files and their old location" do
|
33
|
+
@comparator.moved_old_locations.size.should == 3
|
34
|
+
@comparator.moved_old_locations.each do |hash, path|
|
35
|
+
path.should =~ /file_moved_old_location/
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should be possible find modified files and their new content" do
|
40
|
+
@comparator.modified_new_content.size.should == 1
|
41
|
+
@comparator.modified_new_content.each do |hash, path|
|
42
|
+
path.should =~ /file_changed_content/
|
43
|
+
hash.should =~ /new_content/
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should be possible find modified files and their old content" do
|
48
|
+
@comparator.modified_old_content.size.should == 1
|
49
|
+
@comparator.modified_old_content.each do |hash, path|
|
50
|
+
path.should =~ /file_changed_content/
|
51
|
+
hash.should =~ /old_content/
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should be possible find created files" do
|
56
|
+
@comparator.created.size.should == 2
|
57
|
+
@comparator.created.each do |hash,path|
|
58
|
+
path.should =~ /hereby_created/
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should be possible find deleted files" do
|
63
|
+
@comparator.deleted.size.should == 2
|
64
|
+
@comparator.deleted.each do |hash,path|
|
65
|
+
path.should =~ /hereby_deleted/
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should be possible to track modified files" do
|
70
|
+
@comparator.modified.keys.size.should == 1
|
71
|
+
@comparator.modified.each do |path, checksums|
|
72
|
+
path.should =~ /file_changed_content/
|
73
|
+
checksums[:old_checksum].should =~ /old_content/
|
74
|
+
checksums[:new_checksum].should =~ /new_content/
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should be possible to track moved files" do
|
79
|
+
old_paths_count = 0
|
80
|
+
new_paths_count = 0
|
81
|
+
@comparator.moved.keys.size.should == 2
|
82
|
+
@comparator.moved.each do |checksum, paths|
|
83
|
+
paths[:old_paths].empty?.should be_false
|
84
|
+
old_paths_count += paths[:old_paths].size
|
85
|
+
paths[:old_paths].each do |path|
|
86
|
+
path.should =~ /file_moved_old_location/
|
87
|
+
end
|
88
|
+
paths[:new_paths].empty?.should be_false
|
89
|
+
new_paths_count += paths[:new_paths].size
|
90
|
+
paths[:new_paths].each do |path|
|
91
|
+
path.should =~ /file_moved_new_location/
|
92
|
+
end
|
93
|
+
end
|
94
|
+
old_paths_count.should == 3
|
95
|
+
new_paths_count.should == 2
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Treedisha::Filesystem do
|
4
|
+
it "should be possible to list all files" do
|
5
|
+
path = TestDirHelper.path(".")
|
6
|
+
Treedisha::Filesystem.all_files(path).empty?.should be_false
|
7
|
+
Treedisha::Filesystem.all_files(path).size == 1
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should be possible to get sha1 for a file" do
|
11
|
+
path = TestDirHelper.path("foo.fx")
|
12
|
+
Treedisha::Filesystem.sha1_for_file(path).should == "83a4f6db0964dd03480be5f781eec6c5c2f7f5f2"
|
13
|
+
end
|
14
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: treedisha
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- dpree
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-12-08 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Tiny file tree comparison library using sha1-checksums
|
22
|
+
email: whiterabbit.init@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- LICENSE
|
29
|
+
- README.md
|
30
|
+
files:
|
31
|
+
- lib/treedisha.rb
|
32
|
+
- lib/treedisha/comparator.rb
|
33
|
+
- lib/treedisha/filesystem.rb
|
34
|
+
- LICENSE
|
35
|
+
- README.md
|
36
|
+
- spec/lib/comparator_spec.rb
|
37
|
+
- spec/lib/filesystem_spec.rb
|
38
|
+
- spec/spec_helper.rb
|
39
|
+
- spec/support/fixtures_helper.rb
|
40
|
+
- spec/support/test_dir_helper.rb
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://github.com/dpree/treedisha
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
requirements: []
|
67
|
+
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 1.3.7
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: Tiny file tree comparison library
|
73
|
+
test_files:
|
74
|
+
- spec/lib/comparator_spec.rb
|
75
|
+
- spec/lib/filesystem_spec.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
- spec/support/fixtures_helper.rb
|
78
|
+
- spec/support/test_dir_helper.rb
|