module Eternity class Commit attr_reader :id def initialize(id) @id = id end def short_id id ? id[0,7] : nil end def time Time.parse data['time'] if data['time'] end def author data['author'] end def message data['message'] end def parent_ids data['parents'] || [nil] end def parents parent_ids.map { |id| Commit.new id } end def with_index index = data['index'] ? Index.read_blob(data['index']) : Index.new yield index ensure index.destroy if index end def delta data['delta'] ? Blob.read(:delta, data['delta']) : {} end def base Commit.new data['base'] end def history_ids return [] if nil? Blob.read :history, data['history'] end def history history_ids.map { |id| Commit.new id } end def fast_forward?(commit) return false if nil? return true if commit.nil? history_ids.include? commit.id end def first? parent_ids.compact.empty? end def merge? parent_ids.count == 2 end def nil? id.nil? end def ==(commit) commit.class == self.class && commit.id == id end alias_method :eql?, :== def hash id.hash end def to_s "#{time} - #{short_id} - #{author}: #{message}" end def self.create(options) raise 'Author must be present' if options[:author].to_s.strip.empty? raise 'Message must be present' if options[:message].to_s.strip.empty? # TODO: Move to Repository and Patch history = if options[:parents].count == 2 current_history_ids = [options[:parents][0]] + Commit.new(options[:parents][0]).history_ids target_history_ids = [options[:parents][1]] + Commit.new(options[:parents][1]).history_ids current_history_ids - target_history_ids + target_history_ids else parent_id = options[:parents][0] parent_id ? [parent_id] + Commit.new(parent_id).history_ids : [] end data = { time: Time.now, author: options.fetch(:author), message: options.fetch(:message), parents: options.fetch(:parents), index: options.fetch(:index), delta: options.fetch(:delta), base: options[:parents].count == 2 ? options.fetch(:base) : options[:parents].first, history: Blob.write(:history, history) } new Blob.write(:commit, data) end def self.base_of(commit_1, commit_2) history_1 = [commit_1.id] history_2 = [commit_2.id] base_1 = commit_1 base_2 = commit_2 while (history_1 & history_2).empty? base_1 = base_1.base if base_1 base_2 = base_2.base if base_2 history_1 << base_1.id if base_1 history_2 << base_2.id if base_2 end Commit.new (history_1 & history_2).first end def self.exists?(id) Blob.read :commit, id true rescue false end def self.history_cache_key Eternity.keyspace[:cache][:history] end def self.clear_history_cache Eternity.connection.call('KEYS', history_cache_key['*']).each_slice(1000) do |keys| Eternity.connection.call 'DEL', *keys end end private def data @data ||= id ? Blob.read(:commit, id) : {} end end end