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
2019-06-24 18:12:42 -04:00
before do
require_repository_enabled!
authorize! :download_code , user_project
end
2014-02-18 05:41:21 -05:00
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'
2019-09-19 17:06:29 -04:00
optional :first_parent , type : Boolean , desc : 'Only include the first parent of merges'
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 ]
2019-08-10 13:11:20 -04:00
ref = params [ :ref_name ] . presence || 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 ]
2019-09-19 17:06:29 -04:00
first_parent = params [ :first_parent ]
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 ,
2019-09-19 17:06:29 -04:00
all : all ,
first_parent : first_parent )
2014-02-18 05:41:21 -05:00
2017-03-02 21:06:06 -05:00
commit_count =
2019-09-19 17:06:29 -04:00
if all || path || before || after || first_parent
user_project . repository . count_commits ( ref : ref , path : path , before : before , after : after , all : all , first_parent : first_parent )
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
2019-06-13 06:44:41 -04:00
requires :branch , type : String , desc : 'Name of the branch to commit into. To create a new branch, also provide either `start_branch` or `start_sha`, and optionally `start_project`.' , 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
2019-06-13 06:44:41 -04:00
optional :start_branch , type : String , desc : 'Name of the branch to start the new branch from'
optional :start_sha , type : String , desc : 'SHA of the commit to start the new branch from'
mutually_exclusive :start_branch , :start_sha
optional :start_project , types : [ Integer , String ] , desc : 'The ID or path of the project to start the new branch 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'
2019-06-13 06:44:41 -04:00
optional :force , type : Boolean , default : false , desc : 'When `true` overwrites the target branch with a new commit based on the `start_branch` or `start_sha`'
2016-08-29 19:58:32 -04:00
end
2017-07-26 15:39:37 -04:00
post ':id/repository/commits' do
2019-05-31 10:21:15 -04:00
if params [ :start_project ]
start_project = find_project! ( params [ :start_project ] )
unless user_project . forked_from? ( start_project )
forbidden! ( " Project is not included in the fork network for #{ start_project . full_name } " )
end
end
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 )
2019-06-13 06:44:41 -04:00
attrs [ :start_branch ] || = attrs [ :branch_name ] unless attrs [ :start_sha ]
2019-05-31 10:21:15 -04:00
attrs [ :start_project ] = start_project if start_project
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
2019-07-17 19:45:35 -04:00
Gitlab :: UsageDataCounters :: WebIdeCounter . increment_commits_count 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
2019-11-05 10:06:17 -05:00
raw_diffs = :: Kaminari . paginate_array ( commit . diffs ( expanded : true ) . diffs . to_a )
2018-02-18 22:17:23 -05:00
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
2019-11-13 22:06:25 -05:00
error! ( result . slice ( :message , :error_code ) , 400 , header )
2016-12-12 07:04:13 -05:00
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
2019-11-13 22:06:25 -05:00
error! ( result . slice ( :message , :error_code ) , 400 , header )
2018-11-06 23:27:14 -05:00
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
2019-01-25 02:44:50 -05:00
authorize! :read_merge_request , user_project
2018-03-26 13:56:53 -04:00
commit = user_project . commit ( params [ :sha ] )
not_found! 'Commit' unless commit
2019-01-25 04:22:48 -05:00
commit_merge_requests = MergeRequestsFinder . new (
current_user ,
project_id : user_project . id ,
commit_sha : commit . sha
) . execute
present paginate ( commit_merge_requests ) , with : Entities :: MergeRequestBasic
2018-03-26 13:56:53 -04:00
end
2019-02-07 12:09:14 -05:00
desc " Get a commit's GPG signature " do
success Entities :: CommitSignature
end
params do
requires :sha , type : String , desc : 'A commit sha, or the name of a branch or tag'
end
get ':id/repository/commits/:sha/signature' , requirements : API :: COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project . commit ( params [ :sha ] )
not_found! 'Commit' unless commit
signature = commit . signature
not_found! 'GPG Signature' unless signature
present signature , with : Entities :: CommitSignature
end
2014-02-18 05:41:21 -05:00
end
end
end