Class: MergeRequest
- Inherits:
-
ApplicationRecord
show all
- Extended by:
- Gitlab::Utils::Override
- Includes:
- Approvable, AtomicInternalId, DeprecatedAssignee, EachBatch, FromUnion, Gitlab::Utils::StrongMemoize, IdInOrdered, IgnorableColumns, IidRoutes, Issuable, LabelEventable, ManualInverseAssociation, MilestoneEventable, Noteable, Presentable, ReactiveCaching, Referable, ShaAttribute, Spammable, StateEventable, ThrottledTouch, TimeTrackable, Todoable
- Defined in:
- app/models/merge_request.rb
Defined Under Namespace
Classes: ApprovalRemovalSettings, CleanupSchedule, DiffCommitUser, Metrics, MetricsFinder
Constant Summary
collapse
- SORTING_PREFERENCE_FIELD =
:merge_requests_sort
- KNOWN_MERGE_PARAMS =
[
:auto_merge_strategy,
:should_remove_source_branch,
:force_remove_source_branch,
:commit_message,
:squash_commit_message,
:sha,
:skip_ci
].freeze
- RebaseLockTimeout =
Class.new(StandardError)
- DRAFT_REGEX =
/\A*#{Gitlab::Regex.merge_request_draft}+\s*/i
- MAX_RECENT_DIFF_HEAD_SHAS =
100
ReactiveCaching::ExceededReactiveCacheLimit, ReactiveCaching::InvalidateReactiveCache, ReactiveCaching::WORK_TYPE
ThrottledTouch::TOUCH_INTERVAL
Constants included
from Noteable
Noteable::MAX_NOTES_LIMIT
Constants included
from Issuable
Issuable::DESCRIPTION_HTML_LENGTH_MAX, Issuable::DESCRIPTION_LENGTH_MAX, Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS, Issuable::SEARCHABLE_FIELDS, Issuable::STATE_ID_MAP, Issuable::TITLE_HTML_LENGTH_MAX, Issuable::TITLE_LENGTH_MAX
Constants included
from Taskable
Taskable::COMPLETED, Taskable::COMPLETE_PATTERN, Taskable::INCOMPLETE, Taskable::INCOMPLETE_PATTERN, Taskable::ITEM_PATTERN, Taskable::ITEM_PATTERN_UNTRUSTED, Taskable::REGEX
CacheMarkdownField::INVALIDATED_BY
Constants included
from Redactable
Redactable::UNSUBSCRIBE_PATTERN
Gitlab::SQL::Pattern::MIN_CHARS_FOR_PARTIAL_MATCHING, Gitlab::SQL::Pattern::REGEX_QUOTED_TERM
AtomicInternalId::MissingValueError
ApplicationRecord::MAX_PLUCK
ResetOnUnionError::MAX_RESET_PERIOD
Instance Attribute Summary collapse
Attributes included from Noteable
#system_note_timestamp
#transitioning
Attributes included from Importable
#imported, #importing
#skip_markdown_cache_validation
Class Method Summary
collapse
Instance Method Summary
collapse
-
#actual_head_pipeline ⇒ Object
Use this method whenever you need to make sure the head_pipeline is synced with the branch head commit, for example checking if a merge request can be merged.
-
#actual_head_pipeline_active? ⇒ Boolean
-
#actual_head_pipeline_success? ⇒ Boolean
-
#all_commit_shas ⇒ Object
Note that this could also return SHA from now dangling commits.
-
#all_commits ⇒ Object
-
#all_pipelines ⇒ Object
-
#allow_collaboration ⇒ Object
(also: #allow_collaboration?)
-
#allows_multiple_reviewers? ⇒ Boolean
-
#allows_reviewers? ⇒ Boolean
-
#async_cleanup_refs(only: :all) ⇒ Object
-
#auto_merge_strategy ⇒ Object
-
#auto_merge_strategy=(strategy) ⇒ Object
-
#banzai_render_context(field) ⇒ Object
-
#base_pipeline ⇒ Object
-
#branch_merge_base_commit ⇒ Object
-
#branch_merge_base_sha ⇒ Object
-
#branch_missing? ⇒ Boolean
-
#broken? ⇒ Boolean
-
#cache_merge_request_closes_issues!(current_user = self.author) ⇒ Object
If the merge request closes any issues, save this information in the ‘MergeRequestsClosingIssues` model.
-
#calculate_reactive_cache(identifier, current_user_id = nil, report_type = nil, *args) ⇒ Object
-
#can_allow_collaboration?(user) ⇒ Boolean
-
#can_be_cherry_picked? ⇒ Boolean
-
#can_be_closed? ⇒ Boolean
-
#can_be_merged_by?(user, skip_collaboration_check: false) ⇒ Boolean
-
#can_be_merged_via_command_line_by?(user) ⇒ Boolean
-
#can_be_reverted?(current_user) ⇒ Boolean
-
#can_cancel_auto_merge?(current_user) ⇒ Boolean
-
#can_remove_source_branch?(current_user) ⇒ Boolean
-
#can_suggest_reviewers? ⇒ Boolean
-
#check_for_spam? ⇒ Boolean
-
#check_mergeability(async: false, sync_retry_lease: false) ⇒ Object
-
#cleanup_refs(only: :all) ⇒ Object
-
#clear_memoized_shas ⇒ Object
-
#closed_event ⇒ Object
-
#closed_or_merged_without_fork? ⇒ Boolean
-
#closes_issues(current_user = self.author) ⇒ Object
Return the set of issues that will be closed if this merge request is accepted.
-
#collaborative_push_possible? ⇒ Boolean
-
#commit_notes ⇒ Object
-
#commit_shas(limit: nil) ⇒ Object
-
#commits(limit: nil, load_from_gitaly: false, page: nil) ⇒ Object
-
#commits_count ⇒ Object
-
#committers(with_merge_commits: false) ⇒ Object
-
#compare_accessibility_reports ⇒ Object
-
#compare_codequality_reports ⇒ Object
-
#compare_reports(service_class, current_user = nil, report_type = nil, additional_params = {}) ⇒ Object
-
#compare_sast_reports(current_user) ⇒ Object
-
#compare_secret_detection_reports(current_user) ⇒ Object
-
#compare_test_reports ⇒ Object
-
#comparison_base_pipeline(service_class) ⇒ Object
-
#conflicting_mr_message(conflicting_mr) ⇒ Object
-
#context_commits(limit: nil) ⇒ Object
-
#context_commits_count ⇒ Object
-
#context_commits_diff ⇒ Object
-
#create_merge_request_diff ⇒ Object
-
#current_patch_id_sha ⇒ Object
-
#default_merge_commit_message(include_description: false, user: nil) ⇒ Object
-
#default_squash_commit_message(user: nil) ⇒ Object
-
#diff_base_commit ⇒ Object
-
#diff_base_sha ⇒ Object
-
#diff_head_commit ⇒ Object
-
#diff_head_sha ⇒ Object
-
#diff_refs ⇒ Object
-
#diff_size ⇒ Object
-
#diff_start_commit ⇒ Object
-
#diff_start_sha ⇒ Object
-
#diff_stats ⇒ Object
-
#diffable_merge_ref? ⇒ Boolean
rubocop: enable CodeReuse/ServiceClass.
-
#diffs(diff_options = {}) ⇒ Object
-
#diffs_batch_cache_with_max_age? ⇒ Boolean
-
#discussions_diffs ⇒ Object
-
#discussions_rendered_on_frontend? ⇒ Boolean
-
#diverged_commits_count ⇒ Object
-
#diverged_from_target_branch? ⇒ Boolean
-
#draft? ⇒ Boolean
(also: #work_in_progress?)
-
#draft_title ⇒ Object
(also: #wip_title)
-
#draftless_title ⇒ Object
(also: #wipless_title)
-
#draftless_title_changed(old_title) ⇒ Object
(also: #wipless_title_changed)
Verifies if title has changed not taking into account Draft prefix for merge requests.
-
#eager_fetch_ref! ⇒ Object
-
#enabled_reports ⇒ Object
-
#ensure_merge_request_diff ⇒ Object
-
#ensure_metrics! ⇒ Object
-
#environments_in_head_pipeline(deployment_status: nil) ⇒ Object
-
#execute_merge_checks(params: {}) ⇒ Object
-
#existing_mrs_targeting_same_branch ⇒ Object
-
#fetch_ref! ⇒ Object
-
#ff_merge_possible? ⇒ Boolean
-
#find_actual_head_pipeline ⇒ Object
-
#find_assignee(user) ⇒ Object
-
#find_codequality_mr_diff_reports ⇒ Object
TODO: this method and compare_test_reports use the same result type, which is handled by the controller’s #reports_response.
-
#find_coverage_reports ⇒ Object
TODO: this method and compare_test_reports use the same result type, which is handled by the controller’s #reports_response.
-
#find_exposed_artifacts ⇒ Object
TODO: this method and compare_test_reports use the same result type, which is handled by the controller’s #reports_response.
-
#find_reviewer(user) ⇒ Object
-
#find_terraform_reports ⇒ Object
-
#first_commit ⇒ Object
-
#first_contribution? ⇒ Boolean
rubocop: enable CodeReuse/ServiceClass.
-
#first_multiline_commit ⇒ Object
Returns the oldest multi-line commit.
-
#for_fork? ⇒ Boolean
-
#for_same_project? ⇒ Boolean
-
#force_remove_source_branch? ⇒ Boolean
-
#has_accessibility_reports? ⇒ Boolean
-
#has_ci? ⇒ Boolean
-
#has_codequality_mr_diff_report? ⇒ Boolean
-
#has_codequality_reports? ⇒ Boolean
-
#has_commits? ⇒ Boolean
-
#has_complete_diff_refs? ⇒ Boolean
-
#has_coverage_reports? ⇒ Boolean
-
#has_exposed_artifacts? ⇒ Boolean
-
#has_no_commits? ⇒ Boolean
-
#has_sast_reports? ⇒ Boolean
-
#has_secret_detection_reports? ⇒ Boolean
-
#has_terraform_reports? ⇒ Boolean
-
#has_test_reports? ⇒ Boolean
-
#head_pipeline_active? ⇒ Boolean
-
#hidden? ⇒ Boolean
-
#hook_attrs ⇒ Object
-
#in_locked_state ⇒ Object
-
#includes_ci_config? ⇒ Boolean
-
#issues_mentioned_but_not_closing(current_user) ⇒ Object
-
#keep_around_commit ⇒ Object
rubocop: enable CodeReuse/ServiceClass.
-
#merge_async(user_id, params) ⇒ Object
Calls ‘MergeWorker` to proceed with the merge process and updates `merge_jid` with the MergeWorker#jid.
-
#merge_base_pipeline ⇒ Object
-
#merge_blocked_by_other_mrs? ⇒ Boolean
-
#merge_commit ⇒ Object
-
#merge_event ⇒ Object
-
#merge_ongoing? ⇒ Boolean
-
#merge_participants ⇒ Object
-
#merge_pipeline ⇒ Object
-
#merge_ref_head ⇒ Object
Returns the current merge-ref HEAD commit.
-
#merge_ref_path ⇒ Object
-
#merge_request_assignees_with(user_ids) ⇒ Object
-
#merge_request_diff ⇒ Object
This is the same as latest_merge_request_diff unless: 1.
-
#merge_request_diff_for(diff_refs_or_sha) ⇒ Object
-
#merge_request_reviewers_with(user_ids) ⇒ Object
-
#mergeability_checks ⇒ Object
-
#mergeable?(skip_ci_check: false, skip_discussions_check: false, skip_approved_check: false, check_mergeability_retry_lease: false, skip_rebase_check: false) ⇒ Boolean
-
#mergeable_ci_state? ⇒ Boolean
-
#mergeable_discussions_state? ⇒ Boolean
-
#mergeable_state?(skip_ci_check: false, skip_discussions_check: false, skip_approved_check: false) ⇒ Boolean
-
#merged_at ⇒ Object
-
#merged_commit_sha ⇒ Object
-
#missing_required_squash? ⇒ Boolean
-
#modified_paths(past_merge_request_diff: nil, fallback_on_overflow: false) ⇒ Object
-
#new_paths ⇒ Object
-
#non_latest_diffs ⇒ Object
-
#note_positions_for_paths(paths, user = nil) ⇒ Object
-
#notify_conflict? ⇒ Boolean
-
#permits_force_push? ⇒ Boolean
-
#pipeline_coverage_delta ⇒ Object
-
#predefined_variables ⇒ Object
-
#preloads_discussion_diff_highlighting? ⇒ Boolean
-
#prepare ⇒ Object
-
#prepared? ⇒ Boolean
-
#public_merge_status ⇒ Object
Returns current merge_status except it returns ‘cannot_be_merged_rechecking` as `checking` to avoid exposing unnecessary internal state.
-
#raw_diffs(*args) ⇒ Object
-
#real_time_notes_enabled? ⇒ Boolean
-
#rebase_async(user_id, skip_ci: false) ⇒ Object
Set off a rebase asynchronously, atomically updating the ‘rebase_jid` of the MR so that the status of the operation can be tracked.
-
#rebase_in_progress? ⇒ Boolean
-
#recent_commits(limit: MergeRequestDiff::COMMITS_SAFE_SIZE, load_from_gitaly: false, page: nil) ⇒ Object
-
#recent_context_commits ⇒ Object
-
#recent_diff_head_shas(limit = MAX_RECENT_DIFF_HEAD_SHAS) ⇒ Object
-
#recent_visible_deployments ⇒ Object
-
#recheck_merge_status? ⇒ Boolean
Returns boolean indicating the merge_status should be rechecked in order to switch to either can_be_merged or cannot_be_merged.
-
#ref_path ⇒ Object
-
#refs_to_cleanup(only: :all) ⇒ Object
-
#related_notes ⇒ Object
(also: #discussion_notes)
-
#reload_diff(current_user = nil) ⇒ Object
rubocop: disable CodeReuse/ServiceClass.
-
#reload_diff_if_branch_changed ⇒ Object
-
#remove_source_branch? ⇒ Boolean
-
#reopenable? ⇒ Boolean
-
#repository_diff_refs ⇒ Object
Instead trying to fetch the persisted diff_refs, this method goes straight to the repository to get the most recent data possible.
-
#schedule_cleanup_refs(only: :all) ⇒ Object
-
#short_merge_commit_sha ⇒ Object
-
#short_merged_commit_sha ⇒ Object
-
#should_be_rebased? ⇒ Boolean
-
#should_remove_source_branch? ⇒ Boolean
-
#skipped_mergeable_checks(options = {}) ⇒ Object
-
#source_branch_exists? ⇒ Boolean
-
#source_branch_head ⇒ Object
-
#source_branch_ref ⇒ Object
-
#source_project_missing? ⇒ Boolean
-
#source_project_namespace ⇒ Object
-
#source_project_path ⇒ Object
-
#squash_on_merge? ⇒ Boolean
-
#suggested_reviewer_users ⇒ Object
-
#supports_assignee? ⇒ Boolean
-
#supports_lock_on_merge? ⇒ Boolean
-
#supports_suggestion? ⇒ Boolean
-
#target_branch_exists? ⇒ Boolean
-
#target_branch_head ⇒ Object
-
#target_branch_ref ⇒ Object
-
#target_default_branch? ⇒ Boolean
-
#target_project_namespace ⇒ Object
-
#target_project_path ⇒ Object
-
#to_reference(from = nil, full: false) ⇒ Object
‘from` argument can be a Namespace or Project.
-
#train_ref_path ⇒ Object
-
#update_and_mark_in_progress_merge_commit_sha(commit_id) ⇒ Object
-
#update_diff_discussion_positions(old_diff_refs:, new_diff_refs:, current_user: nil) ⇒ Object
rubocop: disable CodeReuse/ServiceClass.
-
#update_head_pipeline ⇒ Object
-
#update_project_counter_caches ⇒ Object
rubocop: disable CodeReuse/ServiceClass.
-
#use_merge_base_pipeline_for_comparison?(_) ⇒ Boolean
-
#validate_branch_name(attr) ⇒ Object
-
#validate_branches ⇒ Object
-
#validate_fork ⇒ Object
-
#validate_reviewer_size_length ⇒ Object
-
#validate_target_project ⇒ Object
-
#version_params_for(diff_refs) ⇒ Object
-
#viewable_diffs ⇒ Object
-
#visible_closing_issues_for(current_user = self.author) ⇒ Object
extended, extensions, included, method_added, override, prepended, queue_verification, verify!
Methods included from Spammable
#allow_possible_spam?, #check_for_spam, #clear_spam_flags!, #invalidate_if_spam, #needs_recaptcha!, #recaptcha_error!, #render_recaptcha?, #spam, #spam!, #spam_description, #spam_title, #spammable_attribute_changed?, #spammable_entity_type, #spammable_text, #submittable_as_spam?, #submittable_as_spam_by?, #supports_recaptcha?, #unrecoverable_spam_error!
Methods included from Approvable
#approved?, #approved_by?, #eligible_for_approval_by?, #eligible_for_unapproval_by?
#assignee, #assignee=, #assignee_id, #assignee_id=, #assignee_ids, #assignee_ids=, #assignees, #assignees=
#touch
#human_time_change, #human_time_estimate, #human_total_time_spent, #set_time_estimate_default_value, #spend_time, #time_change, #time_estimate=, #total_time_spent
#present
Methods included from Referable
#referable_inspect, #reference_link_text, #to_reference_base
Methods included from Noteable
#after_note_created, #after_note_destroyed, #base_class_name, #broadcast_notes_changed, #capped_notes_count, #commenters, #creatable_note_email_address, #discussion_ids_relation, #discussion_root_note_ids, #discussions, #discussions_can_be_resolved_by?, #discussions_resolvable?, #discussions_resolved?, #discussions_to_be_resolved, #grouped_diff_discussions, #has_any_diff_note_positions?, #human_class_name, #lockable?, #noteable_target_type_name, #resolvable_discussions, #supports_creating_notes_by_email?, #supports_discussions?, #supports_replying_to_individual_notes?, #supports_resolvable_notes?
Methods included from Issuable
#allows_scoped_labels?, #assignee?, #assignee_list, #assignee_or_author?, #assignee_username_list, #can_assign_epic?, #can_move?, #card_attributes, #hook_association_changes, #hook_reviewer_changes, #label_names, #labels_array, #labels_hook_attrs, #notes_with_associations, #open?, #overdue?, #read_ability_for, #resource_parent, #state, #state=, #subscribed_without_subscriptions?, #supports_health_status?, #to_ability_name, #to_hook_data, #updated_tasks, #user_notes_count
Methods included from Exportable
#exportable_association?, #restricted_associations, #to_authorized_json
#run_after_commit, #run_after_commit_or_now
Methods included from Editable
#edited?, #last_edited_by
#disable_transitioning, #enable_transitioning, #transitioning?
Methods included from Taskable
get_tasks, get_updated_tasks, #task_completion_status, #task_list_items, #task_status, #task_status_short, #tasks, #tasks?
Methods included from Awardable
#awarded_emoji?, #downvotes, #emoji_awardable?, #grouped_awards, #upvotes, #user_authored?, #user_can_award?
#strip_attributes!
#lazy_subscription, #set_subscription, #subscribe, #subscribed?, #subscribed_without_subscriptions?, #subscribers, #toggle_subscription, #unsubscribe
#milestone_available?, #supports_milestone?
#all_references, #create_cross_references!, #create_new_cross_references!, #directly_addressed_users, #extractors, #gfm_reference, #local_reference, #matches_cross_reference_regex?, #mentioned_users, #referenced_group_users, #referenced_groups, #referenced_mentionables, #referenced_project_users, #referenced_projects, #referenced_users, #user_mention_class, #user_mention_identifier
#participant?, #participants, #visible_participants
#attribute_invalidated?, #cached_html_for, #cached_html_up_to_date?, #can_cache_field?, #invalidated_markdown_cache?, #latest_cached_markdown_version, #local_version, #mentionable_attributes_changed?, #mentioned_filtered_user_ids_for, #parent_user, #refresh_markdown_cache, #refresh_markdown_cache!, #rendered_field_content, #skip_project_check?, #store_mentions!, #updated_cached_html_for
split_query_to_search_terms
Methods included from IidRoutes
#to_param
group_init, #internal_id_read_scope, #internal_id_scope_attrs, #internal_id_scope_usage, namespace_init, project_init, scope_attrs, scope_usage
cached_column_list, #create_or_load_association, declarative_enum, default_select_columns, id_in, id_not_in, iid_in, pluck_primary_key, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order
#serializable_hash
Instance Attribute Details
#allow_broken ⇒ Object
When this attribute is true some MR validation is ignored It allows us to close or modify broken merge requests
145
146
147
|
# File 'app/models/merge_request.rb', line 145
def allow_broken
@allow_broken
end
|
#can_be_created ⇒ Object
Temporary fields to store compare vars when creating new merge request
153
154
155
|
# File 'app/models/merge_request.rb', line 153
def can_be_created
@can_be_created
end
|
#compare ⇒ Object
Temporary fields to store compare vars when creating new merge request
153
154
155
|
# File 'app/models/merge_request.rb', line 153
def compare
@compare
end
|
#compare_commits ⇒ Object
Temporary fields to store compare vars when creating new merge request
153
154
155
|
# File 'app/models/merge_request.rb', line 153
def compare_commits
@compare_commits
end
|
#diff_options ⇒ Object
Temporary fields to store compare vars when creating new merge request
153
154
155
|
# File 'app/models/merge_request.rb', line 153
def diff_options
@diff_options
end
|
#skip_ensure_merge_request_diff ⇒ Object
149
150
151
|
# File 'app/models/merge_request.rb', line 149
def skip_ensure_merge_request_diff
@skip_ensure_merge_request_diff
end
|
#skip_merge_status_trigger ⇒ Object
Flag to skip triggering mergeRequestMergeStatusUpdated GraphQL subscription.
156
157
158
|
# File 'app/models/merge_request.rb', line 156
def skip_merge_status_trigger
@skip_merge_status_trigger
end
|
#source_branch_sha ⇒ Object
972
973
974
|
# File 'app/models/merge_request.rb', line 972
def source_branch_sha
@source_branch_sha || source_branch_head.try(:sha)
end
|
#target_branch_sha ⇒ Object
968
969
970
|
# File 'app/models/merge_request.rb', line 968
def target_branch_sha
@target_branch_sha || target_branch_head.try(:sha)
end
|
Class Method Details
.available_state_names ⇒ Object
Keep states definition to be evaluated before the state_machine block to
avoid spec failures. If this gets evaluated after, the `merged` and `locked`
states (which are overridden) can be nil.
167
168
169
|
# File 'app/models/merge_request.rb', line 167
def self.available_state_names
super + [:merged, :locked]
end
|
.draft?(title) ⇒ Boolean
644
645
646
|
# File 'app/models/merge_request.rb', line 644
def self.draft?(title)
!!(title =~ DRAFT_REGEX)
end
|
.draft_title(title) ⇒ Object
652
653
654
|
# File 'app/models/merge_request.rb', line 652
def self.draft_title(title)
draft?(title) ? title : "Draft: #{title}"
end
|
.draftless_title(title) ⇒ Object
648
649
650
|
# File 'app/models/merge_request.rb', line 648
def self.draftless_title(title)
title.sub(DRAFT_REGEX, "")
end
|
.in_projects(relation) ⇒ Object
Returns all the merge requests from an ActiveRecord:Relation.
This method uses a UNION as it usually operates on the result of ProjectsFinder#execute. PostgreSQL in particular doesn’t always like queries using multiple sub-queries especially when combined with an OR statement. UNIONs on the other hand perform much better in these cases.
relation - An ActiveRecord::Relation that returns a list of Projects.
Returns an ActiveRecord::Relation.
613
614
615
616
617
618
619
620
|
# File 'app/models/merge_request.rb', line 613
def self.in_projects(relation)
source = unscoped.where(source_project_id: relation)
target = unscoped.where(target_project_id: relation)
from_union([source, target])
end
|
.link_reference_pattern ⇒ Object
591
592
593
|
# File 'app/models/merge_request.rb', line 591
def self.link_reference_pattern
@link_reference_pattern ||= compose_link_reference_pattern('merge_requests', Gitlab::Regex.merge_request)
end
|
.merge_request_ref?(ref) ⇒ Boolean
1589
1590
1591
|
# File 'app/models/merge_request.rb', line 1589
def self.merge_request_ref?(ref)
ref.start_with?("refs/#{Repository::REF_MERGE_REQUEST}/")
end
|
.merge_train_ref?(ref) ⇒ Boolean
1593
1594
1595
|
# File 'app/models/merge_request.rb', line 1593
def self.merge_train_ref?(ref)
%r{\Arefs/#{Repository::REF_MERGE_REQUEST}/\d+/train\z}o.match?(ref)
end
|
.participant_includes ⇒ Object
662
663
664
|
# File 'app/models/merge_request.rb', line 662
def self.participant_includes
[:assignees, :reviewers] + super
end
|
.project_foreign_key ⇒ Object
599
600
601
|
# File 'app/models/merge_request.rb', line 599
def self.project_foreign_key
'target_project_id'
end
|
.recent_target_branches(limit: 100) ⇒ Object
Returns the top 100 target branches
The returned value is a Array containing branch names sort by updated_at of merge request:
['master', 'develop', 'production']
limit - The maximum number of target branch to return.
521
522
523
524
525
526
527
|
# File 'app/models/merge_request.rb', line 521
def self.recent_target_branches(limit: 100)
group(:target_branch)
.select(:target_branch)
.reorder(arel_table[:updated_at].maximum.desc)
.limit(limit)
.pluck(:target_branch)
end
|
.reference_pattern ⇒ Object
Pattern used to extract ‘!123` merge request references from text
This pattern supports cross-project references.
.reference_prefix ⇒ Object
509
510
511
|
# File 'app/models/merge_request.rb', line 509
def self.reference_prefix
'!'
end
|
.reference_valid?(reference) ⇒ Boolean
595
596
597
|
# File 'app/models/merge_request.rb', line 595
def self.reference_valid?(reference)
reference.to_i > 0 && reference.to_i <= Gitlab::Database::MAX_INT_VALUE
end
|
.reviewers_subquery ⇒ Object
540
541
542
543
544
|
# File 'app/models/merge_request.rb', line 540
def self.reviewers_subquery
MergeRequestReviewer.arel_table
.project('true')
.where(Arel::Nodes::SqlLiteral.new("#{to_ability_name}_id = #{to_ability_name}s.id"))
end
|
.set_latest_merge_request_diff_ids! ⇒ Object
This is used after project import, to reset the IDs to the correct values. It is not intended to be called without having already scoped the relation.
Only set ‘regular` merge request diffs as latest so `merge_head` diff won’t be considered as ‘MergeRequest#merge_request_diff`.
628
629
630
631
632
633
634
635
636
637
638
639
640
|
# File 'app/models/merge_request.rb', line 628
def self.set_latest_merge_request_diff_ids!
update = "
latest_merge_request_diff_id = (
SELECT MAX(id)
FROM merge_request_diffs
WHERE merge_requests.id = merge_request_diffs.merge_request_id
AND merge_request_diffs.diff_type = #{MergeRequestDiff.diff_types[:regular]}
)".squish
self.each_batch do |batch|
batch.update_all(update)
end
end
|
.sort_by_attribute(method, excluded_labels: []) ⇒ Object
529
530
531
532
533
534
535
536
537
538
|
# File 'app/models/merge_request.rb', line 529
def self.sort_by_attribute(method, excluded_labels: [])
case method.to_s
when 'merged_at', 'merged_at_asc' then order_merged_at_asc
when 'closed_at', 'closed_at_asc' then order_closed_at_asc
when 'merged_at_desc' then order_merged_at_desc
when 'closed_at_desc' then order_closed_at_desc
else
super
end
end
|
.total_time_to_merge ⇒ Object
.wip_title ⇒ Object
659
660
661
|
# File 'app/models/merge_request.rb', line 659
def self.draft_title(title)
draft?(title) ? title : "Draft: #{title}"
end
|
.wipless_title ⇒ Object
658
659
660
|
# File 'app/models/merge_request.rb', line 658
def self.draftless_title(title)
title.sub(DRAFT_REGEX, "")
end
|
.work_in_progress? ⇒ Boolean
657
658
659
|
# File 'app/models/merge_request.rb', line 657
def self.draft?(title)
!!(title =~ DRAFT_REGEX)
end
|
Instance Method Details
#actual_head_pipeline ⇒ Object
Use this method whenever you need to make sure the head_pipeline is synced with the branch head commit, for example checking if a merge request can be merged. For more information check: gitlab.com/gitlab-org/gitlab-foss/issues/40004
559
560
561
|
# File 'app/models/merge_request.rb', line 559
def actual_head_pipeline
head_pipeline&.matches_sha_or_source_sha?(diff_head_sha) ? head_pipeline : nil
end
|
#actual_head_pipeline_active? ⇒ Boolean
573
574
575
|
# File 'app/models/merge_request.rb', line 573
def actual_head_pipeline_active?
!!actual_head_pipeline&.active?
end
|
#actual_head_pipeline_success? ⇒ Boolean
577
578
579
|
# File 'app/models/merge_request.rb', line 577
def actual_head_pipeline_success?
!!actual_head_pipeline&.success?
end
|
#all_commit_shas ⇒ Object
Note that this could also return SHA from now dangling commits
1829
1830
1831
1832
1833
1834
1835
|
# File 'app/models/merge_request.rb', line 1829
def all_commit_shas
@all_commit_shas ||= begin
return commit_shas unless persisted?
all_commits.pluck(:sha).uniq
end
end
|
#all_commits ⇒ Object
1821
1822
1823
1824
1825
|
# File 'app/models/merge_request.rb', line 1821
def all_commits
MergeRequestDiffCommit
.where(merge_request_diff: merge_request_diffs.recent)
.limit(10_000)
end
|
#allow_collaboration ⇒ Object
Also known as:
allow_collaboration?
1991
1992
1993
|
# File 'app/models/merge_request.rb', line 1991
def allow_collaboration
collaborative_push_possible? && allow_maintainer_to_push
end
|
#allows_multiple_reviewers? ⇒ Boolean
2033
2034
2035
|
# File 'app/models/merge_request.rb', line 2033
def allows_multiple_reviewers?
false
end
|
#allows_reviewers? ⇒ Boolean
2029
2030
2031
|
# File 'app/models/merge_request.rb', line 2029
def allows_reviewers?
true
end
|
#async_cleanup_refs(only: :all) ⇒ Object
1585
1586
1587
|
# File 'app/models/merge_request.rb', line 1585
def async_cleanup_refs(only: :all)
project.repository.async_delete_refs(*refs_to_cleanup(only: only))
end
|
#auto_merge_strategy ⇒ Object
#auto_merge_strategy=(strategy) ⇒ Object
1304
1305
1306
|
# File 'app/models/merge_request.rb', line 1304
def auto_merge_strategy=(strategy)
merge_params['auto_merge_strategy'] = strategy
end
|
#banzai_render_context(field) ⇒ Object
2021
2022
2023
|
# File 'app/models/merge_request.rb', line 2021
def banzai_render_context(field)
super.merge(label_url_method: :project_merge_requests_url)
end
|
#base_pipeline ⇒ Object
1959
1960
1961
1962
1963
|
# File 'app/models/merge_request.rb', line 1959
def base_pipeline
@base_pipeline ||= project.ci_pipelines
.order(id: :desc)
.find_by(sha: diff_base_sha, ref: target_branch)
end
|
#branch_merge_base_commit ⇒ Object
959
960
961
962
963
964
965
966
|
# File 'app/models/merge_request.rb', line 959
def branch_merge_base_commit
start_sha = target_branch_sha
head_sha = source_branch_sha
if start_sha && head_sha
target_project.merge_base_commit(start_sha, head_sha)
end
end
|
#branch_merge_base_sha ⇒ Object
996
997
998
|
# File 'app/models/merge_request.rb', line 996
def branch_merge_base_sha
branch_merge_base_commit.try(:sha)
end
|
#branch_missing? ⇒ Boolean
1508
1509
1510
|
# File 'app/models/merge_request.rb', line 1508
def branch_missing?
!source_branch_exists? || !target_branch_exists?
end
|
#broken? ⇒ Boolean
1512
1513
1514
|
# File 'app/models/merge_request.rb', line 1512
def broken?
has_no_commits? || branch_missing? || cannot_be_merged?
end
|
#cache_merge_request_closes_issues!(current_user = self.author) ⇒ Object
If the merge request closes any issues, save this information in the ‘MergeRequestsClosingIssues` model. This is a performance optimization. Calculating this information for a number of merge requests requires running `ReferenceExtractor` on each of them separately. This optimization does not apply to issues from external sources.
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
|
# File 'app/models/merge_request.rb', line 1364
def cache_merge_request_closes_issues!(current_user = self.author)
return if closed? || merged?
transaction do
self.merge_requests_closing_issues.delete_all
closes_issues(current_user).each do |issue|
next if issue.is_a?(ExternalIssue)
self.merge_requests_closing_issues.create!(issue: issue)
end
end
end
|
#calculate_reactive_cache(identifier, current_user_id = nil, report_type = nil, *args) ⇒ Object
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
|
# File 'app/models/merge_request.rb', line 1801
def calculate_reactive_cache(identifier, current_user_id = nil, report_type = nil, *args)
service_class = identifier.constantize
raise NameError, service_class unless service_class < Ci::CompareReportsBaseService
current_user = User.find_by(id: current_user_id)
service_class.new(project, current_user, id: id, report_type: report_type).execute(comparison_base_pipeline(service_class), actual_head_pipeline)
end
|
#can_allow_collaboration?(user) ⇒ Boolean
2004
2005
2006
2007
|
# File 'app/models/merge_request.rb', line 2004
def can_allow_collaboration?(user)
collaborative_push_possible? &&
Ability.allowed?(user, :push_code, source_project)
end
|
#can_be_cherry_picked? ⇒ Boolean
1884
1885
1886
|
# File 'app/models/merge_request.rb', line 1884
def can_be_cherry_picked?
merge_commit.present?
end
|
#can_be_closed? ⇒ Boolean
1095
1096
1097
|
# File 'app/models/merge_request.rb', line 1095
def can_be_closed?
opened?
end
|
#can_be_merged_by?(user, skip_collaboration_check: false) ⇒ Boolean
1516
1517
1518
1519
|
# File 'app/models/merge_request.rb', line 1516
def can_be_merged_by?(user, skip_collaboration_check: false)
access = ::Gitlab::UserAccess.new(user, container: project, skip_collaboration_check: skip_collaboration_check)
access.can_update_branch?(target_branch)
end
|
#can_be_merged_via_command_line_by?(user) ⇒ Boolean
1521
1522
1523
1524
|
# File 'app/models/merge_request.rb', line 1521
def can_be_merged_via_command_line_by?(user)
access = ::Gitlab::UserAccess.new(user, container: project)
access.can_push_to_branch?(target_branch)
end
|
#can_be_reverted?(current_user) ⇒ Boolean
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
|
# File 'app/models/merge_request.rb', line 1858
def can_be_reverted?(current_user)
return false unless merge_commit
return false unless merged_at
cutoff = merged_at - 1.minute
notes_association = notes_with_associations.where('created_at >= ?', cutoff)
!merge_commit.has_been_reverted?(current_user, notes_association)
end
|
#can_cancel_auto_merge?(current_user) ⇒ Boolean
1278
1279
1280
|
# File 'app/models/merge_request.rb', line 1278
def can_cancel_auto_merge?(current_user)
can_be_merged_by?(current_user) || self.author == current_user
end
|
#can_remove_source_branch?(current_user) ⇒ Boolean
1282
1283
1284
1285
1286
1287
1288
|
# File 'app/models/merge_request.rb', line 1282
def can_remove_source_branch?(current_user)
source_project &&
!ProtectedBranch.protected?(source_project, source_branch) &&
!source_project.root_ref?(source_branch) &&
Ability.allowed?(current_user, :push_code, source_project) &&
diff_head_sha == source_branch_head.try(:sha)
end
|
#can_suggest_reviewers? ⇒ Boolean
2090
2091
2092
|
# File 'app/models/merge_request.rb', line 2090
def can_suggest_reviewers?
false end
|
#check_for_spam? ⇒ Boolean
2110
2111
2112
|
# File 'app/models/merge_request.rb', line 2110
def check_for_spam?(*)
spammable_attribute_changed? && project.public?
end
|
#check_mergeability(async: false, sync_retry_lease: false) ⇒ Object
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
|
# File 'app/models/merge_request.rb', line 1182
def check_mergeability(async: false, sync_retry_lease: false)
return unless recheck_merge_status?
check_service = MergeRequests::MergeabilityCheckService.new(self)
if async
check_service.async_execute
else
check_service.execute(retry_lease: sync_retry_lease)
end
end
|
#cleanup_refs(only: :all) ⇒ Object
1581
1582
1583
|
# File 'app/models/merge_request.rb', line 1581
def cleanup_refs(only: :all)
project.repository.delete_refs(*refs_to_cleanup(only: only))
end
|
#clear_memoized_shas ⇒ Object
1161
1162
1163
1164
1165
1166
|
# File 'app/models/merge_request.rb', line 1161
def clear_memoized_shas
@target_branch_sha = @source_branch_sha = nil
clear_memoization(:source_branch_head)
clear_memoization(:target_branch_head)
end
|
#closed_event ⇒ Object
1209
1210
1211
|
# File 'app/models/merge_request.rb', line 1209
def closed_event
@closed_event ||= target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: :closed).last
end
|
#closed_or_merged_without_fork? ⇒ Boolean
1080
1081
1082
|
# File 'app/models/merge_request.rb', line 1080
def closed_or_merged_without_fork?
(closed? || merged?) && source_project_missing?
end
|
#closes_issues(current_user = self.author) ⇒ Object
Return the set of issues that will be closed if this merge request is accepted.
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
|
# File 'app/models/merge_request.rb', line 1391
def closes_issues(current_user = self.author)
if target_branch == project.default_branch
messages = [title, description]
messages.concat(commits.map(&:safe_message)) if merge_request_diff.persisted?
Gitlab::ClosingIssueExtractor.new(project, current_user)
.closed_by_message(messages.join("\n"))
else
[]
end
end
|
#collaborative_push_possible? ⇒ Boolean
#commit_notes ⇒ Object
1335
1336
1337
1338
1339
1340
1341
1342
1343
|
# File 'app/models/merge_request.rb', line 1335
def commit_notes
commit_ids = commit_shas(limit: 100)
Note
.user
.where(project_id: [source_project_id, target_project_id])
.for_commit_id(commit_ids)
end
|
#commit_shas(limit: nil) ⇒ Object
727
728
729
730
731
732
733
734
735
736
737
738
|
# File 'app/models/merge_request.rb', line 727
def commit_shas(limit: nil)
return merge_request_diff.commit_shas(limit: limit) if merge_request_diff.persisted?
shas =
if compare_commits
compare_commits.to_a.reverse.map(&:sha)
else
Array(diff_head_sha)
end
limit ? shas.take(limit) : shas
end
|
#commits(limit: nil, load_from_gitaly: false, page: nil) ⇒ Object
700
701
702
703
704
705
706
707
708
709
710
711
|
# File 'app/models/merge_request.rb', line 700
def commits(limit: nil, load_from_gitaly: false, page: nil)
return merge_request_diff.commits(limit: limit, load_from_gitaly: load_from_gitaly, page: page) if merge_request_diff.persisted?
commits_arr = if compare_commits
reversed_commits = compare_commits.reverse
limit ? reversed_commits.take(limit) : reversed_commits
else
[]
end
CommitCollection.new(source_project, commits_arr, source_branch)
end
|
#commits_count ⇒ Object
717
718
719
720
721
722
723
724
725
|
# File 'app/models/merge_request.rb', line 717
def commits_count
if merge_request_diff.persisted?
merge_request_diff.commits_count
elsif compare_commits
compare_commits.size
else
0
end
end
|
#committers(with_merge_commits: false) ⇒ Object
666
667
668
|
# File 'app/models/merge_request.rb', line 666
def committers(with_merge_commits: false)
@committers ||= commits.committers(with_merge_commits: with_merge_commits)
end
|
#compare_accessibility_reports ⇒ Object
1695
1696
1697
1698
1699
1700
1701
|
# File 'app/models/merge_request.rb', line 1695
def compare_accessibility_reports
unless has_accessibility_reports?
return { status: :error, status_reason: _('This merge request does not have accessibility reports') }
end
compare_reports(Ci::CompareAccessibilityReportsService)
end
|
#compare_codequality_reports ⇒ Object
1735
1736
1737
1738
1739
1740
1741
|
# File 'app/models/merge_request.rb', line 1735
def compare_codequality_reports
unless has_codequality_reports?
return { status: :error, status_reason: _('This merge request does not have codequality reports') }
end
compare_reports(Ci::CompareCodequalityReportsService)
end
|
#compare_reports(service_class, current_user = nil, report_type = nil, additional_params = {}) ⇒ Object
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
|
# File 'app/models/merge_request.rb', line 1770
def compare_reports(service_class, current_user = nil, report_type = nil, additional_params = {})
with_reactive_cache(service_class.name, current_user&.id, report_type) do |data|
unless service_class.new(project, current_user, id: id, report_type: report_type, additional_params: additional_params)
.latest?(comparison_base_pipeline(service_class), actual_head_pipeline, data)
raise InvalidateReactiveCache
end
data
end || { status: :parsing }
end
|
#compare_sast_reports(current_user) ⇒ Object
1789
1790
1791
1792
1793
|
# File 'app/models/merge_request.rb', line 1789
def compare_sast_reports(current_user)
return missing_report_error("SAST") unless has_sast_reports?
compare_reports(::Ci::CompareSecurityReportsService, current_user, 'sast')
end
|
#compare_secret_detection_reports(current_user) ⇒ Object
1795
1796
1797
1798
1799
|
# File 'app/models/merge_request.rb', line 1795
def compare_secret_detection_reports(current_user)
return missing_report_error("secret detection") unless has_secret_detection_reports?
compare_reports(::Ci::CompareSecurityReportsService, current_user, 'secret_detection')
end
|
#compare_test_reports ⇒ Object
1675
1676
1677
1678
1679
1680
1681
|
# File 'app/models/merge_request.rb', line 1675
def compare_test_reports
unless has_test_reports?
return { status: :error, status_reason: 'This merge request does not have test reports' }
end
compare_reports(Ci::CompareTestReportsService)
end
|
#comparison_base_pipeline(service_class) ⇒ Object
1955
1956
1957
|
# File 'app/models/merge_request.rb', line 1955
def comparison_base_pipeline(service_class)
(use_merge_base_pipeline_for_comparison?(service_class) && merge_base_pipeline) || base_pipeline
end
|
#conflicting_mr_message(conflicting_mr) ⇒ Object
1034
1035
1036
1037
1038
1039
|
# File 'app/models/merge_request.rb', line 1034
def conflicting_mr_message(conflicting_mr)
format(
_("Another open merge request already exists for this source branch: %{conflicting_mr_reference}"),
conflicting_mr_reference: conflicting_mr.to_reference
)
end
|
#context_commits(limit: nil) ⇒ Object
688
689
690
|
# File 'app/models/merge_request.rb', line 688
def context_commits(limit: nil)
@context_commits ||= merge_request_context_commits.order_by_committed_date_desc.limit(limit).map(&:to_commit)
end
|
#context_commits_count ⇒ Object
696
697
698
|
# File 'app/models/merge_request.rb', line 696
def context_commits_count
context_commits.count
end
|
#context_commits_diff ⇒ Object
2070
2071
2072
2073
2074
|
# File 'app/models/merge_request.rb', line 2070
def context_commits_diff
strong_memoize(:context_commits_diff) do
ContextCommitsDiff.new(self)
end
end
|
#create_merge_request_diff ⇒ Object
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
|
# File 'app/models/merge_request.rb', line 1117
def create_merge_request_diff
fetch_ref! unless skip_fetch_ref
Gitlab::GitalyClient.allow_n_plus_1_calls do
merge_request_diffs.create!
reload_merge_request_diff
end
end
|
#current_patch_id_sha ⇒ Object
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
|
# File 'app/models/merge_request.rb', line 2118
def current_patch_id_sha
return merge_request_diff.patch_id_sha if merge_request_diff.patch_id_sha.present?
base_sha = diff_refs&.base_sha
head_sha = diff_refs&.head_sha
return unless base_sha && head_sha
return if base_sha == head_sha
project.repository.get_patch_id(base_sha, head_sha)
end
|
#default_merge_commit_message(include_description: false, user: nil) ⇒ Object
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
|
# File 'app/models/merge_request.rb', line 1456
def default_merge_commit_message(include_description: false, user: nil)
if self.target_project.merge_commit_template.present? && !include_description
return ::Gitlab::MergeRequests::MessageGenerator.new(merge_request: self, current_user: user).merge_commit_message
end
closes_issues_references = visible_closing_issues_for.map do |issue|
issue.to_reference(target_project)
end
message = [
"Merge branch '#{source_branch}' into '#{target_branch}'",
title
]
if !include_description && closes_issues_references.present?
message << "Closes #{closes_issues_references.to_sentence}"
end
message << description if include_description && description.present?
message << "See merge request #{to_reference(full: true)}"
message.join("\n\n")
end
|
#default_squash_commit_message(user: nil) ⇒ Object
1480
1481
1482
1483
1484
1485
1486
|
# File 'app/models/merge_request.rb', line 1480
def default_squash_commit_message(user: nil)
if self.target_project.squash_commit_template.present?
return ::Gitlab::MergeRequests::MessageGenerator.new(merge_request: self, current_user: user).squash_commit_message
end
title
end
|
#diff_base_commit ⇒ Object
877
878
879
880
881
882
883
|
# File 'app/models/merge_request.rb', line 877
def diff_base_commit
if merge_request_diff.persisted?
merge_request_diff.base_commit
else
branch_merge_base_commit
end
end
|
#diff_base_sha ⇒ Object
909
910
911
912
913
914
915
|
# File 'app/models/merge_request.rb', line 909
def diff_base_sha
if merge_request_diff.persisted?
merge_request_diff.base_commit_sha
else
branch_merge_base_commit.try(:sha)
end
end
|
#diff_head_commit ⇒ Object
893
894
895
896
897
898
899
|
# File 'app/models/merge_request.rb', line 893
def diff_head_commit
if merge_request_diff.persisted?
merge_request_diff.head_commit
else
source_branch_head
end
end
|
#diff_head_sha ⇒ Object
917
918
919
920
921
922
923
|
# File 'app/models/merge_request.rb', line 917
def diff_head_sha
if merge_request_diff.persisted?
merge_request_diff.head_commit_sha
else
source_branch_head.try(:sha)
end
end
|
#diff_refs ⇒ Object
976
977
978
979
980
981
982
|
# File 'app/models/merge_request.rb', line 976
def diff_refs
if importing? || persisted?
merge_request_diff.diff_refs
else
repository_diff_refs
end
end
|
#diff_size ⇒ Object
857
858
859
860
861
|
# File 'app/models/merge_request.rb', line 857
def diff_size
merge_request_diff&.real_size || diff_stats&.real_size || diffs.real_size
end
|
#diff_start_commit ⇒ Object
885
886
887
888
889
890
891
|
# File 'app/models/merge_request.rb', line 885
def diff_start_commit
if merge_request_diff.persisted?
merge_request_diff.start_commit
else
target_branch_head
end
end
|
#diff_start_sha ⇒ Object
901
902
903
904
905
906
907
|
# File 'app/models/merge_request.rb', line 901
def diff_start_sha
if merge_request_diff.persisted?
merge_request_diff.start_commit_sha
else
target_branch_head.try(:sha)
end
end
|
#diff_stats ⇒ Object
849
850
851
852
853
854
855
|
# File 'app/models/merge_request.rb', line 849
def diff_stats
return unless diff_refs
strong_memoize(:diff_stats) do
project.repository.diff_stats(diff_refs.base_sha, diff_refs.head_sha)
end
end
|
#diffable_merge_ref? ⇒ Boolean
rubocop: enable CodeReuse/ServiceClass
1195
1196
1197
|
# File 'app/models/merge_request.rb', line 1195
def diffable_merge_ref?
open? && merge_head_diff.present? && can_be_merged?
end
|
#diffs(diff_options = {}) ⇒ Object
802
803
804
805
806
807
808
809
810
811
|
# File 'app/models/merge_request.rb', line 802
def diffs(diff_options = {})
if compare
compare.diffs(diff_options.merge(expanded: true))
else
merge_request_diff.diffs(diff_options)
end
end
|
#diffs_batch_cache_with_max_age? ⇒ Boolean
2098
2099
2100
|
# File 'app/models/merge_request.rb', line 2098
def diffs_batch_cache_with_max_age?
Feature.enabled?(:diffs_batch_cache_with_max_age, project)
end
|
#discussions_diffs ⇒ Object
838
839
840
841
842
843
844
845
846
847
|
# File 'app/models/merge_request.rb', line 838
def discussions_diffs
strong_memoize(:discussions_diffs) do
note_diff_files = NoteDiffFile
.joins(:diff_note)
.merge(notes.or(commit_notes))
.includes(diff_note: :project)
Gitlab::DiscussionsDiff::FileCollection.new(note_diff_files.to_a)
end
end
|
#discussions_rendered_on_frontend? ⇒ Boolean
1971
1972
1973
|
# File 'app/models/merge_request.rb', line 1971
def discussions_rendered_on_frontend?
true
end
|
#diverged_commits_count ⇒ Object
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
|
# File 'app/models/merge_request.rb', line 1612
def diverged_commits_count
cache = Rails.cache.read(:"merge_request_#{id}_diverged_commits")
if cache.blank? || cache[:source_sha] != source_branch_sha || cache[:target_sha] != target_branch_sha
cache = {
source_sha: source_branch_sha,
target_sha: target_branch_sha,
diverged_commits_count: compute_diverged_commits_count
}
Rails.cache.write(:"merge_request_#{id}_diverged_commits", cache)
end
cache[:diverged_commits_count]
end
|
#diverged_from_target_branch? ⇒ Boolean
1635
1636
1637
|
# File 'app/models/merge_request.rb', line 1635
def diverged_from_target_branch?
diverged_commits_count > 0
end
|
#draft? ⇒ Boolean
Also known as:
work_in_progress?
1213
1214
1215
|
# File 'app/models/merge_request.rb', line 1213
def draft?
self.class.draft?(title)
end
|
#draft_title ⇒ Object
Also known as:
wip_title
1223
1224
1225
|
# File 'app/models/merge_request.rb', line 1223
def draft_title
self.class.draft_title(self.title)
end
|
#draftless_title ⇒ Object
Also known as:
wipless_title
1218
1219
1220
|
# File 'app/models/merge_request.rb', line 1218
def draftless_title
self.class.draftless_title(self.title)
end
|
#draftless_title_changed(old_title) ⇒ Object
Also known as:
wipless_title_changed
Verifies if title has changed not taking into account Draft prefix for merge requests.
672
673
674
|
# File 'app/models/merge_request.rb', line 672
def draftless_title_changed(old_title)
self.class.draftless_title(old_title) != self.draftless_title
end
|
#eager_fetch_ref! ⇒ Object
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
|
# File 'app/models/merge_request.rb', line 1103
def eager_fetch_ref!
return unless valid?
track_target_project_iid!
ensure_target_project_iid!
fetch_ref!
@skip_fetch_ref = true
end
|
#enabled_reports ⇒ Object
2057
2058
2059
2060
2061
2062
|
# File 'app/models/merge_request.rb', line 2057
def enabled_reports
{
sast: report_type_enabled?(:sast),
secret_detection: report_type_enabled?(:secret_detection)
}
end
|
#ensure_merge_request_diff ⇒ Object
1099
1100
1101
|
# File 'app/models/merge_request.rb', line 1099
def ensure_merge_request_diff
merge_request_diff.persisted? || create_merge_request_diff
end
|
#ensure_metrics! ⇒ Object
#environments_in_head_pipeline(deployment_status: nil) ⇒ Object
1534
1535
1536
|
# File 'app/models/merge_request.rb', line 1534
def environments_in_head_pipeline(deployment_status: nil)
actual_head_pipeline&.environments_in_self_and_project_descendants(deployment_status: deployment_status) || Environment.none
end
|
#execute_merge_checks(params: {}) ⇒ Object
#existing_mrs_targeting_same_branch ⇒ Object
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
|
# File 'app/models/merge_request.rb', line 1000
def existing_mrs_targeting_same_branch
similar_mrs = target_project
.merge_requests
.where(source_branch: source_branch, target_branch: target_branch)
.where(source_project: source_project)
.opened
similar_mrs = similar_mrs.id_not_in(id) if persisted?
similar_mrs
end
|
#fetch_ref! ⇒ Object
1538
1539
1540
1541
|
# File 'app/models/merge_request.rb', line 1538
def fetch_ref!
target_project.repository.fetch_source_branch!(source_project.repository, source_branch, ref_path)
expire_ancestor_cache
end
|
#ff_merge_possible? ⇒ Boolean
1270
1271
1272
|
# File 'app/models/merge_request.rb', line 1270
def ff_merge_possible?
project.repository.ancestor?(target_branch_sha, diff_head_sha)
end
|
#find_actual_head_pipeline ⇒ Object
2009
2010
2011
|
# File 'app/models/merge_request.rb', line 2009
def find_actual_head_pipeline
all_pipelines.for_sha_or_source_sha(diff_head_sha).first
end
|
#find_assignee(user) ⇒ Object
2041
2042
2043
|
# File 'app/models/merge_request.rb', line 2041
def find_assignee(user)
merge_request_assignees.find_by(user_id: user.id)
end
|
#find_codequality_mr_diff_reports ⇒ Object
TODO: this method and compare_test_reports use the same result type, which is handled by the controller’s #reports_response. we should minimize mistakes by isolating the common parts. issue: gitlab.com/gitlab-org/gitlab/issues/34224
1723
1724
1725
1726
1727
1728
1729
|
# File 'app/models/merge_request.rb', line 1723
def find_codequality_mr_diff_reports
unless has_codequality_mr_diff_report?
return { status: :error, status_reason: 'This merge request does not have codequality mr diff reports' }
end
compare_reports(Ci::GenerateCodequalityMrDiffReportService)
end
|
#find_coverage_reports ⇒ Object
TODO: this method and compare_test_reports use the same result type, which is handled by the controller’s #reports_response. we should minimize mistakes by isolating the common parts. issue: gitlab.com/gitlab-org/gitlab/issues/34224
1707
1708
1709
1710
1711
1712
1713
|
# File 'app/models/merge_request.rb', line 1707
def find_coverage_reports
unless has_coverage_reports?
return { status: :error, status_reason: 'This merge request does not have coverage reports' }
end
compare_reports(Ci::GenerateCoverageReportsService)
end
|
#find_exposed_artifacts ⇒ Object
TODO: this method and compare_test_reports use the same result type, which is handled by the controller’s #reports_response. we should minimize mistakes by isolating the common parts. issue: gitlab.com/gitlab-org/gitlab/issues/34224
1759
1760
1761
1762
1763
1764
1765
|
# File 'app/models/merge_request.rb', line 1759
def find_exposed_artifacts
unless has_exposed_artifacts?
return { status: :error, status_reason: 'This merge request does not have exposed artifacts' }
end
compare_reports(Ci::GenerateExposedArtifactsReportService)
end
|
#find_reviewer(user) ⇒ Object
2049
2050
2051
|
# File 'app/models/merge_request.rb', line 2049
def find_reviewer(user)
merge_request_reviewers.find_by(user_id: user.id)
end
|
1743
1744
1745
1746
1747
1748
1749
|
# File 'app/models/merge_request.rb', line 1743
def find_terraform_reports
unless has_terraform_reports?
return { status: :error, status_reason: 'This merge request does not have terraform reports' }
end
compare_reports(Ci::GenerateTerraformReportsService)
end
|
#first_commit ⇒ Object
794
795
796
|
# File 'app/models/merge_request.rb', line 794
def first_commit
compare_commits.present? ? compare_commits.first : merge_request_diff.first_commit
end
|
#first_contribution? ⇒ Boolean
rubocop: enable CodeReuse/ServiceClass
1981
1982
1983
1984
1985
|
# File 'app/models/merge_request.rb', line 1981
def first_contribution?
return metrics&.first_contribution if merged? & metrics.present?
!project.merge_requests.merged.exists?(author_id: author_id)
end
|
#first_multiline_commit ⇒ Object
Returns the oldest multi-line commit
1489
1490
1491
1492
1493
|
# File 'app/models/merge_request.rb', line 1489
def first_multiline_commit
strong_memoize(:first_multiline_commit) do
recent_commits.without_merge_commits.reverse_each.find(&:description?)
end
end
|
#for_fork? ⇒ Boolean
1351
1352
1353
|
# File 'app/models/merge_request.rb', line 1351
def for_fork?
target_project != source_project
end
|
#for_same_project? ⇒ Boolean
1355
1356
1357
|
# File 'app/models/merge_request.rb', line 1355
def for_same_project?
target_project == source_project
end
|
#force_remove_source_branch? ⇒ Boolean
1294
1295
1296
|
# File 'app/models/merge_request.rb', line 1294
def force_remove_source_branch?
Gitlab::Utils.to_boolean(merge_params['force_remove_source_branch'])
end
|
#has_accessibility_reports? ⇒ Boolean
1683
1684
1685
|
# File 'app/models/merge_request.rb', line 1683
def has_accessibility_reports?
actual_head_pipeline.present? && actual_head_pipeline.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:accessibility))
end
|
#has_ci? ⇒ Boolean
1502
1503
1504
1505
1506
|
# File 'app/models/merge_request.rb', line 1502
def has_ci?
return false if has_no_commits?
!!(head_pipeline_id || all_pipelines.any? || source_project&.ci_integration)
end
|
#has_codequality_mr_diff_report? ⇒ Boolean
1715
1716
1717
|
# File 'app/models/merge_request.rb', line 1715
def has_codequality_mr_diff_report?
actual_head_pipeline&.has_codequality_mr_diff_report?
end
|
#has_codequality_reports? ⇒ Boolean
1731
1732
1733
|
# File 'app/models/merge_request.rb', line 1731
def has_codequality_reports?
actual_head_pipeline&.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:codequality))
end
|
#has_commits? ⇒ Boolean
1936
1937
1938
|
# File 'app/models/merge_request.rb', line 1936
def has_commits?
merge_request_diff.persisted? && commits_count.to_i > 0
end
|
#has_complete_diff_refs? ⇒ Boolean
1888
1889
1890
|
# File 'app/models/merge_request.rb', line 1888
def has_complete_diff_refs?
diff_refs && diff_refs.complete?
end
|
#has_coverage_reports? ⇒ Boolean
1687
1688
1689
|
# File 'app/models/merge_request.rb', line 1687
def has_coverage_reports?
actual_head_pipeline&.has_coverage_reports?
end
|
#has_exposed_artifacts? ⇒ Boolean
1751
1752
1753
|
# File 'app/models/merge_request.rb', line 1751
def has_exposed_artifacts?
actual_head_pipeline&.has_exposed_artifacts?
end
|
#has_no_commits? ⇒ Boolean
1940
1941
1942
|
# File 'app/models/merge_request.rb', line 1940
def has_no_commits?
!has_commits?
end
|
#has_sast_reports? ⇒ Boolean
1781
1782
1783
|
# File 'app/models/merge_request.rb', line 1781
def has_sast_reports?
!!actual_head_pipeline&.complete_and_has_reports?(::Ci::JobArtifact.of_report_type(:sast))
end
|
#has_secret_detection_reports? ⇒ Boolean
1785
1786
1787
|
# File 'app/models/merge_request.rb', line 1785
def has_secret_detection_reports?
!!actual_head_pipeline&.complete_and_has_reports?(::Ci::JobArtifact.of_report_type(:secret_detection))
end
|
1691
1692
1693
|
# File 'app/models/merge_request.rb', line 1691
def has_terraform_reports?
actual_head_pipeline&.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:terraform))
end
|
#has_test_reports? ⇒ Boolean
1652
1653
1654
|
# File 'app/models/merge_request.rb', line 1652
def has_test_reports?
actual_head_pipeline&.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:test))
end
|
#head_pipeline_active? ⇒ Boolean
569
570
571
|
# File 'app/models/merge_request.rb', line 569
def head_pipeline_active?
!!head_pipeline&.active?
end
|
#hidden? ⇒ Boolean
2094
2095
2096
|
# File 'app/models/merge_request.rb', line 2094
def hidden?
Feature.enabled?(:hide_merge_requests_from_banned_users) && author&.banned?
end
|
#in_locked_state ⇒ Object
1597
1598
1599
1600
1601
1602
|
# File 'app/models/merge_request.rb', line 1597
def in_locked_state
lock_mr
yield
ensure
unlock_mr if locked?
end
|
#includes_ci_config? ⇒ Boolean
2064
2065
2066
2067
2068
|
# File 'app/models/merge_request.rb', line 2064
def includes_ci_config?
return false unless diff_stats
diff_stats.map(&:path).include?(project.ci_config_path_or_default)
end
|
#issues_mentioned_but_not_closing(current_user) ⇒ Object
1403
1404
1405
1406
1407
1408
1409
1410
|
# File 'app/models/merge_request.rb', line 1403
def issues_mentioned_but_not_closing(current_user)
return [] unless target_branch == project.default_branch
ext = Gitlab::ReferenceExtractor.new(project, current_user)
ext.analyze("#{title}\n#{description}")
ext.issues - visible_closing_issues_for(current_user)
end
|
#keep_around_commit ⇒ Object
rubocop: enable CodeReuse/ServiceClass
1932
1933
1934
|
# File 'app/models/merge_request.rb', line 1932
def keep_around_commit
project.repository.keep_around(self.merge_commit_sha)
end
|
#merge_async(user_id, params) ⇒ Object
Calls ‘MergeWorker` to proceed with the merge process and updates `merge_jid` with the MergeWorker#jid. This helps tracking enqueued and ongoing merge jobs.
753
754
755
756
757
758
759
760
|
# File 'app/models/merge_request.rb', line 753
def merge_async(user_id, params)
jid = MergeWorker.with_status.perform_async(id, user_id, params.to_h)
update_column(:merge_jid, jid)
expire_etag_cache
end
|
#merge_base_pipeline ⇒ Object
1965
1966
1967
1968
1969
|
# File 'app/models/merge_request.rb', line 1965
def merge_base_pipeline
@merge_base_pipeline ||= project.ci_pipelines
.order(id: :desc)
.find_by(sha: actual_head_pipeline.target_sha, ref: target_branch)
end
|
#merge_blocked_by_other_mrs? ⇒ Boolean
2080
2081
2082
|
# File 'app/models/merge_request.rb', line 2080
def merge_blocked_by_other_mrs?
false end
|
#merge_commit ⇒ Object
1837
1838
1839
|
# File 'app/models/merge_request.rb', line 1837
def merge_commit
@merge_commit ||= project.commit(merge_commit_sha) if merge_commit_sha
end
|
#merge_event ⇒ Object
1205
1206
1207
|
# File 'app/models/merge_request.rb', line 1205
def merge_event
@merge_event ||= target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: :merged).last
end
|
#merge_ongoing? ⇒ Boolean
1072
1073
1074
1075
1076
1077
1078
|
# File 'app/models/merge_request.rb', line 1072
def merge_ongoing?
return true if locked?
!!merge_jid && !merged? && Gitlab::SidekiqStatus.running?(merge_jid)
end
|
#merge_participants ⇒ Object
784
785
786
787
788
789
790
791
792
|
# File 'app/models/merge_request.rb', line 784
def merge_participants
participants = [author]
if auto_merge_enabled? && !participants.include?(merge_user)
participants << merge_user
end
participants.select { |participant| Ability.allowed?(participant, :read_merge_request, self) }
end
|
#merge_pipeline ⇒ Object
563
564
565
566
567
|
# File 'app/models/merge_request.rb', line 563
def merge_pipeline
if sha = merged_commit_sha
target_project.latest_pipeline(target_branch, sha)
end
end
|
#merge_ref_head ⇒ Object
Returns the current merge-ref HEAD commit.
1545
1546
1547
1548
1549
|
# File 'app/models/merge_request.rb', line 1545
def merge_ref_head
return project.repository.commit(merge_ref_sha) if merge_ref_sha
project.repository.commit(merge_ref_path)
end
|
#merge_ref_path ⇒ Object
1555
1556
1557
|
# File 'app/models/merge_request.rb', line 1555
def merge_ref_path
"refs/#{Repository::REF_MERGE_REQUEST}/#{iid}/merge"
end
|
#merge_request_assignees_with(user_ids) ⇒ Object
2045
2046
2047
|
# File 'app/models/merge_request.rb', line 2045
def merge_request_assignees_with(user_ids)
merge_request_assignees.where(user_id: user_ids)
end
|
#merge_request_diff ⇒ Object
This is the same as latest_merge_request_diff unless:
-
There are arguments - in which case we might be trying to force-reload.
-
This association is already loaded.
-
The latest diff does not exist.
-
It doesn’t have any merge_request_diffs - it returns an empty MergeRequestDiff
The second one in particular is important - MergeRequestDiff#merge_request is the inverse of MergeRequest#merge_request_diff, which means it may not be the latest diff, because we could have loaded any diff from this particular MR. If we haven’t already loaded a diff, then it’s fine to load the latest.
84
85
86
87
88
|
# File 'app/models/merge_request.rb', line 84
def merge_request_diff
fallback = latest_merge_request_diff unless association(:merge_request_diff).loaded?
fallback || super || MergeRequestDiff.new(merge_request_id: id)
end
|
#merge_request_diff_for(diff_refs_or_sha) ⇒ Object
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
|
# File 'app/models/merge_request.rb', line 1133
def merge_request_diff_for(diff_refs_or_sha)
matcher =
if diff_refs_or_sha.is_a?(Gitlab::Diff::DiffRefs)
{
'start_commit_sha' => diff_refs_or_sha.start_sha,
'head_commit_sha' => diff_refs_or_sha.head_sha,
'base_commit_sha' => diff_refs_or_sha.base_sha
}
else
{ 'head_commit_sha' => diff_refs_or_sha }
end
viewable_diffs.find do |diff|
diff.attributes.slice(*matcher.keys) == matcher
end
end
|
#merge_request_reviewers_with(user_ids) ⇒ Object
2053
2054
2055
|
# File 'app/models/merge_request.rb', line 2053
def merge_request_reviewers_with(user_ids)
merge_request_reviewers.where(user_id: user_ids)
end
|
#mergeability_checks ⇒ Object
#mergeable?(skip_ci_check: false, skip_discussions_check: false, skip_approved_check: false, check_mergeability_retry_lease: false, skip_rebase_check: false) ⇒ Boolean
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
|
# File 'app/models/merge_request.rb', line 1234
def mergeable?(skip_ci_check: false, skip_discussions_check: false, skip_approved_check: false, check_mergeability_retry_lease: false, skip_rebase_check: false)
return false unless mergeable_state?(
skip_ci_check: skip_ci_check,
skip_discussions_check: skip_discussions_check,
skip_approved_check: skip_approved_check
)
check_mergeability(sync_retry_lease: check_mergeability_retry_lease)
can_be_merged? && (!should_be_rebased? || skip_rebase_check)
end
|
#mergeable_ci_state? ⇒ Boolean
1526
1527
1528
1529
1530
1531
1532
|
# File 'app/models/merge_request.rb', line 1526
def mergeable_ci_state?
return true unless project.only_allow_merge_if_pipeline_succeeds?(inherit_group_setting: true)
return false unless actual_head_pipeline
return true if project.allow_merge_on_skipped_pipeline?(inherit_group_setting: true) && actual_head_pipeline.skipped?
actual_head_pipeline.success?
end
|
#mergeable_discussions_state? ⇒ Boolean
1345
1346
1347
1348
1349
|
# File 'app/models/merge_request.rb', line 1345
def mergeable_discussions_state?
return true unless project.only_allow_merge_if_all_discussions_are_resolved?(inherit_group_setting: true)
unresolved_notes.none?(&:to_be_resolved?)
end
|
#mergeable_state?(skip_ci_check: false, skip_discussions_check: false, skip_approved_check: false) ⇒ Boolean
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
|
# File 'app/models/merge_request.rb', line 1259
def mergeable_state?(skip_ci_check: false, skip_discussions_check: false, skip_approved_check: false)
additional_checks = execute_merge_checks(
params: {
skip_ci_check: skip_ci_check,
skip_discussions_check: skip_discussions_check,
skip_approved_check: skip_approved_check
}
)
additional_checks.success?
end
|
#merged_at ⇒ Object
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
|
# File 'app/models/merge_request.rb', line 1873
def merged_at
strong_memoize(:merged_at) do
next unless merged?
metrics&.merged_at ||
merge_event&.created_at ||
resource_state_events.find_by(state: :merged)&.created_at ||
notes.system.reorder(nil).find_by(note: 'merged')&.created_at
end
end
|
#merged_commit_sha ⇒ Object
1845
1846
1847
1848
1849
1850
|
# File 'app/models/merge_request.rb', line 1845
def merged_commit_sha
return unless merged?
sha = super || merge_commit_sha || squash_commit_sha || diff_head_sha
sha.presence
end
|
#missing_required_squash? ⇒ Boolean
2114
2115
2116
|
# File 'app/models/merge_request.rb', line 2114
def missing_required_squash?
!squash && target_project.squash_always?
end
|
#modified_paths(past_merge_request_diff: nil, fallback_on_overflow: false) ⇒ Object
863
864
865
866
867
868
869
870
871
|
# File 'app/models/merge_request.rb', line 863
def modified_paths(past_merge_request_diff: nil, fallback_on_overflow: false)
if past_merge_request_diff
past_merge_request_diff.modified_paths(fallback_on_overflow: fallback_on_overflow)
elsif compare
diff_stats&.paths || compare.modified_paths
else
merge_request_diff.modified_paths(fallback_on_overflow: fallback_on_overflow)
end
end
|
#new_paths ⇒ Object
873
874
875
|
# File 'app/models/merge_request.rb', line 873
def new_paths
diffs.diff_files.map(&:new_path)
end
|
#non_latest_diffs ⇒ Object
813
814
815
|
# File 'app/models/merge_request.rb', line 813
def non_latest_diffs
merge_request_diffs.where.not(id: merge_request_diff.id)
end
|
#note_positions_for_paths(paths, user = nil) ⇒ Object
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
|
# File 'app/models/merge_request.rb', line 817
def note_positions_for_paths(paths, user = nil)
positions = notes.new_diff_notes.joins(:note_diff_file)
.where('note_diff_files.old_path IN (?) OR note_diff_files.new_path IN (?)', paths, paths)
.positions
collection = Gitlab::Diff::PositionCollection.new(positions, diff_head_sha)
return collection unless user
positions = draft_notes
.authored_by(user)
.positions
.select { |pos| paths.include?(pos.file_path) }
collection.concat(positions)
end
|
#notify_conflict? ⇒ Boolean
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
|
# File 'app/models/merge_request.rb', line 1312
def notify_conflict?
(opened? || locked?) &&
has_commits? &&
!branch_missing? &&
!project.repository.can_be_merged?(diff_head_sha, target_branch)
rescue Gitlab::Git::CommandError
false
end
|
#permits_force_push? ⇒ Boolean
#pipeline_coverage_delta ⇒ Object
1944
1945
1946
1947
1948
|
# File 'app/models/merge_request.rb', line 1944
def pipeline_coverage_delta
if base_pipeline&.coverage && head_pipeline&.coverage
head_pipeline.coverage - base_pipeline.coverage
end
end
|
#predefined_variables ⇒ Object
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
|
# File 'app/models/merge_request.rb', line 1656
def predefined_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'CI_MERGE_REQUEST_ID', value: id.to_s)
variables.append(key: 'CI_MERGE_REQUEST_IID', value: iid.to_s)
variables.append(key: 'CI_MERGE_REQUEST_REF_PATH', value: ref_path.to_s)
variables.append(key: 'CI_MERGE_REQUEST_PROJECT_ID', value: project.id.to_s)
variables.append(key: 'CI_MERGE_REQUEST_PROJECT_PATH', value: project.full_path)
variables.append(key: 'CI_MERGE_REQUEST_PROJECT_URL', value: project.web_url)
variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME', value: target_branch.to_s)
variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_PROTECTED', value: ProtectedBranch.protected?(target_project, target_branch).to_s)
variables.append(key: 'CI_MERGE_REQUEST_TITLE', value: title)
variables.append(key: 'CI_MERGE_REQUEST_ASSIGNEES', value: assignee_username_list) if assignees.present?
variables.append(key: 'CI_MERGE_REQUEST_MILESTONE', value: milestone.title) if milestone
variables.append(key: 'CI_MERGE_REQUEST_LABELS', value: label_names.join(',')) if labels.present?
variables.append(key: 'CI_MERGE_REQUEST_SQUASH_ON_MERGE', value: squash_on_merge?.to_s)
variables.concat(source_project_variables)
end
end
|
#preloads_discussion_diff_highlighting? ⇒ Boolean
834
835
836
|
# File 'app/models/merge_request.rb', line 834
def preloads_discussion_diff_highlighting?
true
end
|
#prepare ⇒ Object
2106
2107
2108
|
# File 'app/models/merge_request.rb', line 2106
def prepare
NewMergeRequestWorker.perform_async(id, author_id)
end
|
#prepared? ⇒ Boolean
2102
2103
2104
|
# File 'app/models/merge_request.rb', line 2102
def prepared?
prepared_at.present?
end
|
#public_merge_status ⇒ Object
Returns current merge_status except it returns ‘cannot_be_merged_rechecking` as `checking` to avoid exposing unnecessary internal state
292
293
294
|
# File 'app/models/merge_request.rb', line 292
def public_merge_status
cannot_be_merged_rechecking? || preparing? ? 'checking' : merge_status
end
|
#raw_diffs(*args) ⇒ Object
798
799
800
|
# File 'app/models/merge_request.rb', line 798
def raw_diffs(*args)
compare.present? ? compare.raw_diffs(*args) : merge_request_diff.raw_diffs(*args)
end
|
#real_time_notes_enabled? ⇒ Boolean
2013
2014
2015
|
# File 'app/models/merge_request.rb', line 2013
def real_time_notes_enabled?
true
end
|
#rebase_async(user_id, skip_ci: false) ⇒ Object
Set off a rebase asynchronously, atomically updating the ‘rebase_jid` of the MR so that the status of the operation can be tracked.
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
|
# File 'app/models/merge_request.rb', line 764
def rebase_async(user_id, skip_ci: false)
with_rebase_lock do
raise ActiveRecord::StaleObjectError if !open? || rebase_in_progress?
jid = Sidekiq::Worker.skipping_transaction_check do
RebaseWorker.with_status.perform_async(id, user_id, skip_ci)
end
update_column(:rebase_jid, jid)
end
expire_etag_cache
end
|
#rebase_in_progress? ⇒ Boolean
546
547
548
|
# File 'app/models/merge_request.rb', line 546
def rebase_in_progress?
rebase_jid.present? && Gitlab::SidekiqStatus.running?(rebase_jid)
end
|
#recent_commits(limit: MergeRequestDiff::COMMITS_SAFE_SIZE, load_from_gitaly: false, page: nil) ⇒ Object
713
714
715
|
# File 'app/models/merge_request.rb', line 713
def recent_commits(limit: MergeRequestDiff::COMMITS_SAFE_SIZE, load_from_gitaly: false, page: nil)
commits(limit: limit, load_from_gitaly: load_from_gitaly, page: page)
end
|
#recent_context_commits ⇒ Object
#recent_diff_head_shas(limit = MAX_RECENT_DIFF_HEAD_SHAS) ⇒ Object
1814
1815
1816
1817
1818
1819
|
# File 'app/models/merge_request.rb', line 1814
def recent_diff_head_shas(limit = MAX_RECENT_DIFF_HEAD_SHAS)
return merge_request_diffs.to_a.sort_by(&:id).reverse.first(limit).pluck(:head_commit_sha) if merge_request_diffs.loaded?
merge_request_diffs.recent(limit).pluck(:head_commit_sha)
end
|
#recent_visible_deployments ⇒ Object
2017
2018
2019
|
# File 'app/models/merge_request.rb', line 2017
def recent_visible_deployments
deployments.visible.includes(:environment).order(id: :desc).limit(10)
end
|
#recheck_merge_status? ⇒ Boolean
Returns boolean indicating the merge_status should be rechecked in order to switch to either can_be_merged or cannot_be_merged.
1201
1202
1203
|
# File 'app/models/merge_request.rb', line 1201
def recheck_merge_status?
self.class.state_machines[:merge_status].check_state?(merge_status)
end
|
#refs_to_cleanup(only: :all) ⇒ Object
1573
1574
1575
1576
1577
1578
1579
|
# File 'app/models/merge_request.rb', line 1573
def refs_to_cleanup(only: :all)
target_refs = []
target_refs << ref_path if %i[all head].include?(only)
target_refs << merge_ref_path if %i[all merge].include?(only)
target_refs << train_ref_path if %i[all train].include?(only)
target_refs
end
|
1323
1324
1325
1326
1327
1328
1329
1330
1331
|
# File 'app/models/merge_request.rb', line 1323
def related_notes
Note
.from_union([notes, commit_notes], remove_duplicates: false)
.includes(:noteable)
end
|
#reload_diff(current_user = nil) ⇒ Object
rubocop: disable CodeReuse/ServiceClass
#reload_diff_if_branch_changed ⇒ Object
1168
1169
1170
1171
1172
1173
|
# File 'app/models/merge_request.rb', line 1168
def reload_diff_if_branch_changed
if (saved_change_to_source_branch? || saved_change_to_target_branch?) &&
(source_branch_head && target_branch_head)
reload_diff
end
end
|
#remove_source_branch? ⇒ Boolean
1308
1309
1310
|
# File 'app/models/merge_request.rb', line 1308
def remove_source_branch?
should_remove_source_branch? || force_remove_source_branch?
end
|
#reopenable? ⇒ Boolean
1091
1092
1093
|
# File 'app/models/merge_request.rb', line 1091
def reopenable?
closed? && !source_project_missing? && source_branch_exists?
end
|
#repository_diff_refs ⇒ Object
Instead trying to fetch the persisted diff_refs, this method goes straight to the repository to get the most recent data possible.
988
989
990
991
992
993
994
|
# File 'app/models/merge_request.rb', line 988
def repository_diff_refs
Gitlab::Diff::DiffRefs.new(
base_sha: branch_merge_base_sha,
start_sha: target_branch_sha,
head_sha: source_branch_sha
)
end
|
#schedule_cleanup_refs(only: :all) ⇒ Object
1563
1564
1565
1566
1567
1568
1569
1570
1571
|
# File 'app/models/merge_request.rb', line 1563
def schedule_cleanup_refs(only: :all)
if Feature.enabled?(:merge_request_delete_gitaly_refs_in_batches, target_project)
async_cleanup_refs(only: only)
elsif Feature.enabled?(:merge_request_cleanup_ref_worker_async, target_project)
MergeRequests::CleanupRefWorker.perform_async(id, only.to_s)
else
cleanup_refs(only: only)
end
end
|
#short_merge_commit_sha ⇒ Object
1841
1842
1843
|
# File 'app/models/merge_request.rb', line 1841
def short_merge_commit_sha
Commit.truncate_sha(merge_commit_sha) if merge_commit_sha
end
|
#short_merged_commit_sha ⇒ Object
1852
1853
1854
1855
1856
|
# File 'app/models/merge_request.rb', line 1852
def short_merged_commit_sha
if sha = merged_commit_sha
Commit.truncate_sha(sha)
end
end
|
#should_be_rebased? ⇒ Boolean
1274
1275
1276
|
# File 'app/models/merge_request.rb', line 1274
def should_be_rebased?
project.ff_merge_must_be_possible? && !ff_merge_possible?
end
|
#should_remove_source_branch? ⇒ Boolean
1290
1291
1292
|
# File 'app/models/merge_request.rb', line 1290
def should_remove_source_branch?
Gitlab::Utils.to_boolean(merge_params['should_remove_source_branch'])
end
|
#skipped_mergeable_checks(options = {}) ⇒ Object
1228
1229
1230
1231
1232
|
# File 'app/models/merge_request.rb', line 1228
def skipped_mergeable_checks(options = {})
{
skip_ci_check: options.fetch(:auto_merge_requested, false)
}
end
|
#source_branch_exists? ⇒ Boolean
1444
1445
1446
1447
1448
|
# File 'app/models/merge_request.rb', line 1444
def source_branch_exists?
return false unless self.source_project
self.source_project.repository.branch_exists?(self.source_branch)
end
|
#source_branch_head ⇒ Object
945
946
947
948
949
950
951
|
# File 'app/models/merge_request.rb', line 945
def source_branch_head
strong_memoize(:source_branch_head) do
if source_project && source_branch_ref
source_project.repository.commit(source_branch_ref)
end
end
end
|
#source_branch_ref ⇒ Object
931
932
933
934
935
936
|
# File 'app/models/merge_request.rb', line 931
def source_branch_ref
return @source_branch_sha if @source_branch_sha
return unless source_branch
Gitlab::Git::BRANCH_REF_PREFIX + source_branch
end
|
#source_project_missing? ⇒ Boolean
1084
1085
1086
1087
1088
1089
|
# File 'app/models/merge_request.rb', line 1084
def source_project_missing?
return false unless for_fork?
return true unless source_project
!source_project.in_fork_network_of?(target_project)
end
|
#source_project_namespace ⇒ Object
1428
1429
1430
1431
1432
1433
1434
|
# File 'app/models/merge_request.rb', line 1428
def source_project_namespace
if source_project && source_project.namespace
source_project.namespace.full_path
else
"(removed)"
end
end
|
#source_project_path ⇒ Object
1420
1421
1422
1423
1424
1425
1426
|
# File 'app/models/merge_request.rb', line 1420
def source_project_path
if source_project
source_project.full_path
else
"(removed)"
end
end
|
#squash_on_merge? ⇒ Boolean
1495
1496
1497
1498
1499
1500
|
# File 'app/models/merge_request.rb', line 1495
def squash_on_merge?
return true if target_project.squash_always?
return false if target_project.squash_never?
squash?
end
|
#suggested_reviewer_users ⇒ Object
70
71
72
|
# File 'app/models/merge_request.rb', line 70
def suggested_reviewer_users
User.none
end
|
#supports_assignee? ⇒ Boolean
2037
2038
2039
|
# File 'app/models/merge_request.rb', line 2037
def supports_assignee?
true
end
|
#supports_lock_on_merge? ⇒ Boolean
744
745
746
747
748
|
# File 'app/models/merge_request.rb', line 744
def supports_lock_on_merge?
return false unless merged?
project.supports_lock_on_merge?
end
|
#supports_suggestion? ⇒ Boolean
740
741
742
|
# File 'app/models/merge_request.rb', line 740
def supports_suggestion?
true
end
|
#target_branch_exists? ⇒ Boolean
1450
1451
1452
1453
1454
|
# File 'app/models/merge_request.rb', line 1450
def target_branch_exists?
return false unless self.target_project
self.target_project.repository.branch_exists?(self.target_branch)
end
|
#target_branch_head ⇒ Object
953
954
955
956
957
|
# File 'app/models/merge_request.rb', line 953
def target_branch_head
strong_memoize(:target_branch_head) do
target_project.repository.commit(target_branch_ref)
end
end
|
#target_branch_ref ⇒ Object
938
939
940
941
942
943
|
# File 'app/models/merge_request.rb', line 938
def target_branch_ref
return @target_branch_sha if @target_branch_sha
return unless target_branch
Gitlab::Git::BRANCH_REF_PREFIX + target_branch
end
|
#target_default_branch? ⇒ Boolean
2076
2077
2078
|
# File 'app/models/merge_request.rb', line 2076
def target_default_branch?
target_branch == project.default_branch
end
|
#target_project_namespace ⇒ Object
1436
1437
1438
1439
1440
1441
1442
|
# File 'app/models/merge_request.rb', line 1436
def target_project_namespace
if target_project && target_project.namespace
target_project.namespace.full_path
else
"(removed)"
end
end
|
#target_project_path ⇒ Object
1412
1413
1414
1415
1416
1417
1418
|
# File 'app/models/merge_request.rb', line 1412
def target_project_path
if target_project
target_project.full_path
else
"(removed)"
end
end
|
#to_reference(from = nil, full: false) ⇒ Object
‘from` argument can be a Namespace or Project.
682
683
684
685
686
|
# File 'app/models/merge_request.rb', line 682
def to_reference(from = nil, full: false)
reference = "#{self.class.reference_prefix}#{iid}"
"#{project.to_reference_base(from, full: full)}#{reference}"
end
|
#train_ref_path ⇒ Object
1559
1560
1561
|
# File 'app/models/merge_request.rb', line 1559
def train_ref_path
"refs/#{Repository::REF_MERGE_REQUEST}/#{iid}/train"
end
|
#update_and_mark_in_progress_merge_commit_sha(commit_id) ⇒ Object
1604
1605
1606
1607
1608
1609
1610
|
# File 'app/models/merge_request.rb', line 1604
def update_and_mark_in_progress_merge_commit_sha(commit_id)
self.update(in_progress_merge_commit_sha: commit_id)
target_project.sticking.stick(:project, target_project.id)
end
|
#update_diff_discussion_positions(old_diff_refs:, new_diff_refs:, current_user: nil) ⇒ Object
rubocop: disable CodeReuse/ServiceClass
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
|
# File 'app/models/merge_request.rb', line 1893
def update_diff_discussion_positions(old_diff_refs:, new_diff_refs:, current_user: nil)
return unless has_complete_diff_refs?
return if new_diff_refs == old_diff_refs
active_diff_discussions = self.notes.new_diff_notes.discussions.select do |discussion|
discussion.active?(old_diff_refs)
end
return if active_diff_discussions.empty?
paths = active_diff_discussions.flat_map { |n| n.diff_file.paths }.uniq
active_discussions_resolved = active_diff_discussions.all?(&:resolved?)
service = Discussions::UpdateDiffPositionService.new(
self.project,
current_user,
old_diff_refs: old_diff_refs,
new_diff_refs: new_diff_refs,
paths: paths
)
active_diff_discussions.each do |discussion|
service.execute(discussion)
discussion.clear_memoized_values
end
if !active_discussions_resolved &&
active_diff_discussions.all?(&:resolved?) &&
project.resolve_outdated_diff_discussions?
MergeRequests::ResolvedDiscussionNotificationService
.new(project: project, current_user: current_user)
.execute(self)
end
end
|
#update_head_pipeline ⇒ Object
1645
1646
1647
1648
1649
1650
|
# File 'app/models/merge_request.rb', line 1645
def update_head_pipeline
find_actual_head_pipeline.try do |pipeline|
self.head_pipeline = pipeline
update_column(:head_pipeline_id, head_pipeline.id) if head_pipeline_id_changed?
end
end
|
#update_project_counter_caches ⇒ Object
rubocop: disable CodeReuse/ServiceClass
#use_merge_base_pipeline_for_comparison?(_) ⇒ Boolean
1951
1952
1953
|
# File 'app/models/merge_request.rb', line 1951
def use_merge_base_pipeline_for_comparison?(_)
false
end
|
#validate_branch_name(attr) ⇒ Object
1041
1042
1043
1044
1045
1046
1047
1048
1049
|
# File 'app/models/merge_request.rb', line 1041
def validate_branch_name(attr)
return unless will_save_change_to_attribute?(attr)
branch = read_attribute(attr)
return unless branch
errors.add(attr) unless Gitlab::GitRefValidator.validate_merge_request_branch(branch)
end
|
#validate_branches ⇒ Object
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
|
# File 'app/models/merge_request.rb', line 1012
def validate_branches
return unless target_project && source_project
if target_project == source_project && target_branch == source_branch
errors.add :branch_conflict, "You can't use same project/branch for source and target"
return
end
[:source_branch, :target_branch].each { |attr| validate_branch_name(attr) }
if opened?
conflicting_mr = existing_mrs_targeting_same_branch.first
if conflicting_mr
errors.add(
:validate_branches,
conflicting_mr_message(conflicting_mr)
)
end
end
end
|
#validate_fork ⇒ Object
1057
1058
1059
1060
1061
1062
1063
|
# File 'app/models/merge_request.rb', line 1057
def validate_fork
return true unless target_project && source_project
return true if target_project == source_project
return true unless source_project_missing?
errors.add :validate_fork, 'Source project is not a fork of the target project'
end
|
#validate_reviewer_size_length ⇒ Object
1065
1066
1067
1068
1069
1070
|
# File 'app/models/merge_request.rb', line 1065
def validate_reviewer_size_length
return true unless reviewers.size > MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
errors.add :reviewers,
-> (_object, _data) { self.class.max_number_of_assignees_or_reviewers_message }
end
|
#validate_target_project ⇒ Object
1051
1052
1053
1054
1055
|
# File 'app/models/merge_request.rb', line 1051
def validate_target_project
return true if target_project.merge_requests_enabled?
errors.add :base, 'Target project has disabled merge requests'
end
|
#version_params_for(diff_refs) ⇒ Object
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
|
# File 'app/models/merge_request.rb', line 1150
def version_params_for(diff_refs)
if diff = merge_request_diff_for(diff_refs)
{ diff_id: diff.id }
elsif diff = merge_request_diff_for(diff_refs.head_sha)
{
diff_id: diff.id,
start_sha: diff_refs.start_sha
}
end
end
|
#viewable_diffs ⇒ Object
1129
1130
1131
|
# File 'app/models/merge_request.rb', line 1129
def viewable_diffs
@viewable_diffs ||= merge_request_diffs.viewable.to_a
end
|
#visible_closing_issues_for(current_user = self.author) ⇒ Object
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
|
# File 'app/models/merge_request.rb', line 1378
def visible_closing_issues_for(current_user = self.author)
strong_memoize(:visible_closing_issues_for) do
if self.target_project.has_external_issue_tracker?
closes_issues(current_user)
else
cached_closes_issues.select do |issue|
Ability.allowed?(current_user, :read_issue, issue)
end
end
end
end
|