copernicium 0.0.2 → 0.0.3

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/lib/repos.rb CHANGED
@@ -4,162 +4,234 @@
4
4
  # in - array of file objects. file object = array of all versions:
5
5
  # {id, content}
6
6
  # out - hash id of snapshot
7
-
7
+ # merge_snapshot: merge in a branch’s history into the current branch. if
8
+ # in - branch name
9
+ # out - [{path => content}, [conflicting paths]]
10
+ # get_snapshot: Return a specific snapshot
11
+ # in - snapshot id
12
+ # out - snapshot object
8
13
  # restore_snapshot: Set current file versions to specified snapshot
9
14
  # in - id of target snapshot
10
15
  # out - Comm object with status
11
-
12
16
  # history: Returns ids for all snapshots
13
17
  # in - branch name
14
18
  # out - Array of snapshot ids
15
-
16
19
  # delete_snapshot: delete specified a snapshot
17
20
  # in - target snapshot
18
21
  # out - Comm object with status
19
-
20
22
  # diff_snapshots: Returns diff between two different snapshots
21
23
  # in - two ids of snapshots to perform diff on
22
24
  # out - list of filenames and versions
23
-
24
25
  # make_branch: make a new branch
25
26
  # in - branch name
26
27
  # out - hash id of new branch
27
-
28
28
  # delete_branch: delete a branch
29
29
  # in - branch name
30
30
  # out - exit status code
31
31
 
32
- # Also do a get_snapshot
33
-
34
32
  module Copernicium
35
33
  class Snapshot
36
34
  attr_accessor :id, :files
37
35
  # id is computed after creation
38
36
  def initialize(files = [])
39
- @files = files
40
- @id = id
37
+ @@files = files
38
+ @@id = id
41
39
  end
42
40
  end
43
41
 
44
- class Repos
45
- attr_reader :snaps
46
- # read in file of snapshots (.cn/history)
42
+ module Repos
43
+ include RevLog # needs diffing and merging
47
44
  # check the current branch (.cn/branch)
48
- def initialize(root, branch = 'master')
49
- @root = root
50
- @copn = File.join(@root, '.cn')
51
- @bpath = File.join(@copn, 'branch')
52
- @spath = File.join(@copn, 'history')
45
+ # read in file of snapshots (.cn/history)
46
+ def Repos.setup(root = Dir.pwd, branch = 'master')
47
+ @@root = root
48
+ @@copn = File.join(@@root, '.cn')
49
+ @@repo = File.join(@@copn, 'repo')
50
+ @@bpath = File.join(@@repo, 'branch')
51
+ @@spath = File.join(@@repo, 'history')
52
+ Dir.mkdir(@@copn) unless Dir.exist?(@@copn)
53
+ Dir.mkdir(@@repo) unless Dir.exist?(@@repo)
54
+
55
+ # check if files exist, read them
56
+ if File.exist?(@@spath) && File.exist?(@@bpath)
57
+ @@branches = Marshal.load readFile(@@spath)
58
+ @@branch = readFile(@@bpath)
59
+ else # use defaults
60
+ @@branches = {branch => []}
61
+ @@branch = branch
62
+ end
53
63
 
54
64
  # check if files exist, read them
55
- if File.exist?(@spath) && File.exist?(@bpath)
56
- @snaps = Marshal.read readFile(@spath)
57
- @branch = readFile(@bpath)
65
+ if File.exist?(@@spath) && File.exist?(@@bpath)
66
+ @@branches = Marshal.load readFile(@@spath)
67
+ @@branch = readFile(@@bpath)
58
68
  else # use defaults
59
- @snaps = {branch => []}
60
- @branch = branch
69
+ @@branches = {branch => []}
70
+ @@branch = branch
61
71
  end
62
72
  end
63
73
 
64
- # returns the hash if of an object
65
- def hasher(obj)
66
- Digest::SHA256.hexdigest Marshal.dump(obj)
74
+ # helper methods for file IO
75
+ def writeFile(path, content)
76
+ f = open(path, 'w')
77
+ f.write(content)
78
+ f.close
67
79
  end
68
80
 
69
- # array of hashes constructor
70
- def hash_array
71
- Hash.new {[]}
81
+ # helper methods for file IO
82
+ def readFile(path)
83
+ f = open(path, 'r')
84
+ txt = f.read
85
+ f.close
86
+ txt
72
87
  end
73
88
 
74
- # Return string array of what branches we have
75
- def branches
76
- @snaps.keys
89
+ # check if any snapshots exist, if not exit
90
+ def Repos.has_snapshots?
91
+ ! Repos.history(@@branch).empty?
92
+ end
93
+
94
+ def Repos.hash_array
95
+ Hash.new {[]}
77
96
  end
78
97
 
79
- def update_snap
80
- writeFile(@spath, Marshal.dump(@snaps))
98
+ # returns the hash of an object
99
+ def Repos.hasher(obj)
100
+ Digest::SHA256.hexdigest Marshal.dump(obj)
81
101
  end
82
102
 
83
- def update_branch
84
- writeFile(@bpath, @branch)
103
+ # Return string array of what branches we have
104
+ def Repos.branches
105
+ @@branches.keys
85
106
  end
86
107
 
87
- # Create snapshot, and return hash ID of snapshot
88
- def make_snapshot(files = [])
108
+ # Create and return snapshot
109
+ def Repos.make_snapshot(files = [])
89
110
  snap = Snapshot.new(files)
90
111
  snap.id = hasher snap
91
- @snaps[@branch] << snap
112
+ @@branches[@@branch] << snap
92
113
 
93
114
  # Update snaps file
94
115
  update_snap
95
116
  snap.id
96
117
  end
97
118
 
98
- # Find snapshot, return snapshot (or just contents) given id
99
- def get_snapshot(target_id)
100
- found_index = @snaps[@branch].index { |x| x.id == target_id }
101
- if found_index
102
- @snaps[@branch][found_index]
103
- else
104
- Snapshot.new
119
+ # helper to write a snapshot, saving a new commit
120
+ def Repos.update_snap
121
+ writeFile @@spath, Marshal.dump(@@branches)
122
+ end
123
+
124
+ # todo - Check to make sure id is from a different branch
125
+ # Merge the target snapshot into HEAD snapshot of the current branch
126
+ def Repos.merge_snapshot(id)
127
+ # run diff to get conflicts
128
+ current = @@branches[@@branch].last
129
+ difference = diff_snapshots(current.id, id)
130
+ conflicts = difference[1]
131
+
132
+ if conflicts.empty? # make snapshot
133
+ make_snap current.files + diffset(get_snapshot(id).files, current.files)
105
134
  end
135
+
136
+ # returns [{path => content}, [conflicting paths]]
137
+ difference
106
138
  end
107
139
 
108
- # Return comm object with status
109
- # change files in workspace back to specified commit
110
- # get clear the current workspace
111
- # revert back to given commit
112
- def restore_snapshot(target_id)
113
- # todo
140
+ # Find snapshot and return snapshot from id
141
+ def Repos.get_snapshot(id)
142
+ @@branches.keys.each do |br|
143
+ @@branches[br].each do |sn|
144
+ return sn if sn.id == id
145
+ end
146
+ end
147
+
148
+ raise "Snapshot not found in this repo."
114
149
  end
115
150
 
116
151
  # Return array of snapshot IDs
117
- def history(branch_name = nil)
152
+ def Repos.history(branch = nil)
118
153
  snapids = []
119
- if branch_name.nil?
120
- @snaps[@branch].each {|x| snapids << x.id }
121
- else
122
- @snaps[branch_name].each{|x| snapids << x.id }
154
+ if branch.nil?
155
+ @@branches[@@branch].each { |x| snapids << x.id }
156
+ elsif
157
+ @@branches[branch].each { |x| snapids << x.id }
123
158
  end
124
159
  snapids
125
160
  end
126
161
 
127
162
  # Find snapshot, delete from snaps/memory
128
- def delete_snapshot(target_id)
129
- @snaps[@branch].delete_if { |x| x.id == target_id }
163
+ def Repos.delete_snapshot(id)
164
+ @@branches[@@branch].delete_if { |x| x.id == id }
130
165
  update_snap
131
166
  end
132
167
 
133
- # Return list of filenames and versions
134
- def diff_snapshots(id1, id2)
135
- diffed = []
136
-
137
- # Put in error catching
168
+ #diff_snapshots needs to catch both files in snap1 that aren’t and snap2 and
169
+ #find individual differences in between the files by calling RevLogs diffy.
170
+ # Return same thing as merge # note: id1 gets priority for history
171
+ def Repos.diff_snapshots(id1, id2)
172
+ new_files = []
173
+ conflicts = []
174
+ diffed = {}
138
175
  files1 = get_snapshot(id1).files
139
176
  files2 = get_snapshot(id2).files
177
+ new_files = diffset(files2, files1)
178
+
179
+ # adding each new file from the merged snapshot
180
+ new_files.each { |x| diffed[x.path] = get_file(x.last) }
181
+
182
+ # handle files that exist in both snapshots
183
+ files1.each do |file|
184
+ # find corresponding file object
185
+ f2_index = files2.index { |y| y == file }
186
+
187
+ # If found, check if same content
188
+ unless f2_index.nil?
189
+ id1 = file.last
190
+ id2 = files2[f2_index].last
191
+
192
+ # get file contents
193
+ content1 = get_file(id1)
194
+ content2 = get_file(id2)
195
+
196
+ # check if the file content for each path is the same
197
+ if content1 == content2
198
+ diffed[file.path] = content1
199
+ else # If not same, diff and add to conflicts
200
+ diffed[file.path] = diff_files(id1, id2)
201
+ conflicts << file.path
202
+ end
203
+ else # not found, use our version
204
+ diffed[file.path] = content1
205
+ end
206
+ end
140
207
 
141
- # Find difference between snapshot1 and snapshot2
142
- files1.each { |x| diffed << x unless !files2.include?(x) }
208
+ # returns [{path => content}, [conflicting paths]]
209
+ [diffed, conflicts]
210
+ end
143
211
 
144
- diffed
212
+ # Select all elements of array1 that are not in array2
213
+ def Repos.diffset(array1, array2)
214
+ array1.select { |x| !array2.any? { |y| x == y } }
145
215
  end
146
216
 
217
+ # BRANCHING
218
+ def current() @@branches end
219
+
147
220
  # Return hash ID of new branch
148
- def make_branch(branch)
149
- @snaps[branch] = @snaps[@branch]
150
- @branch = branch
151
- hasher 1
221
+ def Repos.make_branch(branch)
222
+ @@branches[branch] = @@branches[@@branch]
223
+ @@branch = branch
224
+ hasher @@branches[branch]
152
225
  end
153
226
 
154
- # Merge the target branch into current
155
- def merge_branch(branch)
156
- # todo
227
+ def Repos.update_branch(branch)
228
+ writeFile(@@bpath, branch)
229
+ @@branch = branch
157
230
  end
158
231
 
159
- # Exit status code
160
- def delete_branch(branch)
161
- @snaps.delete(branch)
232
+ def Repos.delete_branch(branch)
233
+ @@branches.delete(branch)
162
234
  end
163
- end # repo class
164
- end
235
+ end # Repos
236
+ end # Copernicium
165
237
 
data/lib/required.rb CHANGED
@@ -9,15 +9,13 @@ require 'socket' # Socket needed for communicating over the network
9
9
  require 'io/console' # Needed to hide password at console
10
10
  require 'net/ssh' # Needed to communicate with the remote
11
11
  require 'net/scp' # Needed for file transfer between servers
12
- #require 'marshal'
13
-
14
12
 
15
13
  # coperncicium files
16
14
 
17
- require_relative "ui"
18
- require_relative "repos"
19
- require_relative "RevLog"
20
15
  require_relative "banners"
16
+ require_relative "RevLog"
17
+ require_relative "repos"
21
18
  require_relative "pushpull"
22
19
  require_relative "workspace"
20
+ require_relative "ui"
23
21
 
data/lib/ui.rb CHANGED
@@ -1,21 +1,34 @@
1
1
  # user interface module - parse and execute commands
2
2
  # integrates all modules, central module
3
3
 
4
-
5
- VERSION = "0.0.2"
4
+ VERSION = "0.0.3"
6
5
 
7
6
  module Copernicium
8
- # Print and exit with a specific code
9
- def pexit(msg, sig)
10
- puts msg
11
- exit sig
7
+ # Communication object that will pass commands to backend modules
8
+ # also used in unit test to make sure command is being parsed ok
9
+ # rev - revision indicator (commit #, branch name, HEAD, etc.)
10
+ # repo - URL/path to a remote repository
11
+ class UIComm
12
+ attr_reader :command, :files, :rev, :cmt_msg, :repo, :opts
13
+ def initialize(command: nil, files: nil, rev: nil,
14
+ cmt_msg: nil, repo: nil, opts: nil)
15
+ @cmt_msg = cmt_msg
16
+ @command = command
17
+ @files = files
18
+ @opts = opts
19
+ @repo = repo
20
+ @rev = rev
21
+ end
12
22
  end
13
23
 
14
- class Driver
15
- # Get some info from the user when they dont specify it
16
- def get(info)
17
- puts "Hi, #{info} not specified. Enter #{info}:"
18
- gets.chomp # read a line from user, and return it
24
+ # main driver for the command line user interface
25
+ module Driver
26
+ include Repos # needed to get branch and history info
27
+ include Workspace # needed for most high level commands
28
+ def setup
29
+ Repos.setup
30
+ RevLog.setup
31
+ Workspace.setup
19
32
  end
20
33
 
21
34
  # Executes the required action for a given user command.
@@ -36,11 +49,14 @@ module Copernicium
36
49
  # get first command
37
50
  cmd = args.shift
38
51
 
52
+ # if -v flag givem show version
53
+ pexit VERSION, 0 if cmd == '-v'
54
+
39
55
  # if no arguments given show help information
40
56
  pexit COMMAND_BANNER, 0 if (cmd == '-h' || cmd == 'help')
41
57
 
42
- # if -v flag givem show version
43
- pexit VERSION, 0 if cmd == '-v'
58
+ # if not in a repo, warn them, tell how to create
59
+ puts REPO_WARNING.yel if (noroot? && cmd != 'init')
44
60
 
45
61
  # Handle standard commands
46
62
  case cmd
@@ -48,6 +64,8 @@ module Copernicium
48
64
  init args
49
65
  when 'status'
50
66
  status args
67
+ when 'history'
68
+ history args
51
69
  when 'branch'
52
70
  branch args
53
71
  when 'clean'
@@ -69,30 +87,94 @@ module Copernicium
69
87
  end
70
88
  end # run
71
89
 
90
+ # Print and exit with a specific code
91
+ def pexit(msg, sig)
92
+ puts msg
93
+ exit sig
94
+ end
95
+
96
+ # Get some info from the user when they dont specify it
97
+ def get(info)
98
+ puts "Note: #{info} not specified. Enter #{info} to continue."
99
+ gets.chomp # read a line from user, and return it
100
+ end
101
+
102
+ # create a new copernicium repository
72
103
  def init(args)
73
- if args.nil?
74
- Workspace.new
104
+ if args.empty?
105
+ Workspace.create_project
75
106
  else # init into a folder
76
- target = File.join Dir.pwd, args.join(' ')
77
- Dir.mkdir target if !File.exists? target
78
- Dir.chdir target
79
- Workspace.new
107
+ Workspace.create_project args.first
80
108
  end
81
- puts "Created Copernicium repo in " + Dir.pwd
109
+ puts "Created Copernicium repo in " + Dir.pwd.grn
82
110
  UIComm.new(command: 'init', opts: args)
83
111
  end
84
112
 
113
+ # show the current repos status
85
114
  def status(args)
86
115
  ui = UIComm.new(command: 'status', opts: args)
87
- st = Workspace.new.status(ui)
88
- puts "added:".grn + st[0].join(', ') unless st[0].empty?
89
- puts "edited:".yel + st[1].join(', ') unless st[1].empty?
90
- puts "removed:".red + st[2].join(', ') unless st[2].empty?
116
+ st = Workspace.status
117
+ st[0].each { |f| puts "Added:\t".grn + f }
118
+ st[1].each { |f| puts "Edited:\t".yel + f }
119
+ st[2].each { |f| puts "Removed:\t".red + f }
91
120
  ui
92
121
  end
93
122
 
123
+ # check whether a specific branch exists
124
+ def isbranch?(branch)
125
+ Repos.branches.include? branch
126
+ end
127
+
128
+ # create and switch to a new branch
129
+ def create_branch(branch)
130
+ new_branch_hash = Repos.make_branch branch
131
+ Repos.update_branch branch
132
+ puts "Created new branch '#{branch}' with head #{new_branch_hash}".grn
133
+ end
134
+
94
135
  def branch(args)
95
- # todo - switch branches, create branches
136
+ branch = args.first
137
+ if branch.nil? # show all branches
138
+ puts "Branches: ".grn + Repos.branches.join(' ')
139
+ elsif branch == '-c' # try to create a new branch
140
+ # If branch name not specified, get it from the user
141
+ branch = args[1]
142
+ branch = get "new branch name" if branch.nil?
143
+
144
+ # Create and switch to the new branch
145
+ create_branch branch
146
+ elsif branch == '-r' # rename the current branch
147
+ # If branch name not specified, get it from the user
148
+ newname = args[1]
149
+ newname = get "new name for current branch" if newname.nil?
150
+
151
+ oldname = Repos.branch
152
+
153
+ # Create and switch to a new branch with the given name
154
+ create_branch newname
155
+
156
+ # Delete the branch with the old name
157
+ Repos.delete_branch oldname
158
+ puts "Deleted branch '#{oldname}'".grn
159
+ puts "Renamed branch '#{oldname}' to '#{newname}'".grn
160
+ elsif branch == '-d' # Delete the specified branch
161
+ # If branch name not specified, get it from the user
162
+ branch = args[1]
163
+ branch = get "branch to delete" if branch.nil?
164
+
165
+ # Do not delete the current branch
166
+ if branch == Repos.branch
167
+ pexit "Cannot delete the current branch!".red, 1
168
+ end
169
+
170
+ # Delete the specified branch
171
+ Repos.delete_branch branch
172
+ puts "Deleted branch '#{branch}'".grn
173
+ elsif isbranch? branch # switch branch
174
+ Repos.update_branch branch
175
+ else # branch does not exist, create it, switch to it
176
+ Repos.create_branch branch
177
+ end
96
178
  end
97
179
 
98
180
  def push(args)
@@ -102,6 +184,7 @@ module Copernicium
102
184
 
103
185
  def pull(args)
104
186
  UIComm.new(command: 'pull', opts: args)
187
+ #def pull(remote, branch', remote_dir)
105
188
  # todo - make call to pushpull, pull remote
106
189
  end
107
190
 
@@ -113,24 +196,22 @@ module Copernicium
113
196
  files = args
114
197
  end
115
198
 
116
- # todo - also, figure out if is branch or rev id
117
- # this can be done by checking if it is a branch, and if not, then just
118
- # assume it is a rev id. if it isnt, then something will break :/
199
+ # if it is a branch, get the last head of it
200
+ rev = Repos.history(rev).last.id if isbranch? rev
119
201
 
120
202
  # call workspace checkout the given / branch
121
203
  ui = UIComm.new(command: 'checkout', rev: rev, files: files)
122
- Workspace.new.checkout(ui)
204
+ Workspace.checkout(ui)
123
205
  ui
124
206
  end
125
207
 
126
208
  def clean(args = [])
127
209
  ui = UIComm.new(command: 'clean', files: args)
128
- Workspace.new.clean(ui)
210
+ Workspace.clean(ui)
129
211
  ui
130
212
  end
131
213
 
132
214
  def clone(args)
133
- # todo - optionally check for folder to clone into, instead of cwd
134
215
  if args.empty?
135
216
  repo = get 'repo url to clone'
136
217
  else
@@ -146,9 +227,9 @@ module Copernicium
146
227
  messflag = args.find_index('-m')
147
228
  if messflag.nil?
148
229
  message = get 'commit message'
149
- elsif message == 0 # commit everything
230
+ elsif messflag == 0 # commit everything
150
231
  # mash everything after -m into a string
151
- message = args[messflag + 1..-1].join ' '
232
+ message = args[1..-1].join ' '
152
233
  else # commit only some files
153
234
  files = args[0..messflag - 1]
154
235
  end
@@ -157,38 +238,29 @@ module Copernicium
157
238
  message = get 'commit message' if message.nil?
158
239
 
159
240
  # perform the commit, with workspace
160
- ui = UIComm.new(command: 'commit', files: files, commit_message: message)
161
- Workspace.new.commit(ui)
241
+ ui = UIComm.new(command: 'commit', files: files, cmt_msg: message)
242
+ Workspace.commit(ui)
162
243
  ui
163
244
  end
164
245
 
246
+ def history(args)
247
+ puts Repos.history
248
+ end
249
+
165
250
  def merge(args)
166
251
  if args.empty?
167
252
  puts 'I need a commit or branch to merge.'
168
253
  rev = get 'single commit or branch to merge'
169
254
  else # use given
170
255
  rev = args.first
256
+ # get all branchs, see if arg is in it.
257
+ # if so, look up snapshot of <branch> head
258
+ # TODO - parse whether given arg is a branch name, else assume snap id
259
+ # todo - call repos merge command
260
+ # todo show conflicting files
261
+ UIComm.new(command: 'merge', rev: rev)
171
262
  end
172
-
173
- # todo - call repos merge command
174
-
175
- UIComm.new(command: 'merge', rev: rev)
176
263
  end
177
- end
178
-
179
- # Communication object that will pass commands to backend modules
180
- # rev - A single revision indicator (commit #, branch name, HEAD, etc.)
181
- # repo - URL/path to a remote repository
182
- class UIComm
183
- attr_reader :command, :arguments, :files, :rev, :commit_message, :repo
184
- def initialize(command: nil, files: nil, rev: nil,
185
- commit_message: nil, repo: nil, opts: nil)
186
- @commit_message = commit_message
187
- @command = command
188
- @files = files
189
- @opts = opts
190
- @repo = repo
191
- @rev = rev
192
- end
193
- end
264
+ end # Driver
194
265
  end
266
+