# Ruby/Git?

**URL:** <https://www.ruby-forum.com/t/ruby-git/70262>\
**Category:** Ruby\
**Created:** [September 27, 2006, 8:33pm UTC](https://www.ruby-forum.com/t/ruby-git/70262 "2006-09-27T20:33:47Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![reflex](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/f05b48/32.png) [@reflex](https://www.ruby-forum.com/u/reflex)\
**Post date:** [September 27, 2006, 8:33pm UTC](https://www.ruby-forum.com/t/ruby-git/70262/1 "2006-09-27T20:33:47Z")

</div>

Hi

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…

Regards,  
Andre

---

<div class="post-metadata">

**Author:** ![reflex](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/f05b48/32.png) [@reflex](https://www.ruby-forum.com/u/reflex)\
**Post date:** [September 27, 2006, 9:21pm UTC](https://www.ruby-forum.com/t/ruby-git/70262/2 "2006-09-27T21:21:53Z")

</div>

Andre N. wrote:

> Hi

Hi.

> Any git users here at all? I wonder if there’s a libgit that could be  
> wrapped in an extension…

Check out the bottom figure caption of Why’s shiny new page:

[http://code.whytheluckystiff.net/](http://code.whytheluckystiff.net/)

Later,

---

<div class="post-metadata">

**Author:** ![reflex](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/f05b48/32.png) [@reflex](https://www.ruby-forum.com/u/reflex)\
**Post date:** [September 27, 2006, 9:58pm UTC](https://www.ruby-forum.com/t/ruby-git/70262/3 "2006-09-27T21:58:00Z")

</div>

On Thu, 2006-09-28 at 04:20 +0900, Bil K. wrote:

> Check out the bottom figure caption of Why’s shiny new page:

Yeah, I saw that… well at least he didn’t have to wait the 2 centuries  
it takes to compile Monotone…

Andre

---

<div class="post-metadata">

**Author:** ![reflex](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/f05b48/32.png) [@reflex](https://www.ruby-forum.com/u/reflex)\
**Post date:** [September 28, 2006, 2:43pm UTC](https://www.ruby-forum.com/t/ruby-git/70262/4 "2006-09-28T14:43:49Z")

</div>

Andre N. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) writes:

> On Thu, 2006-09-28 at 04:20 +0900, Bil K. wrote:
> 
> > Check out the bottom figure caption of Why’s shiny new page:
> 
> Yeah, I saw that… well at least he didn’t have to wait the 2 centuries  
> it takes to compile Monotone…

That’s still nothing compared to darcs and GHC. 🙂

---

<div class="post-metadata">

**Author:** ![reflex](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/f05b48/32.png) [@reflex](https://www.ruby-forum.com/u/reflex)\
**Post date:** [September 27, 2006, 10:32pm UTC](https://www.ruby-forum.com/t/ruby-git/70262/5 "2006-09-27T22:32:06Z")

</div>

Andre N. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) writes:

> 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)  
}
