2017-11-10 14:57:11 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
# A collection of Commit instances for a specific project and Git reference.
|
|
|
|
class CommitCollection
|
|
|
|
include Enumerable
|
2019-02-06 07:33:11 -05:00
|
|
|
include Gitlab::Utils::StrongMemoize
|
2017-11-10 14:57:11 -05:00
|
|
|
|
|
|
|
attr_reader :project, :ref, :commits
|
|
|
|
|
|
|
|
# project - The project the commits belong to.
|
|
|
|
# commits - The Commit instances to store.
|
|
|
|
# ref - The name of the ref (e.g. "master").
|
|
|
|
def initialize(project, commits, ref = nil)
|
|
|
|
@project = project
|
|
|
|
@commits = commits
|
|
|
|
@ref = ref
|
|
|
|
end
|
|
|
|
|
|
|
|
def each(&block)
|
|
|
|
commits.each(&block)
|
|
|
|
end
|
|
|
|
|
2019-02-15 08:58:23 -05:00
|
|
|
def authors
|
|
|
|
emails = without_merge_commits.map(&:author_email).uniq
|
2019-01-03 05:59:14 -05:00
|
|
|
|
|
|
|
User.by_any_email(emails)
|
|
|
|
end
|
|
|
|
|
2019-02-06 07:33:11 -05:00
|
|
|
def without_merge_commits
|
|
|
|
strong_memoize(:without_merge_commits) do
|
2019-03-14 00:20:40 -04:00
|
|
|
# `#enrich!` the collection to ensure all commits contain
|
|
|
|
# the necessary parent data
|
|
|
|
enrich!.commits.reject(&:merge_commit?)
|
2019-02-06 07:33:11 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-03-14 00:20:40 -04:00
|
|
|
def unenriched
|
|
|
|
commits.reject(&:gitaly_commit?)
|
|
|
|
end
|
|
|
|
|
|
|
|
def fully_enriched?
|
|
|
|
unenriched.empty?
|
|
|
|
end
|
|
|
|
|
|
|
|
# Batch load any commits that are not backed by full gitaly data, and
|
|
|
|
# replace them in the collection.
|
|
|
|
def enrich!
|
2019-03-19 19:07:51 -04:00
|
|
|
# A project is needed in order to fetch data from gitaly. Projects
|
|
|
|
# can be absent from commits in certain rare situations (like when
|
|
|
|
# viewing a MR of a deleted fork). In these cases, assume that the
|
|
|
|
# enriched data is not needed.
|
|
|
|
return self if project.blank? || fully_enriched?
|
2019-03-14 00:20:40 -04:00
|
|
|
|
|
|
|
# Batch load full Commits from the repository
|
|
|
|
# and map to a Hash of id => Commit
|
|
|
|
replacements = Hash[unenriched.map do |c|
|
|
|
|
[c.id, Commit.lazy(project, c.id)]
|
|
|
|
end.compact]
|
|
|
|
|
|
|
|
# Replace the commits, keeping the same order
|
|
|
|
@commits = @commits.map do |c|
|
|
|
|
replacements.fetch(c.id, c)
|
|
|
|
end
|
|
|
|
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2017-11-10 14:57:11 -05:00
|
|
|
# Sets the pipeline status for every commit.
|
|
|
|
#
|
|
|
|
# Setting this status ahead of time removes the need for running a query for
|
|
|
|
# every commit we're displaying.
|
|
|
|
def with_pipeline_status
|
2018-12-05 09:39:15 -05:00
|
|
|
statuses = project.ci_pipelines.latest_status_per_commit(map(&:id), ref)
|
2017-11-10 14:57:11 -05:00
|
|
|
|
|
|
|
each do |commit|
|
|
|
|
commit.set_status_for_ref(ref, statuses[commit.id])
|
|
|
|
end
|
|
|
|
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def respond_to_missing?(message, inc_private = false)
|
|
|
|
commits.respond_to?(message, inc_private)
|
|
|
|
end
|
|
|
|
|
|
|
|
# rubocop:disable GitlabSecurity/PublicSend
|
|
|
|
def method_missing(message, *args, &block)
|
|
|
|
commits.public_send(message, *args, &block)
|
|
|
|
end
|
|
|
|
end
|