lib/git/stream.rb



module Git
  
  require 'time'
  
  #====================
  # Stream Classes
  # 
  # These classes must support the following methods
  # 
  #  to_s - write the command to the git protocol stream
  #====================
  
  class StreamCommit
    
    
    attr_accessor :branch, :mark, :author, :committer, :message, :ancestor, :changes
    
    def initialize()
      @branch = nil
      @mark = StreamMark.new
      @author = nil
      @committer = nil
      @message = nil
      @ancestor = nil
      @changes = []
    end
    
    def modify_file(repos_path, data, mode = nil)
      sfm = StreamFileModify.new(repos_path, data)
      sfm.mode = mode unless mode == nil
      changes << sfm
    end
    
    def delete_file(repos_path)
      changes << StreamFileDelete.new(repos_path)
    end
    
    def rename_file(repos_path_from, repos_path_to)
      changes << StreamFileRename.new(repos_path_form, repos_path_to)
    end

    def copy_file(repos_path_from, repos_path_to)
      changes << StreamFileCopy.new(repos_path_form, repos_path_to)
    end
    
    def delete_all_files()
      changes << StreamFileDeleteAll.new
    end
    
    def to_s 
      out = "commit refs/heads/#{branch.to_s}\n"
      out << "mark #{mark}\n" 
      out << "author #{author.name} <#{author.email}> #{author.date.rfc2822}\n" unless author == nil
      out << "committer #{committer.name} <#{committer.email}> #{committer.date.rfc2822}\n" unless committer == nil
      if (message == nil)
        out << StreamData.emit_empty_data
      else
        out << StreamData.emit_inline_data(message)
      end
      out << "from #{ancestor}\n" unless ancestor == nil
      changes.each do |c|
        out << c.to_s
      end
      out << "\n"
    end
  end
  
  class StreamMark

    @@mark_counter = 1
    
    def initialize(id = (@@mark_counter += 1))
      @id = id
    end
    
    def to_s
      ":#{@id}"
    end
  end
  
  # This class is used in the filemodify change on the commit stream
  # At this time only the inline mode data stream is supported
  class StreamFileModify
    
    attr_accessor :mode, :repository_path, :inline_data
    
    def initialize(repository_path, data)
      @mode = 100644
      @repository_path = repository_path
      @inline_data = data
    end
    
    def to_s
      "M #{mode} inline #{repository_path}\n#{StreamData.emit_inline_data(inline_data)}"
    end
  end
  
  class StreamFileDelete
    
    attr_accessor :repository_path
    
    def initialize(repository_path)
      @repository_path = repository_path
    end
    
    def to_s
      "D #{repository_path}\n"
    end
  end
  
  class StreamFileCopy
    
    attr_accessor :repository_path_from, :repository_path_to
    
    def initialize(repository_path_from, repository_path_to)
      @repository_path_from = repository_path_from
      @repository_path_to = repository_path_to
    end
    
    def to_s
      "C #{repository_path_from} #{repository_path_to}\n"
    end
    
  end
  
  class StreamFileRename
    
    attr_accessor :repository_path_from, :repository_path_to
    
    def initialize(repository_path_from,repository_path_to)
      @repository_path_from = repository_path_from
      @repository_path_to = repository_path_to
    end

    def to_s
      "R #{repository_path_from} #{repository_path_to}\n"
    end

  end
  
  class StreamFileDeleteAll
    
    def to_s
      "deleteall\n"
    end
  end
  
  # Represents a stream of data bytes in the git stream
  class StreamData
    
    def self.emit_inline_data(data_string)
      "data #{data_string.length}\n#{data_string}\n" 
    end
    
    def self.emit_empty_data
      "data 0\n\n"
    end
  end
  
  #====================
  # Stream Implementation
  #====================

  # This is an initial implementation of git fast-import/export streams
  # It is not complete!
  class Stream
    
    attr_reader :commands
    
    def initialize
      @commands = []
    end
    
    def commit
      cmt = Git::StreamCommit.new
      yield cmt
      commands << cmt
    end
    
    def to_s
      s = ""
      commands.each do |cmd|
        s << cmd.to_s
      end
      return s
    end
  end
  
end