Владимир обнови решението на 15.11.2015 22:13 (преди около 9 години)
+require 'digest/sha1'
+require 'forwardable'
+
+module Resultable
+
+ def result(message, status, result = nil)
+ Result.new(message, status, result)
+ end
+end
+
+class Result
+
+ attr_reader :message, :result
+
+ def initialize(message, status, result)
+ @message, @status, @result = message, status, result
+ end
+
+ def error?
+ not success?
+ end
+
+ def success?
+ @status
+ end
+end
+
+class Commit
+
+ attr_accessor :adds, :removals, :message, :date
+
+ def initialize(message, changes, branch)
+ @message = message
+ @adds = changes[:add]
+ @removals = changes[:remove]
+ @date = Time.now
+ @branch = branch
+ end
+
+ def hash
+ @hash ||= Digest::SHA1.hexdigest("#{@date.to_s}#{@message}")
+ end
+
+ def objects
+ @branch.objects
+ end
+
+ def to_s
+ "Commit #{hash}\n" +
+ "Date: #{@date.strftime('%a %b %-d %H:%M %Y %z')}\n\n\t" +
+ @message
+ end
+end
+
+class Branch
+
+ include Resultable
+
+ def initialize(name)
+ @name = name
+ @commits = []
+ @current_commit = -1
+ @unstaged = { :add => [], :remove => [] }
+ end
+
+ def to_s
+ @name
+ end
+
+ def head
+ last = @commits[@current_commit]
+ result(last.message, true, last)
+ end
+
+ def add(name, object)
+ @unstaged[:add] << { :name => name, :object => object }
+ result("Added #{name} to stage.", true, object)
+ end
+
+ def remove(name)
+ search = get(name)
+ return result("Object #{name} is not committed.", false) if search.error?
+ @unstaged[:remove] << { :name => name, :object => search.result }
+ result("Object #{name} is not committed.", false)
+ end
+
+ def commit(message)
+ if @unstaged[:add].empty? && @unstaged[:remove].empty?
+ return result('Nothing to commit, working directory clean.', false)
+ end
+ commit = Commit.new(message, @unstaged, self)
+ @current_commit += 1
+ @commits.insert(@current_commit, commit)
+ @unstaged = { :add => [], :remove => [] }
+ changes_count = commit.adds.size + commit.removals.size
+ result("#{message}\n\t#{changes_count} objects changed", true, commit)
+ end
+
+ def checkout(hash)
+ prev_commit = @commits.find { |commit| commit.hash == hash }
+ return result("Commit #{hash} does not exist.", false) unless prev_commit
+ @current_commit = @commits.index(prev_commit)
+ result("Head is now at #{hash}", true, prev_commit)
+ end
+
+ def get(name)
+ added = nil
+ @current_commit.downto(0) do |index|
+ removed = @commits[index].removals.find { |object| object[:name] == name }
+ break if removed
+ added = @commits[index].adds.find { |object| object[:name] == name }
+ break if added
+ end
+ return result("Found object #{name}.", true, added[:object]) if added
+ result("Object #{name} is not committed.", false)
+ end
+
+ def objects
+ objects = []
+ @commits.each do |commit|
+ objects += commit.adds
+ objects -= commit.removals
+ break if commit.hash == @commits[@current_commit].hash
+ end
+ objects
+ end
+
+ def log
+ if @commits.empty?
+ return result("Branch #{@name} does not have any commits yet.", false)
+ end
+ message = []
+ @current_commit.downto(0) do |index|
+ message << @commits[index].to_s
+ end
+ result(message.join("\n\n"), true)
+ end
+end
+
+class BranchStore
+
+ extend Forwardable
+ include Resultable
+
+ def_delegator :current, :add
+ def_delegator :current, :commit
+ def_delegator :current, :remove
+ def_delegator :current, :get
+ def_delegator :current, :head
+ def_delegator :current, :log
+
+ def initialize
+ @current_branch = 'master'
+ @branches = { @current_branch => Branch.new('master') }
+ end
+
+ def list
+ message = @branches.values.map(&:to_s).map do |name|
+ (name == @current_branch ? ' *%s' : ' %s') % name
+ end.join('\n')
+ result(message, true)
+ end
+
+ def create(name)
+ return result("Branch #{name} already exists.", false) if @branches[name]
+ @branches[name] = Branch.new(name)
+ result("Created branch #{name}.", true)
+ end
+
+ def checkout(name)
+ unless @branches[name]
+ return result("Branch #{name} does not exist.", false)
+ end
+ @current_branch = name
+ result("Switched to branch #{@current_branch}.", true)
+ end
+
+ def remove(name)
+ if name == @current_branch
+ return result('Cannot remove current branch.', false)
+ end
+ unless @branches[name]
+ return result("Branch #{name} does not exist.", false)
+ end
+ @branches.delete name
+ result("Removed branch #{name}.", true)
+ end
+
+ def current
+ @branches[@current_branch]
+ end
+end
+
+class ObjectStore
+
+ extend Forwardable
+
+ def_delegator :@branch_store, :add
+ def_delegator :@branch_store, :commit
+ def_delegator :@branch_store, :get
+ def_delegator :@branch_store, :head
+ def_delegator :@branch_store, :log
+ # can not delegate this bad boy...
+ # def_delegator :@branch_store, :remove
+
+ def self.init(&block)
+ instance = self.new
+ instance.instance_eval(&block) if block_given?
+ instance
+ end
+
+ def initialize
+ @branch_store = BranchStore.new
+ end
+
+ def branch
+ @branch_store
+ end
+
+ def remove(name)
+ @branch_store.current.remove(name)
+ end
+
+ def checkout(hash)
+ @branch_store.current.checkout(hash)
+ end
+end