summaryrefslogtreecommitdiff
path: root/app/models/merge_request.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/merge_request.rb')
-rw-r--r--app/models/merge_request.rb83
1 files changed, 73 insertions, 10 deletions
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 1543ef311d7..ef48207f956 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -48,7 +48,7 @@ class MergeRequest < ActiveRecord::Base
after_create :create_merge_request_diff
after_update :update_merge_request_diff
- delegate :commits, :diffs, :diffs_no_whitespace, to: :merge_request_diff, prefix: nil
+ delegate :commits, :diffs, :real_size, to: :merge_request_diff, prefix: nil
# When this attribute is true some MR validation is ignored
# It allows us to close or modify broken merge requests
@@ -56,8 +56,7 @@ class MergeRequest < ActiveRecord::Base
# Temporary fields to store compare vars
# when creating new merge request
- attr_accessor :can_be_created, :compare_failed,
- :compare_commits, :compare_diffs
+ attr_accessor :can_be_created, :compare_commits, :compare
state_machine :state, initial: :opened do
event :close do
@@ -132,15 +131,11 @@ class MergeRequest < ActiveRecord::Base
validate :validate_branches
validate :validate_fork
- scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.projects.select(:id).reorder(nil)) }
scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) }
scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
- scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) }
scope :of_projects, ->(ids) { where(target_project_id: ids) }
- scope :opened, -> { with_states(:opened, :reopened) }
scope :merged, -> { with_state(:merged) }
- scope :closed, -> { with_state(:closed) }
scope :closed_and_merged, -> { with_states(:closed, :merged) }
scope :join_project, -> { joins(:target_project) }
@@ -164,6 +159,24 @@ class MergeRequest < ActiveRecord::Base
super("merge_requests", /(?<merge_request>\d+)/)
end
+ # 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.
+ def self.in_projects(relation)
+ source = where(source_project_id: relation).select(:id)
+ target = where(target_project_id: relation).select(:id)
+ union = Gitlab::SQL::Union.new([source, target])
+
+ where("merge_requests.id IN (#{union.to_sql})")
+ end
+
def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{iid}"
@@ -182,6 +195,10 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
end
+ def diff_size
+ merge_request_diff.size
+ end
+
def diff_base_commit
if merge_request_diff
merge_request_diff.base_commit
@@ -259,8 +276,14 @@ class MergeRequest < ActiveRecord::Base
self.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 =~ /\A\[?WIP(\]|:| )/i)
+ title =~ WIP_REGEX
+ end
+
+ def wipless_title
+ self.title.sub(WIP_REGEX, "")
end
def mergeable?
@@ -487,12 +510,26 @@ class MergeRequest < ActiveRecord::Base
end
end
+ def state_icon_name
+ if merged?
+ "check"
+ elsif closed?
+ "times"
+ else
+ "circle-o"
+ end
+ end
+
def target_sha
- @target_sha ||= target_project.repository.commit(target_branch).sha
+ @target_sha ||= target_project.repository.commit(target_branch).try(:sha)
end
def source_sha
- last_commit.try(:sha)
+ last_commit.try(:sha) || source_tip.try(:sha)
+ end
+
+ def source_tip
+ source_branch && source_project.repository.commit(source_branch)
end
def fetch_ref
@@ -524,6 +561,32 @@ class MergeRequest < ActiveRecord::Base
end
end
+ def diverged_commits_count
+ cache = Rails.cache.read(:"merge_request_#{id}_diverged_commits")
+
+ if cache.blank? || cache[:source_sha] != source_sha || cache[:target_sha] != target_sha
+ cache = {
+ source_sha: source_sha,
+ target_sha: target_sha,
+ diverged_commits_count: compute_diverged_commits_count
+ }
+ Rails.cache.write(:"merge_request_#{id}_diverged_commits", cache)
+ end
+
+ cache[:diverged_commits_count]
+ end
+
+ def compute_diverged_commits_count
+ return 0 unless source_sha && target_sha
+
+ Gitlab::Git::Commit.between(target_project.repository.raw_repository, source_sha, target_sha).size
+ end
+ private :compute_diverged_commits_count
+
+ def diverged_from_target_branch?
+ diverged_commits_count > 0
+ end
+
def ci_commit
@ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit && source_project
end