Andre Nathan <andre / digirati.com.br> writes: > I've been working with subversion for some time now and thought I'd have > a look at trying one of the distributed SCMs. I read about many of them, > and ended up with a choice between mercurial and git. > > Since both seem to be quite similar, and mercurial is written in python, > I was wondering if someone is working (or at least thinking of) writing > ruby wrappers for git, so we could get rid of the stinky shell scripts > from cogito. > > Any git users here at all? I wonder if there's a libgit that could be > wrapped in an extension... I gave it a try some months ago, but lost interest. require 'dl/import' require 'dl/struct' module Git extend DL::Importable dlload "/Users/chris/src/git/libgit.bundle" extern "const char *setup_git_directory()" extern "int git_config (void*)" # extern "int git_default_config (const char *, const char *)" extern "int get_sha1 (const char *, unsigned char *)" extern "void *parse_tree_indirect(const unsigned char *)" extern "int read_tree_recursive(void*, const char*, int, int, const char*, void*)" # extern "int read_tree_recursive(void*, int, int, int, int, void*)" extern "int sha1_object_info(const unsigned char*, void*, long*)" extern "void* read_sha1_file(const unsigned char*, void*, long*)" extern "int index_fd(unsigned char *, int, void *, int, const char *)" extern "int index_pipe(unsigned char *, int, int, const char *)" extern "void* parse_object(const unsigned char *)" Object = struct [ "long flags", "char sha1[20]", ] TreeStruct = struct [ "void *object", "void *buffer", "long size", ] def self.config git_config(symbol('git_default_config', 'ISS')) end def self.hash_object(thing, type="blob") buf = DL.malloc(20) if thing.kind_of? String # filename rstat = File.stat thing stat = DL::PtrData.new(rstat.object_id*2).to_a('P', 5).last file = File.open(thing) index_fd(buf, file.fileno, stat, 0, type) # will close elsif thing.kind_of? IO index_pipe(buf, thing.fileno, 0, type) else raise "Can't hash #{thing}" end SHA1.new buf.to_s(20) end class Commit def initialize(string) replace string end def replace(string) head, @body = string.split("\n\n", 2) @fields = {} head.each { |line| field, value = line.chomp.split(" ", 2) if field == "tree" || field == "parent" value = SHA1[value] end if field == "parent" (@fields[field] ||= []) << value else @fields[field] = value end } end def title @body[/.*/] end def [](field) @fields[field] end end class SHA1 attr_reader :data def self.[](str) if str.size == 20 new str elsif str.size == 40 p str new [str].pack("H*") else raise "Can't make hash object from #{str}" end end def initialize(sha = '\0'*20) @data = sha end def inspect type "#<%s:0x%x %s %s(%d)>" % [self.class.name, object_id, type, to_s, @size] end def to_s @data.unpack("C*").map { |byte| "%02x" % byte }.join end def type buf = DL.malloc(16) buf2 = DL.malloc(4) Git.sha1_object_info(@data, buf, buf2) @size = buf2.to_s(4).unpack("L").first @type = buf.to_s end def read buf = DL.malloc(16) buf2 = DL.malloc(4) buf3 = Git.read_sha1_file(@data, buf, buf2) @size = buf2.to_s(4).unpack("L").first @type = buf.to_s data = buf3.to_s(@size) case @type when "commit" Commit.new data else raise "Can't deal with #{@type} yet." end end def size type @size end def parse obj = Git.parse_object @data Object.new obj end end class Tree def initialize(sha1) @tree = TreeStruct.new Git.parse_tree_indirect(sha1.data) end READ_TREE = DL.callback("IPPIPII") { |sha1, base, baselen, pathname, mode, stage| s = Git::SHA1.new(sha1.to_s(20)) if base path = base.to_s(baselen) else path = "" end path << pathname.to_s id = path.slice!(0, 4).unpack("L").first block = ObjectSpace._id2ref(id & ~1) block.call [s, path, mode, stage] id & 1 } def each(recursive=false, &block) Git.read_tree_recursive(@tree, [block.object_id | (recursive ? 1 : 0)]. pack("L"), 4, 0, nil, READ_TREE) end end def self.sha1(name) buffer = DL.malloc(20) # p buffer.to_s(20) get_sha1(name, buffer) SHA1.new buffer.to_s(20) end def self.parse_tree(sha) Tree.new parse_tree_indirect(sha.data) end end Dir.chdir(File.expand_path("~/src/git")) { p Git.setup_git_directory p Git.config x = Git.sha1("HEAD") p x p x.type commit = x.read while commit['parent'] p commit.title commit = commit['parent'].first.read end Git::Tree.new(x).each { |sh1, file| p [sh1, Git::SHA1.new(sh1.parse.sha1.pack("c*"))] p file } } __END__ x=Time.now Dir.chdir("testrep") 1000.times { `git-hash-object Makefile` } p Time.now-x __END__ head = Git.sha1("HEAD") p head p head.type tree = Git.parse_tree(head) p tree read_tree = DL.callback("IPPIPII") { |sha1, base, baselen, pathname, mode, stage| s = Git::SHA1.new(sha1.to_s(20)) p s p s.type if base path = base.to_s(baselen) else path = "" end path << pathname.to_s p path p mode.to_s(8) p stage 1 } p Git.read_tree_recursive(tree, 0, 0, 0, 0, read_tree) } -- Christian Neukirchen <chneukirchen / gmail.com> http://chneukirchen.org