From 4e694b9a376c912200d0b15412d93b41ba86f870 Mon Sep 17 00:00:00 2001 From: Tom Preston-Werner Date: Thu, 23 Jul 2009 15:21:59 -0700 Subject: [PATCH] further file system abstractions --- lib/grit/git.rb | 121 +++++++++++++++++++++++++++++++++++++++++++++- lib/grit/repo.rb | 60 +++++++++++++++++++++-- test/test_git.rb | 2 +- test/test_repo.rb | 4 +- 4 files changed, 177 insertions(+), 10 deletions(-) diff --git a/lib/grit/git.rb b/lib/grit/git.rb index 58bc7b7..00ad0c8 100644 --- a/lib/grit/git.rb +++ b/lib/grit/git.rb @@ -1,3 +1,4 @@ +require 'tempfile' module Grit class Git @@ -13,7 +14,11 @@ module Grit undef_method :clone include GitRuby - + + def exist? + File.exist?(self.git_dir) + end + def put_raw_object(content, type) ruby_git.put_raw_object(content, type) end @@ -49,6 +54,14 @@ module Grit end alias_method :e, :shell_escape + # Check if a normal file exists on the filesystem + # +file+ is the relative path from the Git dir + # + # Returns Boolean + def fs_exist?(file) + File.exist?(File.join(self.git_dir, file)) + end + # Read a normal file from the filesystem. # +file+ is the relative path from the Git dir # @@ -75,9 +88,113 @@ module Grit # # Returns nothing def fs_delete(file) - FileUtils.rm_f(File.join(self.git_dir,file)) + FileUtils.rm_rf(File.join(self.git_dir, file)) end + # Move a normal file + # +from+ is the relative path to the current file + # +to+ is the relative path to the destination file + # + # Returns nothing + def fs_move(from, to) + FileUtils.mv(File.join(self.git_dir, from), File.join(self.git_dir, to)) + end + + # Make a directory + # +dir+ is the relative path to the directory to create + # + # Returns nothing + def fs_mkdir(dir) + FileUtils.mkdir_p(File.join(self.git_dir, dir)) + end + + # Chmod the the file or dir and everything beneath + # +file+ is the relative path from the Git dir + # + # Returns nothing + def fs_chmod(mode, file = '/') + FileUtils.chmod_R(mode, File.join(self.git_dir, file)) + end + + # Create a new directory + # +path+ is an absolute path + # + # Returns nothing + def fs_mkdir(path) + FileUtils.mkdir_p(path) + end + + def fs_exist(path) + File.exist?(path) + end + + def list_remotes + remotes = [] + Dir.chdir(File.join(self.git_dir, 'refs/remotes')) do + remotes = Dir.glob('*') + end + remotes + rescue + [] + end + + def create_tempfile(seed, unlink = false) + path = Tempfile.new(seed).path + File.unlink(path) if unlink + return path + end + + def check_applies(head_sha, applies_sha) + git_index = create_tempfile('index', true) + (o1, exit1) = raw_git("git read-tree #{head_sha} 2>/dev/null", git_index) + (o2, exit2) = raw_git("git diff #{applies_sha}^ #{applies_sha} | git apply --check --cached >/dev/null 2>/dev/null", git_index) + return (exit1 + exit2) + end + + def get_patch(applies_sha) + git_index = create_tempfile('index', true) + (patch, exit2) = raw_git("git diff #{applies_sha}^ #{applies_sha}", git_index) + patch + end + + def apply_patch(head_sha, patch) + git_index = create_tempfile('index', true) + + git_patch = create_tempfile('patch') + File.open(git_patch, 'w+') { |f| f.print patch } + + raw_git("git read-tree #{head_sha} 2>/dev/null", git_index) + (op, exit) = raw_git("git apply --cached < #{git_patch}", git_index) + if exit == 0 + return raw_git("git write-tree", git_index).first.chomp + end + false + end + + # RAW CALLS WITH ENV SETTINGS + def raw_git_call(command, index) + tmp = ENV['GIT_INDEX_FILE'] + ENV['GIT_INDEX_FILE'] = index + out = `#{command}` + after = ENV['GIT_INDEX_FILE'] # someone fucking with us ?? + ENV['GIT_INDEX_FILE'] = tmp + if after != index + raise 'environment was changed for the git call' + end + [out, $?.exitstatus] + end + + def raw_git(command, index) + output = nil + Dir.chdir(self.git_dir) do + output = raw_git_call(command, index) + end + output + end + # RAW CALLS WITH ENV SETTINGS END + + + # Run the given git command with the specified arguments and return # the result as a String # +cmd+ is the command diff --git a/lib/grit/repo.rb b/lib/grit/repo.rb index 6016645..3553bf0 100644 --- a/lib/grit/repo.rb +++ b/lib/grit/repo.rb @@ -139,6 +139,34 @@ module Grit def remotes Remote.find_all(self) end + + def remote_list + self.git.list_remotes + end + + def remote_add(name, url) + self.git.remote({}, 'add', name, url) + end + + def remote_fetch(name) + self.git.fetch({}, name) + end + + # takes an array of remote names and last pushed dates + # fetches from all of the remotes where the local fetch + # date is earlier than the passed date, then records the + # last fetched date + # + # { 'origin' => date, + # 'peter => date, + # } + def remotes_fetch_needed(remotes) + remotes.each do |remote, date| + # TODO: check against date + self.remote_fetch(remote) + end + end + # An array of Ref objects representing the refs in # this repo @@ -280,9 +308,19 @@ module Grit # Returns Grit::Repo (the newly created repo) def self.init_bare(path, git_options = {}, repo_options = {}) git = Git.new(path) + git.fs_mkdir('..') git.init(git_options) self.new(path, repo_options) end + + def self.init_bare_or_open(path, git_options = {}, repo_options = {}) + git = Git.new(path) + if !git.exist? + git.fs_mkdir(path) + git.init(git_options) + end + self.new(path, repo_options) + end # Fork a bare git repository from this repo # +path+ is the full path of the new repo (traditionally ends with /.git) @@ -292,6 +330,7 @@ module Grit def fork_bare(path, options = {}) default_options = {:bare => true, :shared => true} real_options = default_options.merge(options) + Git.new(path).fs_mkdir('..') self.git.clone(real_options, self.path, path) Repo.new(path) end @@ -335,7 +374,7 @@ module Grit def archive_tar_gz(treeish = 'master', prefix = nil) options = {} options[:prefix] = prefix if prefix - self.git.archive(options, treeish, "| gzip") + self.git.archive(options, treeish, "| gzip -n") end # Write an archive directly to a file @@ -377,10 +416,9 @@ module Grit # # Returns Array[String] (pathnames of alternates) def alternates - alternates_path = File.join(self.path, *%w{objects info alternates}) - - if File.exist?(alternates_path) - File.read(alternates_path).strip.split("\n") + alternates_path = "objects/info/alternates" + if self.git.fs_exist?(alternates_path) + self.git.fs_read(alternates_path).strip.split("\n") else [] end @@ -418,6 +456,18 @@ module Grit commit_sha end + # Rename the current repository directory. + # +name+ is the new name + # + # Returns nothing + def rename(name) + if @bare + self.git.fs_move('/', "../#{name}") + else + self.git.fs_move('/', "../../#{name}") + end + end + # Pretty object inspection def inspect %Q{#} diff --git a/test/test_git.rb b/test/test_git.rb index 48ba5b7..34b83eb 100644 --- a/test/test_git.rb +++ b/test/test_git.rb @@ -99,7 +99,7 @@ class TestGit < Test::Unit::TestCase end def test_fs_delete - FileUtils.expects(:rm_f).with(File.join(@git.git_dir, 'foo')) + FileUtils.expects(:rm_rf).with(File.join(@git.git_dir, 'foo')) @git.fs_delete('foo') end end diff --git a/test/test_repo.rb b/test/test_repo.rb index 0892556..85326a9 100644 --- a/test/test_repo.rb +++ b/test/test_repo.rb @@ -242,7 +242,7 @@ class TestRepo < Test::Unit::TestCase # disable_daemon_serve def test_disable_daemon_serve - FileUtils.expects(:rm_f).with(File.join(@r.path, 'git-daemon-export-ok')) + FileUtils.expects(:rm_rf).with(File.join(@r.path, 'git-daemon-export-ok')) @r.disable_daemon_serve end @@ -255,7 +255,7 @@ class TestRepo < Test::Unit::TestCase def test_alternates_with_two_alternates File.expects(:exist?).with("#{absolute_project_path}/.git/objects/info/alternates").returns(true) - File.expects(:read).returns("/path/to/repo1/.git/objects\n/path/to/repo2.git/objects\n") + File.any_instance.expects(:read).returns("/path/to/repo1/.git/objects\n/path/to/repo2.git/objects\n") assert_equal ["/path/to/repo1/.git/objects", "/path/to/repo2.git/objects"], @r.alternates end