vcs_toolkit 0.1.0
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.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +8 -0
- data/lib/vcs_toolkit.rb +16 -0
- data/lib/vcs_toolkit/conflict.rb +55 -0
- data/lib/vcs_toolkit/diff.rb +61 -0
- data/lib/vcs_toolkit/exceptions.rb +10 -0
- data/lib/vcs_toolkit/file_store.rb +139 -0
- data/lib/vcs_toolkit/merge.rb +96 -0
- data/lib/vcs_toolkit/object_store.rb +51 -0
- data/lib/vcs_toolkit/objects.rb +5 -0
- data/lib/vcs_toolkit/objects/blob.rb +37 -0
- data/lib/vcs_toolkit/objects/commit.rb +76 -0
- data/lib/vcs_toolkit/objects/label.rb +29 -0
- data/lib/vcs_toolkit/objects/object.rb +47 -0
- data/lib/vcs_toolkit/objects/tree.rb +81 -0
- data/lib/vcs_toolkit/repository.rb +302 -0
- data/lib/vcs_toolkit/serializable.rb +23 -0
- data/lib/vcs_toolkit/utils/hashable_object.rb +45 -0
- data/lib/vcs_toolkit/utils/memory_file_store.rb +101 -0
- data/lib/vcs_toolkit/utils/status.rb +60 -0
- data/lib/vcs_toolkit/utils/sync.rb +73 -0
- data/lib/vcs_toolkit/version.rb +3 -0
- metadata +124 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module VCSToolkit
|
2
|
+
module Serializable
|
3
|
+
def serialize_on(*attributes)
|
4
|
+
define_method :to_hash do
|
5
|
+
attribute_values = attributes.map { |attribute| public_send attribute }
|
6
|
+
|
7
|
+
Hash[attributes.zip(attribute_values)]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def from_hash(hash, **other_args)
|
12
|
+
kwargs = {}
|
13
|
+
|
14
|
+
hash.each do |key, value|
|
15
|
+
kwargs[key.to_sym] = value
|
16
|
+
end
|
17
|
+
|
18
|
+
kwargs.merge! other_args
|
19
|
+
|
20
|
+
new **kwargs
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module VCSToolkit
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
module HashableObject
|
5
|
+
module ClassMethods
|
6
|
+
def hash_on(*attributes)
|
7
|
+
if attributes.size == 1
|
8
|
+
define_method :hash_objects do
|
9
|
+
public_send attributes.first
|
10
|
+
end
|
11
|
+
else
|
12
|
+
define_method :hash_objects do
|
13
|
+
attributes.map { |attribute| public_send attribute }.to_a.inspect
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module InstanceMethods
|
20
|
+
def hash_objects
|
21
|
+
raise NotImplementedError
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def generate_id
|
27
|
+
hash_data = hash_objects
|
28
|
+
hash_data = hash_data.inspect unless hash_data.is_a? String
|
29
|
+
|
30
|
+
Digest::SHA1.hexdigest(hash_data)
|
31
|
+
end
|
32
|
+
|
33
|
+
def id_valid?
|
34
|
+
@id == generate_id
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.included(receiver)
|
39
|
+
receiver.extend ClassMethods
|
40
|
+
receiver.send :include, InstanceMethods
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'vcs_toolkit/file_store'
|
2
|
+
|
3
|
+
module VCSToolkit
|
4
|
+
module Utils
|
5
|
+
|
6
|
+
class MemoryFileStore < FileStore
|
7
|
+
def initialize(file_hash = {})
|
8
|
+
@files = {}
|
9
|
+
|
10
|
+
file_hash.each do |path, content|
|
11
|
+
store path, content
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def store(path, content)
|
16
|
+
path = sanitize_path(path, false)
|
17
|
+
|
18
|
+
if path.end_with? '/'
|
19
|
+
raise 'You can store only files. The directories will be infered from the path'
|
20
|
+
end
|
21
|
+
|
22
|
+
@files[path] = content
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete_file(path)
|
26
|
+
@files.delete sanitize_path(path, false)
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete_dir(path)
|
30
|
+
# Do nothing as the directories are infered from the file paths
|
31
|
+
end
|
32
|
+
|
33
|
+
def fetch(path)
|
34
|
+
@files.fetch sanitize_path(path, false)
|
35
|
+
end
|
36
|
+
|
37
|
+
def file?(path)
|
38
|
+
@files.key? sanitize_path(path, false)
|
39
|
+
end
|
40
|
+
|
41
|
+
def directory?(path)
|
42
|
+
return false if file? path
|
43
|
+
|
44
|
+
path = sanitize_path(path, true)
|
45
|
+
|
46
|
+
@files.keys.any? do |file_path|
|
47
|
+
file_path.start_with? path
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def changed?(path, blob)
|
52
|
+
content = fetch path
|
53
|
+
|
54
|
+
content != blob.content
|
55
|
+
end
|
56
|
+
|
57
|
+
def each_file(path='')
|
58
|
+
path = path.sub(/\/+$/, '')
|
59
|
+
path = "#{path}/" unless path.empty?
|
60
|
+
|
61
|
+
@files.each do |file_path, file|
|
62
|
+
name = file_path.sub(path, '').sub(/^\/+/, '')
|
63
|
+
|
64
|
+
if file_path.start_with?(path) and not name.empty? and not name.include?('/')
|
65
|
+
yield name
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def each_directory(path='')
|
71
|
+
yielded_dirs = {}
|
72
|
+
|
73
|
+
path = sanitize_path(path, true) + '/' unless path.empty?
|
74
|
+
|
75
|
+
@files.each do |file_path, _|
|
76
|
+
name = file_path.sub(path, '')
|
77
|
+
|
78
|
+
if file_path.start_with?(path) and not name.empty? and name.include?('/')
|
79
|
+
name = name.split('/').first
|
80
|
+
|
81
|
+
yield name unless name.empty? or yielded_dirs.key? name
|
82
|
+
yielded_dirs[name] = true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def sanitize_path(path, remove_trailing_slash)
|
90
|
+
path.gsub(/\/+|\\+/, '/').gsub(/^\//, '')
|
91
|
+
|
92
|
+
if remove_trailing_slash
|
93
|
+
path.sub(/\/$/, '')
|
94
|
+
else
|
95
|
+
path
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module VCSToolkit
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
class Status
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def compare_tree_and_store(tree, file_store, object_store, ignore: [])
|
8
|
+
store_file_paths = file_store.all_files(ignore: ignore).to_a
|
9
|
+
|
10
|
+
return {created: store_file_paths, changed: [], deleted: []} if tree.nil?
|
11
|
+
|
12
|
+
tree_files = Hash[tree.all_files(object_store, ignore: ignore).to_a]
|
13
|
+
tree_file_paths = tree_files.keys
|
14
|
+
|
15
|
+
created_files = store_file_paths - tree_file_paths
|
16
|
+
deleted_files = tree_file_paths - store_file_paths
|
17
|
+
|
18
|
+
changed_files = (tree_file_paths & store_file_paths).select do |file|
|
19
|
+
file_store.changed?(file, object_store.fetch(tree_files[file]))
|
20
|
+
end
|
21
|
+
|
22
|
+
{created: created_files, changed: changed_files, deleted: deleted_files}
|
23
|
+
end
|
24
|
+
|
25
|
+
def compare_trees(base_tree, new_tree, object_store, ignore: [])
|
26
|
+
created_files = []
|
27
|
+
changed_files = []
|
28
|
+
deleted_files = []
|
29
|
+
|
30
|
+
if base_tree.nil? and new_tree.nil?
|
31
|
+
# Do nothing... No changed or deleted files.
|
32
|
+
elsif new_tree.nil?
|
33
|
+
deleted_files = base_tree.all_files(object_store, ignore: ignore).map(&:first)
|
34
|
+
elsif base_tree.nil?
|
35
|
+
created_files = new_tree.all_files(object_store, ignore: ignore).map(&:first)
|
36
|
+
else
|
37
|
+
base_files = Hash[base_tree.all_files(object_store, ignore: ignore).to_a]
|
38
|
+
new_files = Hash[new_tree.all_files(object_store, ignore: ignore).to_a]
|
39
|
+
|
40
|
+
base_file_paths = base_files.keys
|
41
|
+
new_file_paths = new_files.keys
|
42
|
+
|
43
|
+
created_files = new_file_paths - base_file_paths
|
44
|
+
deleted_files = base_file_paths - new_file_paths
|
45
|
+
|
46
|
+
changed_files = (base_file_paths & new_file_paths).select do |file|
|
47
|
+
# A file has changed if the ID of it's blob is different
|
48
|
+
base_files[file] != new_files[file]
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
{created: created_files, changed: changed_files, deleted: deleted_files}
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module VCSToolkit
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
class Sync
|
5
|
+
def self.sync(*args)
|
6
|
+
new(*args).sync
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(source_store, source_label_name, destination_store, destination_label_name)
|
10
|
+
@source_store = source_store
|
11
|
+
@source_label = source_store.fetch source_label_name
|
12
|
+
@destination_store = destination_store
|
13
|
+
@destination_label = destination_store.fetch destination_label_name
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Syncs source history starting at `source_label` to
|
18
|
+
# `destination_store` starting at `destination_label`.
|
19
|
+
#
|
20
|
+
def sync
|
21
|
+
raise 'Nothing to sync' if @source_label.reference_id.nil?
|
22
|
+
|
23
|
+
destination_commit_id = @destination_label.reference_id
|
24
|
+
raise DivergedHistoriesError unless destination_commit_id.nil? or @source_store.key? destination_commit_id
|
25
|
+
|
26
|
+
source_commit = @source_store.fetch @source_label.reference_id
|
27
|
+
commits_to_push = source_commit.history_diff(@source_store) do |commit|
|
28
|
+
# Do not follow parent references for commits
|
29
|
+
# that are already on the remote.
|
30
|
+
@destination_store.key? commit.id
|
31
|
+
end
|
32
|
+
|
33
|
+
commits_to_push.each do |commit|
|
34
|
+
transfer_commit commit
|
35
|
+
end
|
36
|
+
|
37
|
+
# Now that every object is transferred change the destination label
|
38
|
+
@destination_label.reference_id = source_commit.id
|
39
|
+
@destination_store.store @destination_label.id, @destination_label
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def transfer_commit(commit)
|
45
|
+
transfer_tree @source_store.fetch(commit.tree)
|
46
|
+
@destination_store.store commit.id, commit
|
47
|
+
end
|
48
|
+
|
49
|
+
def transfer_tree(tree)
|
50
|
+
# Transfer all blobs
|
51
|
+
tree.files.each do |_, blob_id|
|
52
|
+
blob = @source_store.fetch blob_id
|
53
|
+
@destination_store.store blob.id, blob
|
54
|
+
end
|
55
|
+
|
56
|
+
# Transfer all nested trees
|
57
|
+
tree.trees.each do |_, tree_id|
|
58
|
+
nested_tree = @source_store.fetch tree_id
|
59
|
+
transfer_tree nested_tree
|
60
|
+
end
|
61
|
+
|
62
|
+
@destination_store.store tree.id, tree
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class DivergedHistoriesError < VCSToolkitError
|
67
|
+
def initialize(message='The local and remote histories have diverged')
|
68
|
+
super
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vcs_toolkit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Georgy Angelov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: diff-lcs
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: fuubar
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Allows easy to use platform for building a Version Control System. It's
|
70
|
+
a proof-of-concept that VCS systems such as Git are simple in their implementation
|
71
|
+
email:
|
72
|
+
- georgyangelov@gmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- LICENSE
|
78
|
+
- README.md
|
79
|
+
- lib/vcs_toolkit.rb
|
80
|
+
- lib/vcs_toolkit/conflict.rb
|
81
|
+
- lib/vcs_toolkit/diff.rb
|
82
|
+
- lib/vcs_toolkit/exceptions.rb
|
83
|
+
- lib/vcs_toolkit/file_store.rb
|
84
|
+
- lib/vcs_toolkit/merge.rb
|
85
|
+
- lib/vcs_toolkit/object_store.rb
|
86
|
+
- lib/vcs_toolkit/objects.rb
|
87
|
+
- lib/vcs_toolkit/objects/blob.rb
|
88
|
+
- lib/vcs_toolkit/objects/commit.rb
|
89
|
+
- lib/vcs_toolkit/objects/label.rb
|
90
|
+
- lib/vcs_toolkit/objects/object.rb
|
91
|
+
- lib/vcs_toolkit/objects/tree.rb
|
92
|
+
- lib/vcs_toolkit/repository.rb
|
93
|
+
- lib/vcs_toolkit/serializable.rb
|
94
|
+
- lib/vcs_toolkit/utils/hashable_object.rb
|
95
|
+
- lib/vcs_toolkit/utils/memory_file_store.rb
|
96
|
+
- lib/vcs_toolkit/utils/status.rb
|
97
|
+
- lib/vcs_toolkit/utils/sync.rb
|
98
|
+
- lib/vcs_toolkit/version.rb
|
99
|
+
homepage: http://github.com/stormbreakerbg/vcs-toolkit
|
100
|
+
licenses:
|
101
|
+
- MIT
|
102
|
+
metadata: {}
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 2.2.1
|
120
|
+
signing_key:
|
121
|
+
specification_version: 4
|
122
|
+
summary: A Ruby gem designed to allow its users to easily implement their own Version
|
123
|
+
Control System
|
124
|
+
test_files: []
|