copernicium 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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
+