gabynaiman/eternity

View on GitHub
lib/eternity/commit.rb

Summary

Maintainability
B
4 hrs
Test Coverage
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