2018-09-29 22:34:47 +00:00
# frozen_string_literal: true
2014-02-18 10:41:21 +00:00
require 'mime/types'
module API
2020-10-15 00:08:42 +00:00
class Commits < :: API :: Base
2016-11-21 19:15:46 +00:00
include PaginationParams
2020-10-29 12:08:50 +00:00
feature_category :source_code_management
2019-06-24 22:12:42 +00:00
before do
require_repository_enabled!
authorize! :download_code , user_project
end
2014-02-18 10:41:21 +00:00
2018-07-11 15:26:00 +00:00
helpers do
def user_access
2020-07-21 18:09:45 +00:00
@user_access || = Gitlab :: UserAccess . new ( current_user , container : user_project )
2018-07-11 15:26:00 +00:00
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 10:09:02 +00:00
params do
requires :id , type : String , desc : 'The ID of a project'
end
2021-11-12 18:12:20 +00:00
resource :projects , requirements : API :: NAMESPACE_OR_PROJECT_REQUIREMENTS , urgency : :low do
2016-10-15 10:09:02 +00:00
desc 'Get a project repository commits' do
2017-10-05 08:48:05 +00:00
success Entities :: Commit
2016-10-15 10:09:02 +00:00
end
params do
2022-02-11 00:17:04 +00:00
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 12:15:35 +00: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 21:06:29 +00:00
optional :first_parent , type : Boolean , desc : 'Only include the first parent of merges'
2020-02-18 00:09:20 +00:00
optional :order , type : String , desc : 'List commits in order' , default : 'default' , values : %w[ default topo ]
2021-06-07 09:10:26 +00:00
optional :trailers , type : Boolean , desc : 'Parse and include Git trailers for every commit' , default : false
2017-03-03 02:06:06 +00:00
use :pagination
2016-10-15 10:09:02 +00:00
end
2021-11-12 18:12:20 +00:00
get ':id/repository/commits' , urgency : :low do
2022-02-18 21:15:49 +00:00
not_found! 'Repository' unless user_project . repository_exists?
2018-06-06 12:15:35 +00:00
path = params [ :path ]
2017-03-03 02:06:06 +00:00
before = params [ :until ]
2018-06-06 12:15:35 +00:00
after = params [ :since ]
2021-07-23 12:09:05 +00:00
ref = params [ :ref_name ] . presence || user_project . default_branch unless params [ :all ]
2017-02-05 09:30:36 +00:00
offset = ( params [ :page ] - 1 ) * params [ :per_page ]
2018-06-06 12:15:35 +00:00
all = params [ :all ]
with_stats = params [ :with_stats ]
2019-09-19 21:06:29 +00:00
first_parent = params [ :first_parent ]
2020-02-13 21:08:59 +00:00
order = params [ :order ]
2016-10-15 10:09:02 +00:00
commits = user_project . repository . commits ( ref ,
2017-03-03 02:06:06 +00:00
path : path ,
2016-10-15 10:09:02 +00:00
limit : params [ :per_page ] ,
offset : offset ,
2017-03-03 02:06:06 +00:00
before : before ,
2018-02-16 16:39:23 +00:00
after : after ,
2019-09-19 21:06:29 +00:00
all : all ,
2020-02-13 21:08:59 +00:00
first_parent : first_parent ,
2021-06-07 09:10:26 +00:00
order : order ,
trailers : params [ :trailers ] )
2014-02-18 10:41:21 +00:00
2020-09-30 15:09:46 +00:00
serializer = with_stats ? Entities :: CommitWithStats : Entities :: Commit
2017-03-03 02:06:06 +00:00
2020-10-13 06:09:09 +00:00
# This tells kaminari that there is 1 more commit after the one we've
# loaded, meaning there will be a next page, if the currently loaded set
# of commits is equal to the requested page size.
commit_count = offset + commits . size + 1
paginated_commits = Kaminari . paginate_array ( commits , total_count : commit_count )
2017-01-20 15:04:16 +00:00
2020-10-13 06:09:09 +00:00
present paginate ( paginated_commits , exclude_total_headers : true ) , with : serializer
2014-02-18 10:41:21 +00:00
end
2016-08-29 23:58:32 +00:00
desc 'Commit multiple file changes as one commit' do
2017-10-05 08:48:05 +00:00
success Entities :: CommitDetail
2016-08-29 23:58:32 +00:00
detail 'This feature was introduced in GitLab 8.13'
end
params do
2019-06-13 10:44:41 +00: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 23:58:32 +00:00
requires :commit_message , type : String , desc : 'Commit message'
2018-09-23 10:48:29 +00: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 10:44:41 +00: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 23:58:32 +00:00
optional :author_email , type : String , desc : 'Author email for commit'
optional :author_name , type : String , desc : 'Author name for commit'
2018-09-27 15:35:15 +00:00
optional :stats , type : Boolean , default : true , desc : 'Include commit stats'
2019-06-13 10:44:41 +00: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 23:58:32 +00:00
end
2017-07-26 19:39:37 +00:00
post ':id/repository/commits' do
2019-05-31 14:21:15 +00: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 15:26:00 +00:00
authorize_push_to_branch! ( params [ :branch ] )
2016-08-29 23:58:32 +00:00
2017-08-04 17:25:35 +00:00
attrs = declared_params
attrs [ :branch_name ] = attrs . delete ( :branch )
2019-06-13 10:44:41 +00:00
attrs [ :start_branch ] || = attrs [ :branch_name ] unless attrs [ :start_sha ]
2019-05-31 14:21:15 +00:00
attrs [ :start_project ] = start_project if start_project
2017-02-02 14:24:30 +00:00
2016-08-29 23:58:32 +00:00
result = :: Files :: MultiService . new ( user_project , current_user , attrs ) . execute
if result [ :status ] == :success
2017-07-03 22:46:52 +00:00
commit_detail = user_project . repository . commit ( result [ :result ] )
2018-10-01 11:15:31 +00:00
2020-09-01 03:10:22 +00:00
if find_user_from_warden
Gitlab :: UsageDataCounters :: WebIdeCounter . increment_commits_count
Gitlab :: UsageDataCounters :: EditorUniqueCounter . track_web_ide_edit_action ( author : current_user )
end
2018-10-01 11:15:31 +00:00
2018-09-27 15:35:15 +00:00
present commit_detail , with : Entities :: CommitDetail , stats : params [ :stats ]
2016-08-29 23:58:32 +00:00
else
render_api_error! ( result [ :message ] , 400 )
end
end
2016-10-15 10:09:02 +00:00
desc 'Get a specific commit of a project' do
2017-10-05 08:48:05 +00:00
success Entities :: CommitDetail
2017-07-26 19:39:37 +00:00
failure [ [ 404 , 'Commit Not Found' ] ]
2016-10-15 10:09:02 +00:00
end
params do
requires :sha , type : String , desc : 'A commit sha, or the name of a branch or tag'
2018-01-09 11:36:12 +00:00
optional :stats , type : Boolean , default : true , desc : 'Include commit stats'
2016-10-15 10:09:02 +00:00
end
2017-09-23 13:21:32 +00:00
get ':id/repository/commits/:sha' , requirements : API :: COMMIT_ENDPOINT_REQUIREMENTS do
2016-10-15 10:09:02 +00:00
commit = user_project . commit ( params [ :sha ] )
2017-07-26 19:39:37 +00:00
not_found! 'Commit' unless commit
2016-10-15 10:09:02 +00:00
2020-01-30 21:08:47 +00:00
present commit , with : Entities :: CommitDetail , stats : params [ :stats ] , current_user : current_user
2014-02-18 10:41:21 +00:00
end
2016-10-15 10:09:02 +00:00
desc 'Get the diff for a specific commit of a project' do
2017-07-26 19:39:37 +00:00
failure [ [ 404 , 'Commit Not Found' ] ]
2016-10-15 10:09:02 +00:00
end
params do
requires :sha , type : String , desc : 'A commit sha, or the name of a branch or tag'
2018-02-19 03:17:23 +00:00
use :pagination
2016-10-15 10:09:02 +00:00
end
2021-11-12 18:12:20 +00:00
get ':id/repository/commits/:sha/diff' , requirements : API :: COMMIT_ENDPOINT_REQUIREMENTS , urgency : :low do
2016-10-15 10:09:02 +00:00
commit = user_project . commit ( params [ :sha ] )
2017-07-26 19:39:37 +00:00
not_found! 'Commit' unless commit
2016-10-15 10:09:02 +00:00
2019-11-05 15:06:17 +00:00
raw_diffs = :: Kaminari . paginate_array ( commit . diffs ( expanded : true ) . diffs . to_a )
2018-02-19 03:17:23 +00:00
present paginate ( raw_diffs ) , with : Entities :: Diff
2014-02-18 10:41:21 +00:00
end
2014-06-27 14:48:30 +00:00
2016-10-15 10:09:02 +00:00
desc " Get a commit's comments " do
success Entities :: CommitNote
2017-07-26 19:39:37 +00:00
failure [ [ 404 , 'Commit Not Found' ] ]
2016-10-15 10:09:02 +00:00
end
params do
2016-11-21 19:15:46 +00:00
use :pagination
2016-10-15 10:09:02 +00:00
requires :sha , type : String , desc : 'A commit sha, or the name of a branch or tag'
end
2017-09-23 13:21:32 +00:00
get ':id/repository/commits/:sha/comments' , requirements : API :: COMMIT_ENDPOINT_REQUIREMENTS do
2016-10-15 10:09:02 +00:00
commit = user_project . commit ( params [ :sha ] )
2014-06-27 14:48:30 +00:00
not_found! 'Commit' unless commit
2021-03-31 00:09:32 +00:00
notes = commit . notes . with_api_entity_associations . fresh
2016-10-15 10:09:02 +00:00
2014-06-27 14:48:30 +00:00
present paginate ( notes ) , with : Entities :: CommitNote
end
2016-12-12 12:04:13 +00:00
desc 'Cherry pick commit into a branch' do
detail 'This feature was introduced in GitLab 8.15'
2017-10-05 08:48:05 +00:00
success Entities :: Commit
2016-12-12 12:04:13 +00:00
end
params do
2017-07-26 19:39:37 +00:00
requires :sha , type : String , desc : 'A commit sha, or the name of a branch or tag to be cherry picked'
2018-09-11 22:02:09 +00:00
requires :branch , type : String , desc : 'The name of the branch' , allow_blank : false
2020-07-30 18:09:39 +00:00
optional :dry_run , type : Boolean , default : false , desc : " Does not commit any changes "
2021-05-27 15:10:39 +00:00
optional :message , type : String , desc : 'A custom commit message to use for the picked commit'
2016-12-12 12:04:13 +00:00
end
2017-09-23 13:21:32 +00:00
post ':id/repository/commits/:sha/cherry_pick' , requirements : API :: COMMIT_ENDPOINT_REQUIREMENTS do
2018-07-11 15:26:00 +00:00
authorize_push_to_branch! ( params [ :branch ] )
2016-12-12 12:04:13 +00:00
commit = user_project . commit ( params [ :sha ] )
not_found! ( 'Commit' ) unless commit
2018-09-08 08:55:17 +00:00
find_branch! ( params [ :branch ] )
2016-12-12 12:04:13 +00:00
commit_params = {
commit : commit ,
2017-02-17 00:24:56 +00:00
start_branch : params [ :branch ] ,
2020-07-30 18:09:39 +00:00
branch_name : params [ :branch ] ,
2021-05-27 15:10:39 +00:00
dry_run : params [ :dry_run ] ,
message : params [ :message ]
2016-12-12 12:04:13 +00:00
}
2018-11-09 16:34:17 +00:00
result = :: Commits :: CherryPickService
. new ( user_project , current_user , commit_params )
. execute
2016-12-12 12:04:13 +00:00
if result [ :status ] == :success
2020-07-30 18:09:39 +00:00
if params [ :dry_run ]
present dry_run : :success
status :ok
else
present user_project . repository . commit ( result [ :result ] ) ,
with : Entities :: Commit
end
2016-12-12 12:04:13 +00:00
else
2020-07-30 18:09:39 +00:00
response = result . slice ( :message , :error_code )
response [ :dry_run ] = :error if params [ :dry_run ]
error! ( response , 400 , header )
2016-12-12 12:04:13 +00:00
end
end
2018-11-07 04:27:14 +00:00
desc 'Revert a commit in a branch' do
2018-11-14 14:00:23 +00:00
detail 'This feature was introduced in GitLab 11.5'
2018-11-07 04:27:14 +00: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
2020-07-30 18:09:39 +00:00
optional :dry_run , type : Boolean , default : false , desc : " Does not commit any changes "
2018-11-07 04:27:14 +00:00
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 ] ,
2020-07-30 18:09:39 +00:00
branch_name : params [ :branch ] ,
dry_run : params [ :dry_run ]
2018-11-07 04:27:14 +00:00
}
result = :: Commits :: RevertService
. new ( user_project , current_user , commit_params )
. execute
if result [ :status ] == :success
2020-07-30 18:09:39 +00:00
if params [ :dry_run ]
present dry_run : :success
status :ok
else
present user_project . repository . commit ( result [ :result ] ) ,
with : Entities :: Commit
end
2018-11-07 04:27:14 +00:00
else
2020-07-30 18:09:39 +00:00
response = result . slice ( :message , :error_code )
response [ :dry_run ] = :error if params [ :dry_run ]
error! ( response , 400 , header )
2018-11-07 04:27:14 +00:00
end
end
2018-02-09 13:30:27 +00:00
desc 'Get all references a commit is pushed to' do
2017-10-24 10:28:06 +00:00
detail 'This feature was introduced in GitLab 10.6'
2018-02-09 13:30:27 +00:00
success Entities :: BasicRef
2017-10-24 10:28:06 +00:00
end
params do
requires :sha , type : String , desc : 'A commit sha'
2018-02-13 19:22:37 +00:00
optional :type , type : String , values : %w[ branch tag all ] , default : 'all' , desc : 'Scope'
use :pagination
2017-10-24 10:28:06 +00:00
end
2021-11-12 18:12:20 +00:00
get ':id/repository/commits/:sha/refs' , requirements : API :: COMMIT_ENDPOINT_REQUIREMENTS , urgency : :low do
2017-10-24 10:28:06 +00:00
commit = user_project . commit ( params [ :sha ] )
not_found! ( 'Commit' ) unless commit
2018-02-13 19:22:37 +00: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 10:28:06 +00:00
2018-02-13 19:22:37 +00:00
present paginate ( refs ) , with : Entities :: BasicRef
2017-10-24 10:28:06 +00:00
end
2016-10-15 10:09:02 +00:00
desc 'Post comment to commit' do
success Entities :: CommitNote
end
params do
2017-07-26 19:39:37 +00: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 10:09:02 +00: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 13:30:27 +00:00
requires :line_type , type : String , values : %w[ new old ] , default : 'new' , desc : 'The type of the line'
2016-10-15 10:09:02 +00:00
end
end
2017-09-23 13:21:32 +00:00
post ':id/repository/commits/:sha/comments' , requirements : API :: COMMIT_ENDPOINT_REQUIREMENTS do
2016-10-15 10:09:02 +00:00
commit = user_project . commit ( params [ :sha ] )
2014-06-27 14:48:30 +00:00
not_found! 'Commit' unless commit
2016-10-15 10:09:02 +00:00
2014-06-27 14:48:30 +00:00
opts = {
note : params [ :note ] ,
noteable_type : 'Commit' ,
commit_id : commit . id
}
2016-10-15 10:09:02 +00:00
if params [ :path ]
2017-05-31 19:41:25 +00:00
commit . raw_diffs ( limits : false ) . each do | diff |
2014-06-27 14:48:30 +00:00
next unless diff . new_path == params [ :path ]
2017-11-14 09:02:39 +00:00
2016-03-03 17:38:44 +00:00
lines = Gitlab :: Diff :: Parser . new . parse ( diff . diff . each_line )
2014-06-27 14:48:30 +00:00
lines . each do | line |
2021-08-25 09:10:52 +00:00
next unless line . line == params [ :line ] && line . type == params [ :line_type ]
2017-11-14 09:02:39 +00:00
2017-10-10 17:44:14 +00:00
break opts [ :line_code ] = Gitlab :: Git . diff_line_code ( diff . new_path , line . new_pos , line . old_pos )
2014-06-27 14:48:30 +00:00
end
break if opts [ :line_code ]
end
2016-05-10 22:41:46 +00:00
opts [ :type ] = LegacyDiffNote . name if opts [ :line_code ]
2014-06-27 14:48:30 +00:00
end
note = :: Notes :: CreateService . new ( user_project , current_user , opts ) . execute
if note . save
present note , with : Entities :: CommitNote
else
2015-01-07 09:46:00 +00:00
render_api_error! ( " Failed to save note #{ note . errors . messages } " , 400 )
2014-06-27 14:48:30 +00:00
end
end
2018-03-26 17:56:53 +00: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
2021-11-12 18:12:20 +00:00
get ':id/repository/commits/:sha/merge_requests' , requirements : API :: COMMIT_ENDPOINT_REQUIREMENTS , urgency : :low do
2019-01-25 07:44:50 +00:00
authorize! :read_merge_request , user_project
2018-03-26 17:56:53 +00:00
commit = user_project . commit ( params [ :sha ] )
not_found! 'Commit' unless commit
2019-01-25 09:22:48 +00:00
commit_merge_requests = MergeRequestsFinder . new (
current_user ,
project_id : user_project . id ,
commit_sha : commit . sha
2021-03-24 15:09:19 +00:00
) . execute . with_api_entity_associations
2019-01-25 09:22:48 +00:00
present paginate ( commit_merge_requests ) , with : Entities :: MergeRequestBasic
2018-03-26 17:56:53 +00:00
end
2019-02-07 17:09:14 +00:00
2020-04-03 18:10:03 +00:00
desc " Get a commit's signature " do
2019-02-07 17:09:14 +00:00
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
2020-04-03 18:10:03 +00:00
not_found! 'Signature' unless commit . has_signature?
2019-02-07 17:09:14 +00:00
2020-04-03 18:10:03 +00:00
present commit , with : Entities :: CommitSignature
2019-02-07 17:09:14 +00:00
end
2014-02-18 10:41:21 +00:00
end
end
end