index.rb 3.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
module Gitlab
  module Git
    class Index
      DEFAULT_MODE = 0o100644

      attr_reader :repository, :raw_index

      def initialize(repository)
        @repository = repository
        @raw_index = repository.rugged.index
      end

D
Douwe Maan 已提交
13
      delegate :read_tree, to: :raw_index
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122

      def write_tree
        raw_index.write_tree(repository.rugged)
      end

      def get(*args)
        raw_index.get(*args)
      end

      def create(options)
        normalize_options!(options)

        file_entry = raw_index.get(options[:file_path])
        if file_entry
          raise Gitlab::Git::Repository::InvalidBlobName.new("Filename already exists")
        end

        add_blob(options)
      end

      def create_dir(options)
        normalize_options!(options)

        file_entry = raw_index.get(options[:file_path])
        if file_entry
          raise Gitlab::Git::Repository::InvalidBlobName.new("Directory already exists as a file")
        end

        options = options.dup
        options[:file_path] += '/.gitkeep'
        options[:content] = ''

        add_blob(options)
      end

      def update(options)
        normalize_options!(options)

        file_entry = raw_index.get(options[:file_path])
        unless file_entry
          raise Gitlab::Git::Repository::InvalidBlobName.new("File doesn't exist")
        end

        add_blob(options, mode: file_entry[:mode])
      end

      def move(options)
        normalize_options!(options)

        file_entry = raw_index.get(options[:previous_path])
        unless file_entry
          raise Gitlab::Git::Repository::InvalidBlobName.new("File doesn't exist")
        end

        raw_index.remove(options[:previous_path])

        add_blob(options, mode: file_entry[:mode])
      end

      def delete(options)
        normalize_options!(options)

        file_entry = raw_index.get(options[:file_path])
        unless file_entry
          raise Gitlab::Git::Repository::InvalidBlobName.new("File doesn't exist")
        end

        raw_index.remove(options[:file_path])
      end

      private

      def normalize_options!(options)
        options[:file_path] = normalize_path(options[:file_path]) if options[:file_path]
        options[:previous_path] = normalize_path(options[:previous_path]) if options[:previous_path]
      end

      def normalize_path(path)
        pathname = Gitlab::Git::PathHelper.normalize_path(path)

        if pathname.each_filename.include?('..')
          raise Gitlab::Git::Repository::InvalidBlobName.new('Invalid path')
        end

        pathname.to_s
      end

      def add_blob(options, mode: nil)
        content = options[:content]
        return unless content

        content = Base64.decode64(content) if options[:encoding] == 'base64'

        detect = CharlockHolmes::EncodingDetector.new.detect(content)
        unless detect && detect[:type] == :binary
          # When writing to the repo directly as we are doing here,
          # the `core.autocrlf` config isn't taken into account.
          content.gsub!("\r\n", "\n") if repository.autocrlf
        end

        oid = repository.rugged.write(content, :blob)

        raw_index.add(path: options[:file_path], oid: oid, mode: mode || DEFAULT_MODE)
      rescue Rugged::IndexError => e
        raise Gitlab::Git::Repository::InvalidBlobName.new(e.message)
      end
    end
  end
end