409f2f4dd2
Rails is slow to generate paths dynamically especially when called hundreds/thousands of times. Also, rendering many partials hundreds of times can be slower. This change reduces the number of partials rendered and introduces two fast path methods to speed up path generation.
159 lines
4.7 KiB
Ruby
159 lines
4.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module TreeHelper
|
|
FILE_LIMIT = 1_000
|
|
|
|
# Sorts a repository's tree so that folders are before files and renders
|
|
# their corresponding partials
|
|
#
|
|
# tree - A `Tree` object for the current tree
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
|
def render_tree(tree)
|
|
# Sort submodules and folders together by name ahead of files
|
|
folders, files, submodules = tree.trees, tree.blobs, tree.submodules
|
|
tree = []
|
|
items = (folders + submodules).sort_by(&:name) + files
|
|
|
|
if items.size > FILE_LIMIT
|
|
tree << render(partial: 'projects/tree/truncated_notice_tree_row',
|
|
locals: { limit: FILE_LIMIT, total: items.size })
|
|
items = items.take(FILE_LIMIT)
|
|
end
|
|
|
|
tree << render(partial: 'projects/tree/tree_row', collection: items) if items.present?
|
|
tree.join.html_safe
|
|
end
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
|
|
|
# Return an image icon depending on the file type and mode
|
|
#
|
|
# type - String type of the tree item; either 'folder' or 'file'
|
|
# mode - File unix mode
|
|
# name - File name
|
|
def tree_icon(type, mode, name)
|
|
icon([file_type_icon_class(type, mode, name), 'fw'])
|
|
end
|
|
|
|
# Using Rails `*_path` methods can be slow, especially when generating
|
|
# many paths, as with a repository tree that has thousands of items.
|
|
def fast_project_blob_path(project, blob_path)
|
|
Addressable::URI.escape(
|
|
File.join(relative_url_root, project.path_with_namespace, 'blob', blob_path)
|
|
)
|
|
end
|
|
|
|
def fast_project_tree_path(project, tree_path)
|
|
Addressable::URI.escape(
|
|
File.join(relative_url_root, project.path_with_namespace, 'tree', tree_path)
|
|
)
|
|
end
|
|
|
|
# Simple shortcut to File.join
|
|
def tree_join(*args)
|
|
File.join(*args)
|
|
end
|
|
|
|
def on_top_of_branch?(project = @project, ref = @ref)
|
|
project.repository.branch_exists?(ref)
|
|
end
|
|
|
|
def can_edit_tree?(project = nil, ref = nil)
|
|
project ||= @project
|
|
ref ||= @ref
|
|
|
|
return false unless on_top_of_branch?(project, ref)
|
|
|
|
can_collaborate_with_project?(project, ref: ref)
|
|
end
|
|
|
|
def tree_edit_branch(project = @project, ref = @ref)
|
|
return unless can_edit_tree?(project, ref)
|
|
|
|
if user_access(project).can_push_to_branch?(ref)
|
|
ref
|
|
else
|
|
project = tree_edit_project(project)
|
|
project.repository.next_branch('patch')
|
|
end
|
|
end
|
|
|
|
def tree_edit_project(project = @project)
|
|
if can?(current_user, :push_code, project)
|
|
project
|
|
elsif current_user && current_user.already_forked?(project)
|
|
current_user.fork_of(project)
|
|
end
|
|
end
|
|
|
|
def edit_in_new_fork_notice_now
|
|
"You're not allowed to make changes to this project directly." +
|
|
" A fork of this project is being created that you can make changes in, so you can submit a merge request."
|
|
end
|
|
|
|
def edit_in_new_fork_notice
|
|
"You're not allowed to make changes to this project directly." +
|
|
" A fork of this project has been created that you can make changes in, so you can submit a merge request."
|
|
end
|
|
|
|
def edit_in_new_fork_notice_action(action)
|
|
edit_in_new_fork_notice + " Try to #{action} this file again."
|
|
end
|
|
|
|
def commit_in_fork_help
|
|
_("A new branch will be created in your fork and a new merge request will be started.")
|
|
end
|
|
|
|
def commit_in_single_accessible_branch
|
|
branch_name = ERB::Util.html_escape(selected_branch)
|
|
|
|
message = _("Your changes can be committed to %{branch_name} because a merge "\
|
|
"request is open.") % { branch_name: "<strong>#{branch_name}</strong>" }
|
|
|
|
message.html_safe
|
|
end
|
|
|
|
def path_breadcrumbs(max_links = 6)
|
|
if @path.present?
|
|
part_path = ""
|
|
parts = @path.split('/')
|
|
|
|
yield('..', File.join(*parts.first(parts.count - 2))) if parts.count > max_links
|
|
|
|
parts.each do |part|
|
|
part_path = File.join(part_path, part) unless part_path.empty?
|
|
part_path = part if part_path.empty?
|
|
|
|
next if parts.count > max_links && !parts.last(2).include?(part)
|
|
|
|
yield(part, part_path)
|
|
end
|
|
end
|
|
end
|
|
|
|
def up_dir_path
|
|
file = File.join(@path, "..")
|
|
tree_join(@ref, file)
|
|
end
|
|
|
|
# returns the relative path of the first subdir that doesn't have only one directory descendant
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
|
def flatten_tree(root_path, tree)
|
|
return tree.flat_path.sub(%r{\A#{Regexp.escape(root_path)}/}, '') if tree.flat_path.present?
|
|
|
|
subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path)
|
|
if subtree.count == 1 && subtree.first.dir?
|
|
return tree_join(tree.name, flatten_tree(root_path, subtree.first))
|
|
else
|
|
return tree.name
|
|
end
|
|
end
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
|
|
|
def selected_branch
|
|
@branch_name || tree_edit_branch
|
|
end
|
|
|
|
def relative_url_root
|
|
Gitlab.config.gitlab.relative_url_root.presence || '/'
|
|
end
|
|
end
|