2018-12-05 23:21:45 +05:30
# frozen_string_literal: true
2014-09-02 18:07:02 +05:30
class Projects :: BranchesController < Projects :: ApplicationController
2015-04-26 12:48:37 +05:30
include ActionView :: Helpers :: SanitizeHelper
2016-09-13 17:45:13 +05:30
include SortingHelper
2017-08-17 22:00:37 +05:30
2014-09-02 18:07:02 +05:30
# Authorize
2017-08-17 22:00:37 +05:30
before_action :require_non_empty_project , except : :create
2023-03-04 22:38:38 +05:30
before_action :authorize_read_code!
2017-08-17 22:00:37 +05:30
before_action :authorize_push_code! , only : [ :new , :create , :destroy , :destroy_all_merged ]
2014-09-02 18:07:02 +05:30
2018-03-27 19:54:05 +05:30
# Support legacy URLs
before_action :redirect_for_legacy_index_sort_or_search , only : [ :index ]
2019-12-21 20:55:43 +05:30
before_action :limit_diverging_commit_counts! , only : [ :diverging_commit_counts ]
2016-09-29 09:46:39 +05:30
2021-01-03 14:25:43 +05:30
feature_category :source_code_management
2021-12-11 22:18:48 +05:30
urgency :low , [ :index , :diverging_commit_counts , :create , :destroy ]
2021-01-03 14:25:43 +05:30
2018-03-27 19:54:05 +05:30
def index
2016-09-29 09:46:39 +05:30
respond_to do | format |
2017-08-17 22:00:37 +05:30
format . html do
2023-04-23 21:23:45 +05:30
@mode = fetch_mode
next render_404 unless @mode
@sort = sort_param || default_sort
2018-03-27 19:54:05 +05:30
@overview_max_branches = 5
# Fetch branches for the specified mode
fetch_branches_by_mode
2019-02-15 15:39:39 +05:30
@refs_pipelines = @project . ci_pipelines . latest_successful_for_refs ( @branches . map ( & :name ) )
2018-05-09 12:01:36 +05:30
@merged_branch_names = repository . merged_branch_names ( @branches . map ( & :name ) )
2021-01-29 00:20:46 +05:30
@branch_pipeline_statuses = Ci :: CommitStatusesFinder . new ( @project , repository , current_user , @branches ) . execute
2018-05-09 12:01:36 +05:30
2020-06-23 00:09:42 +05:30
# https://gitlab.com/gitlab-org/gitlab/-/issues/22851
2018-11-08 19:23:39 +05:30
Gitlab :: GitalyClient . allow_n_plus_1_calls do
render
end
2022-06-21 17:19:12 +05:30
rescue Gitlab :: Git :: CommandError
2021-11-18 22:05:49 +05:30
@gitaly_unavailable = true
2022-06-21 17:19:12 +05:30
render status : :service_unavailable
2017-08-17 22:00:37 +05:30
end
2016-09-29 09:46:39 +05:30
format . json do
2018-03-27 19:54:05 +05:30
branches = BranchesFinder . new ( @repository , params ) . execute
branches = Kaminari . paginate_array ( branches ) . page ( params [ :page ] )
render json : branches . map ( & :name )
2016-09-29 09:46:39 +05:30
end
end
2014-09-02 18:07:02 +05:30
end
2019-09-30 21:07:59 +05:30
def diverging_commit_counts
respond_to do | format |
format . json do
2020-01-01 13:55:28 +05:30
service = :: Branches :: DivergingCommitCountsService . new ( repository )
2019-09-30 21:07:59 +05:30
branches = BranchesFinder . new ( repository , params . permit ( names : [ ] ) ) . execute
Gitlab :: GitalyClient . allow_n_plus_1_calls do
2021-04-29 21:17:54 +05:30
render json : branches . to_h { | branch | [ branch . name , service . call ( branch ) ] }
2019-09-30 21:07:59 +05:30
end
end
end
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2014-09-02 18:07:02 +05:30
def create
2019-07-31 22:56:46 +05:30
branch_name = strip_tags ( sanitize ( params [ :branch_name ] ) )
2015-09-11 14:41:01 +05:30
branch_name = Addressable :: URI . unescape ( branch_name )
2016-06-02 11:05:42 +05:30
2018-03-17 18:26:18 +05:30
redirect_to_autodeploy = project . empty_repo? && project . deployment_platform . present?
2017-08-17 22:00:37 +05:30
2020-01-01 13:55:28 +05:30
result = :: Branches :: CreateService . new ( project , current_user )
2017-09-10 17:25:29 +05:30
. execute ( branch_name , ref )
2014-09-02 18:07:02 +05:30
2018-03-17 18:26:18 +05:30
success = ( result [ :status ] == :success )
if params [ :issue_iid ] && success
2019-09-30 21:07:59 +05:30
target_project = confidential_issue_project || @project
issue = IssuesFinder . new ( current_user , project_id : target_project . id ) . find_by ( iid : params [ :issue_iid ] )
SystemNoteService . new_issue_branch ( issue , target_project , current_user , branch_name , branch_project : @project ) if issue
2016-06-02 11:05:42 +05:30
end
2017-08-17 22:00:37 +05:30
respond_to do | format |
format . html do
2018-03-17 18:26:18 +05:30
if success
2017-08-17 22:00:37 +05:30
if redirect_to_autodeploy
redirect_to url_to_autodeploy_setup ( project , branch_name ) ,
notice : view_context . autodeploy_flash_notice ( branch_name )
else
2017-09-10 17:25:29 +05:30
redirect_to project_tree_path ( @project , branch_name )
2017-08-17 22:00:37 +05:30
end
else
@error = result [ :message ]
render action : 'new'
end
end
format . json do
2018-03-17 18:26:18 +05:30
if success
2017-09-10 17:25:29 +05:30
render json : { name : branch_name , url : project_tree_url ( @project , branch_name ) }
2017-08-17 22:00:37 +05:30
else
2023-05-27 22:25:52 +05:30
render json : result [ :message ] , status : :unprocessable_entity
2017-08-17 22:00:37 +05:30
end
end
2015-04-26 12:48:37 +05:30
end
2014-09-02 18:07:02 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2014-09-02 18:07:02 +05:30
def destroy
2021-12-11 22:18:48 +05:30
result = :: Branches :: DeleteService . new ( project , current_user ) . execute ( params [ :id ] )
2017-09-10 17:25:29 +05:30
2014-09-02 18:07:02 +05:30
respond_to do | format |
2015-04-26 12:48:37 +05:30
format . html do
2019-07-31 22:56:46 +05:30
flash_type = result . error? ? :alert : :notice
flash [ flash_type ] = result . message
2017-09-10 17:25:29 +05:30
2022-07-16 23:28:13 +05:30
redirect_back_or_default ( default : project_branches_path ( @project ) , options : { status : :see_other } )
2015-04-26 12:48:37 +05:30
end
2017-08-17 22:00:37 +05:30
2019-07-31 22:56:46 +05:30
format . js { head result . http_status }
format . json { render json : { message : result . message } , status : result . http_status }
2014-09-02 18:07:02 +05:30
end
end
2016-06-02 11:05:42 +05:30
2017-08-17 22:00:37 +05:30
def destroy_all_merged
2020-01-01 13:55:28 +05:30
:: Branches :: DeleteMergedService . new ( @project , current_user ) . async_execute
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
redirect_to project_branches_path ( @project ) ,
2019-07-07 11:18:12 +05:30
notice : _ ( 'Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes.' )
2017-08-17 22:00:37 +05:30
end
2016-06-02 11:05:42 +05:30
private
2023-04-23 21:23:45 +05:30
def sort_param
2022-04-04 11:22:00 +05:30
sort = params [ :sort ] . presence
unless sort . in? ( supported_sort_options )
flash . now [ :alert ] = _ ( " Unsupported sort value. " )
sort = nil
end
2021-02-22 17:27:13 +05:30
2022-04-04 11:22:00 +05:30
sort
end
def default_sort
2023-04-23 21:23:45 +05:30
'stale' == @mode ? SORT_UPDATED_OLDEST : SORT_UPDATED_RECENT
2021-02-22 17:27:13 +05:30
end
2022-04-04 11:22:00 +05:30
def supported_sort_options
2023-04-23 21:23:45 +05:30
[ nil , SORT_NAME , SORT_UPDATED_OLDEST , SORT_UPDATED_RECENT ]
2022-04-04 11:22:00 +05:30
end
2019-12-21 20:55:43 +05:30
# It can be expensive to calculate the diverging counts for each
# branch. Normally the frontend should be specifying a set of branch
# names, but prior to
# https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/32496, the
# frontend could omit this set. To prevent excessive I/O, we require
# that a list of names be specified.
def limit_diverging_commit_counts!
limit = Kaminari . config . default_per_page
# If we don't have many branches in the repository, then go ahead.
return if project . repository . branch_count < = limit
return if params [ :names ] . present? && Array ( params [ :names ] ) . length < = limit
render json : { error : " Specify at least one and at most #{ limit } branch names " } , status : :unprocessable_entity
end
2016-06-02 11:05:42 +05:30
def ref
if params [ :ref ]
2019-07-31 22:56:46 +05:30
ref_escaped = strip_tags ( sanitize ( params [ :ref ] ) )
2016-06-02 11:05:42 +05:30
Addressable :: URI . unescape ( ref_escaped )
else
2021-09-04 01:27:46 +05:30
@project . default_branch_or_main
2016-06-02 11:05:42 +05:30
end
end
2017-08-17 22:00:37 +05:30
def url_to_autodeploy_setup ( project , branch_name )
2017-09-10 17:25:29 +05:30
project_new_blob_path (
2017-08-17 22:00:37 +05:30
project ,
branch_name ,
file_name : '.gitlab-ci.yml' ,
commit_message : 'Set up auto deploy' ,
target_branch : branch_name ,
context : 'autodeploy'
)
end
2018-03-27 19:54:05 +05:30
def redirect_for_legacy_index_sort_or_search
# Normalize a legacy URL with redirect
if request . format != :json && ! params [ :state ] . presence && [ :sort , :search , :page ] . any? { | key | params [ key ] . presence }
2019-07-07 11:18:12 +05:30
redirect_to project_branches_filtered_path ( @project , state : 'all' ) , notice : _ ( 'Update your bookmarked URLs as filtered/sorted branches URL has been changed.' )
2018-03-27 19:54:05 +05:30
end
end
def fetch_branches_by_mode
2021-02-22 17:27:13 +05:30
return fetch_branches_for_overview if @mode == 'overview'
2021-03-11 19:13:27 +05:30
@branches , @prev_path , @next_path =
Projects :: BranchesByModeService . new ( @project , params . merge ( sort : @sort , mode : @mode ) ) . execute
2021-02-22 17:27:13 +05:30
end
def fetch_branches_for_overview
# Here we get one more branch to indicate if there are more data we're not showing
limit = @overview_max_branches + 1
2021-09-04 01:27:46 +05:30
@active_branches =
2023-04-23 21:23:45 +05:30
BranchesFinder . new ( @repository , { per_page : limit , sort : SORT_UPDATED_RECENT } )
2021-09-04 01:27:46 +05:30
. execute ( gitaly_pagination : true ) . select ( & :active? )
@stale_branches =
2023-04-23 21:23:45 +05:30
BranchesFinder . new ( @repository , { per_page : limit , sort : SORT_UPDATED_OLDEST } )
2021-09-04 01:27:46 +05:30
. execute ( gitaly_pagination : true ) . select ( & :stale? )
2021-02-22 17:27:13 +05:30
@branches = @active_branches + @stale_branches
2018-03-27 19:54:05 +05:30
end
2019-09-30 21:07:59 +05:30
2023-04-23 21:23:45 +05:30
def fetch_mode
state = params [ :state ] . presence
return 'overview' unless state
state . presence_in ( %w[ active stale all overview ] )
end
2019-09-30 21:07:59 +05:30
def confidential_issue_project
return if params [ :confidential_issue_project_id ] . blank?
confidential_issue_project = Project . find ( params [ :confidential_issue_project_id ] )
return unless can? ( current_user , :update_issue , confidential_issue_project )
confidential_issue_project
end
2014-09-02 18:07:02 +05:30
end