lib/grit/git-ruby/internal/loose.rb (105 lines of code) (raw):
#
# converted from the gitrb project
#
# authors:
# Matthias Lederhofer <matled@gmx.net>
# Simon 'corecode' Schubert <corecode@fs.ei.tum.de>
# Scott Chacon <schacon@gmail.com>
#
# provides native ruby access to git objects and pack files
#
require 'zlib'
require 'digest/sha1'
require 'grit/git-ruby/internal/raw_object'
module Grit
module GitRuby
module Internal
class LooseObjectError < StandardError
end
class LooseStorage
def initialize(directory)
@directory = directory
end
def [](sha1)
sha1 = sha1.unpack("H*")[0]
begin
return nil unless sha1[0...2] && sha1[2..39]
path = @directory + '/' + sha1[0...2] + '/' + sha1[2..39]
get_raw_object(open(path, 'rb') { |f| f.read })
rescue Errno::ENOENT
nil
end
end
def get_raw_object(buf)
if buf.bytesize < 2
raise LooseObjectError, "object file too small"
end
if legacy_loose_object?(buf)
content = Zlib::Inflate.inflate(buf)
header, content = content.split(/\0/, 2)
if !header || !content
raise LooseObjectError, "invalid object header"
end
type, size = header.split(/ /, 2)
if !%w(blob tree commit tag).include?(type) || size !~ /^\d+$/
raise LooseObjectError, "invalid object header"
end
type = type.to_sym
size = size.to_i
else
type, size, used = unpack_object_header_gently(buf)
content = Zlib::Inflate.inflate(buf[used..-1])
end
raise LooseObjectError, "size mismatch" if content.bytesize != size
return RawObject.new(type, content)
end
# currently, I'm using the legacy format because it's easier to do
# this function takes content and a type and writes out the loose object and returns a sha
def put_raw_object(content, type)
size = content.bytesize.to_s
LooseStorage.verify_header(type, size)
header = "#{type} #{size}\0"
store = header + content
sha1 = Digest::SHA1.hexdigest(store)
path = @directory+'/'+sha1[0...2]+'/'+sha1[2..40]
if !File.exists?(path)
content = Zlib::Deflate.deflate(store)
FileUtils.mkdir_p(@directory+'/'+sha1[0...2])
File.open(path, 'wb') do |f|
f.write content
end
end
return sha1
end
# simply figure out the sha
def self.calculate_sha(content, type)
size = content.bytesize.to_s
verify_header(type, size)
header = "#{type} #{size}\0"
store = header + content
Digest::SHA1.hexdigest(store)
end
def self.verify_header(type, size)
if !%w(blob tree commit tag).include?(type) || size !~ /^\d+$/
raise LooseObjectError, "invalid object header"
end
end
# private
def unpack_object_header_gently(buf)
used = 0
c = buf.getord(used)
used += 1
type = (c >> 4) & 7;
size = c & 15;
shift = 4;
while c & 0x80 != 0
if buf.bytesize <= used
raise LooseObjectError, "object file too short"
end
c = buf.getord(used)
used += 1
size += (c & 0x7f) << shift
shift += 7
end
type = OBJ_TYPES[type]
if ![:blob, :tree, :commit, :tag].include?(type)
raise LooseObjectError, "invalid loose object type"
end
return [type, size, used]
end
private :unpack_object_header_gently
def legacy_loose_object?(buf)
word = (buf.getord(0) << 8) + buf.getord(1)
buf.getord(0) == 0x78 && word % 31 == 0
end
private :legacy_loose_object?
end
end
end
end