<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -10,8 +10,13 @@ spec = Gem::Specification.new do |s|
     s.email     =   &quot;schacon@gmail.com&quot;
     s.summary   =   &quot;A pure ruby implementation of Git&quot;
     s.files     =   FileList['lib/**/*', 'tests/**/*', 'doc/**/*'].to_a
+
+    s.bindir = 'bin'
+    s.executables &lt;&lt; &quot;gitr&quot;
+    s.homepage = &quot;http://github/schacon/git-ruby&quot;
+
     s.require_path  =   &quot;lib&quot;
-    s.autorequire   =   &quot;git&quot;
+    s.autorequire   =   &quot;git-ruby&quot;
     s.test_files = Dir.glob('tests/*.rb')
     s.has_rdoc  =   true
     s.extra_rdoc_files  =   [&quot;README&quot;]</diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -1,8 +1,3 @@
-== first pass ==
-
-repo.add(@name)
-repo.commit(message)
-
 == second pass ==
 
 $repo.log.first.gtree.children.map</diff>
      <filename>TODO</filename>
    </modified>
    <modified>
      <diff>@@ -23,6 +23,7 @@ require 'git-ruby/author'
 
 require 'git-ruby/raw/repository'
 require 'fileutils'
+require 'logger'
 
 
 # Git-Ruby Library
@@ -68,5 +69,19 @@ module GitRuby
   def self.init(working_dir = '.', options = {})
     Base.init(working_dir, options)
   end
+  
+  # clones a remote repository
+  #
+  # options
+  #   :bare =&gt; true (does a bare clone)
+  #   :repository =&gt; '/path/to/alt_git_dir'
+  #   :index =&gt; '/path/to/alt_index_file'
+  #
+  # example
+  #  Git.clone('git://repo.or.cz/rubygit.git', 'clone.git', :bare =&gt; true)
+  #
+  def self.clone(repository, name, options = {})
+    Base.clone(repository, name, options)
+  end
     
 end</diff>
      <filename>lib/git-ruby.rb</filename>
    </modified>
    <modified>
      <diff>@@ -35,7 +35,7 @@ module GitRuby
     def self.init(working_dir, opts = {})
       default = {:working_directory =&gt; File.expand_path(working_dir),
                  :repository =&gt; File.join(working_dir, '.git')}
-      git_options = default.merge(opts)
+      git_options = default.merge(opts)      
       
       if git_options[:working_directory]
         # if !working_dir, make it
@@ -49,15 +49,34 @@ module GitRuby
       GitRuby::Repository.init(git_options[:repository])
       
       self.new(git_options)
-    end      
-        
+    end    
+    
+    # clones a git repository locally
+    #
+    #  repository - http://repo.or.cz/w/sinatra.git
+    #  name - sinatra
+    #
+    # options:
+    #   :repository
+    #
+    #    :bare
+    #   or 
+    #    :working_directory
+    #    :index_file
+    #
+    def self.clone(repository, name, opts = {})
+      # run git-clone 
+      self.new(GitRuby::Lib.new(nil, opts[:logger]).clone(repository, name, opts))
+    end
+    
+            
     def initialize(options = {})
       if working_dir = options[:working_directory]
         options[:repository] = File.join(working_dir, '.git') if !options[:repository]
         options[:index] = File.join(working_dir, '.git', 'index') if !options[:index]
       end
-      if options[:log]
-        @logger = options[:log]
+      if options[:logger]
+        @logger = options[:logger]
         @logger.info(&quot;Starting Git&quot;)
       end
       
@@ -228,8 +247,17 @@ module GitRuby
       self.index.ls_files
     end
     
-    def add(file)
-      self.lib.add(file)
+    def add(file = '.')
+      if file == '.'
+        # add all files
+        Dir.glob(&quot;**/*&quot;).each do |file|
+          if File.file?(file)
+            self.lib.add(file)
+          end
+        end
+      else
+        self.lib.add(file)
+      end
     end
     
     def commit(message)</diff>
      <filename>lib/git-ruby/base.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,9 +1,9 @@
 require 'tempfile'
+require 'net/https'
 
 module GitRuby
   
-  class GitRubyExecuteError &lt; StandardError 
-  end
+  class GitRubyInvalidTransport &lt; StandardError; end
   
   class Lib
       
@@ -30,6 +30,147 @@ module GitRuby
       end
     end    
     
+    
+    # tries to clone the given repo
+    #
+    # returns {:repository} (if bare)
+    #         {:working_directory} otherwise
+    #
+    # accepts options:
+    #  :remote - name of remote (rather than 'origin')
+    #  :bare   - no working directory
+    # 
+    # TODO - make this work with SSH password or auth_key
+    #
+    def clone(repository, name, opts = {})
+      @path = opts[:path] || '.'
+      opts[:path] ? clone_dir = File.join(@path, name) : clone_dir = name
+
+      working_dir = clone_dir
+
+      # initialize repository
+      if(!opts[:bare])
+        clone_dir += '/.git'
+      end
+            
+      GitRuby::Repository.init(clone_dir, opts[:bare])
+      @git_dir = File.expand_path(clone_dir)
+      
+      remote_name = opts[:remote] || 'origin'
+      
+      # look at #{repository} for http://, user@, git://
+      if repository =~ /^http:\/\//
+        # http fetch
+        clone_http(repository, false, remote_name, clone_dir)
+      elsif repository =~ /^https:\/\//
+        # https fetch
+        clone_http(repository, true, remote_name, clone_dir)
+      elsif repository =~ /^git:\/\//
+        # git fetch
+        raise GitRubyInvalidTransport('transport git:// not yet supported') 
+      else
+        raise GitRubyInvalidTransport('unknown transport') 
+      end
+            
+      if opts[:bare]
+        return {:repository =&gt; clone_dir}
+      else
+        # !! TODO : checkout to working_dir !!
+        return {:working_directory =&gt; working_dir}
+      end
+    end
+    
+    # implements cloning a repository over http/s
+    # meant to be called 
+    def clone_http(repo_url, use_ssl, remote_name, clone_dir)
+      # refs : 909e4d4f706c11cafbe35fd9729dc6cce24d6d6f        refs/heads/master
+      # packs: P pack-8607f42392be437e8f46408898de44948ccd357f.pack
+      
+      Dir.chdir(clone_dir) do
+        # fetch (url)/info/refs
+        log('fetching server refs')
+        refs = Net::HTTP.get(URI.parse(&quot;#{repo_url}/info/refs&quot;))        
+        fetch_refs = map_refs(refs)
+
+        # fetch (url)/HEAD, write as FETCH_HEAD
+        log('fetching remote HEAD')
+        remote_head = Net::HTTP.get(URI.parse(&quot;#{repo_url}/HEAD&quot;))
+        if !(remote_head =~ /^ref: refs\//)
+          fetch_refs[remote_head] = false
+        end
+        
+        fetch_refs.each do |sha, ref|
+          log(&quot;fetching REF : #{ref} #{sha}&quot;)
+          if http_fetch(repo_url, sha, 'commit')
+            puts 'UPDATE REF'
+            update_ref(&quot;refs/remotes/#{remote_name}/#{ref}&quot;, sha) if ref
+          end
+        end
+                
+      end
+    end
+    
+    def map_refs(refs)
+      # process the refs file
+      # get a list of all the refs/heads/
+      fetch_refs = {}
+      refs.split(&quot;\n&quot;).each do |ref|
+        if ref =~ /refs\/heads/
+          sha, head = ref.split(&quot;\t&quot;)
+          head = head.sub('refs/heads/', '').strip
+          fetch_refs[sha] = head
+        end
+      end
+      fetch_refs
+    end
+
+    def http_fetch(url, sha, type)
+      # fetch from server objects/sh/a1value
+      dir = sha[0...2]
+      obj = sha[2..40]
+      
+      path = File.join('objects', dir)
+            
+      if !get_raw_repo.object_exists?(sha)
+        res = Net::HTTP.get_response(URI.parse(&quot;#{url}/objects/#{dir}/#{obj}&quot;))
+        if res.kind_of?(Net::HTTPSuccess)
+          Dir.mkdir(path) if !File.directory?(path)
+          write_file(File.join('objects', dir, obj), res.body)
+          log(&quot;#{type} : #{sha} fetched&quot;)
+        else
+          # file may be packed - get the packfiles if we haven't already and lets try those
+          # fetch (url)/objects/info/packs
+            # fetch packs we don't have, look for it there
+          puts &quot;FAIL #{sha}&quot; + res.to_s
+          return false
+        end
+      end
+      
+      response = true
+      
+      case type
+      when 'commit':
+        # if it's a commit, walk the tree, then get it's parents
+        commit = commit_data(sha)
+        log('walking ' + commit['tree'])
+        http_fetch(url, commit['tree'], 'tree')
+        commit['parent'].each do |parent|
+          log('walking ' + parent)
+          response &amp;&amp;= http_fetch(url, parent, 'commit')
+        end
+      when 'tree':
+        data = ls_tree(sha)
+        data['blob'].each do |key, blob|
+          response &amp;&amp;= http_fetch(url, blob[:sha], 'blob')          
+        end
+        data['tree'].each do |key, tree|
+          response &amp;&amp;= http_fetch(url, tree[:sha], 'tree')          
+        end
+      end
+      
+      response
+    end
+    
     ## READ COMMANDS ##
         
     def process_commit_data(data, sha = nil)
@@ -101,7 +242,8 @@ module GitRuby
       head = File.join(@git_dir, 'refs', 'tags', string)
       return File.read(head).chomp if File.file?(head)
       
-      ## !! more !!
+      ## !! check packed-refs file, too !! 
+      ## !! more - partials and such !!
       
       return string
     end
@@ -143,7 +285,7 @@ module GitRuby
       contents = []
       contents &lt;&lt; ['tree', tree].join(' ')
       parents.each do |p|
-        contents &lt;&lt; ['parent', p].join(' ')
+        contents &lt;&lt; ['parent', p].join(' ') if p        
       end
 
       name = config_get('user.name')
@@ -171,8 +313,9 @@ module GitRuby
     
     def update_ref(ref, sha)
       ref_file = File.join(@git_dir, ref)
-      return false if !File.exists?(ref_file)
-      
+      if(!File.exists?(ref))
+        FileUtils.mkdir_p(File.basedir(ref_file)) rescue nil
+      end
       File.open(ref_file, 'w') do |f|
         f.write sha
       end
@@ -266,6 +409,16 @@ module GitRuby
       Dir.chdir(tag_dir) { tags = Dir.glob('*') }
       return tags
     end
+    
+    def log(message)
+      @logger.info(message) if @logger
+    end
+    
+    def write_file(name, contents)
+      File.open(name, 'w') do |f|
+        f.write contents
+      end
+    end
         
   end
 end</diff>
      <filename>lib/git-ruby/lib.rb</filename>
    </modified>
    <modified>
      <diff>@@ -26,9 +26,8 @@ module GitRuby
 
         def [](sha1)
           sha1 = sha1.unpack(&quot;H*&quot;)[0]
-
-          path = @directory+'/'+sha1[0...2]+'/'+sha1[2..40]
           begin
+            path = @directory+'/'+sha1[0...2]+'/'+sha1[2..40]
             get_raw_object(File.read(path))
           rescue Errno::ENOENT
             nil</diff>
      <filename>lib/git-ruby/raw/internal/loose.rb</filename>
    </modified>
    <modified>
      <diff>@@ -91,7 +91,8 @@ module GitRuby
 
         def find_object(sha1)
           slot = sha1[0]
-          first, last = @offsets[slot,2]
+          return nil if !slot
+          first, last = @offsets[slot,2] 
           while first &lt; last
             mid = (first + last) / 2
             midsha1 = @idx[SHA1Start + mid * EntrySize,SHA1Size]</diff>
      <filename>lib/git-ruby/raw/internal/pack.rb</filename>
    </modified>
    <modified>
      <diff>@@ -76,6 +76,27 @@ module GitRuby
         @loose.put_raw_object(content, type)
       end
       
+      def object_exists?(sha1)
+        sha_hex = [sha1].pack(&quot;H*&quot;)
+        return true if in_packs?(sha_hex)
+        return true if in_loose?(sha_hex)
+        return true if in_packs?(sha_hex) #maybe the object got packed in the meantime
+        false
+      end
+      
+      def in_packs?(sha_hex)
+        # try packs
+        @packs.each do |pack|
+          return true if pack[sha_hex]
+        end
+        false
+      end
+      
+      def in_loose?(sha_hex)
+        return true if @loose[sha_hex]
+        false
+      end
+      
       def get_raw_object_by_sha1(sha1)
         sha1 = [sha1].pack(&quot;H*&quot;)
 
@@ -112,11 +133,13 @@ module GitRuby
             pack.close
           end
           @packs = []
-          Dir.open(git_path(&quot;objects/pack/&quot;)) do |dir|
-            dir.each do |entry|
-              if entry =~ /\.pack$/i
-                @packs &lt;&lt; GitRuby::Raw::Internal::PackStorage.new(git_path(&quot;objects/pack/&quot; \
-                                                                  + entry))
+          if File.exists?(git_path(&quot;objects/pack&quot;))
+            Dir.open(git_path(&quot;objects/pack/&quot;)) do |dir|
+              dir.each do |entry|
+                if entry =~ /\.pack$/i
+                  @packs &lt;&lt; GitRuby::Raw::Internal::PackStorage.new(git_path(&quot;objects/pack/&quot; \
+                                                                    + entry))
+                end
               end
             end
           end</diff>
      <filename>lib/git-ruby/raw/repository.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,9 @@ module GitRuby
   class Repository &lt; Path
     
     # initialize a git repository
-    def self.init(dir)
+    def self.init(dir, bare = false)
+      puts 'init' + dir
+      
       FileUtils.mkdir_p(dir) if !File.exists?(dir)
       
       FileUtils.cd(dir) do
@@ -10,14 +12,14 @@ module GitRuby
           return false # already initialized
         else
           # initialize directory
-          create_initial_config
+          create_initial_config(bare)
           FileUtils.mkdir_p('refs/heads')
           FileUtils.mkdir_p('refs/tags')
           FileUtils.mkdir_p('objects/info')
           FileUtils.mkdir_p('objects/pack')
           FileUtils.mkdir_p('branches')
           add_file('description', 'Unnamed repository; edit this file to name it for gitweb.')
-          add_file('HEAD', 'ref: refs/heads/master')
+          add_file('HEAD', &quot;ref: refs/heads/master\n&quot;)
           FileUtils.mkdir_p('hooks')
           FileUtils.cd('hooks') do
             add_file('applypatch-msg', '# add shell script and make executable to enable')
@@ -37,8 +39,9 @@ module GitRuby
     
     private
 
-    def self.create_initial_config
-      config = &quot;[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true&quot;
+    def self.create_initial_config(bare = false)
+      bare ? bare_status = 'true' : bare_status = 'false'
+      config = &quot;[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = #{bare_status}\n\tlogallrefupdates = true&quot;
       add_file('config', config)
     end
       </diff>
      <filename>lib/git-ruby/repository.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>b31112ed69d7b286f14d7c1936f9456bd11df19c</id>
    </parent>
  </parents>
  <author>
    <name>Scott Chacon</name>
    <email>schacon@gmail.com</email>
  </author>
  <url>http://github.com/schacon/git-ruby/commit/80c599f671e19e4ebdd7c80d8fb4a72ecea19924</url>
  <id>80c599f671e19e4ebdd7c80d8fb4a72ecea19924</id>
  <committed-date>2008-03-31T10:56:28-07:00</committed-date>
  <authored-date>2008-03-31T10:56:28-07:00</authored-date>
  <message>added gitr to the gem package as a binary, beginning of http-fetch implementation
can fully clone a loose object git repo over http</message>
  <tree>afb6ae1c0ac4a57f1b04a2b660caebbe362fb207</tree>
  <committer>
    <name>Scott Chacon</name>
    <email>schacon@gmail.com</email>
  </committer>
</commit>
