cedbb3366b
https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24034 introduced a regression where only the first 20 branches were used to determine whether a branch has been merged because the pagination was applied incorrectly. Requesting the second page of branches via the API would always have the wrong merge status. We fix this by properly paginating the branches before requesting their merge status. Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/56250
179 lines
6.5 KiB
Ruby
179 lines
6.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'mime/types'
|
|
|
|
module API
|
|
class Branches < Grape::API
|
|
include PaginationParams
|
|
|
|
BRANCH_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(branch: API::NO_SLASH_URL_PART_REGEX)
|
|
|
|
before { authorize! :download_code, user_project }
|
|
|
|
helpers do
|
|
params :filter_params do
|
|
optional :search, type: String, desc: 'Return list of branches matching the search criteria'
|
|
optional :sort, type: String, desc: 'Return list of branches sorted by the given field'
|
|
end
|
|
end
|
|
|
|
params do
|
|
requires :id, type: String, desc: 'The ID of a project'
|
|
end
|
|
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
|
desc 'Get a project repository branches' do
|
|
success Entities::Branch
|
|
end
|
|
params do
|
|
use :pagination
|
|
use :filter_params
|
|
end
|
|
get ':id/repository/branches' do
|
|
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42329')
|
|
|
|
repository = user_project.repository
|
|
|
|
branches = BranchesFinder.new(repository, declared_params(include_missing: false)).execute
|
|
branches = paginate(::Kaminari.paginate_array(branches))
|
|
merged_branch_names = repository.merged_branch_names(branches.map(&:name))
|
|
|
|
present(
|
|
branches,
|
|
with: Entities::Branch,
|
|
current_user: current_user,
|
|
project: user_project,
|
|
merged_branch_names: merged_branch_names
|
|
)
|
|
end
|
|
|
|
resource ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
|
|
desc 'Get a single branch' do
|
|
success Entities::Branch
|
|
end
|
|
params do
|
|
requires :branch, type: String, desc: 'The name of the branch'
|
|
end
|
|
head do
|
|
user_project.repository.branch_exists?(params[:branch]) ? status(204) : status(404)
|
|
end
|
|
get do
|
|
branch = find_branch!(params[:branch])
|
|
|
|
present branch, with: Entities::Branch, current_user: current_user, project: user_project
|
|
end
|
|
end
|
|
|
|
# Note: This API will be deprecated in favor of the protected branches API.
|
|
# Note: The internal data model moved from `developers_can_{merge,push}` to `allowed_to_{merge,push}`
|
|
# in `gitlab-org/gitlab-ce!5081`. The API interface has not been changed (to maintain compatibility),
|
|
# but it works with the changed data model to infer `developers_can_merge` and `developers_can_push`.
|
|
desc 'Protect a single branch' do
|
|
success Entities::Branch
|
|
end
|
|
params do
|
|
requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
|
|
optional :developers_can_push, type: Boolean, desc: 'Flag if developers can push to that branch'
|
|
optional :developers_can_merge, type: Boolean, desc: 'Flag if developers can merge to that branch'
|
|
end
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
|
put ':id/repository/branches/:branch/protect', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
|
|
authorize_admin_project
|
|
|
|
branch = find_branch!(params[:branch])
|
|
|
|
protected_branch = user_project.protected_branches.find_by(name: branch.name)
|
|
|
|
protected_branch_params = {
|
|
name: branch.name,
|
|
developers_can_push: params[:developers_can_push],
|
|
developers_can_merge: params[:developers_can_merge]
|
|
}
|
|
|
|
service_args = [user_project, current_user, protected_branch_params]
|
|
|
|
protected_branch = if protected_branch
|
|
::ProtectedBranches::LegacyApiUpdateService.new(*service_args).execute(protected_branch)
|
|
else
|
|
::ProtectedBranches::LegacyApiCreateService.new(*service_args).execute
|
|
end
|
|
|
|
if protected_branch.valid?
|
|
present branch, with: Entities::Branch, current_user: current_user, project: user_project
|
|
else
|
|
render_api_error!(protected_branch.errors.full_messages, 422)
|
|
end
|
|
end
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
|
|
|
# Note: This API will be deprecated in favor of the protected branches API.
|
|
desc 'Unprotect a single branch' do
|
|
success Entities::Branch
|
|
end
|
|
params do
|
|
requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
|
|
end
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
|
put ':id/repository/branches/:branch/unprotect', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
|
|
authorize_admin_project
|
|
|
|
branch = find_branch!(params[:branch])
|
|
protected_branch = user_project.protected_branches.find_by(name: branch.name)
|
|
protected_branch&.destroy
|
|
|
|
present branch, with: Entities::Branch, current_user: current_user, project: user_project
|
|
end
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
|
|
|
desc 'Create branch' do
|
|
success Entities::Branch
|
|
end
|
|
params do
|
|
requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
|
|
requires :ref, type: String, desc: 'Create branch from commit sha or existing branch', allow_blank: false
|
|
end
|
|
post ':id/repository/branches' do
|
|
authorize_push_project
|
|
|
|
result = CreateBranchService.new(user_project, current_user)
|
|
.execute(params[:branch], params[:ref])
|
|
|
|
if result[:status] == :success
|
|
present result[:branch],
|
|
with: Entities::Branch,
|
|
current_user: current_user,
|
|
project: user_project
|
|
else
|
|
render_api_error!(result[:message], 400)
|
|
end
|
|
end
|
|
|
|
desc 'Delete a branch'
|
|
params do
|
|
requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
|
|
end
|
|
delete ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
|
|
authorize_push_project
|
|
|
|
branch = find_branch!(params[:branch])
|
|
|
|
commit = user_project.repository.commit(branch.dereferenced_target)
|
|
|
|
destroy_conditionally!(commit, last_updated: commit.authored_date) do
|
|
result = DeleteBranchService.new(user_project, current_user)
|
|
.execute(params[:branch])
|
|
|
|
if result[:status] != :success
|
|
render_api_error!(result[:message], result[:return_code])
|
|
end
|
|
end
|
|
end
|
|
|
|
desc 'Delete all merged branches'
|
|
delete ':id/repository/merged_branches' do
|
|
DeleteMergedBranchesService.new(user_project, current_user).async_execute
|
|
|
|
accepted!
|
|
end
|
|
end
|
|
end
|
|
end
|