Skip to content
代码片段 群组 项目
提交 cd7c2cb6 编辑于 作者: Paco Guzman's avatar Paco Guzman
浏览文件

Cache highlighted diff lines for merge requests

Introducing the concept of SafeDiffs which relates 
diffs with UI highlighting.
上级 195b20e1
No related branches found
No related tags found
无相关合并请求
显示
171 个添加27 个删除
...@@ -10,6 +10,7 @@ v 8.11.0 (unreleased) ...@@ -10,6 +10,7 @@ v 8.11.0 (unreleased)
- The Repository class is now instrumented - The Repository class is now instrumented
- Cache the commit author in RequestStore to avoid extra lookups in PostReceive - Cache the commit author in RequestStore to avoid extra lookups in PostReceive
- Expand commit message width in repo view (ClemMakesApps) - Expand commit message width in repo view (ClemMakesApps)
- Cache highlighted diff lines for merge requests
- Fix of 'Commits being passed to custom hooks are already reachable when using the UI' - Fix of 'Commits being passed to custom hooks are already reachable when using the UI'
- Add support for using RequestStore within Sidekiq tasks via SIDEKIQ_REQUEST_STORE env variable - Add support for using RequestStore within Sidekiq tasks via SIDEKIQ_REQUEST_STORE env variable
- Optimize maximum user access level lookup in loading of notes - Optimize maximum user access level lookup in loading of notes
......
module DiffForPath module DiffForPath
extend ActiveSupport::Concern extend ActiveSupport::Concern
def render_diff_for_path(diffs, diff_refs, project) def render_diff_for_path(diffs)
diff_file = safe_diff_files(diffs, diff_refs: diff_refs, repository: project.repository).find do |diff| diff_file = diffs.diff_files.find do |diff|
diff.old_path == params[:old_path] && diff.new_path == params[:new_path] diff.old_path == params[:old_path] && diff.new_path == params[:new_path]
end end
...@@ -14,7 +14,7 @@ def render_diff_for_path(diffs, diff_refs, project) ...@@ -14,7 +14,7 @@ def render_diff_for_path(diffs, diff_refs, project)
locals = { locals = {
diff_file: diff_file, diff_file: diff_file,
diff_commit: diff_commit, diff_commit: diff_commit,
diff_refs: diff_refs, diff_refs: diffs.diff_refs,
blob: blob, blob: blob,
project: project project: project
} }
......
...@@ -28,7 +28,7 @@ def show ...@@ -28,7 +28,7 @@ def show
end end
def diff_for_path def diff_for_path
render_diff_for_path(@diffs, @commit.diff_refs, @project) render_diff_for_path(SafeDiffs::Commit.new(@commit, diff_options: diff_options))
end end
def builds def builds
...@@ -110,7 +110,7 @@ def define_commit_vars ...@@ -110,7 +110,7 @@ def define_commit_vars
opts = diff_options opts = diff_options
opts[:ignore_whitespace_change] = true if params[:format] == 'diff' opts[:ignore_whitespace_change] = true if params[:format] == 'diff'
@diffs = commit.diffs(opts) @diffs = SafeDiffs::Commit.new(commit, diff_options: opts)
@notes_count = commit.notes.count @notes_count = commit.notes.count
end end
......
...@@ -21,7 +21,7 @@ def show ...@@ -21,7 +21,7 @@ def show
def diff_for_path def diff_for_path
return render_404 unless @compare return render_404 unless @compare
render_diff_for_path(@diffs, @diff_refs, @project) render_diff_for_path(SafeDiffs::Compare.new(@compare, project: @project, diff_options: diff_options))
end end
def create def create
...@@ -46,12 +46,12 @@ def define_diff_vars ...@@ -46,12 +46,12 @@ def define_diff_vars
@commit = @project.commit(@head_ref) @commit = @project.commit(@head_ref)
@base_commit = @project.merge_base_commit(@start_ref, @head_ref) @base_commit = @project.merge_base_commit(@start_ref, @head_ref)
@diffs = @compare.diffs(diff_options) diff_refs = Gitlab::Diff::DiffRefs.new(
@diff_refs = Gitlab::Diff::DiffRefs.new(
base_sha: @base_commit.try(:sha), base_sha: @base_commit.try(:sha),
start_sha: @start_commit.try(:sha), start_sha: @start_commit.try(:sha),
head_sha: @commit.try(:sha) head_sha: @commit.try(:sha)
) )
@diffs = SafeDiffs::Compare.new(@compare, project: @project, diff_options: diff_options, diff_refs: diff_refs)
@diff_notes_disabled = true @diff_notes_disabled = true
@grouped_diff_discussions = {} @grouped_diff_discussions = {}
......
...@@ -103,9 +103,8 @@ def diff_for_path ...@@ -103,9 +103,8 @@ def diff_for_path
end end
define_commit_vars define_commit_vars
diffs = @merge_request.diffs(diff_options)
render_diff_for_path(diffs, @merge_request.diff_refs, @merge_request.project) render_diff_for_path(SafeDiffs::MergeRequest.new(merge_request, diff_options: diff_options))
end end
def commits def commits
...@@ -153,7 +152,12 @@ def new ...@@ -153,7 +152,12 @@ def new
@commits = @merge_request.compare_commits.reverse @commits = @merge_request.compare_commits.reverse
@commit = @merge_request.diff_head_commit @commit = @merge_request.diff_head_commit
@base_commit = @merge_request.diff_base_commit @base_commit = @merge_request.diff_base_commit
@diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare if @merge_request.compare
@diffs = SafeDiffs::Compare.new(@merge_request.compare,
project: @merge_request.project,
diff_refs: @merge_request.diff_refs,
diff_options: diff_options)
end
@diff_notes_disabled = true @diff_notes_disabled = true
@pipeline = @merge_request.pipeline @pipeline = @merge_request.pipeline
......
...@@ -206,10 +206,10 @@ def commit_person_link(commit, options = {}) ...@@ -206,10 +206,10 @@ def commit_person_link(commit, options = {})
end end
end end
def view_file_btn(commit_sha, diff, project) def view_file_btn(commit_sha, diff_new_path, project)
link_to( link_to(
namespace_project_blob_path(project.namespace, project, namespace_project_blob_path(project.namespace, project,
tree_join(commit_sha, diff.new_path)), tree_join(commit_sha, diff_new_path)),
class: 'btn view-file js-view-file btn-file-option' class: 'btn view-file js-view-file btn-file-option'
) do ) do
raw('View file @') + content_tag(:span, commit_sha[0..6], raw('View file @') + content_tag(:span, commit_sha[0..6],
......
...@@ -23,18 +23,17 @@ def diff_view ...@@ -23,18 +23,17 @@ def diff_view
end end
def diff_options def diff_options
options = { ignore_whitespace_change: hide_whitespace?, no_collapse: expand_all_diffs? } options = SafeDiffs.default_options.merge(
ignore_whitespace_change: hide_whitespace?,
no_collapse: expand_all_diffs?
)
if action_name == 'diff_for_path' if action_name == 'diff_for_path'
options[:no_collapse] = true options[:no_collapse] = true
options[:paths] = params.values_at(:old_path, :new_path) options[:paths] = params.values_at(:old_path, :new_path)
end end
Commit.max_diff_options.merge(options) options
end
def safe_diff_files(diffs, diff_refs: nil, repository: nil)
diffs.decorate! { |diff| Gitlab::Diff::File.new(diff, diff_refs: diff_refs, repository: repository) }
end end
def unfold_bottom_class(bottom) def unfold_bottom_class(bottom)
......
...@@ -313,6 +313,8 @@ def reload_diff ...@@ -313,6 +313,8 @@ def reload_diff
merge_request_diff.reload_content merge_request_diff.reload_content
MergeRequests::MergeRequestDiffCacheService.new.execute(self)
new_diff_refs = self.diff_refs new_diff_refs = self.diff_refs
update_diff_notes_positions( update_diff_notes_positions(
......
module SafeDiffs
def self.default_options
::Commit.max_diff_options.merge(ignore_whitespace_change: false, no_collapse: false)
end
end
module SafeDiffs
class Base
attr_reader :project, :diff_options, :diff_view, :diff_refs
delegate :count, :real_size, to: :diff_files
def initialize(diffs, project:, diff_options:, diff_refs: nil)
@diffs = diffs
@project = project
@diff_options = diff_options
@diff_refs = diff_refs
end
def diff_files
@diff_files ||= begin
diffs = @diffs.decorate! do |diff|
Gitlab::Diff::File.new(diff, diff_refs: @diff_refs, repository: @project.repository)
end
highlight!(diffs)
diffs
end
end
private
def highlight!(diff_files)
if cacheable?
cache_highlight!(diff_files)
else
diff_files.each { |diff_file| highlight_diff_file!(diff_file) }
end
end
def cacheable?
false
end
def cache_highlight!
raise NotImplementedError
end
def highlight_diff_file_from_cache!(diff_file, cache_diff_lines)
diff_file.diff_lines = cache_diff_lines.map do |line|
Gitlab::Diff::Line.init_from_hash(line)
end
end
def highlight_diff_file!(diff_file)
diff_file.diff_lines = Gitlab::Diff::Highlight.new(diff_file, repository: diff_file.repository).highlight
diff_file.highlighted_diff_lines = diff_file.diff_lines # To be used on parallel diff
diff_file
end
end
end
module SafeDiffs
class Commit < Base
def initialize(commit, diff_options:)
super(commit.diffs(diff_options),
project: commit.project,
diff_options: diff_options,
diff_refs: commit.diff_refs)
end
end
end
module SafeDiffs
class Compare < Base
def initialize(compare, project:, diff_options:, diff_refs: nil)
super(compare.diffs(diff_options),
project: project,
diff_options: diff_options,
diff_refs: diff_refs)
end
end
end
module SafeDiffs
class MergeRequest < Base
def initialize(merge_request, diff_options:)
@merge_request = merge_request
super(merge_request.diffs(diff_options),
project: merge_request.project,
diff_options: diff_options,
diff_refs: merge_request.diff_refs)
end
private
#
# If we find the highlighted diff files lines on the cache we replace existing diff_files lines (no highlighted)
# for the highlighted ones, so we just skip their execution.
# If the highlighted diff files lines are not cached we calculate and cache them.
#
# The content of the cache is and Hash where the key correspond to the file_path and the values are Arrays of
# hashes than represent serialized diff lines.
#
def cache_highlight!(diff_files)
highlighted_cache = Rails.cache.read(cache_key) || {}
highlighted_cache_was_empty = highlighted_cache.empty?
diff_files.each do |diff_file|
file_path = diff_file.file_path
if highlighted_cache[file_path]
highlight_diff_file_from_cache!(diff_file, highlighted_cache[file_path])
else
highlight_diff_file!(diff_file)
highlighted_cache[file_path] = diff_file.diff_lines.map(&:to_hash)
end
end
if highlighted_cache_was_empty
Rails.cache.write(cache_key, highlighted_cache)
end
diff_files
end
def cacheable?
@merge_request.merge_request_diff.present?
end
def cache_key
[@merge_request.merge_request_diff, 'highlighted-safe-diff-files', diff_options]
end
end
end
module MergeRequests
class MergeRequestDiffCacheService
def execute(merge_request)
# Executing the iteration we cache all the highlighted diff information
SafeDiffs::MergeRequest.new(merge_request, diff_options: SafeDiffs.default_options).diff_files.to_a
end
end
end
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
- blob = diff_file.blob - blob = diff_file.blob
- if blob && blob.respond_to?(:text?) && blob_text_viewable?(blob) - if blob && blob.respond_to?(:text?) && blob_text_viewable?(blob)
%table.code.white %table.code.white
- diff_file.highlighted_diff_lines.each do |line| - diff_file.diff_lines.each do |line|
= render "projects/diffs/line", line: line, diff_file: diff_file, plain: true = render "projects/diffs/line", line: line, diff_file: diff_file, plain: true
- else - else
No preview for this file type No preview for this file type
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
= render "ci_menu" = render "ci_menu"
- else - else
%div.block-connector %div.block-connector
= render "projects/diffs/diffs", diffs: @diffs, project: @project, diff_refs: @commit.diff_refs = render "projects/diffs/diffs", diff_files: @diffs.diff_files, project: @diffs.project, diff_refs: @diffs.diff_refs
= render "projects/notes/notes_with_form" = render "projects/notes/notes_with_form"
- if can_collaborate_with_project? - if can_collaborate_with_project?
- %w(revert cherry-pick).each do |type| - %w(revert cherry-pick).each do |type|
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
- if @commits.present? - if @commits.present?
= render "projects/commits/commit_list" = render "projects/commits/commit_list"
= render "projects/diffs/diffs", diffs: @diffs, project: @project, diff_refs: @diff_refs = render "projects/diffs/diffs", diff_files: @diffs.diff_files, project: @diffs.project, diff_refs: @diffs.diff_refs
- else - else
.light-well .light-well
.center .center
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
- if diff_view == 'parallel' - if diff_view == 'parallel'
- fluid_layout true - fluid_layout true
- diff_files = safe_diff_files(diffs, diff_refs: diff_refs, repository: project.repository)
.content-block.oneline-block.files-changed .content-block.oneline-block.files-changed
.inline-parallel-buttons .inline-parallel-buttons
- if !expand_all_diffs? && diff_files.any? { |diff_file| diff_file.collapsed? } - if !expand_all_diffs? && diff_files.any? { |diff_file| diff_file.collapsed? }
......
...@@ -15,6 +15,6 @@ ...@@ -15,6 +15,6 @@
from_merge_request_id: @merge_request.id, from_merge_request_id: @merge_request.id,
skip_visible_check: true) skip_visible_check: true)
= view_file_btn(diff_commit.id, diff_file, project) = view_file_btn(diff_commit.id, diff_file.new_path, project)
= render 'projects/diffs/content', diff_file: diff_file, diff_commit: diff_commit, diff_refs: diff_refs, blob: blob, project: project = render 'projects/diffs/content', diff_file: diff_file, diff_commit: diff_commit, blob: blob, project: project
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%table.text-file.code.js-syntax-highlight{ data: diff_view_data, class: too_big ? 'hide' : '' } %table.text-file.code.js-syntax-highlight{ data: diff_view_data, class: too_big ? 'hide' : '' }
- last_line = 0 - last_line = 0
- diff_file.highlighted_diff_lines.each do |line| - diff_file.diff_lines.each do |line|
- last_line = line.new_pos - last_line = line.new_pos
= render "projects/diffs/line", line: line, diff_file: diff_file = render "projects/diffs/line", line: line, diff_file: diff_file
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册