Андрея обнови решението на 23.11.2015 16:45 (преди около 9 години)
+require 'digest/sha1'
+
+class ObjectStore
+ ADD_TO_STAGE_MSG = 'Added %s to stage.'
+ COMMIT_MSG = "%s\n\t%s objects changed"
+ CHECKOUT_COMMIT_MSG = 'HEAD is now at %s.'
+ NOTHING_TO_COMMIT_MSG = 'Nothing to commit, working directory clean.'
+ COMMIT_DOES_NOT_EXIST_MSG = 'Commit %s does not exist.'
+ NO_COMMITS_IN_BRANCH_MSG = 'Branch %s does not have any commits yet.'
+
+ attr_accessor :current_branch
+ attr_reader :branches
+
+ def self.init(&block)
+ object_store = ObjectStore.new
+ object_store.instance_eval(&block) if block_given?
+ object_store
+ end
+
+ def add(name, object)
+ @staged.store(name, object)
+ Result.new(ADD_TO_STAGE_MSG % name, :success, object)
+ end
+
+ def commit(message)
+ if @staged.empty?
+ Operation.new(NOTHING_TO_COMMIT_MSG, :error)
+ else
+ date = Time.now
+ commit_hash = Digest::SHA1.hexdigest("#{date}#{message}")
+ commit = Commit.new(date, message, @staged.dup, commit_hash)
+ @current_branch.commits.store(commit.hash, commit)
+ @staged.clear
+ operation_message = COMMIT_MSG % [message, commit.objects.size]
+ Result.new(operation_message, :success, commit)
+ end
+ end
+
+ def checkout(commit_hash)
+ if @current_branch.commits.key?(commit_hash)
+ commit = @current_branch.commits[commit_hash]
+ @current_branch.commits.delete_if { |_, v| v.date > commit.date }
+ message = CHECKOUT_COMMIT_MSG % commit_hash
+ Result.new(message, :success, commit)
+ else
+ Operation.new(COMMIT_DOES_NOT_EXIST_MSG % commit_hash, :error)
+ end
+ end
+
+ def branch
+ @current_branch
+ end
+
+ def log
+ if @current_branch.commits.empty?
+ message = NO_COMMITS_IN_BRANCH_MSG % @current_branch.name
+ Operation.new(message, :error)
+ else
+ message = construct_log_message
+ Operation.new(message, :success)
+ end
+ end
+
+ def head
+ if @current_branch.commits.empty?
+ message = NO_COMMITS_IN_BRANCH_MSG % @current_branch.name
+ Operation.new(message, :error)
+ else
+ last_commit = @current_branch.commits.values.last
+ Result.new("#{last_commit.message}", :success, last_commit)
+ end
+ end
+
+ private
+
+ def initialize
+ @current_branch = Branch.new(self, 'master', {})
+ @branches = { @current_branch.name => @current_branch }
+ @staged = {}
+ end
+
+ def construct_log_message
+ @current_branch.commits.values.sort { |f, s| f.date <=> s.date }
+ .collect do |commit|
+ date = commit.date.strftime('%a %b %-d %H:%M %Y %z')
+ "Commit #{commit.hash}\n" \
+ "Date: #{date}\n\n" \
+ "\t#{commit.message}"
+ end
+ .reverse
+ .join("\n\n")
+ end
+end
+
+class Branch
+ CREATED_BRANCH_MSG = 'Created branch %s.'
+ BRANCH_EXISTS_MSG = 'Branch %s already exists.'
+ BRANCH_REMOVED_MSG = 'Removed branch %s.'
+ BRANCH_SWITCH_MSG = 'Switched to branch %s.'
+ BRANCH_DOES_NOT_EXIST_MSG = 'Branch %s does not exist.'
+ CANNOT_REMOVE_BRANCH_MSG = 'Cannot remove current branch.'
+ attr_accessor :name, :commits
+
+ def create(branch_name)
+ if @object_store.branches.key?(branch_name)
+ Operation.new(BRANCH_EXISTS_MSG % branch_name, :error)
+ else
+ branch = Branch.new(@object_store, branch_name, @commits.dup)
+ @object_store.branches.store(branch.name, branch)
+ Operation.new(CREATED_BRANCH_MSG % branch_name, :success)
+ end
+ end
+
+ def checkout(branch_name)
+ if @object_store.branches.key?(branch_name)
+ @object_store.current_branch = @object_store.branches[branch_name]
+ Operation.new(BRANCH_SWITCH_MSG % branch_name, :success)
+ else
+ Operation.new(BRANCH_DOES_NOT_EXIST_MSG % branch_name, :error)
+ end
+ end
+
+ def remove(branch_name)
+ if @object_store.branches.key?(branch_name)
+ if (@name == branch_name)
+ Operation.new(CANNOT_REMOVE_BRANCH_MSG, :error)
+ else
+ @object_store.branches.delete(branch_name)
+ Operation.new(BRANCH_REMOVED_MSG % branch_name, :success)
+ end
+ else
+ Operation.new(BRANCH_DOES_NOT_EXIST_MSG % branch_name, :error)
+ end
+ end
+
+ def list
+ branch_names = @object_store.branches.keys.collect do |name|
+ prefix = name == @object_store.current_branch.name ? '* ' : ' '
+ prefix + name
+ end
+ Operation.new(branch_names.join("\n"), :success)
+ end
+
+ def initialize(object_store, name, commits)
+ @object_store = object_store
+ @name = name
+ @commits = commits
+ end
+end
+
+class Operation
+ attr_reader :message
+
+ def initialize(message, status)
+ @message = message
+ @status = status
+ end
+
+ def success?
+ @status == :success
+ end
+
+ def error?
+ @status == :error
+ end
+end
+
+class Result < Operation
+ attr_reader :result
+
+ def initialize(message, status, result)
+ super(message, status)
+ @result = result
+ end
+end
+
+class Commit
+ attr_reader :date, :message, :hash
+
+ def initialize(date, message, objects, hash)
+ @date = date
+ @message = message
+ @objects = objects
+ @hash = hash
+ end
+
+ def objects
+ @objects.values
+ end
+end