2019-05-03 19:53:19 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
# NOTE: This code is legacy. Do not add/modify code here unless you have
|
|
|
|
# discussed with the Gitaly team. See
|
|
|
|
# https://docs.gitlab.com/ee/development/gitaly.html#legacy-rugged-code
|
|
|
|
# for more details.
|
|
|
|
|
|
|
|
module Gitlab
|
|
|
|
module Git
|
|
|
|
module RuggedImpl
|
|
|
|
module Tree
|
|
|
|
module ClassMethods
|
|
|
|
extend ::Gitlab::Utils::Override
|
2019-09-30 21:07:59 +05:30
|
|
|
include Gitlab::Git::RuggedImpl::UseRugged
|
2019-05-03 19:53:19 +05:30
|
|
|
|
2021-11-11 11:23:49 +05:30
|
|
|
TREE_SORT_ORDER = { tree: 0, blob: 1, commit: 2 }.freeze
|
|
|
|
|
2019-05-03 19:53:19 +05:30
|
|
|
override :tree_entries
|
2021-10-27 15:23:28 +05:30
|
|
|
def tree_entries(repository, sha, path, recursive, pagination_params = nil)
|
2019-09-30 21:07:59 +05:30
|
|
|
if use_rugged?(repository, :rugged_tree_entries)
|
2021-11-11 11:23:49 +05:30
|
|
|
entries = execute_rugged_call(:tree_entries_with_flat_path_from_rugged, repository, sha, path, recursive)
|
|
|
|
|
|
|
|
if pagination_params
|
|
|
|
paginated_response(entries, pagination_params[:limit], pagination_params[:page_token].to_s)
|
|
|
|
else
|
|
|
|
[entries, nil]
|
|
|
|
end
|
2019-05-03 19:53:19 +05:30
|
|
|
else
|
|
|
|
super
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-11-11 11:23:49 +05:30
|
|
|
# Rugged version of TreePagination in Go: https://gitlab.com/gitlab-org/gitaly/-/merge_requests/3611
|
|
|
|
def paginated_response(entries, limit, token)
|
|
|
|
total_entries = entries.count
|
|
|
|
|
|
|
|
return [[], nil] if limit == 0 || limit.blank?
|
|
|
|
|
|
|
|
entries = Gitlab::Utils.stable_sort_by(entries) { |x| TREE_SORT_ORDER[x.type] }
|
|
|
|
|
|
|
|
if token.blank?
|
|
|
|
index = 0
|
|
|
|
else
|
|
|
|
index = entries.index { |entry| entry.id == token }
|
|
|
|
|
|
|
|
raise Gitlab::Git::CommandError, "could not find starting OID: #{token}" if index.nil?
|
|
|
|
|
|
|
|
index += 1
|
|
|
|
end
|
|
|
|
|
|
|
|
return [entries[index..], nil] if limit < 0
|
|
|
|
|
|
|
|
last_index = index + limit
|
|
|
|
result = entries[index...last_index]
|
|
|
|
|
|
|
|
if last_index < total_entries
|
|
|
|
cursor = Gitaly::PaginationCursor.new(next_cursor: result.last.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
[result, cursor]
|
|
|
|
end
|
|
|
|
|
2019-05-03 19:53:19 +05:30
|
|
|
def tree_entries_with_flat_path_from_rugged(repository, sha, path, recursive)
|
|
|
|
tree_entries_from_rugged(repository, sha, path, recursive).tap do |entries|
|
|
|
|
# This was an optimization to reduce N+1 queries for Gitaly
|
|
|
|
# (https://gitlab.com/gitlab-org/gitaly/issues/530). It
|
|
|
|
# used to be done lazily in the view via
|
|
|
|
# TreeHelper#flatten_tree, so it's possible there's a
|
|
|
|
# performance impact by loading this eagerly.
|
|
|
|
rugged_populate_flat_path(repository, sha, path, entries)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def tree_entries_from_rugged(repository, sha, path, recursive)
|
|
|
|
current_path_entries = get_tree_entries_from_rugged(repository, sha, path)
|
|
|
|
ordered_entries = []
|
|
|
|
|
|
|
|
current_path_entries.each do |entry|
|
|
|
|
ordered_entries << entry
|
|
|
|
|
|
|
|
if recursive && entry.dir?
|
|
|
|
ordered_entries.concat(tree_entries_from_rugged(repository, sha, entry.path, true))
|
|
|
|
end
|
|
|
|
end
|
2019-07-07 11:18:12 +05:30
|
|
|
|
|
|
|
ordered_entries
|
2019-05-03 19:53:19 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
def rugged_populate_flat_path(repository, sha, path, entries)
|
|
|
|
entries.each do |entry|
|
|
|
|
entry.flat_path = entry.path
|
|
|
|
|
|
|
|
next unless entry.dir?
|
|
|
|
|
|
|
|
entry.flat_path =
|
|
|
|
if path
|
|
|
|
File.join(path, rugged_flatten_tree(repository, sha, entry, path))
|
|
|
|
else
|
|
|
|
rugged_flatten_tree(repository, sha, entry, path)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns the relative path of the first subdir that doesn't have only one directory descendant
|
|
|
|
def rugged_flatten_tree(repository, sha, tree, root_path)
|
|
|
|
subtree = tree_entries_from_rugged(repository, sha, tree.path, false)
|
|
|
|
|
|
|
|
if subtree.count == 1 && subtree.first.dir?
|
|
|
|
File.join(tree.name, rugged_flatten_tree(repository, sha, subtree.first, root_path))
|
|
|
|
else
|
|
|
|
tree.name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_tree_entries_from_rugged(repository, sha, path)
|
|
|
|
commit = repository.lookup(sha)
|
|
|
|
root_tree = commit.tree
|
|
|
|
|
|
|
|
tree = if path
|
|
|
|
id = find_id_by_path(repository, root_tree.oid, path)
|
|
|
|
if id
|
|
|
|
repository.lookup(id)
|
|
|
|
else
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
else
|
|
|
|
root_tree
|
|
|
|
end
|
|
|
|
|
|
|
|
tree.map do |entry|
|
|
|
|
current_path = path ? File.join(path, entry[:name]) : entry[:name]
|
|
|
|
|
|
|
|
new(
|
|
|
|
id: entry[:oid],
|
|
|
|
root_id: root_tree.oid,
|
|
|
|
name: entry[:name],
|
|
|
|
type: entry[:type],
|
|
|
|
mode: entry[:filemode].to_s(8),
|
|
|
|
path: current_path,
|
|
|
|
commit_id: sha
|
|
|
|
)
|
|
|
|
end
|
|
|
|
rescue Rugged::ReferenceError
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|