BigW Consortium Gitlab

commits.rb 7.29 KB
Newer Older
1 2 3 4
require 'mime/types'

module API
  class Commits < Grape::API
5 6
    include PaginationParams

7 8 9
    before { authenticate! }
    before { authorize! :download_code, user_project }

10 11 12
    params do
      requires :id, type: String, desc: 'The ID of a project'
    end
13
    resource :projects, requirements: { id: %r{[^/]+} } do
14 15 16 17 18
      desc 'Get a project repository commits' do
        success 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'
19 20
        optional :since,    type: DateTime, desc: 'Only commits after or on this date will be returned'
        optional :until,    type: DateTime, desc: 'Only commits before or on this date will be returned'
21
        optional :path,     type: String, desc: 'The file path'
22
        use :pagination
23
      end
24
      get ":id/repository/commits" do
25 26 27 28
        path   = params[:path]
        before = params[:until]
        after  = params[:since]
        ref    = params[:ref_name] || user_project.try(:default_branch) || 'master'
29
        offset = (params[:page] - 1) * params[:per_page]
30 31

        commits = user_project.repository.commits(ref,
32
                                                  path: path,
33 34
                                                  limit: params[:per_page],
                                                  offset: offset,
35 36
                                                  before: before,
                                                  after: after)
37

38 39 40 41 42 43 44 45 46
        commit_count =
          if path || before || after
            user_project.repository.count_commits(ref: ref, path: path, before: before, after: after)
          else
            # Cacheable commit count.
            user_project.repository.commit_count_for_ref(ref)
          end

        paginated_commits = Kaminari.paginate_array(commits, total_count: commit_count)
47 48

        present paginate(paginated_commits), with: Entities::RepoCommit
49 50
      end

Marc Siegfriedt committed
51
      desc 'Commit multiple file changes as one commit' do
52
        success Entities::RepoCommitDetail
Marc Siegfriedt committed
53 54 55
        detail 'This feature was introduced in GitLab 8.13'
      end
      params do
56
        requires :branch, type: String, desc: 'The name of branch'
Marc Siegfriedt committed
57
        requires :commit_message, type: String, desc: 'Commit message'
58
        requires :actions, type: Array[Hash], desc: 'Actions to perform in commit'
Marc Siegfriedt committed
59 60 61 62 63 64
        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

65
        attrs = declared_params.merge(start_branch: declared_params[:branch], branch_name: declared_params[:branch])
66

Marc Siegfriedt committed
67 68 69 70 71 72 73 74 75 76
        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: Entities::RepoCommitDetail
        else
          render_api_error!(result[:message], 400)
        end
      end

77 78 79 80 81 82 83
      desc 'Get a specific commit of a project' do
        success 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
84
      get ":id/repository/commits/:sha" do
85 86
        commit = user_project.commit(params[:sha])

87
        not_found! "Commit" unless commit
88

89 90 91
        present commit, with: Entities::RepoCommitDetail
      end

92 93 94 95 96 97
      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
98
      get ":id/repository/commits/:sha/diff" do
99 100
        commit = user_project.commit(params[:sha])

101
        not_found! "Commit" unless commit
102

103
        commit.raw_diffs.to_a
104
      end
105

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

117
        not_found! 'Commit' unless commit
118
        notes = user_project.notes.where(commit_id: commit.id).order(:created_at)
119

120 121 122
        present paginate(notes), with: Entities::CommitNote
      end

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
      desc 'Cherry pick commit into a branch' do
        detail 'This feature was introduced in GitLab 8.15'
        success 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
      post ':id/repository/commits/:sha/cherry_pick' do
        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,
142
          start_branch: params[:branch],
143
          branch_name: params[:branch]
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: Entities::RepoCommit
        else
          render_api_error!(result[:message], 400)
        end
      end

156 157 158 159 160 161 162 163 164
      desc 'Post comment to commit' do
        success 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'
Douwe Maan committed
165
          requires :line_type, type: String, values: %w(new old), default: 'new', desc: 'The type of the line'
166 167
        end
      end
168
      post ':id/repository/commits/:sha/comments' do
169
        commit = user_project.commit(params[:sha])
170
        not_found! 'Commit' unless commit
171

172 173 174 175 176 177
        opts = {
          note: params[:note],
          noteable_type: 'Commit',
          commit_id: commit.id
        }

178
        if params[:path]
179
          commit.raw_diffs(all_diffs: true).each do |diff|
180
            next unless diff.new_path == params[:path]
181
            lines = Gitlab::Diff::Parser.new.parse(diff.diff.each_line)
182 183

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

            break if opts[:line_code]
          end
190 191

          opts[:type] = LegacyDiffNote.name if opts[:line_code]
192 193 194 195 196 197 198
        end

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

        if note.save
          present note, with: Entities::CommitNote
        else
199
          render_api_error!("Failed to save note #{note.errors.messages}", 400)
200 201
        end
      end
202 203 204
    end
  end
end