BigW Consortium Gitlab

index.rb 3.83 KB
Newer Older
1 2 3
module Gitlab
  module Git
    class Index
4 5
      IndexError = Class.new(StandardError)

6 7
      DEFAULT_MODE = 0o100644

8 9
      ACTIONS = %w(create create_dir update move delete).freeze

10 11 12 13 14 15 16
      attr_reader :repository, :raw_index

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

Douwe Maan committed
17
      delegate :read_tree, :get, to: :raw_index
18 19 20 21 22

      def write_tree
        raw_index.write_tree(repository.rugged)
      end

Douwe Maan committed
23 24
      def dir_exists?(path)
        raw_index.find { |entry| entry[:path].start_with?("#{path}/") }
25 26 27
      end

      def create(options)
Douwe Maan committed
28
        options = normalize_options(options)
29

30 31
        if get(options[:file_path])
          raise IndexError, "A file with this name already exists"
32 33 34 35 36 37
        end

        add_blob(options)
      end

      def create_dir(options)
Douwe Maan committed
38
        options = normalize_options(options)
39

40 41
        if get(options[:file_path])
          raise IndexError, "A file with this name already exists"
42 43
        end

Douwe Maan committed
44
        if dir_exists?(options[:file_path])
45
          raise IndexError, "A directory with this name already exists"
Douwe Maan committed
46 47
        end

48 49 50 51 52 53 54 55
        options = options.dup
        options[:file_path] += '/.gitkeep'
        options[:content] = ''

        add_blob(options)
      end

      def update(options)
Douwe Maan committed
56
        options = normalize_options(options)
57

Douwe Maan committed
58
        file_entry = get(options[:file_path])
59
        unless file_entry
60
          raise IndexError, "A file with this name doesn't exist"
61 62 63 64 65 66
        end

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

      def move(options)
Douwe Maan committed
67
        options = normalize_options(options)
68

Douwe Maan committed
69
        file_entry = get(options[:previous_path])
70
        unless file_entry
71 72 73 74 75
          raise IndexError, "A file with this name doesn't exist"
        end

        if get(options[:file_path])
          raise IndexError, "A file with this name already exists"
76 77 78 79 80 81 82 83
        end

        raw_index.remove(options[:previous_path])

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

      def delete(options)
Douwe Maan committed
84
        options = normalize_options(options)
85

86 87
        unless get(options[:file_path])
          raise IndexError, "A file with this name doesn't exist"
88 89 90 91 92 93 94
        end

        raw_index.remove(options[:file_path])
      end

      private

Douwe Maan committed
95 96
      def normalize_options(options)
        options = options.dup
97 98
        options[:file_path] = normalize_path(options[:file_path]) if options[:file_path]
        options[:previous_path] = normalize_path(options[:previous_path]) if options[:previous_path]
Douwe Maan committed
99
        options
100 101 102
      end

      def normalize_path(path)
103 104 105 106
        unless path
          raise IndexError, "You must provide a file path"
        end

Douwe Maan committed
107
        pathname = Gitlab::Git::PathHelper.normalize_path(path.dup)
108

109 110 111 112 113 114 115 116
        pathname.each_filename do |segment|
          if segment == '..'
            raise IndexError, 'Path cannot include directory traversal'
          end

          unless segment =~ Gitlab::Regex.file_name_regex
            raise IndexError, "Path #{Gitlab::Regex.file_name_regex_message}"
          end
117 118 119 120 121 122 123
        end

        pathname.to_s
      end

      def add_blob(options, mode: nil)
        content = options[:content]
124 125 126 127
        unless content
          raise IndexError, "You must provide content"
        end

128 129 130 131 132 133 134 135 136 137 138 139 140
        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
141
        raise IndexError, e.message
142 143 144 145
      end
    end
  end
end