BigW Consortium Gitlab

ref_service.rb 7.39 KB
Newer Older
1 2
module Gitlab
  module GitalyClient
3
    class RefService
4 5
      include Gitlab::EncodingHelper

6 7
      # 'repository' is a Gitlab::Git::Repository
      def initialize(repository)
8
        @repository = repository
9
        @gitaly_repo = repository.gitaly_repository
10
        @storage = repository.storage
11 12
      end

13 14 15 16 17 18
      def branches
        request = Gitaly::FindAllBranchesRequest.new(repository: @gitaly_repo)
        response = GitalyClient.call(@storage, :ref_service, :find_all_branches, request)

        response.flat_map do |message|
          message.branches.map do |branch|
19
            target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target)
20 21 22 23 24
            Gitlab::Git::Branch.new(@repository, branch.name, branch.target.id, target_commit)
          end
        end
      end

25
      def default_branch_name
26
        request = Gitaly::FindDefaultBranchNameRequest.new(repository: @gitaly_repo)
27
        response = GitalyClient.call(@storage, :ref_service, :find_default_branch_name, request)
28
        Gitlab::Git.branch_name(response.name)
29 30 31
      end

      def branch_names
32
        request = Gitaly::FindAllBranchNamesRequest.new(repository: @gitaly_repo)
33
        response = GitalyClient.call(@storage, :ref_service, :find_all_branch_names, request)
34
        consume_refs_response(response) { |name| Gitlab::Git.branch_name(name) }
35 36 37
      end

      def tag_names
38
        request = Gitaly::FindAllTagNamesRequest.new(repository: @gitaly_repo)
39
        response = GitalyClient.call(@storage, :ref_service, :find_all_tag_names, request)
40
        consume_refs_response(response) { |name| Gitlab::Git.tag_name(name) }
41 42
      end

43
      def find_ref_name(commit_id, ref_prefix)
Jacob Vosmaer committed
44
        request = Gitaly::FindRefNameRequest.new(
45
          repository: @gitaly_repo,
46 47 48
          commit_id: commit_id,
          prefix: ref_prefix
        )
49 50
        response = GitalyClient.call(@storage, :ref_service, :find_ref_name, request, timeout: GitalyClient.medium_timeout)
        encode!(response.name.dup)
51 52
      end

53 54 55 56 57 58 59 60
      def count_tag_names
        tag_names.count
      end

      def count_branch_names
        branch_names.count
      end

61 62 63
      def local_branches(sort_by: nil)
        request = Gitaly::FindLocalBranchesRequest.new(repository: @gitaly_repo)
        request.sort_by = sort_by_param(sort_by) if sort_by
64
        response = GitalyClient.call(@storage, :ref_service, :find_local_branches, request)
65
        consume_branches_response(response)
66 67
      end

68 69 70 71 72 73
      def tags
        request = Gitaly::FindAllTagsRequest.new(repository: @gitaly_repo)
        response = GitalyClient.call(@storage, :ref_service, :find_all_tags, request)
        consume_tags_response(response)
      end

74
      def ref_exists?(ref_name)
75
        request = Gitaly::RefExistsRequest.new(repository: @gitaly_repo, ref: GitalyClient.encode(ref_name))
76 77 78 79 80 81
        response = GitalyClient.call(@storage, :ref_service, :ref_exists, request)
        response.value
      rescue GRPC::InvalidArgument => e
        raise ArgumentError, e.message
      end

82
      def find_branch(branch_name)
83
        request = Gitaly::FindBranchRequest.new(
84 85 86 87 88 89 90 91 92 93 94 95
          repository: @gitaly_repo,
          name: GitalyClient.encode(branch_name)
        )

        response = GitalyClient.call(@repository.storage, :ref_service, :find_branch, request)
        branch = response.branch
        return unless branch

        target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
        Gitlab::Git::Branch.new(@repository, encode!(branch.name.dup), branch.target_commit.id, target_commit)
      end

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 123 124 125 126 127 128 129
      def create_branch(ref, start_point)
        request = Gitaly::CreateBranchRequest.new(
          repository: @gitaly_repo,
          name: GitalyClient.encode(ref),
          start_point: GitalyClient.encode(start_point)
        )

        response = GitalyClient.call(@repository.storage, :ref_service, :create_branch, request)

        case response.status
        when :OK
          branch = response.branch
          target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
          Gitlab::Git::Branch.new(@repository, branch.name, branch.target_commit.id, target_commit)
        when :ERR_INVALID
          invalid_ref!("Invalid ref name")
        when :ERR_EXISTS
          invalid_ref!("Branch #{ref} already exists")
        when :ERR_INVALID_START_POINT
          invalid_ref!("Invalid reference #{start_point}")
        else
          raise "Unknown response status: #{response.status}"
        end
      end

      def delete_branch(branch_name)
        request = Gitaly::DeleteBranchRequest.new(
          repository: @gitaly_repo,
          name: GitalyClient.encode(branch_name)
        )

        GitalyClient.call(@repository.storage, :ref_service, :delete_branch, request)
      end

130 131 132 133 134 135 136 137 138
      def delete_refs(except_with_prefixes:)
        request = Gitaly::DeleteRefsRequest.new(
          repository: @gitaly_repo,
          except_with_prefix: except_with_prefixes
        )

        GitalyClient.call(@repository.storage, :ref_service, :delete_refs, request)
      end

139 140
      private

141 142
      def consume_refs_response(response)
        response.flat_map { |message| message.names.map { |name| yield(name) } }
143
      end
144 145

      def sort_by_param(sort_by)
146 147
        sort_by = 'name' if sort_by == 'name_asc'

148 149
        enum_value = Gitaly::FindLocalBranchesRequest::SortBy.resolve(sort_by.upcase.to_sym)
        raise ArgumentError, "Invalid sort_by key `#{sort_by}`" unless enum_value
150

151 152 153 154
        enum_value
      end

      def consume_branches_response(response)
155 156 157 158 159
        response.flat_map do |message|
          message.branches.map do |gitaly_branch|
            Gitlab::Git::Branch.new(
              @repository,
              encode!(gitaly_branch.name.dup),
160 161
              gitaly_branch.commit_id,
              commit_from_local_branches_response(gitaly_branch)
162 163 164
            )
          end
        end
165
      end
166

167 168
      def consume_tags_response(response)
        response.flat_map do |message|
169
          message.tags.map { |gitaly_tag| Util.gitlab_tag_from_gitaly_tag(@repository, gitaly_tag) }
170 171 172
        end
      end

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
      def commit_from_local_branches_response(response)
        # Git messages have no encoding enforcements. However, in the UI we only
        # handle UTF-8, so basically we cross our fingers that the message force
        # encoded to UTF-8 is readable.
        message = response.commit_subject.dup.force_encoding('UTF-8')

        # NOTE: For ease of parsing in Gitaly, we have only the subject of
        # the commit and not the full message. This is ok, since all the
        # code that uses `local_branches` only cares at most about the
        # commit message.
        # TODO: Once gitaly "takes over" Rugged consider separating the
        # subject from the message to make it clearer when there's one
        # available but not the other.
        hash = {
          id: response.commit_id,
          message: message,
          authored_date: Time.at(response.commit_author.date.seconds),
190 191
          author_name: response.commit_author.name.dup,
          author_email: response.commit_author.email.dup,
192
          committed_date: Time.at(response.commit_committer.date.seconds),
193 194
          committer_name: response.commit_committer.name.dup,
          committer_email: response.commit_committer.email.dup
195 196
        }

197
        Gitlab::Git::Commit.decorate(@repository, hash)
198
      end
199 200 201 202

      def invalid_ref!(message)
        raise Gitlab::Git::Repository::InvalidRef.new(message)
      end
203 204 205
    end
  end
end