diff options
author | Paco Guzman <pacoguzmanp@gmail.com> | 2016-09-21 14:46:10 +0200 |
---|---|---|
committer | Paco Guzman <pacoguzmanp@gmail.com> | 2016-10-06 16:51:55 +0200 |
commit | b4819a5d750ee458a424cf7965926c5758bf784a (patch) | |
tree | 427a3df9f9ac740622b2000ca7e36c6f07792b21 /app | |
parent | 0bbeff3d5e6c1b5ea3b364f052ed6f777c3aa645 (diff) | |
download | gitlab-ce-18663-commits-reference-mentionables.tar.gz |
GitPushService group but author cross_reference creation18663-commits-reference-mentionables
The reference extractor phase happens once per type not once
pero pushed commit, so we could be avoiding a lot of DB queries
Diffstat (limited to 'app')
-rw-r--r-- | app/models/commit.rb | 4 | ||||
-rw-r--r-- | app/models/concerns/mentionable.rb | 12 | ||||
-rw-r--r-- | app/services/git_push_service.rb | 61 |
3 files changed, 45 insertions, 32 deletions
diff --git a/app/models/commit.rb b/app/models/commit.rb index e64fd1e0c1b..41a49064276 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -171,6 +171,8 @@ class Commit end def author + return @author if defined?(@author) + if RequestStore.active? key = "commit_author:#{author_email.downcase}" # nil is a valid value since no author may exist in the system @@ -181,7 +183,7 @@ class Commit RequestStore.store[key] = @author end else - @author ||= find_author_by_any_email + @author = find_author_by_any_email end end diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index eb2ff0428f6..4b50c3e9c03 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -14,6 +14,10 @@ module Mentionable attr = attr.to_s mentionable_attrs << [attr, options] end + + def mentionable_options_for(attr) + mentionable_attrs.detect { |attribute, _options| attribute.to_sym == attr }.try(:last) || {} + end end included do @@ -63,8 +67,8 @@ module Mentionable # Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference. def referenced_mentionables(current_user = self.author) - refs = all_references(current_user) - refs = (refs.issues + refs.merge_requests + refs.commits) + extractor = all_references(current_user) + refs = (extractor.issues + extractor.merge_requests + extractor.commits) # We're using this method instead of Array diffing because that requires # both of the object's `hash` values to be the same, which may not be the @@ -73,8 +77,8 @@ module Mentionable end # Create a cross-reference Note for each GFM reference to another Mentionable found in the +mentionable_attrs+. - def create_cross_references!(author = self.author, without = []) - refs = referenced_mentionables(author) + def create_cross_references!(author = self.author, without = [], refs: nil) + refs ||= referenced_mentionables(author) # We're using this method instead of Array diffing because that requires # both of the object's `hash` values to be the same, which may not be the diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index c499427605a..f3fb7232242 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -106,35 +106,43 @@ class GitPushService < BaseService # Extract any GFM references from the pushed commit messages. If the configured issue-closing regex is matched, # close the referenced Issue. Create cross-reference Notes corresponding to any other referenced Mentionables. def process_commit_messages - is_default_branch = is_default_branch? + closed_issues = commits_close_issues! - authors = Hash.new do |hash, commit| - email = commit.author_email - next hash[email] if hash.has_key?(email) + # Exclude any mentioned Issues to be closed from cross-referencing even if the commits are being pushed to + # a different branch. + commits_cross_references!(closed_issues) + end - hash[email] = commit_user(commit) - end + def commits_close_issues! + # Keep track of the issues that will be actually closed because they are on a default branch. + # Hence, when creating cross-reference notes, the not-closed issues (on non-default branches) + # will also have cross-reference. + closed_issues = Hash.new { |h, k| h[k] = [] } + return closed_issues unless is_default_branch? @push_commits.each do |commit| - # Keep track of the issues that will be actually closed because they are on a default branch. - # Hence, when creating cross-reference notes, the not-closed issues (on non-default branches) - # will also have cross-reference. - closed_issues = [] - - if is_default_branch - # Close issues if these commits were pushed to the project's default branch and the commit message matches the - # closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to - # a different branch. - closed_issues = commit.closes_issues(current_user) - closed_issues.each do |issue| - if can?(current_user, :update_issue, issue) - Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit: commit) - end + # Close issues if these commits were pushed to the project's default branch and the commit message matches the + # closing regex. + closed_issues[commit] = commit.closes_issues(current_user) + closed_issues[commit].each do |issue| + if can?(current_user, :update_issue, issue) + Issues::CloseService.new(project, commit_user(commit), {}).execute(issue, commit: commit) end end + end + + closed_issues + end + + def commits_cross_references!(without) + push_extractor = Gitlab::CrossReferenceExtractor.new(project, current_user) + push_extractor.references_with_object(@push_commits, :safe_message) do |commit, refs| + next if refs.empty? - commit.create_cross_references!(authors[commit], closed_issues) - update_issue_metrics(commit, authors) + referenced_issues = refs.select { |ref| ref.is_a?(Issue) } + update_issue_metrics(referenced_issues, commit.committed_date) + + commit.create_cross_references!(authors[commit], without[commit], refs: refs) end end @@ -180,6 +188,7 @@ class GitPushService < BaseService (Gitlab::Git.ref_name(params[:ref]) == project.default_branch || project.default_branch.nil?) end + # Performance is not affected for long list of commits because by default RequestStore is on def commit_user(commit) commit.author || current_user end @@ -188,10 +197,8 @@ class GitPushService < BaseService @branch_name ||= Gitlab::Git.ref_name(params[:ref]) end - def update_issue_metrics(commit, authors) - mentioned_issues = commit.all_references(authors[commit]).issues - - Issue::Metrics.where(issue_id: mentioned_issues.map(&:id), first_mentioned_in_commit_at: nil). - update_all(first_mentioned_in_commit_at: commit.committed_date) + def update_issue_metrics(referenced_issues, referenced_at) + Issue::Metrics.where(issue_id: referenced_issues.map(&:id), first_mentioned_in_commit_at: nil). + update_all(first_mentioned_in_commit_at: referenced_at) end end |