Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

308 lines (263 sloc) 7.215 kB
#
# converted from the gitrb project
#
# authors:
# Matthias Lederhofer <matled@gmx.net>
# Simon 'corecode' Schubert <corecode@fs.ei.tum.de>
#
# provides native ruby access to git objects and pack files
#
require 'digest/sha1'
module Git
class RawObject
attr_accessor :type, :content, :length
def initialize(type, content)
@type = type
@content = content
@length = content.length
end
def sha1
Digest::SHA1.digest("%s %d\0" % [@type, @content.length] + @content)
end
end
# class for author/committer/tagger lines
class UserInfo
attr_accessor :name, :email, :date, :offset
def initialize(str)
m = /^(.*?) <(.*)> (\d+) ([+-])0*(\d+?)$/.match(str)
if !m
raise RuntimeError, "invalid %s header in commit" % str
end
@name = m[1]
@email = m[2]
@date = Time.at(Integer(m[3]))
@offset = (m[4] == "-" ? -1 : 1)*Integer(m[5])
end
def to_s
"%s <%s> %s %+05d" % [@name, @email, @date.to_i, @offset]
end
end
# base class for all git objects (blob, tree, commit, tag)
class Object
attr_accessor :repository
def Object.from_raw(rawobject, repository = nil)
case rawobject.type
when :blob
return Blob.from_raw(rawobject, repository)
when :tree
return Tree.from_raw(rawobject, repository)
when :commit
return Commit.from_raw(rawobject, repository)
when :tag
return Tag.from_raw(rawobject, repository)
else
raise RuntimeError, "got invalid object-type"
end
end
def initialize
raise NotImplemented, "abstract class"
end
def type
raise NotImplemented, "abstract class"
end
def raw_content
raise NotImplemented, "abstract class"
end
def sha1
Digest::SHA1.hexdigest("%s %d\0" % \
[self.type, self.raw_content.length] + \
self.raw_content)
end
end
class Blob < Object
attr_accessor :content
def self.from_raw(rawobject, repository)
new(rawobject.content)
end
def initialize(content, repository=nil)
@content = content
@repository = repository
end
def type
:blob
end
def raw_content
@content
end
end
class DirectoryEntry
S_IFMT = 00170000
S_IFLNK = 0120000
S_IFREG = 0100000
S_IFDIR = 0040000
attr_accessor :mode, :name, :sha1
def initialize(buf)
m = /^(\d+) (.*)\0(.{20})$/m.match(buf)
if !m
raise RuntimeError, "invalid directory entry"
end
@mode = 0
m[1].each_byte do |i|
@mode = (@mode << 3) | (i-'0'[0])
end
@name = m[2]
@sha1 = m[3].unpack("H*")[0]
if ![S_IFLNK, S_IFDIR, S_IFREG].include?(@mode & S_IFMT)
raise RuntimeError, "unknown type for directory entry"
end
end
def type
case @mode & S_IFMT
when S_IFLNK
@type = :link
when S_IFDIR
@type = :directory
when S_IFREG
@type = :file
else
raise RuntimeError, "unknown type for directory entry"
end
end
def type=(type)
case @type
when :link
@mode = (@mode & ~S_IFMT) | S_IFLNK
when :directory
@mode = (@mode & ~S_IFMT) | S_IFDIR
when :file
@mode = (@mode & ~S_IFMT) | S_IFREG
else
raise RuntimeError, "invalid type"
end
end
def format_type
case type
when :link
'link'
when :directory
'tree'
when :file
'blob'
end
end
def format_mode
"%06o" % @mode
end
def raw
"%o %s\0%s" % [@mode, @name, [@sha1].pack("H*")]
end
end
class Tree < Object
attr_accessor :entry
def self.from_raw(rawobject, repository=nil)
entries = []
rawobject.content.scan(/\d+ .*?\0.{20}/m) do |raw|
entries << DirectoryEntry.new(raw)
end
new(entries, repository)
end
def initialize(entries=[], repository = nil)
@entry = entries
@repository = repository
end
def type
:tree
end
def raw_content
# TODO: sort correctly
#@entry.sort { |a,b| a.name <=> b.name }.
@entry.collect { |e| [[e.format_mode, e.format_type, e.sha1].join(' '), e.name].join("\t") }.join("\n")
end
def actual_raw
#@entry.collect { |e| e.raw.join(' '), e.name].join("\t") }.join("\n")
end
end
class Commit < Object
attr_accessor :author, :committer, :tree, :parent, :message
def self.from_raw(rawobject, repository=nil)
parent = []
tree = author = committer = nil
headers, message = rawobject.content.split(/\n\n/, 2)
headers = headers.split(/\n/).map { |header| header.split(/ /, 2) }
headers.each do |key, value|
case key
when "tree"
tree = value
when "parent"
parent.push(value)
when "author"
author = UserInfo.new(value)
when "committer"
committer = UserInfo.new(value)
else
warn "unknown header '%s' in commit %s" % \
[key, rawobject.sha1.unpack("H*")[0]]
end
end
if not tree && author && committer
raise RuntimeError, "incomplete raw commit object"
end
new(tree, parent, author, committer, message, repository)
end
def initialize(tree, parent, author, committer, message, repository=nil)
@tree = tree
@author = author
@parent = parent
@committer = committer
@message = message
@repository = repository
end
def type
:commit
end
def raw_content
"tree %s\n%sauthor %s\ncommitter %s\n\n" % [
@tree,
@parent.collect { |i| "parent %s\n" % i }.join,
@author, @committer] + @message
end
end
class Tag < Object
attr_accessor :object, :type, :tag, :tagger, :message
def self.from_raw(rawobject, repository=nil)
headers, message = rawobject.content.split(/\n\n/, 2)
headers = headers.split(/\n/).map { |header| header.split(/ /, 2) }
headers.each do |key, value|
case key
when "object"
object = value
when "type"
if !["blob", "tree", "commit", "tag"].include?(value)
raise RuntimeError, "invalid type in tag"
end
type = value.to_sym
when "tag"
tag = value
when "tagger"
tagger = UserInfo.new(value)
else
warn "unknown header '%s' in tag" % \
[key, rawobject.sha1.unpack("H*")[0]]
end
if not object && type && tag && tagger
raise RuntimeError, "incomplete raw tag object"
end
end
new(object, type, tag, tagger, repository)
end
def initialize(object, type, tag, tagger, repository=nil)
@object = object
@type = type
@tag = tag
@tagger = tagger
@repository = repository
end
def raw_content
"object %s\ntype %s\ntag %s\ntagger %s\n\n" % \
[@object, @type, @tag, @tagger] + @message
end
def type
:tag
end
end
end
Jump to Line
Something went wrong with that request. Please try again.