commits.rb 7.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
require 'mime/types'

module API
  module V3
    class Commits < Grape::API
      include PaginationParams

      before { authenticate! }
      before { authorize! :download_code, user_project }

      params do
        requires :id, type: String, desc: 'The ID of a project'
      end
14
      resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
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
        desc 'Get a project repository commits' do
          success ::API::Entities::RepoCommit
        end
        params do
          optional :ref_name, type: String, desc: 'The name of a repository branch or tag, if not given the default branch is used'
          optional :since,    type: DateTime, desc: 'Only commits after or in this date will be returned'
          optional :until,    type: DateTime, desc: 'Only commits before or in this date will be returned'
          optional :page,     type: Integer, default: 0, desc: 'The page for pagination'
          optional :per_page, type: Integer, default: 20, desc: 'The number of results per page'
          optional :path,     type: String, desc: 'The file path'
        end
        get ":id/repository/commits" do
          ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
          offset = params[:page] * params[:per_page]

          commits = user_project.repository.commits(ref,
                                                    path: params[:path],
                                                    limit: params[:per_page],
                                                    offset: offset,
                                                    after: params[:since],
                                                    before: params[:until])

          present commits, with: ::API::Entities::RepoCommit
        end

        desc 'Commit multiple file changes as one commit' do
          success ::API::Entities::RepoCommitDetail
          detail 'This feature was introduced in GitLab 8.13'
        end
        params do
          requires :branch_name, type: String, desc: 'The name of branch'
          requires :commit_message, type: String, desc: 'Commit message'
          requires :actions, type: Array[Hash], desc: 'Actions to perform in commit'
          optional :author_email, type: String, desc: 'Author email for commit'
          optional :author_name, type: String, desc: 'Author name for commit'
        end
        post ":id/repository/commits" do
          authorize! :push_code, user_project

          attrs = declared_params.dup
          branch = attrs.delete(:branch_name)
D
Douwe Maan 已提交
56
          attrs.merge!(start_branch: branch, branch_name: branch)
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

          result = ::Files::MultiService.new(user_project, current_user, attrs).execute

          if result[:status] == :success
            commit_detail = user_project.repository.commits(result[:result], limit: 1).first
            present commit_detail, with: ::API::Entities::RepoCommitDetail
          else
            render_api_error!(result[:message], 400)
          end
        end

        desc 'Get a specific commit of a project' do
          success ::API::Entities::RepoCommitDetail
          failure [[404, 'Not Found']]
        end
        params do
          requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
        end
75
        get ":id/repository/commits/:sha", requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
76 77 78 79 80 81 82 83 84 85 86 87 88
          commit = user_project.commit(params[:sha])

          not_found! "Commit" unless commit

          present commit, with: ::API::Entities::RepoCommitDetail
        end

        desc 'Get the diff for a specific commit of a project' do
          failure [[404, 'Not Found']]
        end
        params do
          requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
        end
89
        get ":id/repository/commits/:sha/diff", requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
          commit = user_project.commit(params[:sha])

          not_found! "Commit" unless commit

          commit.raw_diffs.to_a
        end

        desc "Get a commit's comments" do
          success ::API::Entities::CommitNote
          failure [[404, 'Not Found']]
        end
        params do
          use :pagination
          requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
        end
105
        get ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
          commit = user_project.commit(params[:sha])

          not_found! 'Commit' unless commit
          notes = Note.where(commit_id: commit.id).order(:created_at)

          present paginate(notes), with: ::API::Entities::CommitNote
        end

        desc 'Cherry pick commit into a branch' do
          detail 'This feature was introduced in GitLab 8.15'
          success ::API::Entities::RepoCommit
        end
        params do
          requires :sha, type: String, desc: 'A commit sha to be cherry picked'
          requires :branch, type: String, desc: 'The name of the branch'
        end
122
        post ':id/repository/commits/:sha/cherry_pick', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
123 124 125 126 127 128 129 130 131 132
          authorize! :push_code, user_project

          commit = user_project.commit(params[:sha])
          not_found!('Commit') unless commit

          branch = user_project.repository.find_branch(params[:branch])
          not_found!('Branch') unless branch

          commit_params = {
            commit: commit,
133
            start_branch: params[:branch],
D
Douwe Maan 已提交
134
            branch_name: params[:branch]
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
          }

          result = ::Commits::CherryPickService.new(user_project, current_user, commit_params).execute

          if result[:status] == :success
            branch = user_project.repository.find_branch(params[:branch])
            present user_project.repository.commit(branch.dereferenced_target), with: ::API::Entities::RepoCommit
          else
            render_api_error!(result[:message], 400)
          end
        end

        desc 'Post comment to commit' do
          success ::API::Entities::CommitNote
        end
        params do
          requires :sha, type: String, regexp: /\A\h{6,40}\z/, desc: "The commit's SHA"
          requires :note, type: String, desc: 'The text of the comment'
          optional :path, type: String, desc: 'The file path'
          given :path do
            requires :line, type: Integer, desc: 'The line number'
D
Douwe Maan 已提交
156
            requires :line_type, type: String, values: %w(new old), default: 'new', desc: 'The type of the line'
157 158
          end
        end
159
        post ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
160 161 162 163 164 165 166 167 168 169
          commit = user_project.commit(params[:sha])
          not_found! 'Commit' unless commit

          opts = {
            note: params[:note],
            noteable_type: 'Commit',
            commit_id: commit.id
          }

          if params[:path]
D
Douwe Maan 已提交
170
            commit.raw_diffs(limits: false).each do |diff|
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
              next unless diff.new_path == params[:path]
              lines = Gitlab::Diff::Parser.new.parse(diff.diff.each_line)

              lines.each do |line|
                next unless line.new_pos == params[:line] && line.type == params[:line_type]
                break opts[:line_code] = Gitlab::Diff::LineCode.generate(diff.new_path, line.new_pos, line.old_pos)
              end

              break if opts[:line_code]
            end

            opts[:type] = LegacyDiffNote.name if opts[:line_code]
          end

          note = ::Notes::CreateService.new(user_project, current_user, opts).execute

          if note.save
            present note, with: ::API::Entities::CommitNote
          else
            render_api_error!("Failed to save note #{note.errors.messages}", 400)
          end
        end
      end
    end
  end
end