115 lines
3.1 KiB
Ruby
115 lines
3.1 KiB
Ruby
module Gitlab
|
|
class TreeSummary
|
|
include ::Gitlab::Utils::StrongMemoize
|
|
|
|
attr_reader :commit, :project, :path, :offset, :limit
|
|
|
|
attr_reader :resolved_commits
|
|
private :resolved_commits
|
|
|
|
def initialize(commit, project, params = {})
|
|
@commit = commit
|
|
@project = project
|
|
|
|
@path = params.fetch(:path, nil).presence
|
|
@offset = params.fetch(:offset, 0).to_i
|
|
@limit = (params.fetch(:limit, 25) || 25).to_i
|
|
|
|
# Ensure that if multiple tree entries share the same last commit, they share
|
|
# a ::Commit instance. This prevents us from rendering the same commit title
|
|
# multiple times
|
|
@resolved_commits = {}
|
|
end
|
|
|
|
# Creates a summary of the tree entries for a commit, within the window of
|
|
# entries defined by the offset and limit parameters. This consists of two
|
|
# return values:
|
|
#
|
|
# - An Array of Hashes containing the following keys:
|
|
# - file_name: The full path of the tree entry
|
|
# - type: One of :blob, :tree, or :submodule
|
|
# - commit: The last ::Commit to touch this entry in the tree
|
|
# - commit_path: URI of the commit in the web interface
|
|
# - An Array of the unique ::Commit objects in the first value
|
|
def summarize
|
|
summary = contents
|
|
.map { |content| build_entry(content) }
|
|
.tap { |summary| fill_last_commits!(summary) }
|
|
|
|
[summary, commits]
|
|
end
|
|
|
|
# Does the tree contain more entries after the given offset + limit?
|
|
def more?
|
|
all_contents[next_offset].present?
|
|
end
|
|
|
|
# The offset of the next batch of tree entries. If more? returns false, this
|
|
# batch will be empty
|
|
def next_offset
|
|
[all_contents.size + 1, offset + limit].min
|
|
end
|
|
|
|
private
|
|
|
|
def contents
|
|
all_contents[offset, limit]
|
|
end
|
|
|
|
def commits
|
|
resolved_commits.values
|
|
end
|
|
|
|
def repository
|
|
project.repository
|
|
end
|
|
|
|
def entry_path(entry)
|
|
File.join(*[path, entry[:file_name]].compact)
|
|
end
|
|
|
|
def build_entry(entry)
|
|
{ file_name: entry.name, type: entry.type }
|
|
end
|
|
|
|
def fill_last_commits!(entries)
|
|
# n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37433
|
|
Gitlab::GitalyClient.allow_n_plus_1_calls do
|
|
entries.each do |entry|
|
|
raw_commit = repository.last_commit_for_path(commit.id, entry_path(entry))
|
|
|
|
if raw_commit
|
|
commit = resolve_commit(raw_commit)
|
|
|
|
entry[:commit] = commit
|
|
entry[:commit_path] = commit_path(commit)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def resolve_commit(raw_commit)
|
|
return nil unless raw_commit.present?
|
|
|
|
resolved_commits[raw_commit.id] ||= ::Commit.new(raw_commit, project)
|
|
end
|
|
|
|
def commit_path(commit)
|
|
Gitlab::Routing.url_helpers.project_commit_path(project, commit)
|
|
end
|
|
|
|
def all_contents
|
|
strong_memoize(:all_contents) do
|
|
[
|
|
*tree.trees,
|
|
*tree.blobs,
|
|
*tree.submodules
|
|
]
|
|
end
|
|
end
|
|
|
|
def tree
|
|
strong_memoize(:tree) { repository.tree(commit.id, path) }
|
|
end
|
|
end
|
|
end
|