2018-09-29 18:34:47 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2014-02-18 05:41:21 -05:00
|
|
|
require 'mime/types'
|
|
|
|
|
|
|
|
module API
|
|
|
|
class Commits < Grape::API
|
2016-11-21 14:15:46 -05:00
|
|
|
include PaginationParams
|
|
|
|
|
2014-02-18 05:41:21 -05:00
|
|
|
before { authorize! :download_code, user_project }
|
|
|
|
|
2018-07-11 11:26:00 -04:00
|
|
|
helpers do
|
|
|
|
def user_access
|
|
|
|
@user_access ||= Gitlab::UserAccess.new(current_user, project: user_project)
|
|
|
|
end
|
|
|
|
|
|
|
|
def authorize_push_to_branch!(branch)
|
|
|
|
unless user_access.can_push_to_branch?(branch)
|
|
|
|
forbidden!("You are not allowed to push into this branch")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-10-15 06:09:02 -04:00
|
|
|
params do
|
|
|
|
requires :id, type: String, desc: 'The ID of a project'
|
|
|
|
end
|
2018-11-08 07:18:17 -05:00
|
|
|
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
2016-10-15 06:09:02 -04:00
|
|
|
desc 'Get a project repository commits' do
|
2017-10-05 04:48:05 -04:00
|
|
|
success Entities::Commit
|
2016-10-15 06:09:02 -04:00
|
|
|
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'
|
2018-06-06 08:15:35 -04:00
|
|
|
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'
|
|
|
|
optional :path, type: String, desc: 'The file path'
|
|
|
|
optional :all, type: Boolean, desc: 'Every commit will be returned'
|
|
|
|
optional :with_stats, type: Boolean, desc: 'Stats about each commit will be added to the response'
|
2017-03-02 21:06:06 -05:00
|
|
|
use :pagination
|
2016-10-15 06:09:02 -04:00
|
|
|
end
|
2017-07-26 15:39:37 -04:00
|
|
|
get ':id/repository/commits' do
|
2018-06-06 08:15:35 -04:00
|
|
|
path = params[:path]
|
2017-03-02 21:06:06 -05:00
|
|
|
before = params[:until]
|
2018-06-06 08:15:35 -04:00
|
|
|
after = params[:since]
|
|
|
|
ref = params[:ref_name] || user_project.try(:default_branch) || 'master' unless params[:all]
|
2017-02-05 04:30:36 -05:00
|
|
|
offset = (params[:page] - 1) * params[:per_page]
|
2018-06-06 08:15:35 -04:00
|
|
|
all = params[:all]
|
|
|
|
with_stats = params[:with_stats]
|
2016-10-15 06:09:02 -04:00
|
|
|
|
|
|
|
commits = user_project.repository.commits(ref,
|
2017-03-02 21:06:06 -05:00
|
|
|
path: path,
|
2016-10-15 06:09:02 -04:00
|
|
|
limit: params[:per_page],
|
|
|
|
offset: offset,
|
2017-03-02 21:06:06 -05:00
|
|
|
before: before,
|
2018-02-16 11:39:23 -05:00
|
|
|
after: after,
|
|
|
|
all: all)
|
2014-02-18 05:41:21 -05:00
|
|
|
|
2017-03-02 21:06:06 -05:00
|
|
|
commit_count =
|
2018-02-19 09:42:00 -05:00
|
|
|
if all || path || before || after
|
2018-02-16 11:39:23 -05:00
|
|
|
user_project.repository.count_commits(ref: ref, path: path, before: before, after: after, all: all)
|
2017-03-02 21:06:06 -05:00
|
|
|
else
|
|
|
|
# Cacheable commit count.
|
|
|
|
user_project.repository.commit_count_for_ref(ref)
|
|
|
|
end
|
|
|
|
|
|
|
|
paginated_commits = Kaminari.paginate_array(commits, total_count: commit_count)
|
2017-01-20 10:04:16 -05:00
|
|
|
|
2018-06-06 08:15:35 -04:00
|
|
|
serializer = with_stats ? Entities::CommitWithStats : Entities::Commit
|
|
|
|
|
|
|
|
present paginate(paginated_commits), with: serializer
|
2014-02-18 05:41:21 -05:00
|
|
|
end
|
|
|
|
|
2016-08-29 19:58:32 -04:00
|
|
|
desc 'Commit multiple file changes as one commit' do
|
2017-10-05 04:48:05 -04:00
|
|
|
success Entities::CommitDetail
|
2016-08-29 19:58:32 -04:00
|
|
|
detail 'This feature was introduced in GitLab 8.13'
|
|
|
|
end
|
|
|
|
params do
|
2018-09-11 18:02:09 -04:00
|
|
|
requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.', allow_blank: false
|
2016-08-29 19:58:32 -04:00
|
|
|
requires :commit_message, type: String, desc: 'Commit message'
|
2018-09-23 06:48:29 -04:00
|
|
|
requires :actions, type: Array, desc: 'Actions to perform in commit' do
|
|
|
|
requires :action, type: String, desc: 'The action to perform, `create`, `delete`, `move`, `update`, `chmod`', values: %w[create update move delete chmod].freeze
|
|
|
|
requires :file_path, type: String, desc: 'Full path to the file. Ex. `lib/class.rb`'
|
|
|
|
given action: ->(action) { action == 'move' } do
|
|
|
|
requires :previous_path, type: String, desc: 'Original full path to the file being moved. Ex. `lib/class1.rb`'
|
|
|
|
end
|
|
|
|
given action: ->(action) { %w[create move].include? action } do
|
|
|
|
optional :content, type: String, desc: 'File content'
|
|
|
|
end
|
|
|
|
given action: ->(action) { action == 'update' } do
|
|
|
|
requires :content, type: String, desc: 'File content'
|
|
|
|
end
|
|
|
|
optional :encoding, type: String, desc: '`text` or `base64`', default: 'text', values: %w[text base64]
|
|
|
|
given action: ->(action) { %w[update move delete].include? action } do
|
|
|
|
optional :last_commit_id, type: String, desc: 'Last known file commit id'
|
|
|
|
end
|
|
|
|
given action: ->(action) { action == 'chmod' } do
|
|
|
|
requires :execute_filemode, type: Boolean, desc: 'When `true/false` enables/disables the execute flag on the file.'
|
|
|
|
end
|
|
|
|
end
|
2017-08-04 13:18:07 -04:00
|
|
|
optional :start_branch, type: String, desc: 'Name of the branch to start the new commit from'
|
2016-08-29 19:58:32 -04:00
|
|
|
optional :author_email, type: String, desc: 'Author email for commit'
|
|
|
|
optional :author_name, type: String, desc: 'Author name for commit'
|
2018-09-27 11:35:15 -04:00
|
|
|
optional :stats, type: Boolean, default: true, desc: 'Include commit stats'
|
2016-08-29 19:58:32 -04:00
|
|
|
end
|
2017-07-26 15:39:37 -04:00
|
|
|
post ':id/repository/commits' do
|
2018-07-11 11:26:00 -04:00
|
|
|
authorize_push_to_branch!(params[:branch])
|
2016-08-29 19:58:32 -04:00
|
|
|
|
2017-08-04 13:25:35 -04:00
|
|
|
attrs = declared_params
|
|
|
|
attrs[:branch_name] = attrs.delete(:branch)
|
2017-08-04 13:18:07 -04:00
|
|
|
attrs[:start_branch] ||= attrs[:branch_name]
|
2017-02-02 09:24:30 -05:00
|
|
|
|
2016-08-29 19:58:32 -04:00
|
|
|
result = ::Files::MultiService.new(user_project, current_user, attrs).execute
|
|
|
|
|
|
|
|
if result[:status] == :success
|
2017-07-03 18:46:52 -04:00
|
|
|
commit_detail = user_project.repository.commit(result[:result])
|
2018-10-01 07:15:31 -04:00
|
|
|
|
2018-10-02 11:33:43 -04:00
|
|
|
Gitlab::WebIdeCommitsCounter.increment if find_user_from_warden
|
2018-10-01 07:15:31 -04:00
|
|
|
|
2018-09-27 11:35:15 -04:00
|
|
|
present commit_detail, with: Entities::CommitDetail, stats: params[:stats]
|
2016-08-29 19:58:32 -04:00
|
|
|
else
|
|
|
|
render_api_error!(result[:message], 400)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-10-15 06:09:02 -04:00
|
|
|
desc 'Get a specific commit of a project' do
|
2017-10-05 04:48:05 -04:00
|
|
|
success Entities::CommitDetail
|
2017-07-26 15:39:37 -04:00
|
|
|
failure [[404, 'Commit Not Found']]
|
2016-10-15 06:09:02 -04:00
|
|
|
end
|
|
|
|
params do
|
|
|
|
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
|
2018-01-09 06:36:12 -05:00
|
|
|
optional :stats, type: Boolean, default: true, desc: 'Include commit stats'
|
2016-10-15 06:09:02 -04:00
|
|
|
end
|
2017-09-23 09:21:32 -04:00
|
|
|
get ':id/repository/commits/:sha', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
|
2016-10-15 06:09:02 -04:00
|
|
|
commit = user_project.commit(params[:sha])
|
|
|
|
|
2017-07-26 15:39:37 -04:00
|
|
|
not_found! 'Commit' unless commit
|
2016-10-15 06:09:02 -04:00
|
|
|
|
2018-01-09 06:36:12 -05:00
|
|
|
present commit, with: Entities::CommitDetail, stats: params[:stats]
|
2014-02-18 05:41:21 -05:00
|
|
|
end
|
|
|
|
|
2016-10-15 06:09:02 -04:00
|
|
|
desc 'Get the diff for a specific commit of a project' do
|
2017-07-26 15:39:37 -04:00
|
|
|
failure [[404, 'Commit Not Found']]
|
2016-10-15 06:09:02 -04:00
|
|
|
end
|
|
|
|
params do
|
|
|
|
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
|
2018-02-18 22:17:23 -05:00
|
|
|
use :pagination
|
2016-10-15 06:09:02 -04:00
|
|
|
end
|
2017-09-23 09:21:32 -04:00
|
|
|
get ':id/repository/commits/:sha/diff', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
|
2016-10-15 06:09:02 -04:00
|
|
|
commit = user_project.commit(params[:sha])
|
|
|
|
|
2017-07-26 15:39:37 -04:00
|
|
|
not_found! 'Commit' unless commit
|
2016-10-15 06:09:02 -04:00
|
|
|
|
2018-02-18 22:17:23 -05:00
|
|
|
raw_diffs = ::Kaminari.paginate_array(commit.raw_diffs.to_a)
|
|
|
|
|
|
|
|
present paginate(raw_diffs), with: Entities::Diff
|
2014-02-18 05:41:21 -05:00
|
|
|
end
|
2014-06-27 10:48:30 -04:00
|
|
|
|
2016-10-15 06:09:02 -04:00
|
|
|
desc "Get a commit's comments" do
|
|
|
|
success Entities::CommitNote
|
2017-07-26 15:39:37 -04:00
|
|
|
failure [[404, 'Commit Not Found']]
|
2016-10-15 06:09:02 -04:00
|
|
|
end
|
|
|
|
params do
|
2016-11-21 14:15:46 -05:00
|
|
|
use :pagination
|
2016-10-15 06:09:02 -04:00
|
|
|
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
|
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2017-09-23 09:21:32 -04:00
|
|
|
get ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
|
2016-10-15 06:09:02 -04:00
|
|
|
commit = user_project.commit(params[:sha])
|
|
|
|
|
2014-06-27 10:48:30 -04:00
|
|
|
not_found! 'Commit' unless commit
|
2017-11-07 11:11:37 -05:00
|
|
|
notes = commit.notes.order(:created_at)
|
2016-10-15 06:09:02 -04:00
|
|
|
|
2014-06-27 10:48:30 -04:00
|
|
|
present paginate(notes), with: Entities::CommitNote
|
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2014-06-27 10:48:30 -04:00
|
|
|
|
2016-12-12 07:04:13 -05:00
|
|
|
desc 'Cherry pick commit into a branch' do
|
|
|
|
detail 'This feature was introduced in GitLab 8.15'
|
2017-10-05 04:48:05 -04:00
|
|
|
success Entities::Commit
|
2016-12-12 07:04:13 -05:00
|
|
|
end
|
|
|
|
params do
|
2017-07-26 15:39:37 -04:00
|
|
|
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag to be cherry picked'
|
2018-09-11 18:02:09 -04:00
|
|
|
requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
|
2016-12-12 07:04:13 -05:00
|
|
|
end
|
2017-09-23 09:21:32 -04:00
|
|
|
post ':id/repository/commits/:sha/cherry_pick', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
|
2018-07-11 11:26:00 -04:00
|
|
|
authorize_push_to_branch!(params[:branch])
|
2016-12-12 07:04:13 -05:00
|
|
|
|
|
|
|
commit = user_project.commit(params[:sha])
|
|
|
|
not_found!('Commit') unless commit
|
|
|
|
|
2018-09-08 04:55:17 -04:00
|
|
|
find_branch!(params[:branch])
|
2016-12-12 07:04:13 -05:00
|
|
|
|
|
|
|
commit_params = {
|
|
|
|
commit: commit,
|
2017-02-16 19:24:56 -05:00
|
|
|
start_branch: params[:branch],
|
2017-04-19 20:37:44 -04:00
|
|
|
branch_name: params[:branch]
|
2016-12-12 07:04:13 -05:00
|
|
|
}
|
|
|
|
|
2018-11-09 11:34:17 -05:00
|
|
|
result = ::Commits::CherryPickService
|
|
|
|
.new(user_project, current_user, commit_params)
|
|
|
|
.execute
|
2016-12-12 07:04:13 -05:00
|
|
|
|
|
|
|
if result[:status] == :success
|
2018-11-09 11:34:17 -05:00
|
|
|
present user_project.repository.commit(result[:result]),
|
|
|
|
with: Entities::Commit
|
2016-12-12 07:04:13 -05:00
|
|
|
else
|
|
|
|
render_api_error!(result[:message], 400)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-06 23:27:14 -05:00
|
|
|
desc 'Revert a commit in a branch' do
|
2018-11-14 09:00:23 -05:00
|
|
|
detail 'This feature was introduced in GitLab 11.5'
|
2018-11-06 23:27:14 -05:00
|
|
|
success Entities::Commit
|
|
|
|
end
|
|
|
|
params do
|
|
|
|
requires :sha, type: String, desc: 'Commit SHA to revert'
|
|
|
|
requires :branch, type: String, desc: 'Target branch name', allow_blank: false
|
|
|
|
end
|
|
|
|
post ':id/repository/commits/:sha/revert', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
|
|
|
|
authorize_push_to_branch!(params[:branch])
|
|
|
|
|
|
|
|
commit = user_project.commit(params[:sha])
|
|
|
|
not_found!('Commit') unless commit
|
|
|
|
|
|
|
|
find_branch!(params[:branch])
|
|
|
|
|
|
|
|
commit_params = {
|
|
|
|
commit: commit,
|
|
|
|
start_branch: params[:branch],
|
|
|
|
branch_name: params[:branch]
|
|
|
|
}
|
|
|
|
|
|
|
|
result = ::Commits::RevertService
|
|
|
|
.new(user_project, current_user, commit_params)
|
|
|
|
.execute
|
|
|
|
|
|
|
|
if result[:status] == :success
|
|
|
|
present user_project.repository.commit(result[:result]),
|
|
|
|
with: Entities::Commit
|
|
|
|
else
|
|
|
|
render_api_error!(result[:message], 400)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-02-09 08:30:27 -05:00
|
|
|
desc 'Get all references a commit is pushed to' do
|
2017-10-24 06:28:06 -04:00
|
|
|
detail 'This feature was introduced in GitLab 10.6'
|
2018-02-09 08:30:27 -05:00
|
|
|
success Entities::BasicRef
|
2017-10-24 06:28:06 -04:00
|
|
|
end
|
|
|
|
params do
|
|
|
|
requires :sha, type: String, desc: 'A commit sha'
|
2018-02-13 14:22:37 -05:00
|
|
|
optional :type, type: String, values: %w[branch tag all], default: 'all', desc: 'Scope'
|
|
|
|
use :pagination
|
2017-10-24 06:28:06 -04:00
|
|
|
end
|
|
|
|
get ':id/repository/commits/:sha/refs', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
|
|
|
|
commit = user_project.commit(params[:sha])
|
|
|
|
not_found!('Commit') unless commit
|
|
|
|
|
2018-02-13 14:22:37 -05:00
|
|
|
refs = []
|
|
|
|
refs.concat(user_project.repository.branch_names_contains(commit.id).map {|name| { type: 'branch', name: name }}) unless params[:type] == 'tag'
|
|
|
|
refs.concat(user_project.repository.tag_names_contains(commit.id).map {|name| { type: 'tag', name: name }}) unless params[:type] == 'branch'
|
|
|
|
refs = Kaminari.paginate_array(refs)
|
2017-10-24 06:28:06 -04:00
|
|
|
|
2018-02-13 14:22:37 -05:00
|
|
|
present paginate(refs), with: Entities::BasicRef
|
2017-10-24 06:28:06 -04:00
|
|
|
end
|
|
|
|
|
2016-10-15 06:09:02 -04:00
|
|
|
desc 'Post comment to commit' do
|
|
|
|
success Entities::CommitNote
|
|
|
|
end
|
|
|
|
params do
|
2017-07-26 15:39:37 -04:00
|
|
|
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag on which to post a comment'
|
2016-10-15 06:09:02 -04:00
|
|
|
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'
|
2018-02-09 08:30:27 -05:00
|
|
|
requires :line_type, type: String, values: %w[new old], default: 'new', desc: 'The type of the line'
|
2016-10-15 06:09:02 -04:00
|
|
|
end
|
|
|
|
end
|
2017-09-23 09:21:32 -04:00
|
|
|
post ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
|
2016-10-15 06:09:02 -04:00
|
|
|
commit = user_project.commit(params[:sha])
|
2014-06-27 10:48:30 -04:00
|
|
|
not_found! 'Commit' unless commit
|
2016-10-15 06:09:02 -04:00
|
|
|
|
2014-06-27 10:48:30 -04:00
|
|
|
opts = {
|
|
|
|
note: params[:note],
|
|
|
|
noteable_type: 'Commit',
|
|
|
|
commit_id: commit.id
|
|
|
|
}
|
|
|
|
|
2016-10-15 06:09:02 -04:00
|
|
|
if params[:path]
|
2017-05-31 15:41:25 -04:00
|
|
|
commit.raw_diffs(limits: false).each do |diff|
|
2014-06-27 10:48:30 -04:00
|
|
|
next unless diff.new_path == params[:path]
|
2017-11-14 04:02:39 -05:00
|
|
|
|
2016-03-03 12:38:44 -05:00
|
|
|
lines = Gitlab::Diff::Parser.new.parse(diff.diff.each_line)
|
2014-06-27 10:48:30 -04:00
|
|
|
|
|
|
|
lines.each do |line|
|
2016-10-15 06:09:02 -04:00
|
|
|
next unless line.new_pos == params[:line] && line.type == params[:line_type]
|
2017-11-14 04:02:39 -05:00
|
|
|
|
2017-10-10 13:44:14 -04:00
|
|
|
break opts[:line_code] = Gitlab::Git.diff_line_code(diff.new_path, line.new_pos, line.old_pos)
|
2014-06-27 10:48:30 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
break if opts[:line_code]
|
|
|
|
end
|
2016-05-10 18:41:46 -04:00
|
|
|
|
|
|
|
opts[:type] = LegacyDiffNote.name if opts[:line_code]
|
2014-06-27 10:48:30 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
note = ::Notes::CreateService.new(user_project, current_user, opts).execute
|
|
|
|
|
|
|
|
if note.save
|
|
|
|
present note, with: Entities::CommitNote
|
|
|
|
else
|
2015-01-07 04:46:00 -05:00
|
|
|
render_api_error!("Failed to save note #{note.errors.messages}", 400)
|
2014-06-27 10:48:30 -04:00
|
|
|
end
|
|
|
|
end
|
2018-03-26 13:56:53 -04:00
|
|
|
|
|
|
|
desc 'Get Merge Requests associated with a commit' do
|
|
|
|
success Entities::MergeRequestBasic
|
|
|
|
end
|
|
|
|
params do
|
|
|
|
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag on which to find Merge Requests'
|
|
|
|
use :pagination
|
|
|
|
end
|
|
|
|
get ':id/repository/commits/:sha/merge_requests', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
|
|
|
|
commit = user_project.commit(params[:sha])
|
|
|
|
not_found! 'Commit' unless commit
|
|
|
|
|
|
|
|
present paginate(commit.merge_requests), with: Entities::MergeRequestBasic
|
|
|
|
end
|
2014-02-18 05:41:21 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|