From 0f87b4d9020fff756c18323106b3fd4e2f422135 Mon Sep 17 00:00:00 2001 From: Scott Chacon Date: Fri, 22 Aug 2008 14:51:56 -0700 Subject: [PATCH] merged recent changes: * accepts relative alt paths * orders tree writes properly * can init a git repo --- lib/grit/git-ruby.rb | 11 ++++- lib/grit/git-ruby/internal/pack.rb | 1 - lib/grit/git-ruby/repository.rb | 48 +++++++++++++++++++ lib/grit/index.rb | 37 +++++++++++---- lib/grit/repo.rb | 6 +++ lib/grit/status.rb | 32 +++++++------ test/test_head.rb | 6 +++ test/test_rubygit.rb | 11 +++++ test/test_rubygit_index.rb | 76 ++++++++++++++++++++++++++++++ test/test_rubygit_iv2.rb | 17 ++----- 10 files changed, 203 insertions(+), 42 deletions(-) create mode 100644 test/test_rubygit_index.rb diff --git a/lib/grit/git-ruby.rb b/lib/grit/git-ruby.rb index d0b6317..33f5c79 100644 --- a/lib/grit/git-ruby.rb +++ b/lib/grit/git-ruby.rb @@ -1,6 +1,5 @@ require 'grit/git-ruby/repository' require 'grit/git-ruby/file_index' -require 'grit/git-ruby/commit_db' module Grit @@ -11,6 +10,14 @@ module Grit attr_accessor :ruby_git_repo, :git_file_index + def init(options) + if options.size == 0 + Grit::GitRuby::Repository.init(@git_dir) + else + method_missing('init', options) + end + end + def cat_file(options, ref) if options[:t] file_type(ref) @@ -172,4 +179,4 @@ module Grit # git log --pretty='raw' --max-count='1' 'master' -- 'test' end -end \ No newline at end of file +end diff --git a/lib/grit/git-ruby/internal/pack.rb b/lib/grit/git-ruby/internal/pack.rb index 37b2d7c..51a8071 100644 --- a/lib/grit/git-ruby/internal/pack.rb +++ b/lib/grit/git-ruby/internal/pack.rb @@ -162,7 +162,6 @@ module Grit pos += CrcSize end @size.times do |i| - puts "#{i} : #{pos}" offset = idx[pos,OffsetSize].unpack('N')[0] data[i][2] = offset pos += OffsetSize diff --git a/lib/grit/git-ruby/repository.rb b/lib/grit/git-ruby/repository.rb index 4e2887b..2cc656b 100644 --- a/lib/grit/git-ruby/repository.rb +++ b/lib/grit/git-ruby/repository.rb @@ -37,6 +37,7 @@ module Grit def initialize(git_dir, options = {}) @git_dir = git_dir @options = options + @packs = [] end # returns the loose objects object lazily @@ -599,6 +600,53 @@ module Grit found_data end + # initialize a git repository + def self.init(dir, bare = false) + + FileUtils.mkdir_p(dir) if !File.exists?(dir) + + FileUtils.cd(dir) do + if(File.exists?('objects')) + return false # already initialized + else + # initialize directory + 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\n") + FileUtils.mkdir_p('hooks') + FileUtils.cd('hooks') do + add_file('applypatch-msg', '# add shell script and make executable to enable') + add_file('post-commit', '# add shell script and make executable to enable') + add_file('post-receive', '# add shell script and make executable to enable') + add_file('post-update', '# add shell script and make executable to enable') + add_file('pre-applypatch', '# add shell script and make executable to enable') + add_file('pre-commit', '# add shell script and make executable to enable') + add_file('pre-rebase', '# add shell script and make executable to enable') + add_file('update', '# add shell script and make executable to enable') + end + FileUtils.mkdir_p('info') + add_file('info/exclude', "# *.[oa]\n# *~") + end + end + end + + def self.create_initial_config(bare = false) + bare ? bare_status = 'true' : bare_status = 'false' + config = "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = #{bare_status}\n\tlogallrefupdates = true" + add_file('config', config) + end + + def self.add_file(name, contents) + File.open(name, 'w') do |f| + f.write contents + end + end + def close @packs.each do |pack| pack.close diff --git a/lib/grit/index.rb b/lib/grit/index.rb index 6763ff1..489e13c 100644 --- a/lib/grit/index.rb +++ b/lib/grit/index.rb @@ -1,11 +1,12 @@ module Grit class Index - attr_accessor :repo, :tree + attr_accessor :repo, :tree, :current_tree def initialize(repo) self.repo = repo self.tree = {} + self.current_tree = nil end # Add a file to the index @@ -28,12 +29,16 @@ module Grit current[filename] = data end + def read_tree(tree) + self.current_tree = self.repo.tree(tree) + end + # Commit the contents of the index # +message+ is the commit message # # Returns a String of the SHA1 of the commit - def commit(message, parents = nil, actor = nil, last_tree = nil) - tree_sha1 = write_tree(self.tree) + def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master') + tree_sha1 = write_tree(self.tree, self.current_tree) return false if tree_sha1 == last_tree # don't write identical commits contents = [] @@ -60,7 +65,7 @@ module Grit commit_sha1 = self.repo.git.ruby_git.put_raw_object(contents.join("\n"), 'commit') # self.repo.git.update_ref({}, 'HEAD', commit_sha1) - File.open(File.join(self.repo.path, 'refs', 'heads', 'master'), 'w') do |f| + File.open(File.join(self.repo.path, 'refs', 'heads', head), 'w') do |f| f.write(commit_sha1) end if commit_sha1 @@ -71,23 +76,35 @@ module Grit # +tree+ is the tree # # Returns the SHA1 String of the tree - def write_tree(tree) - tree_contents = '' + def write_tree(tree, now_tree = nil) + tree_contents = {} + + # fill in original tree + now_tree.contents.each do |obj| + sha = [obj.id].pack("H*") + k = obj.name + k += '/' if (obj.class == Grit::Tree) + tree_contents[k] = "%s %s\0%s" % [obj.mode.to_s, obj.name, sha] + end if now_tree + + # overwrite with new tree contents tree.each do |k, v| case v when String: sha = write_blob(v) sha = [sha].pack("H*") str = "%s %s\0%s" % ['100644', k, sha] - tree_contents += str + tree_contents[k] = str when Hash: - sha = write_tree(v) + ctree = now_tree/k if now_tree + sha = write_tree(v, ctree) sha = [sha].pack("H*") str = "%s %s\0%s" % ['040000', k, sha] - tree_contents += str + tree_contents[k + '/'] = str end end - self.repo.git.ruby_git.put_raw_object(tree_contents, 'tree') + tr = tree_contents.sort.map { |k, v| v }.join('') + self.repo.git.ruby_git.put_raw_object(tr, 'tree') end # Write the blob to the index diff --git a/lib/grit/repo.rb b/lib/grit/repo.rb index 16d979c..7c9417b 100644 --- a/lib/grit/repo.rb +++ b/lib/grit/repo.rb @@ -5,6 +5,7 @@ module Grit # The path of the git repo as a String attr_accessor :path + attr_accessor :working_dir attr_reader :bare # The git command line interface object @@ -22,6 +23,7 @@ module Grit epath = File.expand_path(path) if File.exist?(File.join(epath, '.git')) + self.working_dir = epath self.path = File.join(epath, '.git') @bare = false elsif File.exist?(epath) && (epath =~ /\.git$/ || options[:is_bare]) @@ -60,6 +62,10 @@ module Grit alias_method :branches, :heads + def is_head?(head_name) + heads.find { |h| h.name == head_name } + end + # Object reprsenting the current repo head. # # Returns Grit::Head (baked) diff --git a/lib/grit/status.rb b/lib/grit/status.rb index 25afc1c..92c48ab 100644 --- a/lib/grit/status.rb +++ b/lib/grit/status.rb @@ -88,25 +88,27 @@ module Grit def construct_status @files = ls_files - # find untracked in working dir - Dir.glob('**/*') do |file| - if !@files[file] - @files[file] = {:path => file, :untracked => true} if !File.directory?(file) + Dir.chdir(@base.working_dir) do + # find untracked in working dir + Dir.glob('**/*') do |file| + if !@files[file] + @files[file] = {:path => file, :untracked => true} if !File.directory?(file) + end end - end - # find modified in tree - diff_files.each do |path, data| - @files[path] ? @files[path].merge!(data) : @files[path] = data - end + # find modified in tree + diff_files.each do |path, data| + @files[path] ? @files[path].merge!(data) : @files[path] = data + end - # find added but not committed - new files - diff_index('HEAD').each do |path, data| - @files[path] ? @files[path].merge!(data) : @files[path] = data - end + # find added but not committed - new files + diff_index('HEAD').each do |path, data| + @files[path] ? @files[path].merge!(data) : @files[path] = data + end - @files.each do |k, file_hash| - @files[k] = StatusFile.new(@base, file_hash) + @files.each do |k, file_hash| + @files[k] = StatusFile.new(@base, file_hash) + end end end diff --git a/test/test_head.rb b/test/test_head.rb index 6754f5d..3bb49c0 100644 --- a/test/test_head.rb +++ b/test/test_head.rb @@ -28,6 +28,12 @@ class TestHead < Test::Unit::TestCase head = @r.heads[2] assert_equal %Q{#}, head.inspect end + + def test_is_head + assert @r.is_head?('master') + assert @r.is_head?('test/chacon') + assert !@r.is_head?('masterblah') + end def test_head_count assert_equal 5, @r.heads.size diff --git a/test/test_rubygit.rb b/test/test_rubygit.rb index 94e4314..b105aea 100644 --- a/test/test_rubygit.rb +++ b/test/test_rubygit.rb @@ -1,4 +1,5 @@ require File.dirname(__FILE__) + '/helper' +require 'tempfile' class TestRubyGit < Test::Unit::TestCase @@ -9,6 +10,16 @@ class TestRubyGit < Test::Unit::TestCase @blob_sha = '4232d073306f01cf0b895864e5a5cfad7dd76fce' end + def test_init_gitdir + tf = Tempfile.new('gitdir') + temppath = tf.path + tf.unlink + + git = Git.new(temppath) + git.init({}) + assert File.exists?(File.join(temppath, 'config')) + end + def test_log_merge c1 = '420eac97a826bfac8724b6b0eef35c20922124b7' c2 = '30e367cef2203eba2b341dc9050993b06fd1e108' diff --git a/test/test_rubygit_index.rb b/test/test_rubygit_index.rb new file mode 100644 index 0000000..1713bc4 --- /dev/null +++ b/test/test_rubygit_index.rb @@ -0,0 +1,76 @@ +require File.dirname(__FILE__) + '/helper' +require 'pp' + +class TestRubyGitIndex < Test::Unit::TestCase + + def setup + @base_repo = create_temp_repo(File.join(File.dirname(__FILE__), *%w[dot_git_iv2])) + @git = Grit::Repo.new(@base_repo, :is_bare => true) + @rgit = @git.git.ruby_git + @user = Actor.from_string("Tom Werner ") + end + + def teardown + FileUtils.rm_r(@base_repo) + end + + def create_temp_repo(clone_path) + filename = 'git_test' + Time.now.to_i.to_s + rand(300).to_s.rjust(3, '0') + tmp_path = File.join("/tmp/", filename) + FileUtils.mkdir_p(tmp_path) + FileUtils.cp_r(clone_path, tmp_path) + File.join(tmp_path, 'dot_git_iv2') + end + + def test_add_files + sha = @git.commits.first.tree.id + + i = @git.index + i.read_tree(sha) + i.add('atester.rb', 'test stuff') + i.commit('message', [@git.commits.first], @user, nil, 'master') + + b = @git.commits.first.tree/'atester.rb' + assert_equal 'f80c3b68482d5e1c8d24c9b8139340f0d0a928d0', b.id + end + + def test_add_path_file + sha = @git.commits.first.tree.id + + i = @git.index + i.read_tree(sha) + i.add('lib/atester.rb', 'test stuff') + i.commit('message', [@git.commits.first], @user, nil, 'master') + + b = @git.commits.first.tree/'lib'/'atester.rb' + assert_equal 'f80c3b68482d5e1c8d24c9b8139340f0d0a928d0', b.id + b = @git.commits.first.tree/'lib'/'grit.rb' + assert_equal '77aa887449c28a922a660b2bb749e4127f7664e5', b.id + end + + def test_ordered_properly + sha = @git.commits.first.tree.id + + i = @git.index + i.read_tree(sha) + i.add('lib.rb', 'test stuff') + i.commit('message', [@git.commits.first], @user, nil, 'master') + + tr = @git.commits.first.tree.contents + entries = tr.select { |c| c.name[0, 3] == 'lib' }.map { |c| c.name } + assert_equal 'lib.rb', entries[0] + assert_equal 'lib', entries[1] + end + + def test_modify_file + sha = @git.commits.first.tree.id + + i = @git.index + i.read_tree(sha) + i.add('README.txt', 'test more stuff') + i.commit('message', [@git.commits.first], @user, nil, 'master') + + b = @git.commits.first.tree/'README.txt' + assert_equal 'e45d6b418e34951ddaa3e78e4fc4d3d92a46d3d1', b.id + end +end \ No newline at end of file diff --git a/test/test_rubygit_iv2.rb b/test/test_rubygit_iv2.rb index 4efdefd..19b810f 100644 --- a/test/test_rubygit_iv2.rb +++ b/test/test_rubygit_iv2.rb @@ -7,21 +7,10 @@ class TestRubyGitIv2 < Test::Unit::TestCase @git = Grit::Repo.new(File.join(File.dirname(__FILE__), *%w[dot_git_iv2]), :is_bare => true) @rgit = @git.git.ruby_git @commit_sha = 'ca8a30f5a7f0f163bbe3b6f0abf18a6c83b0687a' - @tree_sha = 'cd7422af5a2e0fff3e94d6fb1a8fff03b2841881' - @blob_sha = '4232d073306f01cf0b895864e5a5cfad7dd76fce' + @tree_sha = 'cd7422af5a2e0fff3e94d6fb1a8fff03b2841881' + @blob_sha = '4232d073306f01cf0b895864e5a5cfad7dd76fce' end - -=begin - def test_sha - i = 0 - @rgit.packs[0].each_sha1 do |sha| - i += 1 - sha = sha.unpack("H*")[0] - #puts "#{i} : #{sha}" - end - end -=end - + def test_basic assert @rgit.object_exists?(@commit_sha) assert_equal 10, @git.commits.size