lib/git/object.rb
module Git
class GitTagNameDoesNotExist< StandardError
end
# represents a git object
class Object
class AbstractObject
attr_accessor :objectish, :type, :mode
attr_writer :size
def initialize(base, objectish)
@base = base
@objectish = objectish.to_s
@contents = nil
@trees = nil
@size = nil
@sha = nil
end
def sha
@sha ||= @base.lib.revparse(@objectish)
end
def size
@size ||= @base.lib.object_size(@objectish)
end
# Get the object's contents.
# If no block is given, the contents are cached in memory and returned as a string.
# If a block is given, it yields an IO object (via IO::popen) which could be used to
# read a large file in chunks.
#
# Use this for large files so that they are not held in memory.
def contents(&block)
if block_given?
@base.lib.object_contents(@objectish, &block)
else
@contents ||= @base.lib.object_contents(@objectish)
end
end
def contents_array
self.contents.split("\n")
end
def to_s
@objectish
end
def grep(string, path_limiter = nil, opts = {})
opts = {:object => sha, :path_limiter => path_limiter}.merge(opts)
@base.lib.grep(string, opts)
end
def diff(objectish)
Git::Diff.new(@base, @objectish, objectish)
end
def log(count = 30)
Git::Log.new(@base, count).object(@objectish)
end
# creates an archive of this object (tree)
def archive(file = nil, opts = {})
@base.lib.archive(@objectish, file, opts)
end
def tree?; false; end
def blob?; false; end
def commit?; false; end
def tag?; false; end
end
class Blob < AbstractObject
def initialize(base, sha, mode = nil)
super(base, sha)
@mode = mode
end
def blob?
true
end
end
class Tree < AbstractObject
def initialize(base, sha, mode = nil)
super(base, sha)
@mode = mode
@trees = nil
@blobs = nil
end
def children
blobs.merge(subtrees)
end
def blobs
@blobs ||= check_tree[:blobs]
end
alias_method :files, :blobs
def trees
@trees ||= check_tree[:trees]
end
alias_method :subtrees, :trees
alias_method :subdirectories, :trees
def full_tree
@base.lib.full_tree(@objectish)
end
def depth
@base.lib.tree_depth(@objectish)
end
def tree?
true
end
private
# actually run the git command
def check_tree
@trees = {}
@blobs = {}
data = @base.lib.ls_tree(@objectish)
data['tree'].each do |key, tree|
@trees[key] = Git::Object::Tree.new(@base, tree[:sha], tree[:mode])
end
data['blob'].each do |key, blob|
@blobs[key] = Git::Object::Blob.new(@base, blob[:sha], blob[:mode])
end
{
:trees => @trees,
:blobs => @blobs
}
end
end
class Commit < AbstractObject
def initialize(base, sha, init = nil)
super(base, sha)
@tree = nil
@parents = nil
@author = nil
@committer = nil
@message = nil
if init
set_commit(init)
end
end
def message
check_commit
@message
end
def name
@base.lib.namerev(sha)
end
def gtree
check_commit
Tree.new(@base, @tree)
end
def parent
parents.first
end
# array of all parent commits
def parents
check_commit
@parents
end
# git author
def author
check_commit
@author
end
def author_date
author.date
end
# git author
def committer
check_commit
@committer
end
def committer_date
committer.date
end
alias_method :date, :committer_date
def diff_parent
diff(parent)
end
def set_commit(data)
@sha ||= data['sha']
@committer = Git::Author.new(data['committer'])
@author = Git::Author.new(data['author'])
@tree = Git::Object::Tree.new(@base, data['tree'])
@parents = data['parent'].map{ |sha| Git::Object::Commit.new(@base, sha) }
@message = data['message'].chomp
end
def commit?
true
end
private
# see if this object has been initialized and do so if not
def check_commit
return if @tree
data = @base.lib.commit_data(@objectish)
set_commit(data)
end
end
class Tag < AbstractObject
attr_accessor :name
def initialize(base, sha, name)
super(base, sha)
@name = name
@annotated = nil
@loaded = false
end
def annotated?
@annotated ||= (@base.lib.object_type(self.name) == 'tag')
end
def message
check_tag()
return @message
end
def tag?
true
end
def tagger
check_tag()
return @tagger
end
private
def check_tag
return if @loaded
if !self.annotated?
@message = @tagger = nil
else
tdata = @base.lib.tag_data(@name)
@message = tdata['message'].chomp
@tagger = Git::Author.new(tdata['tagger'])
end
@loaded = true
end
end
# if we're calling this, we don't know what type it is yet
# so this is our little factory method
def self.new(base, objectish, type = nil, is_tag = false)
if is_tag
sha = base.lib.tag_sha(objectish)
if sha == ''
raise Git::GitTagNameDoesNotExist.new(objectish)
end
return Git::Object::Tag.new(base, sha, objectish)
end
type ||= base.lib.object_type(objectish)
klass =
case type
when /blob/ then Blob
when /commit/ then Commit
when /tree/ then Tree
end
klass.new(base, objectish)
end
end
end