copernicium 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.
- checksums.yaml +7 -0
- data/bin/cn +10 -0
- data/lib/RevLog.rb +141 -0
- data/lib/banners.rb +23 -0
- data/lib/pushpull.rb +168 -0
- data/lib/repos.rb +165 -0
- data/lib/required.rb +21 -0
- data/lib/ui.rb +161 -0
- data/lib/workspace.rb +212 -0
- metadata +152 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cffaf1f93a4556043ac0a484ef0e10c24f7ea692
|
4
|
+
data.tar.gz: 41ece5cd459baf3690d2701e153b232293f3a433
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 215e18ebaa93fb182b3ea4407bbf266cee5004baef77e1ff195cbcd32061a7314e7c49b02b1f762482c950436c495b802f302d8d2bf976c0b17518bd17d5444c
|
7
|
+
data.tar.gz: ebf39e146f63bc17bc70de329ea98c2b5f0cbea79f6a4aa620f9f21719f96879ea7737864e34a2d747ee75861ba2c5f9daad1c9a6f55c81a509a6a95a324f265
|
data/bin/cn
ADDED
data/lib/RevLog.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# Revlog Top Level Function Definitions (Xiangru)
|
2
|
+
#
|
3
|
+
# add_file: add a file to the revision history
|
4
|
+
# in - file name, content
|
5
|
+
# out - hash id of file (file_id)
|
6
|
+
#
|
7
|
+
# delete_file: a delete a file from revision history
|
8
|
+
# in - file_id
|
9
|
+
# out - exit status code
|
10
|
+
#
|
11
|
+
# diff_files: generate the differences between 2 files
|
12
|
+
# in - two file_ids
|
13
|
+
# out - list of differences
|
14
|
+
#
|
15
|
+
# get_file: get the contents of a file based on hash id
|
16
|
+
# in - file_id
|
17
|
+
# out - content of specified file
|
18
|
+
#
|
19
|
+
# hash_file: generate hash id for a given file
|
20
|
+
# in - file name, content
|
21
|
+
# out - hashed id
|
22
|
+
#
|
23
|
+
# merge: given two files, try to merge them
|
24
|
+
# in - file_id_1, file_id_2
|
25
|
+
# out - success and merged file name/content, or failure and conflict
|
26
|
+
|
27
|
+
module Copernicium
|
28
|
+
class RevLog
|
29
|
+
def initialize(project_path)
|
30
|
+
@project_path = project_path
|
31
|
+
@cop_path = File.join(project_path, ".cop")
|
32
|
+
@log_path = File.join(@cop_path, "logmap.yaml")
|
33
|
+
@hash_path = File.join(@cop_path, "hashmap.yaml")
|
34
|
+
if File.exist?(@log_path) and File.exist?(@hash_path) then
|
35
|
+
@logmap = default_hash_factory.merge(YAML.load_file(@log_path))
|
36
|
+
@hashmap = default_hash_factory.merge(YAML.load_file(@hash_path))
|
37
|
+
else
|
38
|
+
@logmap = default_hash_factory()
|
39
|
+
@hashmap = default_hash_factory()
|
40
|
+
unless File.exist?(@cop_path)
|
41
|
+
Dir.mkdir(@cop_path)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def default_hash_factory()
|
47
|
+
Hash.new {[]}
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_file(file_name, content)
|
51
|
+
hash = hash_file(file_name, content)
|
52
|
+
File.open(File.join(@cop_path, hash), "w") { |f|
|
53
|
+
f.write(content)
|
54
|
+
}
|
55
|
+
@logmap[file_name] = @logmap[file_name] << {:time => Time.now,
|
56
|
+
:hash => hash}
|
57
|
+
@hashmap[hash] = @hashmap[hash] << {:time => Time.now,
|
58
|
+
:filename => file_name}
|
59
|
+
update_log_file()
|
60
|
+
return hash
|
61
|
+
end
|
62
|
+
|
63
|
+
## return 1 if succeed, otherwise 0
|
64
|
+
def delete_file(file_id)
|
65
|
+
begin
|
66
|
+
file_name = @hashmap[file_id][0][:filename]
|
67
|
+
@hashmap[file_id].delete_if { |e|
|
68
|
+
e[:filename] == file_name
|
69
|
+
}
|
70
|
+
@logmap[file_name].delete_if { |e|
|
71
|
+
e[:hash] == file_id
|
72
|
+
}
|
73
|
+
update_log_file()
|
74
|
+
File.delete(File.join(@cop_path, file_id))
|
75
|
+
return 1
|
76
|
+
rescue Exception
|
77
|
+
return 0
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def get_file(file_id)
|
83
|
+
file_path = File.join(@cop_path, file_id)
|
84
|
+
if File.exist?(file_path)
|
85
|
+
File.open(file_path, "r") { |f|
|
86
|
+
return f.read
|
87
|
+
}
|
88
|
+
else
|
89
|
+
raise Exception, "Invalid file_id!"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def diff_files(file_id1, file_id2)
|
95
|
+
return Diffy::Diff.new(get_file(file_id1),
|
96
|
+
get_file(file_id2)).to_s()
|
97
|
+
end
|
98
|
+
|
99
|
+
def hash_file(file_name, content)
|
100
|
+
Digest::SHA256.hexdigest(file_name + content.to_s)
|
101
|
+
end
|
102
|
+
|
103
|
+
def merge(file_id1, file_id2)
|
104
|
+
diff_a = Diffy::Diff.new(get_file(file_id1),
|
105
|
+
get_file(file_id2)).each_chunk.to_a()
|
106
|
+
if diff_a.all? { |d| d[0]!="-"}
|
107
|
+
return get_file(file_id2)
|
108
|
+
end
|
109
|
+
# if diff_a.all? { |d| d[0]!="+"}
|
110
|
+
# return get_file(file_id1)
|
111
|
+
# end
|
112
|
+
return diff_a
|
113
|
+
end
|
114
|
+
|
115
|
+
def history(file_name)
|
116
|
+
hashs = []
|
117
|
+
for m in @logmap[file_name]
|
118
|
+
hashs << m[:hash]
|
119
|
+
end
|
120
|
+
return hashs
|
121
|
+
end
|
122
|
+
|
123
|
+
def update_log_file()
|
124
|
+
File.open(File.join(@cop_path, "logmap.yaml"), "w") { |f|
|
125
|
+
f.write(@logmap.to_yaml)
|
126
|
+
}
|
127
|
+
File.open(File.join(@cop_path, "hashmap.yaml"), "w") { |f|
|
128
|
+
f.write(@hashmap.to_yaml)
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
# def alterFile(fileObject, fileReferenceString, versionReferenceString)
|
133
|
+
# end
|
134
|
+
|
135
|
+
# def deleteFileVersion(fileReferenceString, versionReferenceString)
|
136
|
+
# end
|
137
|
+
|
138
|
+
# def viewFileHistory(fileReferenceString)
|
139
|
+
# end
|
140
|
+
end
|
141
|
+
end
|
data/lib/banners.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# file for large constant strings
|
2
|
+
|
3
|
+
|
4
|
+
HELP_BANNER = <<-EOS
|
5
|
+
Copernicium - simple DVCS
|
6
|
+
|
7
|
+
Starting:
|
8
|
+
init - create a new repository
|
9
|
+
status - check repo status
|
10
|
+
|
11
|
+
Commands:
|
12
|
+
commit <files> -m [message]
|
13
|
+
checkout [branch name]
|
14
|
+
merge [branch name]
|
15
|
+
clone [remote url]
|
16
|
+
push <remote name>
|
17
|
+
pull <remote name>
|
18
|
+
|
19
|
+
Options:
|
20
|
+
-v: print version
|
21
|
+
-h: show help
|
22
|
+
EOS
|
23
|
+
|
data/lib/pushpull.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
# Frank Tamburrino
|
2
|
+
# CSC 253
|
3
|
+
# PushPull Module
|
4
|
+
# November 6, 2015
|
5
|
+
|
6
|
+
|
7
|
+
module Copernicium
|
8
|
+
class PushPull
|
9
|
+
|
10
|
+
def connect(remote, user = nil, passwd = nil, &block)
|
11
|
+
exit_code = false
|
12
|
+
if(block.nil?)
|
13
|
+
begin
|
14
|
+
if(user.nil?)
|
15
|
+
print "Username for remote repo?: "
|
16
|
+
user = (STDIN.readline).strip
|
17
|
+
end
|
18
|
+
if(passwd.nil?)
|
19
|
+
print "Password for #{user}: "
|
20
|
+
passwd = (STDIN.noecho(&:gets)).strip
|
21
|
+
puts
|
22
|
+
end
|
23
|
+
Net::SSH.start(remote, user, :password => passwd) do |ssh|
|
24
|
+
result = ssh.exec!("echo Successful Connection!")
|
25
|
+
puts result
|
26
|
+
exit_code = true;
|
27
|
+
end
|
28
|
+
rescue
|
29
|
+
puts "Connection Unsuccessful"
|
30
|
+
end
|
31
|
+
else
|
32
|
+
begin
|
33
|
+
if(user.nil?)
|
34
|
+
print "Username for remote repo: "
|
35
|
+
user = (STDIN.readline).strip
|
36
|
+
end
|
37
|
+
if(passwd.nil?)
|
38
|
+
print "Password for #{user}: "
|
39
|
+
passwd = (STDIN.noecho(&:gets)).strip
|
40
|
+
puts
|
41
|
+
end
|
42
|
+
Net::SSH.start(remote, user, :password => passwd) do |ssh|
|
43
|
+
yield ssh
|
44
|
+
end
|
45
|
+
exit_code = true;
|
46
|
+
rescue
|
47
|
+
puts "Unable to execute command!"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
return exit_code
|
51
|
+
end
|
52
|
+
|
53
|
+
def transfer(remote, local, dest, user = nil, passwd = nil)
|
54
|
+
exit_code = false
|
55
|
+
if(user.nil?)
|
56
|
+
print "Username for remote repo: "
|
57
|
+
user = (STDIN.readline).strip
|
58
|
+
end
|
59
|
+
if(passwd.nil?)
|
60
|
+
print "Password for #{user}: "
|
61
|
+
passwd = (STDIN.noecho(&:gets)).strip
|
62
|
+
puts
|
63
|
+
end
|
64
|
+
begin
|
65
|
+
Net::SCP.start(remote, user, :password => passwd) do |scp|
|
66
|
+
scp.upload!(local, dest)
|
67
|
+
end
|
68
|
+
exit_code = true
|
69
|
+
rescue
|
70
|
+
puts "Unable to upload file!"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def fetch(remote, dest, local, user = nil, passwd = nil)
|
75
|
+
exit_code = false
|
76
|
+
if(user.nil?)
|
77
|
+
print "Username for remote repo: "
|
78
|
+
user = (STDIN.readline).strip
|
79
|
+
end
|
80
|
+
if(passwd.nil?)
|
81
|
+
print "Password for #{user}: "
|
82
|
+
passwd = (STDIN.noecho(&:gets)).strip
|
83
|
+
puts
|
84
|
+
end
|
85
|
+
begin
|
86
|
+
Net::SCP.start(remote, user, :password => passwd) do |scp|
|
87
|
+
scp.download!(dest, local, :recursive => true)
|
88
|
+
end
|
89
|
+
exit_code = true
|
90
|
+
rescue
|
91
|
+
puts "Unable to fetch file(s)!"
|
92
|
+
end
|
93
|
+
|
94
|
+
return exit_code
|
95
|
+
end
|
96
|
+
|
97
|
+
def push(remote, branch, remote_dir)
|
98
|
+
################ One way we can handle it ###################
|
99
|
+
################ Needs Repos and Revlog Functionality! ######
|
100
|
+
# snap_id = (Repos::Repo.history(branch)).last
|
101
|
+
# snap = RevLog::RevLog.get_file(snap_id)
|
102
|
+
# connect(remote) do |x|
|
103
|
+
# result = test.exec!("<call to Repos to diff the snapshots>")
|
104
|
+
# end
|
105
|
+
# print "Username for remote repo: "
|
106
|
+
# user = (STDIN.readline).strip
|
107
|
+
# print "Password for #{user}: "
|
108
|
+
# passwd = (STDIN.noecho(&:gets)).strip
|
109
|
+
# puts
|
110
|
+
# for result.each do |x|
|
111
|
+
# transfer(remote, "./#{x}", remote_dir, user, passwd)
|
112
|
+
# end
|
113
|
+
# connect(remote) do |x|
|
114
|
+
# test.exec!("<call to Repos to merge the files>")
|
115
|
+
# test.exec!("<clean up the files>")
|
116
|
+
# end
|
117
|
+
#############################################################
|
118
|
+
end
|
119
|
+
|
120
|
+
def pull(remote, branch, remote_dir)
|
121
|
+
################ One way we can handle it ###################
|
122
|
+
################ Needs Repos and Revlog Functionality! ######
|
123
|
+
# snap_id = (Repos::Repo.history(branch)).last
|
124
|
+
# snap = RevLog::RevLog.get_file(snap_id)
|
125
|
+
# connect(remote) do |x|
|
126
|
+
# result = test.exec!("<call to Repos to diff the snapshots>")
|
127
|
+
# end
|
128
|
+
# print "Username for remote repo: "
|
129
|
+
# user = (STDIN.readline).strip
|
130
|
+
# print "Password for #{user}: "
|
131
|
+
# passwd = (STDIN.noecho(&:gets)).strip
|
132
|
+
# puts
|
133
|
+
# for result.each do |x|
|
134
|
+
# fetch(remote, remote_dir, "./#{x}", user, passwd)
|
135
|
+
# RevLog::Revlog.merge(x, local_x)
|
136
|
+
# File.delete(x)
|
137
|
+
# end
|
138
|
+
#############################################################
|
139
|
+
end
|
140
|
+
|
141
|
+
def clone(remote, dir, user = nil, passwd = nil)
|
142
|
+
exit_code = false
|
143
|
+
if(user.nil?)
|
144
|
+
print "Username for remote repo: "
|
145
|
+
user = (STDIN.readline).strip
|
146
|
+
end
|
147
|
+
if(passwd.nil?)
|
148
|
+
print "Password for #{user}: "
|
149
|
+
passwd = (STDIN.noecho(&:gets)).strip
|
150
|
+
puts
|
151
|
+
end
|
152
|
+
begin
|
153
|
+
fetch(remote, dir, ".", user, passwd)
|
154
|
+
nd = File.basename(dir)
|
155
|
+
################ Needs Repos Functionality! #################
|
156
|
+
# Initializes the folder as a Repo
|
157
|
+
# Repos::Repos.make_branch(nd)
|
158
|
+
#############################################################
|
159
|
+
exit_code = true;
|
160
|
+
rescue
|
161
|
+
puts "Failed to clone the remote branch!"
|
162
|
+
end
|
163
|
+
|
164
|
+
return exit_code
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
end
|
data/lib/repos.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
# repos module
|
2
|
+
|
3
|
+
#require 'marshal'
|
4
|
+
# Details from this link:
|
5
|
+
# https://docs.google.com/document/d/1r3-NquhyRLbCncqTOQPwsznSZ-en6G6xzLbWIAmxhys/edit#heading=h.7pyingf1unu
|
6
|
+
|
7
|
+
|
8
|
+
# Repos Top Level Function Definitions (Logan)
|
9
|
+
|
10
|
+
# make_snapshot: Creates new snapshot from current files and versions
|
11
|
+
# in - array of file objects. file object = array of all versions: {id, content}
|
12
|
+
# out - hash id of snapshot
|
13
|
+
|
14
|
+
# restore_snapshot: Set current file versions to specified snapshot
|
15
|
+
# in - id of target snapshot
|
16
|
+
# out - Comm object with status
|
17
|
+
|
18
|
+
# history: Returns ids for all snapshots
|
19
|
+
# in - branch name
|
20
|
+
# out - Array of snapshot ids
|
21
|
+
|
22
|
+
# delete_snapshot: delete specified a snapshot
|
23
|
+
# in - target snapshot
|
24
|
+
# out - Comm object with status
|
25
|
+
|
26
|
+
# diff_snapshots: Returns diff between two different snapshots
|
27
|
+
# in - two ids of snapshots to perform diff on
|
28
|
+
# out - list of filenames and versions
|
29
|
+
|
30
|
+
# make_branch: make a new branch
|
31
|
+
# in - branch name
|
32
|
+
# out - hash id of new branch
|
33
|
+
|
34
|
+
# delete_branch: delete a branch
|
35
|
+
# in - branch name
|
36
|
+
# out - exit status code
|
37
|
+
|
38
|
+
# Also do a get_snapshot
|
39
|
+
|
40
|
+
module Copernicium
|
41
|
+
class Snapshot
|
42
|
+
def initialize(in_array)
|
43
|
+
@files = in_array
|
44
|
+
@id = ""
|
45
|
+
# id = hash of in array?
|
46
|
+
end
|
47
|
+
|
48
|
+
def set_id(in_id)
|
49
|
+
@id = in_id
|
50
|
+
end
|
51
|
+
|
52
|
+
# drop get prefix?
|
53
|
+
def get_id()
|
54
|
+
@id
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_files()
|
58
|
+
@files
|
59
|
+
end
|
60
|
+
# Initialize hash at startup
|
61
|
+
# Possible? Or problem with self object?
|
62
|
+
end
|
63
|
+
|
64
|
+
class Repos
|
65
|
+
def initialize()
|
66
|
+
# Create manifest
|
67
|
+
# It's a list of snapshots in chronological order
|
68
|
+
@manifest = {"default" => []}
|
69
|
+
@curr_branch = "default"
|
70
|
+
# what to do about branch IDs?
|
71
|
+
# Read in project path and make manifest file?
|
72
|
+
# Create current
|
73
|
+
end
|
74
|
+
|
75
|
+
def manifest()
|
76
|
+
@manifest
|
77
|
+
end
|
78
|
+
|
79
|
+
def make_snapshot(file_array)
|
80
|
+
# Return hash ID of snapshot
|
81
|
+
new_snap = Snapshot.new(file_array)
|
82
|
+
# Set ID, consider breaking up that line
|
83
|
+
new_snap.set_id(Digest::SHA256.hexdigest(Marshal.dump(new_snap)))
|
84
|
+
@manifest[@curr_branch].push(new_snap)
|
85
|
+
# Update manifest file?
|
86
|
+
return new_snap.get_id()
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_snapshot(target_id)
|
90
|
+
# Return snapshot (or just contents) given id
|
91
|
+
# Find snapshot
|
92
|
+
found_index = @manifest[@curr_branch].index{ |x| x.get_id() == target_id }
|
93
|
+
# Handle not found case
|
94
|
+
# Return it
|
95
|
+
if(found_index)
|
96
|
+
return @manifest[@curr_branch][found_index]
|
97
|
+
else
|
98
|
+
return found_index
|
99
|
+
end
|
100
|
+
#return ret_snap
|
101
|
+
end
|
102
|
+
|
103
|
+
# Not sure how I'm gonna do this one
|
104
|
+
def restore_snapshot(target_id)
|
105
|
+
# Return comm object with status
|
106
|
+
# Need a way to change files in workspace
|
107
|
+
return 1
|
108
|
+
end
|
109
|
+
|
110
|
+
#def history(branch_name)
|
111
|
+
def history()
|
112
|
+
# Return array of snapshot IDs
|
113
|
+
names_list = []
|
114
|
+
@manifest[@curr_branch].each {|x| names_list.push(x.get_id())}
|
115
|
+
return names_list
|
116
|
+
end
|
117
|
+
|
118
|
+
def delete_snapshot(target_id)
|
119
|
+
# Return comm object with status
|
120
|
+
# Find snapshot, delete from manifest/memory
|
121
|
+
@manifest[@curr_branch].delete_if { |x| x.get_id() == target_id }
|
122
|
+
# catch error
|
123
|
+
# update manifest file?
|
124
|
+
end
|
125
|
+
|
126
|
+
# Finds the files in snap1 that aren't in snap2, change this?
|
127
|
+
# Consider using diffy?
|
128
|
+
def diff_snapshots(id1, id2)
|
129
|
+
# Return list of filenames and versions
|
130
|
+
diff_files = []
|
131
|
+
# Put in error catching
|
132
|
+
files1 = get_snapshot(id1).get_files()
|
133
|
+
files2 = get_snapshot(id2).get_files()
|
134
|
+
# Find snapshot1 and snapshot2
|
135
|
+
files1.each do |x|
|
136
|
+
if(!files2.include?(x))
|
137
|
+
diff_files.push(x)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
return diff_files
|
141
|
+
# Use revlog diff on each set of files? Look at Diffy
|
142
|
+
end
|
143
|
+
|
144
|
+
def make_branch(branch_name)
|
145
|
+
# Return hash ID of new branch
|
146
|
+
# Not sure where to store branches
|
147
|
+
# What goes in to the hash?
|
148
|
+
@manifest[branch_name] = @manifest[@curr_branch]
|
149
|
+
@curr_branch = branch_name
|
150
|
+
return 1
|
151
|
+
end
|
152
|
+
|
153
|
+
def delete_branch(branch_name)
|
154
|
+
# Exit status code
|
155
|
+
@manifest.delete("branch_name")
|
156
|
+
end
|
157
|
+
|
158
|
+
def clear()
|
159
|
+
# Just a placeholder for now
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
|
data/lib/required.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# gem requirements
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'diffy'
|
5
|
+
require 'digest'
|
6
|
+
require 'pathname'
|
7
|
+
require 'singleton'
|
8
|
+
require 'socket' # Socket needed for communicating over the network
|
9
|
+
require 'io/console' # Needed to hide password at console
|
10
|
+
require 'net/ssh' # Needed to communicate with the remote
|
11
|
+
require 'net/scp' # Needed for file transfer between servers
|
12
|
+
|
13
|
+
# coperncicium files
|
14
|
+
|
15
|
+
require_relative "ui"
|
16
|
+
require_relative "repos"
|
17
|
+
require_relative "RevLog"
|
18
|
+
require_relative "banners"
|
19
|
+
require_relative "pushpull"
|
20
|
+
require_relative "workspace"
|
21
|
+
|
data/lib/ui.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
# user interface module - parse and execute commands
|
2
|
+
# integrates all modules, central module
|
3
|
+
|
4
|
+
|
5
|
+
VERSION = "0.0.1"
|
6
|
+
|
7
|
+
module Copernicium
|
8
|
+
class Driver
|
9
|
+
# Get some info from the user when they dont specify it
|
10
|
+
def get(info)
|
11
|
+
puts "Hi, #{info} not specified. Enter #{info}:"
|
12
|
+
gets.chomp # read a line from user, and return it
|
13
|
+
end
|
14
|
+
|
15
|
+
# Print and exit with a specific code
|
16
|
+
def pexit(msg, sig)
|
17
|
+
puts msg
|
18
|
+
exit sig
|
19
|
+
end
|
20
|
+
|
21
|
+
# Executes the required action for a given user command.
|
22
|
+
#
|
23
|
+
# Parameters:
|
24
|
+
# * args - an array containing the tokenized argv from the user
|
25
|
+
# For instance: "cn hello world" -> ['hello', 'world']
|
26
|
+
#
|
27
|
+
# Return value:
|
28
|
+
# A UIComm object containing details of the command to be
|
29
|
+
# executed by the respective backend module.
|
30
|
+
#
|
31
|
+
def run(args)
|
32
|
+
|
33
|
+
# if no arguments given show help information
|
34
|
+
pexit HELP_BANNER, 0 if args.empty?
|
35
|
+
|
36
|
+
# get first command
|
37
|
+
cmd = args.shift
|
38
|
+
|
39
|
+
# if no arguments given show help information
|
40
|
+
pexit HELP_BANNER, 0 if cmd == '-h'
|
41
|
+
|
42
|
+
# if -v flag givem show version
|
43
|
+
pexit VERSION, 0 if cmd == '-v'
|
44
|
+
|
45
|
+
# Handle standard commands
|
46
|
+
case cmd
|
47
|
+
when 'init'
|
48
|
+
init args
|
49
|
+
when 'status'
|
50
|
+
status args
|
51
|
+
when 'clone'
|
52
|
+
clone args
|
53
|
+
when 'commit'
|
54
|
+
commit args
|
55
|
+
when 'checkout'
|
56
|
+
checkout args
|
57
|
+
when 'merge'
|
58
|
+
merge args
|
59
|
+
when 'push'
|
60
|
+
push args
|
61
|
+
when 'pull'
|
62
|
+
pull args
|
63
|
+
else # handle an unrecognized argument, show help and exit
|
64
|
+
pexit "Unrecognized command #{cmd}\n" + HELP_BANNER, 1
|
65
|
+
end
|
66
|
+
end # run
|
67
|
+
|
68
|
+
def init(args)
|
69
|
+
UIComm.new(command: 'init', opts: args)
|
70
|
+
# todo - make call to repos to create repo
|
71
|
+
end
|
72
|
+
|
73
|
+
def status(args)
|
74
|
+
UIComm.new(command: 'status', opts: args)
|
75
|
+
# todo - make call to workspace, get and show status
|
76
|
+
end
|
77
|
+
|
78
|
+
def push(args)
|
79
|
+
UIComm.new(command: 'push', opts: args)
|
80
|
+
# todo - make call to pushpull, push remote
|
81
|
+
end
|
82
|
+
|
83
|
+
def pull(args)
|
84
|
+
UIComm.new(command: 'pull', opts: args)
|
85
|
+
# todo - make call to pushpull, pull remote
|
86
|
+
end
|
87
|
+
|
88
|
+
def checkout(args)
|
89
|
+
if args.empty?
|
90
|
+
rev = get 'branch or revision'
|
91
|
+
else
|
92
|
+
rev = args.shift
|
93
|
+
files = args
|
94
|
+
end
|
95
|
+
|
96
|
+
# todo - call repos checkout the given / branch
|
97
|
+
# todo - also, figure out if is branch or rev id
|
98
|
+
# this can be done by checking if it is a branch, and if not, then just
|
99
|
+
# assume it is a rev id. if it isnt, then something will break :/
|
100
|
+
|
101
|
+
UIComm.new(command: 'checkout', rev: rev, files: files)
|
102
|
+
end
|
103
|
+
|
104
|
+
def clone(args)
|
105
|
+
# todo - optionally check for folder to clone into, instead of cwd
|
106
|
+
if args.empty?
|
107
|
+
repo = get 'repo url to clone'
|
108
|
+
else
|
109
|
+
repo = args.first
|
110
|
+
end
|
111
|
+
|
112
|
+
# todo - actually clone remote locally
|
113
|
+
|
114
|
+
UIComm.new(command: 'clone', repo: repo)
|
115
|
+
end
|
116
|
+
|
117
|
+
def commit(args)
|
118
|
+
messflag = args.find_index('-m')
|
119
|
+
if messflag.nil?
|
120
|
+
message = get 'commit message'
|
121
|
+
else # mash everything after -m into a single string
|
122
|
+
message = args[messflag + 1..-1].join ' '
|
123
|
+
end
|
124
|
+
|
125
|
+
# todo parse file list, in case just commiting some files
|
126
|
+
# todo call revlog, commit files
|
127
|
+
# todo call repos, update commit
|
128
|
+
|
129
|
+
UIComm.new(command: 'commit', commit_message: message)
|
130
|
+
end
|
131
|
+
|
132
|
+
def merge(args)
|
133
|
+
if args.empty?
|
134
|
+
puts 'I need a commit to merge.'
|
135
|
+
rev = get 'single commit to merge'
|
136
|
+
else # use given
|
137
|
+
rev = args.first
|
138
|
+
end
|
139
|
+
|
140
|
+
# todo - call repos merge command
|
141
|
+
|
142
|
+
UIComm.new(command: 'merge', rev: rev)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Communication object that will pass commands to backend modules
|
147
|
+
# rev - A single revision indicator (commit #, branch name, HEAD, etc.)
|
148
|
+
# repo - URL/path to a remote repository
|
149
|
+
class UIComm
|
150
|
+
attr_reader :command, :arguments, :files, :rev, :commit_message, :repo
|
151
|
+
def initialize(command: nil, files: nil, rev: nil,
|
152
|
+
commit_message: nil, repo: nil, opts: nil)
|
153
|
+
@commit_message = commit_message
|
154
|
+
@command = command
|
155
|
+
@files = files
|
156
|
+
@opts = opts
|
157
|
+
@repo = repo
|
158
|
+
@rev = rev
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/lib/workspace.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
# This is the workspace module
|
2
|
+
# The functions are clean, commit, checkout and status
|
3
|
+
|
4
|
+
module Copernicium
|
5
|
+
class FileObj
|
6
|
+
attr_reader :path, :history_hash_ids
|
7
|
+
def initialize(path, ids)
|
8
|
+
@path = path
|
9
|
+
@history_hash_ids = ids
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(rhs)
|
13
|
+
if rhs.is_a? String
|
14
|
+
@path == rhs
|
15
|
+
else
|
16
|
+
@path == rhs.path
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Workspace
|
22
|
+
def writeFile(path, content)
|
23
|
+
f = open(path, 'w')
|
24
|
+
f.write(content)
|
25
|
+
f.close
|
26
|
+
end
|
27
|
+
|
28
|
+
def readFile(path)
|
29
|
+
f = open(path, 'r')
|
30
|
+
txt = f.read
|
31
|
+
f.close
|
32
|
+
txt
|
33
|
+
end
|
34
|
+
|
35
|
+
#private_class_method :writeFile
|
36
|
+
#private_class_method :readFile
|
37
|
+
|
38
|
+
def initialize(bname = 'master', rootdir = './workspace')
|
39
|
+
@files = []
|
40
|
+
@branch_name = bname
|
41
|
+
@revlog = Copernicium::RevLog.new('.')
|
42
|
+
@repos = Copernicium::Repos.new
|
43
|
+
@root = rootdir
|
44
|
+
if !File.directory?(@root)
|
45
|
+
Dir.mkdir(@root)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def indexOf(x)
|
50
|
+
index = -1
|
51
|
+
@files.each_with_index do |e,i|
|
52
|
+
if e.path == x
|
53
|
+
index = i
|
54
|
+
break
|
55
|
+
end
|
56
|
+
end
|
57
|
+
index
|
58
|
+
end
|
59
|
+
|
60
|
+
# if include all the elements in list_files
|
61
|
+
def include?(list_files)
|
62
|
+
list_files.each do |x|
|
63
|
+
if indexOf(x) == -1
|
64
|
+
return false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
# if list_files is nil, then rollback the list of files from the branch
|
71
|
+
# or rollback to the entire branch head pointed
|
72
|
+
def clean(comm)
|
73
|
+
list_files = comm.files
|
74
|
+
if list_files.nil? # reset first: delete them from disk and reset @files
|
75
|
+
@files.each{|x| File.delete(x.path)}
|
76
|
+
@files = []
|
77
|
+
# restore it with checkout() if we have had a branch name
|
78
|
+
if @branch_name != ''
|
79
|
+
# or it is the initial state, no commit and no checkout
|
80
|
+
return checkout(@branch_name)
|
81
|
+
else
|
82
|
+
return 0
|
83
|
+
end
|
84
|
+
|
85
|
+
else #list_files are not nil
|
86
|
+
# check that every file need to be reset should have been recognized by the workspace
|
87
|
+
#workspace_files_paths = @files.each{|x| x.path}
|
88
|
+
return -1 if (self.include? list_files) == false
|
89
|
+
|
90
|
+
# the actual action, delete all of them from the workspace first
|
91
|
+
list_files.each do |x|
|
92
|
+
File.delete(x)
|
93
|
+
idx = indexOf(x)
|
94
|
+
if !idx==-1
|
95
|
+
@files.delete_at(idx)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# if we have had a branch, first we get the latest snapshot of it
|
100
|
+
# and then checkout with the restored version of them
|
101
|
+
if @branch_name != ''
|
102
|
+
return checkout(list_files)
|
103
|
+
else
|
104
|
+
return 0
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# commit a list of files or the entire workspace to make a new snapshot
|
110
|
+
def commit(comm)
|
111
|
+
# for this commented version, we first get all files in the workspace and then add files from comm obj
|
112
|
+
# it's not used at this time
|
113
|
+
# Linfeng Song
|
114
|
+
#list_files = @files.each{|x| x.path}
|
115
|
+
#if comm.files != nil
|
116
|
+
# comm.files.each do |x|
|
117
|
+
# if indexOf(x) == -1
|
118
|
+
# list_files.push(x)
|
119
|
+
# end
|
120
|
+
# end
|
121
|
+
#end
|
122
|
+
list_files = Dir[ File.join(@root, '**', '*') ].reject { |p| File.directory? p }
|
123
|
+
if list_files != nil
|
124
|
+
list_files.each do |x|
|
125
|
+
if indexOf(x) == -1
|
126
|
+
content = readFile(x)
|
127
|
+
hash = @revlog.add_file(x, content)
|
128
|
+
fobj = FileObj.new(x, [hash,])
|
129
|
+
@files.push(fobj)
|
130
|
+
else
|
131
|
+
content = readFile(x)
|
132
|
+
hash = @revlog.add_file(x, content)
|
133
|
+
if @files[indexOf(x)].history_hash_ids[-1] != hash
|
134
|
+
@files[indexOf(x)].history_hash_ids << hash
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
return @repos.make_snapshot(@files)
|
140
|
+
end
|
141
|
+
|
142
|
+
def checkout(comm)
|
143
|
+
argu = comm.files
|
144
|
+
# if argu is an Array Object, we assume it is a list of files to be added to the workspace
|
145
|
+
if argu != nil
|
146
|
+
# we add the list of files to @files regardless whether it has been in it.
|
147
|
+
# that means there may be multiple versions of a file.
|
148
|
+
list_files = argu
|
149
|
+
snapshot_id = @repos.history()[-1]
|
150
|
+
returned_snapshot = @repos.get_snapshot(snapshot_id)
|
151
|
+
list_files_last_commit = returned_snapshot.get_files()
|
152
|
+
list_files_last_commit.each do |x|
|
153
|
+
if list_files.include? x.path
|
154
|
+
path = x.path
|
155
|
+
content = @revlog.get_file(x.history_hash_ids[-1])
|
156
|
+
idx = indexOf(x.path)
|
157
|
+
if idx == -1
|
158
|
+
@files.push(x)
|
159
|
+
else
|
160
|
+
@files[idx] = x
|
161
|
+
end
|
162
|
+
writeFile(path,content)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
# if argu is not an Array, we assume it is a String, representing the branch name
|
166
|
+
# we first get the last snapshot id of the branch, and then get the commit object
|
167
|
+
# and finally push all files of it to the workspace
|
168
|
+
else
|
169
|
+
argu = comm.rev #branch name
|
170
|
+
snapshot_id = @repos.history()[-1]
|
171
|
+
snapshot_obj = @repos.get_snapshot(snapshot_id).get_files()
|
172
|
+
snapshot_obj.each do |fff|
|
173
|
+
idx = indexOf(fff.path)
|
174
|
+
if idx == -1
|
175
|
+
@files.push(fff)
|
176
|
+
else
|
177
|
+
@files[idx] = fff
|
178
|
+
end
|
179
|
+
path = fff.path
|
180
|
+
content = @revlog.get_file(fff.history_hash_ids[-1])
|
181
|
+
writeFile(path,content)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def status(comm)
|
187
|
+
adds = []
|
188
|
+
deletes = []
|
189
|
+
edits = []
|
190
|
+
wsFiles = Dir[ File.join(@root, '**', '*') ].reject { |p| File.directory? p }
|
191
|
+
wsFiles.each do |f|
|
192
|
+
idx = indexOf(f)
|
193
|
+
if idx != -1
|
194
|
+
x1 = @revlog.get_file(@files[idx].history_hash_ids[-1])
|
195
|
+
x2 = readFile(f)
|
196
|
+
if x1 != x2
|
197
|
+
edits.push(f)
|
198
|
+
end
|
199
|
+
else
|
200
|
+
adds.push(f)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
@files.each do |f|
|
204
|
+
if ! (wsFiles.include? f.path)
|
205
|
+
deletes.push(f.path)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
return [adds, edits, deletes]
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
metadata
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: copernicium
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Team Copernicium
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: diffy
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 3.0.7
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.0'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.0.7
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: net-scp
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.2'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.2.1
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '1.2'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.2.1
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: net-ssh
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '3.0'
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 3.0.1
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.0'
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 3.0.1
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: minitest
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '5.8'
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 5.8.1
|
83
|
+
type: :development
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '5.8'
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 5.8.1
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: minitest-reporters
|
95
|
+
requirement: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '1.1'
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 1.1.4
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '1.1'
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 1.1.4
|
113
|
+
description: A simple distributed version control system written in Ruby.
|
114
|
+
email: jeremywrnr@gmail.com
|
115
|
+
executables:
|
116
|
+
- cn
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- bin/cn
|
121
|
+
- lib/RevLog.rb
|
122
|
+
- lib/banners.rb
|
123
|
+
- lib/pushpull.rb
|
124
|
+
- lib/repos.rb
|
125
|
+
- lib/required.rb
|
126
|
+
- lib/ui.rb
|
127
|
+
- lib/workspace.rb
|
128
|
+
homepage: http://github.com/jeremywrnr/copernicium
|
129
|
+
licenses:
|
130
|
+
- MIT
|
131
|
+
metadata: {}
|
132
|
+
post_install_message:
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
requirements: []
|
147
|
+
rubyforge_project:
|
148
|
+
rubygems_version: 2.4.8
|
149
|
+
signing_key:
|
150
|
+
specification_version: 4
|
151
|
+
summary: Simple DVCS in Ruby.
|
152
|
+
test_files: []
|