Изтрит обнови решението на 21.11.2015 05:38 (преди около 9 години)
+require 'digest/sha1'
+
+class ObjectStore
+ def self.init(&f)
+ repo = Repo.new()
+ repo.instance_eval(&f) if block_given?
+ repo
+ end
+end
+
+class Repo
+ def initialize
+ @added_objects = {}
+ @removed_objects = {}
+ @branches_objects_map = {}
+ @branches_map = {}
+ @current_branch = "master"
+
+ callback = lambda { |branch| @current_branch = branch }
+ @branch = Branch.new(@branches_map, @branches_objects_map, callback)
+ @branch.create("master")
+ @branch.checkout("master")
+ end
+
+ def add(name, object)
+ @added_objects[name] = object
+ Result.new("Added #{name} to stage.", :success, object)
+ end
+
+ def commit(message)
+ if @added_objects.empty? and @removed_objects.empty?
+ Result.new("Nothing to commit, working directory clean.", :error)
+ else
+ num_changed = @added_objects.length + @removed_objects.length
+
+ objects = merge_objects()
+
+ commit = Commit.new(message, Time.now, objects.values)
+ @branches_map[@current_branch].push(commit)
+ @added_objects = {}
+ @removed_objects = {}
+ Result.new("#{message}\n\t#{num_changed} " +
+ "objects changed", :success, commit)
+ end
+ end
+
+ def merge_objects
+ objects = @branches_objects_map[@current_branch]
+ objects.merge!(@added_objects)
+ objects.delete_if { |key, value| @removed_objects.has_key? key }
+ objects
+ end
+
+ def remove(name)
+ obj = @branches_objects_map[@current_branch][name]
+ if obj
+ @removed_objects[name] = obj
+ Result.new("Added #{name} for removal.", :success, obj)
+ else
+ Result.new("Object #{name} is not committed.", :error)
+ end
+ end
+
+ def checkout(commit_hash)
+ new_commits = []
+ current_commit = nil
+
+ @branches_map[@current_branch].each do |commit|
+ new_commits.push(commit)
+ if commit.hash == commit_hash
+ current_commit = commit
+ break
+ end
+ end
+
+ @branches_map[@current_branch] = new_commits
+
+ if current_commit
+ Result.new("HEAD is now at #{commit_hash}.", :success, current_commit)
+ else
+ Result.new("Commit #{commit_hash} does not exist.", :error)
+ end
+ end
+
+ def branch
+ @branch
+ end
+
+ def log
+ commits = @branches_map[@current_branch]
+ if commits.empty?
+ Result.new("Branch #{@current_branch} " +
+ "does not have any commits yet.", :error)
+ else
+ logs = []
+ commits.each do |commit|
+ logs.push(commit.log)
+ end
+
+ Result.new(logs.join("\n\n"), :success)
+ end
+ end
+
+ def head
+ commits = @branches_map[@current_branch]
+ if commits.empty?
+ Result.new("Branch #{current_branch} " +
+ "does not have any commits yet.", :error)
+ else
+ Result.new(commits.last.message, :success, commits.last)
+ end
+ end
+
+ def get(name)
+ commits = @branches_map[@current_branch]
+ obj = @branches_objects_map[@current_branch][name]
+
+ if commits.last && commits.last.objects.include?(obj)
+ Result.new("Found object #{name}.", :success, obj)
+ else
+ Result.new("Object #{name} is not committed.", :error)
+ end
+ end
+end
+
+class Branch
+ def initialize(branches_map, branches_objects_map, checkout_callback)
+ @branches_map = branches_map
+ @branches_objects_map = branches_objects_map
+ @checkout_callback = checkout_callback
+ end
+
+ def create(branch_name)
+ if @branches_map.has_key? branch_name
+ Result.new("Branch #{branch_name} already exists.", :error)
+ else
+ @branches_map[branch_name] =
+ @current_branch ? @branches_map[@current_branch].dup : []
+ @branches_objects_map[branch_name] =
+ @current_branch ? @branches_objects_map[@current_branch].clone : {}
+ Result.new("Created branch #{branch_name}.", :success)
+ end
+ end
+
+ def checkout(branch_name)
+ if not @branches_map[branch_name]
+ Result.new("Branch #{branch_name} does not exist.", :error)
+ else
+ @current_branch = branch_name
+ @checkout_callback.call(branch_name)
+ Result.new("Switched to branch #{branch_name}.", :success)
+ end
+ end
+
+ def remove(branch_name)
+ if not @branches_map[branch_name]
+ Result.new("Branch #{branch_name} does not exist.", :error)
+ elsif branch_name == @current_branch
+ Result.new("Cannot remove current branch.", :error)
+ else
+ @branches_map.delete(branch_name)
+ @branches_objects_map.delete(branch_name)
+ Result.new("Removed branch #{branch_name}.", :success)
+ end
+ end
+
+ def list
+ result = []
+ @branches_map.keys.sort!.each do |name|
+ if name == @current_branch
+ result.push("* #{name}")
+ else
+ result.push(" #{name}")
+ end
+ end
+
+ Result.new(result.join("\n"), :success)
+ end
+end
+
+class Commit
+ def initialize(message, date, objects)
+ @message = message
+ @date = date
+ @objects = objects
+ date_str = date.strftime("%a %b %e %H:%M:%S %Y %z")
+ @hash = Digest::SHA1.hexdigest "#{date_str}#{@message}"
+ end
+
+ def hash
+ @hash
+ end
+
+ def message
+ @message
+ end
+
+ def objects
+ @objects
+ end
+
+ def date
+ @date
+ end
+
+ def log
+ date_str = date
+ "Commit #{@hash}\nDate: #{date_str}\n\n\t#{@message}"
+ end
+end
+
+class Result
+ def initialize(message, state, result = nil)
+ @message = message
+ @state = state
+ @result = result
+
+ if result
+ define_singleton_method(:result) do
+ @result
+ end
+ end
+ end
+
+ def message
+ @message
+ end
+
+ def success?
+ @state == :success
+ end
+
+ def error?
+ @state == :error
+ end
+end