Commit 7d4b717c authored by Douwe Maan's avatar Douwe Maan

Merge branch 'fast_project_blob_path' into 'master'

Improve performance of tree rendering in repositories with a lot of items

See merge request gitlab-org/gitlab-ce!16511
parents 18718eb1 409f2f4d
...@@ -109,6 +109,8 @@ module IconsHelper ...@@ -109,6 +109,8 @@ module IconsHelper
def file_type_icon_class(type, mode, name) def file_type_icon_class(type, mode, name)
if type == 'folder' if type == 'folder'
icon_class = 'folder' icon_class = 'folder'
elsif type == 'archive'
icon_class = 'archive'
elsif mode == '120000' elsif mode == '120000'
icon_class = 'share' icon_class = 'share'
else else
......
...@@ -31,11 +31,21 @@ module TreeHelper ...@@ -31,11 +31,21 @@ module TreeHelper
# mode - File unix mode # mode - File unix mode
# name - File name # name - File name
def tree_icon(type, mode, name) def tree_icon(type, mode, name)
icon("#{file_type_icon_class(type, mode, name)} fw") icon([file_type_icon_class(type, mode, name), 'fw'])
end end
def tree_hex_class(content) # Using Rails `*_path` methods can be slow, especially when generating
"file_#{hexdigest(content.name)}" # 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 end
# Simple shortcut to File.join # Simple shortcut to File.join
...@@ -142,4 +152,8 @@ module TreeHelper ...@@ -142,4 +152,8 @@ module TreeHelper
def selected_branch def selected_branch
@branch_name || tree_edit_branch @branch_name || tree_edit_branch
end end
def relative_url_root
Gitlab.config.gitlab.relative_url_root.presence || '/'
end
end end
- is_lfs_blob = @lfs_blob_ids.include?(blob_item.id)
%tr{ class: "tree-item #{tree_hex_class(blob_item)}" }
%td.tree-item-file-name
= tree_icon(type, blob_item.mode, blob_item.name)
- file_name = blob_item.name
= link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), class: 'str-truncated', title: file_name do
%span= file_name
- if is_lfs_blob
%span.badge.label-lfs.prepend-left-5 LFS
%td.d-none.d-sm-table-cell.tree-commit
%td.tree-time-ago.cgray.text-right
= render 'projects/tree/spinner'
%span.log_loading.hide
%i.fa.fa-spinner.fa-spin
Loading commit data...
%tr.tree-item
%td.tree-item-file-name
%i.fa.fa-archive.fa-fw
= submodule_link(submodule_item, @ref)
%td
%td.d-none.d-sm-table-cell
%tr{ class: "tree-item #{tree_hex_class(tree_item)}" }
%td.tree-item-file-name
= tree_icon(type, tree_item.mode, tree_item.name)
- path = flatten_tree(@path, tree_item)
= link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), class: 'str-truncated', title: path do
%span= path
%td.d-none.d-sm-table-cell.tree-commit
%td.tree-time-ago.text-right
= render 'projects/tree/spinner'
- if tree_row.type == :tree - tree_row_name = tree_row.name
= render partial: 'projects/tree/tree_item', object: tree_row, as: 'tree_item', locals: { type: 'folder' } - tree_row_type = tree_row.type
- elsif tree_row.type == :blob
= render partial: 'projects/tree/blob_item', object: tree_row, as: 'blob_item', locals: { type: 'file' } %tr{ class: "tree-item file_#{hexdigest(tree_row_name)}" }
- elsif tree_row.type == :commit %td.tree-item-file-name
= render partial: 'projects/tree/submodule_item', object: tree_row, as: 'submodule_item' - if tree_row_type == :tree
= tree_icon('folder', tree_row.mode, tree_row.name)
- path = flatten_tree(@path, tree_row)
%a.str-truncated{ href: fast_project_tree_path(@project, tree_join(@id || @commit.id, path)), title: path }
%span= path
- elsif tree_row_type == :blob
= tree_icon('file', tree_row.mode, tree_row_name)
%a.str-truncated{ href: fast_project_blob_path(@project, tree_join(@id || @commit.id, tree_row_name)), title: tree_row_name }
%span= tree_row_name
- if @lfs_blob_ids.include?(tree_row.id)
%span.badge.label-lfs.prepend-left-5 LFS
- elsif tree_row_type == :commit
= tree_icon('archive', tree_row.mode, tree_row.name)
= submodule_link(tree_row, @ref)
%td.d-none.d-sm-table-cell.tree-commit
%td.tree-time-ago.text-right
%span.log_loading.hide
%i.fa.fa-spinner.fa-spin
Loading commit data...
---
title: Improve performance of tree rendering in repositories with lots of items
merge_request:
author:
type: performance
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe TreeHelper do describe TreeHelper do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:repository) { project.repository } let(:repository) { project.repository }
let(:sha) { 'ce369011c189f62c815f5971d096b26759bab0d1' } let(:sha) { 'c1c67abbaf91f624347bb3ae96eabe3a1b742478' }
describe '.render_tree' do describe '.render_tree' do
before do before do
...@@ -32,6 +32,49 @@ describe TreeHelper do ...@@ -32,6 +32,49 @@ describe TreeHelper do
end end
end end
describe '.fast_project_blob_path' do
it 'generates the same path as project_blob_path' do
blob_path = repository.tree(sha, 'with space').entries.first.path
fast_path = fast_project_blob_path(project, blob_path)
std_path = project_blob_path(project, blob_path)
expect(fast_path).to eq(std_path)
end
it 'generates the same path with encoded file names' do
tree = repository.tree(sha, 'encoding')
blob_path = tree.entries.find { |entry| entry.path == 'encoding/テスト.txt' }.path
fast_path = fast_project_blob_path(project, blob_path)
std_path = project_blob_path(project, blob_path)
expect(fast_path).to eq(std_path)
end
it 'respects a configured relative URL' do
allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root')
blob_path = repository.tree(sha, '').entries.first.path
fast_path = fast_project_blob_path(project, blob_path)
expect(fast_path).to start_with('/gitlab/root')
end
end
describe '.fast_project_tree_path' do
let(:tree_path) { repository.tree(sha, 'with space').path }
let(:fast_path) { fast_project_tree_path(project, tree_path) }
let(:std_path) { project_tree_path(project, tree_path) }
it 'generates the same path as project_tree_path' do
expect(fast_path).to eq(std_path)
end
it 'respects a configured relative URL' do
allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root')
expect(fast_path).to start_with('/gitlab/root')
end
end
describe 'flatten_tree' do describe 'flatten_tree' do
let(:tree) { repository.tree(sha, 'files') } let(:tree) { repository.tree(sha, 'files') }
let(:root_path) { 'files' } let(:root_path) { 'files' }
......
require 'spec_helper' require 'spec_helper'
describe 'projects/tree/_blob_item' do describe 'projects/tree/_tree_row' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:repository) { project.repository } let(:repository) { project.repository }
let(:blob_item) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files/ruby').first } let(:blob_item) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files/ruby').first }
...@@ -31,10 +31,7 @@ describe 'projects/tree/_blob_item' do ...@@ -31,10 +31,7 @@ describe 'projects/tree/_blob_item' do
end end
end end
def render_partial(blob_item) def render_partial(items)
render partial: 'projects/tree/blob_item', locals: { render partial: 'projects/tree/tree_row', collection: [items].flatten
blob_item: blob_item,
type: 'blob'
}
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment