1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
# frozen_string_literal: true
module Gitlab
module Diff
module FileCollection
class Base
include Gitlab::Utils::StrongMemoize
attr_reader :project, :diff_options, :diff_refs, :fallback_diff_refs, :diffable
delegate :count, :size, :real_size, to: :raw_diff_files
def self.default_options
::Commit.max_diff_options.merge(ignore_whitespace_change: false, expanded: false, include_stats: true, use_extra_viewer_as_main: false)
end
def initialize(diffable, project:, diff_options: nil, diff_refs: nil, fallback_diff_refs: nil)
diff_options = self.class.default_options.merge(diff_options || {})
@diffable = diffable
@include_stats = diff_options.delete(:include_stats)
@pagination_data = diff_options.delete(:pagination_data)
@project = project
@diff_options = diff_options
@diff_refs = diff_refs
@fallback_diff_refs = fallback_diff_refs
@repository = project.repository
@use_extra_viewer_as_main = diff_options.delete(:use_extra_viewer_as_main)
end
def diffs
@diffs ||= diffable.raw_diffs(diff_options)
end
def diff_files(sorted: false)
raw_diff_files(sorted: sorted)
end
def raw_diff_files(sorted: false)
strong_memoize(:"raw_diff_files_#{sorted}") do
collection = diffs.decorate! { |diff| decorate_diff!(diff) }
collection = sort_diffs(collection) if sorted
collection
end
end
# This is either the new path, otherwise the old path for the diff_file
def diff_file_paths
diffs.map do |diff|
diff.new_path.presence || diff.old_path
end
end
# This is both the new and old paths for the diff_file
def diff_paths
diff_files.map(&:paths).flatten.uniq
end
def pagination_data
@pagination_data || empty_pagination_data
end
# This mutates `diff_files` lines.
def unfold_diff_files(positions)
positions_grouped_by_path = positions.group_by { |position| position.file_path }
diff_files.each do |diff_file|
positions = positions_grouped_by_path.fetch(diff_file.file_path, [])
positions.each { |position| diff_file.unfold_diff_lines(position) }
end
end
def diff_file_with_old_path(old_path)
diff_files.find { |diff_file| diff_file.old_path == old_path }
end
def diff_file_with_new_path(new_path)
diff_files.find { |diff_file| diff_file.new_path == new_path }
end
def clear_cache
# No-op
end
def write_cache
# No-op
end
def overflow?
raw_diff_files.overflow?
end
private
def empty_pagination_data
{ total_pages: nil }
end
def diff_stats_collection
strong_memoize(:diff_stats) do
next unless fetch_diff_stats?
@repository.diff_stats(diff_refs.base_sha, diff_refs.head_sha)
end
end
def fetch_diff_stats?
# There are scenarios where we don't need to request Diff Stats,
# when caching for instance.
@include_stats && diff_refs
end
def decorate_diff!(diff)
return diff if diff.is_a?(File)
stats = diff_stats_collection&.find_by_path(diff.new_path)
diff_file = Gitlab::Diff::File.new(diff,
repository: project.repository,
diff_refs: diff_refs,
fallback_diff_refs: fallback_diff_refs,
stats: stats)
if @use_extra_viewer_as_main && diff_file.has_renderable?
diff_file.rendered
else
diff_file
end
end
def sort_diffs(diffs)
Gitlab::Diff::FileCollectionSorter.new(diffs).sort
end
end
end
end
end
|