lib/gitlab_git/repository.rb in gitlab_git-7.0.0.rc7 vs lib/gitlab_git/repository.rb in gitlab_git-7.0.0.rc8

- old
+ new

@@ -20,10 +20,12 @@ attr_reader :name # Rugged repo object attr_reader :rugged + # 'path' must be the path to a _bare_ git repository, e.g. + # /path/to/my-repo.git def initialize(path) @path = path @name = path.split("/").last @root_ref = discover_default_branch end @@ -129,10 +131,11 @@ ref ||= root_ref commit = Gitlab::Git::Commit.find(self, ref) return nil unless commit extension = nil + git_archive_format = nil pipe_cmd = nil case format when "tar.bz2", "tbz", "tbz2", "tb2", "bz2" extension = ".tar.bz2" @@ -140,28 +143,42 @@ when "tar" extension = ".tar" pipe_cmd = %W(cat) when "zip" extension = ".zip" + git_archive_format = "zip" pipe_cmd = %W(cat) else # everything else should fall back to tar.gz extension = ".tar.gz" + git_archive_format = nil pipe_cmd = %W(gzip -n) end # Build file path file_name = self.name.gsub("\.git", "") + "-" + commit.id.to_s + extension file_path = File.join(storage_path, self.name, file_name) - # Create dir for archive file - parent_path = File.join(storage_path, self.name) - FileUtils.mkdir_p(parent_path) unless File.directory?(parent_path) + # Put files into a directory before archiving + prefix = File.basename(self.name) + "/" # Create file if not exists - create_archive(ref, pipe_cmd, file_path) unless File.exist?(file_path) + unless File.exists?(file_path) + FileUtils.mkdir_p File.dirname(file_path) + # Create the archive in temp file, to avoid leaving a corrupt archive + # to be downloaded by the next user if we get interrupted while + # creating the archive. Note that we do not care about cleaning up + # the temp file in that scenario, because GitLab cleans up the + # directory holding the archive files periodically. + temp_file_path = file_path + ".#{Process.pid}-#{Time.now.to_i}" + archive_to_file(ref, prefix, temp_file_path, git_archive_format, pipe_cmd) + + # move temp file to persisted location + FileUtils.move(temp_file_path, file_path) + end + file_path end # Return repo size in megabytes def size @@ -814,82 +831,38 @@ paths.any? do |p| p.match(/^#{Regexp.escape(path_to_match)}/) end end - # Create an archive with the repository's files - def create_archive(ref_name, pipe_cmd, file_path) - # Put files into a prefix directory in the archive - prefix = File.basename(name) - extension = Pathname.new(file_path).extname + def archive_to_file(treeish = 'master', prefix = nil, filename = 'archive.tar.gz', format = nil, compress_cmd = %W(gzip)) + git_archive_cmd = %W(git --git-dir=#{path} archive) + git_archive_cmd << "--prefix=#{prefix}" if prefix + git_archive_cmd << "--format=#{format}" if format + git_archive_cmd += %W(-- #{treeish}) - if extension == '.zip' - create_zip_archive(ref_name, file_path, prefix) - else - # Open the file with the final result - FileUtils.mkdir_p(Pathname.new(file_path).dirname) - archive_file = File.new(file_path, 'wb') - - # Create a pipe to communicate with the compressor process + open(filename, 'w') do |file| + # Create a pipe to act as the '|' in 'git archive ... | gzip' pipe_rd, pipe_wr = IO.pipe - compress_pid = spawn(*pipe_cmd, in: pipe_rd, out: archive_file) - # pipe_rd and archive_file belong to the compressor process now; close - # them straightaway in our process. + + # Get the compression process ready to accept data from the read end + # of the pipe + compress_pid = spawn(*compress_cmd, :in => pipe_rd, :out => file) + # The read end belongs to the compression process now; we should + # close our file descriptor for it. pipe_rd.close - archive_file.close - # Change the external encoding of pipe_wr to prevent Ruby from trying - # to convert binary to UTF-8. - pipe_wr = IO.new(pipe_wr.fileno, 'w:ASCII-8BIT') - Gem::Package::TarWriter.new(pipe_wr) do |tar| - tar.mkdir(prefix, 33261) - - populated_index(ref_name).each do |entry| - add_archive_entry(tar, prefix, entry) - end - end - # We are done with pipe_wr, close it straightaway. + # Start 'git archive' and tell it to write into the write end of the + # pipe. + git_archive_pid = spawn(*git_archive_cmd, :out => pipe_wr) + # The write end belongs to 'git archive' now; close it. pipe_wr.close + # When 'git archive' and the compression process are finished, we are + # done. + Process.waitpid(git_archive_pid) + raise "#{git_archive_cmd.join(' ')} failed" unless $?.success? Process.waitpid(compress_pid) - end - end - - # Create a zip file with the contents of the repo - def create_zip_archive(ref_name, archive_path, prefix) - Zip::File.open(archive_path, Zip::File::CREATE) do |zipfile| - populated_index(ref_name).each do |entry| - add_archive_entry(zipfile, prefix, entry) - end - end - end - - # Add a file or directory from the index to the given tar or zip file - def add_archive_entry(archive, prefix, entry) - prefixed_path = File.join(prefix, entry[:path]) - - if submodule?(entry) - # Create an empty directory for submodules - mask = case archive - when Zip::File then 0755 - else '100755'.to_i(8) - end - archive.mkdir(prefixed_path, mask) - else - blob = rugged.lookup(entry[:oid]) - content = blob.content - - # Write the blob contents to the archive - if archive.is_a?(Zip::File) - archive.get_output_stream(prefixed_path) do |os| - os.write(content) - end - else - archive.add_file_simple(prefixed_path, - entry[:mode], blob.size) do |tf| - tf.write(content) - end - end + raise "#{compress_cmd.join(' ')} failed" unless $?.success? end end # Returns true if the index entry has the special file mode that denotes # a submodule.