Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/merge_request.rb')
-rw-r--r--app/models/merge_request.rb139
1 files changed, 100 insertions, 39 deletions
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index f7d1253d957..fdf54cc8a7e 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -3,11 +3,10 @@ class MergeRequest < ActiveRecord::Base
include Issuable
include Referable
include Sortable
- include Taskable
include Importable
- belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project"
- belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project"
+ belongs_to :target_project, class_name: "Project"
+ belongs_to :source_project, class_name: "Project"
belongs_to :merge_user, class_name: "User"
has_many :merge_request_diffs, dependent: :destroy
@@ -16,6 +15,8 @@ class MergeRequest < ActiveRecord::Base
has_many :events, as: :target, dependent: :destroy
+ has_many :merge_requests_closing_issues, class_name: 'MergeRequestsClosingIssues', dependent: :delete_all
+
serialize :merge_params, Hash
after_create :ensure_merge_request_diff, unless: :importing?
@@ -29,7 +30,7 @@ class MergeRequest < ActiveRecord::Base
# Temporary fields to store compare vars
# when creating new merge request
- attr_accessor :can_be_created, :compare_commits, :compare
+ attr_accessor :can_be_created, :compare_commits, :diff_options, :compare
state_machine :state, initial: :opened do
event :close do
@@ -135,6 +136,10 @@ class MergeRequest < ActiveRecord::Base
reference.to_i > 0 && reference.to_i <= Gitlab::Database::MAX_INT_VALUE
end
+ def self.project_foreign_key
+ 'target_project_id'
+ end
+
# Returns all the merge requests from an ActiveRecord:Relation.
#
# This method uses a UNION as it usually operates on the result of
@@ -153,6 +158,20 @@ class MergeRequest < ActiveRecord::Base
where("merge_requests.id IN (#{union.to_sql})")
end
+ WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze
+
+ def self.work_in_progress?(title)
+ !!(title =~ WIP_REGEX)
+ end
+
+ def self.wipless_title(title)
+ title.sub(WIP_REGEX, "")
+ end
+
+ def self.wip_title(title)
+ work_in_progress?(title) ? title : "WIP: #{title}"
+ end
+
def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{iid}"
@@ -180,7 +199,7 @@ class MergeRequest < ActiveRecord::Base
end
def diff_size
- merge_request_diff.size
+ diffs(diff_options).size
end
def diff_base_commit
@@ -306,21 +325,17 @@ class MergeRequest < ActiveRecord::Base
def validate_fork
return true unless target_project && source_project
return true if target_project == source_project
- return true unless forked_source_project_missing?
+ return true unless source_project_missing?
errors.add :validate_fork,
'Source project is not a fork of the target project'
end
def closed_without_fork?
- closed? && forked_source_project_missing?
- end
-
- def closed_without_source_project?
- closed? && !source_project
+ closed? && source_project_missing?
end
- def forked_source_project_missing?
+ def source_project_missing?
return false unless for_fork?
return true unless source_project
@@ -328,9 +343,7 @@ class MergeRequest < ActiveRecord::Base
end
def reopenable?
- return false if closed_without_fork? || closed_without_source_project? || merged?
-
- closed?
+ closed? && !source_project_missing? && source_branch_exists?
end
def ensure_merge_request_diff
@@ -387,14 +400,16 @@ class MergeRequest < ActiveRecord::Base
@closed_event ||= target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last
end
- WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze
-
def work_in_progress?
- !!(title =~ WIP_REGEX)
+ self.class.work_in_progress?(title)
end
def wipless_title
- self.title.sub(WIP_REGEX, "")
+ self.class.wipless_title(self.title)
+ end
+
+ def wip_title
+ self.class.wip_title(self.title)
end
def mergeable?(skip_ci_check: false)
@@ -410,6 +425,7 @@ class MergeRequest < ActiveRecord::Base
return false if work_in_progress?
return false if broken?
return false unless skip_ci_check || mergeable_ci_state?
+ return false unless mergeable_discussions_state?
true
end
@@ -426,11 +442,11 @@ class MergeRequest < ActiveRecord::Base
end
def should_remove_source_branch?
- merge_params['should_remove_source_branch'].present?
+ Gitlab::Utils.to_boolean(merge_params['should_remove_source_branch'])
end
def force_remove_source_branch?
- merge_params['force_remove_source_branch'].present?
+ Gitlab::Utils.to_boolean(merge_params['force_remove_source_branch'])
end
def remove_source_branch?
@@ -478,6 +494,16 @@ class MergeRequest < ActiveRecord::Base
discussions_resolvable? && diff_discussions.none?(&:to_be_resolved?)
end
+ def discussions_to_be_resolved?
+ discussions_resolvable? && !discussions_resolved?
+ end
+
+ def mergeable_discussions_state?
+ return true unless project.only_allow_merge_if_all_discussions_are_resolved?
+
+ !discussions_to_be_resolved?
+ end
+
def hook_attrs
attrs = {
source: source_project.try(:hook_attrs),
@@ -501,6 +527,23 @@ class MergeRequest < ActiveRecord::Base
target_project
end
+ # 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.
+ def cache_merge_request_closes_issues!(current_user = self.author)
+ return if project.has_external_issue_tracker?
+
+ transaction do
+ self.merge_requests_closing_issues.delete_all
+
+ closes_issues(current_user).each do |issue|
+ self.merge_requests_closing_issues.create!(issue: issue)
+ end
+ end
+ end
+
def closes_issue?(issue)
closes_issues.include?(issue)
end
@@ -508,7 +551,8 @@ class MergeRequest < ActiveRecord::Base
# Return the set of issues that will be closed if this merge request is accepted.
def closes_issues(current_user = self.author)
if target_branch == project.default_branch
- messages = commits.map(&:safe_message) << description
+ messages = [description]
+ messages.concat(commits.map(&:safe_message)) if merge_request_diff
Gitlab::ClosingIssueExtractor.new(project, current_user).
closed_by_message(messages.join("\n"))
@@ -574,13 +618,11 @@ class MergeRequest < ActiveRecord::Base
end
def merge_commit_message
- message = "Merge branch '#{source_branch}' into '#{target_branch}'"
- message << "\n\n"
- message << title.to_s
- message << "\n\n"
- message << description.to_s
- message << "\n\n"
- message << "See merge request !#{iid}"
+ message = "Merge branch '#{source_branch}' into '#{target_branch}'\n\n"
+ message << "#{title}\n\n"
+ message << "#{description}\n\n" if description.present?
+ message << "See merge request #{to_reference}"
+
message
end
@@ -624,7 +666,7 @@ class MergeRequest < ActiveRecord::Base
end
def has_ci?
- source_project.ci_service && commits.any?
+ source_project.try(:ci_service) && commits.any?
end
def branch_missing?
@@ -648,14 +690,20 @@ class MergeRequest < ActiveRecord::Base
def mergeable_ci_state?
return true unless project.only_allow_merge_if_build_succeeds?
- !pipeline || pipeline.success?
+ !pipeline || pipeline.success? || pipeline.skipped?
end
def environments
- return unless diff_head_commit
+ return [] unless diff_head_commit
- target_project.environments.select do |environment|
- environment.includes_commit?(diff_head_commit)
+ @environments ||= begin
+ target_envs = target_project.environments_for(
+ target_branch, commit: diff_head_commit, with_tags: true)
+
+ source_envs = source_project.environments_for(
+ source_branch, commit: diff_head_commit) if source_project
+
+ (target_envs.to_a + source_envs.to_a).uniq
end
end
@@ -745,10 +793,23 @@ class MergeRequest < ActiveRecord::Base
end
def all_pipelines
- @all_pipelines ||=
- if diff_head_sha && source_project
- source_project.pipelines.order(id: :desc).where(sha: commits_sha, ref: source_branch)
- end
+ return unless source_project
+
+ @all_pipelines ||= source_project.pipelines
+ .where(sha: all_commits_sha, ref: source_branch)
+ .order(id: :desc)
+ end
+
+ # Note that this could also return SHA from now dangling commits
+ #
+ def all_commits_sha
+ if persisted?
+ merge_request_diffs.flat_map(&:commits_sha).uniq
+ elsif compare_commits
+ compare_commits.to_a.reverse.map(&:id)
+ else
+ [diff_head_sha]
+ end
end
def merge_commit
@@ -818,7 +879,7 @@ class MergeRequest < ActiveRecord::Base
# files.
conflicts.files.each(&:lines)
@conflicts_can_be_resolved_in_ui = conflicts.files.length > 0
- rescue Rugged::OdbError, Gitlab::Conflict::Parser::ParserError, Gitlab::Conflict::FileCollection::ConflictSideMissing
+ rescue Rugged::OdbError, Gitlab::Conflict::Parser::UnresolvableError, Gitlab::Conflict::FileCollection::ConflictSideMissing
@conflicts_can_be_resolved_in_ui = false
end
end