Class: Gitlab::Diff::File
- Inherits:
-
Object
- Object
- Gitlab::Diff::File
- Includes:
- Utils::StrongMemoize
- Defined in:
- lib/gitlab/diff/file.rb
Constant Summary collapse
- RICH_VIEWERS =
Finding a viewer for a diff file happens based only on extension and whether the diff file blobs are binary or text, which means 1 diff file should only be matched by 1 viewer, and the order of these viewers doesn't really matter.
However, when the diff file blobs are LFS pointers, we cannot know for sure whether the file being pointed to is binary or text. In this case, we match only on extension, preferring binary viewers over text ones if both exist, since the large files referred to in “Large File Storage” are much more likely to be binary than text.
[ DiffViewer::Image ].sort_by { |v| v.binary? ? 0 : 1 }.freeze
Instance Attribute Summary collapse
-
#diff ⇒ Object
readonly
Returns the value of attribute diff.
-
#diff_refs ⇒ Object
readonly
Returns the value of attribute diff_refs.
-
#fallback_diff_refs ⇒ Object
readonly
Returns the value of attribute fallback_diff_refs.
-
#repository ⇒ Object
readonly
Returns the value of attribute repository.
-
#unique_identifier ⇒ Object
readonly
Returns the value of attribute unique_identifier.
Instance Method Summary collapse
- #added_lines ⇒ Object
- #alternate_viewer ⇒ Object
- #binary? ⇒ Boolean
- #binary_in_repo? ⇒ Boolean
- #blob ⇒ Object
- #content_changed? ⇒ Boolean
- #content_sha ⇒ Object
-
#diff_hunk(diff_line) ⇒ Object
Returns the raw diff content up to the given line index.
-
#diff_lines ⇒ Object
Array of Gitlab::Diff::Line objects.
-
#diff_lines_for_serializer ⇒ Object
This adds the bottom match line to the array if needed.
- #diffable? ⇒ Boolean
- #different_type? ⇒ Boolean
-
#empty? ⇒ Boolean
rubocop: enable CodeReuse/ActiveRecord.
- #external_storage ⇒ Object
- #external_storage_error? ⇒ Boolean
- #file_hash ⇒ Object
- #file_identifier ⇒ Object
- #file_identifier_hash ⇒ Object
- #file_path ⇒ Object
- #fully_expanded? ⇒ Boolean
- #highlight_loaded? ⇒ Boolean
- #highlighted_diff_lines ⇒ Object
- #highlighted_diff_lines=(value) ⇒ Object
-
#initialize(diff, repository:, diff_refs: nil, fallback_diff_refs: nil, stats: nil, unique_identifier: nil) ⇒ File
constructor
A new instance of File.
- #line_code(line) ⇒ Object
- #line_code_for_position(pos) ⇒ Object
- #line_for_line_code(code) ⇒ Object
- #line_for_position(pos) ⇒ Object
- #new_blob ⇒ Object
- #new_blob_lines_between(from_line, to_line) ⇒ Object
- #new_content_sha ⇒ Object
- #new_sha ⇒ Object
- #next_line(index) ⇒ Object
- #old_blob ⇒ Object
- #old_content_sha ⇒ Object
- #old_sha ⇒ Object
-
#parallel_diff_lines ⇒ Object
#paths ⇒ Object
- #position(position_marker, position_type: :text) ⇒ Object
- #position_for_line_code(code) ⇒ Object
- #prev_line(index) ⇒ Object
- #raw_diff ⇒ Object
- #raw_size ⇒ Object
rubocop: disable CodeReuse/ActiveRecord.
- #removed_lines ⇒ Object
- #rendered_as_text?(ignore_errors: true) ⇒ Boolean
- #rich_viewer ⇒ Object
- #simple_viewer ⇒ Object
- #size ⇒ Object
rubocop: disable CodeReuse/ActiveRecord.
- #stored_externally? ⇒ Boolean
- #text? ⇒ Boolean
- #text_in_repo? ⇒ Boolean
- #unfold_diff_lines(position) ⇒ Object
Changes diff_lines according to the given position.
- #unfolded? ⇒ Boolean
- #viewer ⇒ Object
Methods included from Utils::StrongMemoize
#clear_memoization, #strong_memoize, #strong_memoized?
Constructor Details
#initialize(diff, repository:, diff_refs: nil, fallback_diff_refs: nil, stats: nil, unique_identifier: nil) ⇒ File
Returns a new instance of File.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
# File 'lib/gitlab/diff/file.rb', line 27 def initialize( diff, repository:, diff_refs: nil, fallback_diff_refs: nil, stats: nil, unique_identifier: nil) @diff = diff @stats = stats @repository = repository @diff_refs = diff_refs @fallback_diff_refs = fallback_diff_refs @unique_identifier = unique_identifier @unfolded = false # Ensure items are collected in the the batch new_blob_lazy old_blob_lazy end
Instance Attribute Details
#diff ⇒ Object (readonly)
Returns the value of attribute diff
8 9 10
# File 'lib/gitlab/diff/file.rb', line 8 def diff @diff end
#diff_refs ⇒ Object (readonly)
Returns the value of attribute diff_refs
8 9 10
# File 'lib/gitlab/diff/file.rb', line 8 def diff_refs @diff_refs end
#fallback_diff_refs ⇒ Object (readonly)
Returns the value of attribute fallback_diff_refs
8 9 10
# File 'lib/gitlab/diff/file.rb', line 8 def fallback_diff_refs @fallback_diff_refs end
#repository ⇒ Object (readonly)
Returns the value of attribute repository
8 9 10
# File 'lib/gitlab/diff/file.rb', line 8 def repository @repository end
#unique_identifier ⇒ Object (readonly)
Returns the value of attribute unique_identifier
8 9 10
# File 'lib/gitlab/diff/file.rb', line 8 def unique_identifier @unique_identifier end
Instance Method Details
#added_lines ⇒ Object
232 233 234 235 236
# File 'lib/gitlab/diff/file.rb', line 232 def added_lines strong_memoize(:added_lines) do @stats&.additions || diff_lines.count(&:added?) end end
#alternate_viewer ⇒ Object
329 330 331
# File 'lib/gitlab/diff/file.rb', line 329 def alternate_viewer alternate_viewer_class&.new(self) end
#binary? ⇒ Boolean
303 304 305 306 307
# File 'lib/gitlab/diff/file.rb', line 303 def binary? strong_memoize(:is_binary) do try_blobs(:binary?) end end
#binary_in_repo? ⇒ Boolean
256 257 258
# File 'lib/gitlab/diff/file.rb', line 256 def binary_in_repo? has_binary_notice? || try_blobs(:binary_in_repo?) end
#blob ⇒ Object
161 162 163
# File 'lib/gitlab/diff/file.rb', line 161 def blob new_blob || old_blob end
#content_changed? ⇒ Boolean
276 277 278 279 280 281
# File 'lib/gitlab/diff/file.rb', line 276 def content_changed? return blobs_changed? if diff_refs return false if new_file? || deleted_file? || renamed_file? text? && diff_lines.any? end
#content_sha ⇒ Object
157 158 159
# File 'lib/gitlab/diff/file.rb', line 157 def content_sha new_content_sha || old_content_sha end
#diff_hunk(diff_line) ⇒ Object
Returns the raw diff content up to the given line index
102 103 104 105 106 107 108 109
# File 'lib/gitlab/diff/file.rb', line 102 def diff_hunk(diff_line) diff_line_index = diff_line.index # @@ (match) header is not kept if it's found in the top of the file, # therefore we should keep an extra line on this scenario. diff_line_index += 1 unless diff_lines.first.match? diff_lines.select { |line| line.index <= diff_line_index }.map(&:text).join("\n") end
#diff_lines ⇒ Object
Array of Gitlab::Diff::Line objects
171 172 173 174
# File 'lib/gitlab/diff/file.rb', line 171 def diff_lines @diff_lines ||= Gitlab::Diff::Parser.new.parse(raw_diff.each_line, diff_file: self).to_a end
#diff_lines_for_serializer ⇒ Object
This adds the bottom match line to the array if needed. It contains the data to load more context lines.
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
# File 'lib/gitlab/diff/file.rb', line 339 def diff_lines_for_serializer strong_memoize(:diff_lines_for_serializer) do lines = highlighted_diff_lines next if lines.empty? next if blob.nil? last_line = lines.last if last_line.new_pos < total_blob_lines(blob) && !deleted_file? match_line = Gitlab::Diff::Line.new("", 'match', nil, last_line.old_pos, last_line.new_pos) lines.push(match_line) end lines end end
#diffable? ⇒ Boolean
252 253 254
# File 'lib/gitlab/diff/file.rb', line 252 def diffable? repository.attributes(file_path).fetch('diff') { true } end
#different_type? ⇒ Boolean
283 284 285
# File 'lib/gitlab/diff/file.rb', line 283 def different_type? old_blob && new_blob && old_blob.binary? != new_blob.binary? end
#empty? ⇒ Boolean
rubocop: enable CodeReuse/ActiveRecord
299 300 301
# File 'lib/gitlab/diff/file.rb', line 299 def empty? valid_blobs.map(&:empty?).all? end
#external_storage ⇒ Object
272 273 274
# File 'lib/gitlab/diff/file.rb', line 272 def external_storage try_blobs(:external_storage) end
#external_storage_error? ⇒ Boolean
264 265 266
# File 'lib/gitlab/diff/file.rb', line 264 def external_storage_error? try_blobs(:external_storage_error?) end
#file_hash ⇒ Object
228 229 230
# File 'lib/gitlab/diff/file.rb', line 228 def file_hash Digest::SHA1.hexdigest(file_path) end
#file_identifier ⇒ Object
244 245 246
# File 'lib/gitlab/diff/file.rb', line 244 def file_identifier "#{file_path}-#{new_file?}-#{deleted_file?}-#{renamed_file?}" end
#file_identifier_hash ⇒ Object
248 249 250
# File 'lib/gitlab/diff/file.rb', line 248 def file_identifier_hash Digest::SHA1.hexdigest(file_identifier) end
#file_path ⇒ Object
224 225 226
# File 'lib/gitlab/diff/file.rb', line 224 def file_path new_path.presence || old_path end
#fully_expanded? ⇒ Boolean
357 358 359 360 361 362 363 364 365
# File 'lib/gitlab/diff/file.rb', line 357 def return true if binary? lines = diff_lines_for_serializer return true if lines.nil? lines.none? { |line| line.type.to_s == 'match' } end
#highlight_loaded? ⇒ Boolean
194 195 196
# File 'lib/gitlab/diff/file.rb', line 194 def highlight_loaded? @highlighted_diff_lines.present? end
#highlighted_diff_lines ⇒ Object
198 199 200 201
# File 'lib/gitlab/diff/file.rb', line 198 def highlighted_diff_lines @highlighted_diff_lines ||= Gitlab::Diff::Highlight.new(self, repository: self.repository).highlight end
#highlighted_diff_lines=(value) ⇒ Object
165 166 167 168
# File 'lib/gitlab/diff/file.rb', line 165 def highlighted_diff_lines=(value) clear_memoization(:diff_lines_for_serializer) @highlighted_diff_lines = value end
#line_code(line) ⇒ Object
67 68 69 70 71
# File 'lib/gitlab/diff/file.rb', line 67 def line_code(line) return if line. Gitlab::Git.diff_line_code(file_path, line.new_pos, line.old_pos) end
#line_code_for_position(pos) ⇒ Object
96 97 98 99
# File 'lib/gitlab/diff/file.rb', line 96 def line_code_for_position(pos) line = line_for_position(pos) line_code(line) if line end
#line_for_line_code(code) ⇒ Object
73 74 75
# File 'lib/gitlab/diff/file.rb', line 73 def line_for_line_code(code) diff_lines.find { |line| line_code(line) == code } end
#line_for_position(pos) ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 89
# File 'lib/gitlab/diff/file.rb', line 77 def line_for_position(pos) return unless pos.position_type == 'text' # This method is normally used to find which line the diff was # commented on, and in this context, it's normally the raw diff persisted # at `note_diff_files`, which is a fraction of the entire diff # (it goes from the first line, to the commented line, or # one line below). Therefore it's more performant to fetch # from bottom to top instead of the other way around. diff_lines .reverse_each .find { |line| line.old_line == pos.old_line && line.new_line == pos.new_line } end
#new_blob ⇒ Object
135 136 137 138 139
# File 'lib/gitlab/diff/file.rb', line 135 def new_blob strong_memoize(:new_blob) do new_blob_lazy&.itself end end
#new_blob_lines_between(from_line, to_line) ⇒ Object
147 148 149 150 151 152 153 154 155
# File 'lib/gitlab/diff/file.rb', line 147 def new_blob_lines_between(from_line, to_line) return [] unless new_blob from_index = from_line - 1 to_index = to_line - 1 new_blob.load_all_data! new_blob.data.lines[from_index..to_index] end
#new_content_sha ⇒ Object
119 120 121 122 123 124 125
# File 'lib/gitlab/diff/file.rb', line 119 def new_content_sha return if deleted_file? return @new_content_sha if defined?(@new_content_sha) refs = diff_refs || fallback_diff_refs @new_content_sha = refs&.head_sha end
#new_sha ⇒ Object
115 116 117
# File 'lib/gitlab/diff/file.rb', line 115 def new_sha diff_refs&.head_sha end
#next_line(index) ⇒ Object
212 213 214
# File 'lib/gitlab/diff/file.rb', line 212 def next_line(index) diff_lines[index + 1] end
#old_blob ⇒ Object
141 142 143 144 145
# File 'lib/gitlab/diff/file.rb', line 141 def old_blob strong_memoize(:old_blob) do old_blob_lazy&.itself end end
#old_content_sha ⇒ Object
127 128 129 130 131 132 133
# File 'lib/gitlab/diff/file.rb', line 127 def old_content_sha return if new_file? return @old_content_sha if defined?(@old_content_sha) refs = diff_refs || fallback_diff_refs @old_content_sha = refs&.base_sha end
#old_sha ⇒ Object
111 112 113
# File 'lib/gitlab/diff/file.rb', line 111 def old_sha diff_refs&.base_sha end
#parallel_diff_lines ⇒ Object
204 205 206
# File 'lib/gitlab/diff/file.rb', line 204 def parallel_diff_lines @parallel_diff_lines ||= Gitlab::Diff::ParallelDiff.new(self).parallelize end
#paths ⇒ Object
220 221 222
# File 'lib/gitlab/diff/file.rb', line 220 def paths [old_path, new_path].compact end
#position(position_marker, position_type: :text) ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
# File 'lib/gitlab/diff/file.rb', line 48 def position(position_marker, position_type: :text) return unless diff_refs data = { diff_refs: diff_refs, position_type: position_type.to_s, old_path: old_path, new_path: new_path } if position_type == :text data.merge!(text_position_properties(position_marker)) else data.merge!(image_position_properties(position_marker)) end Position.new(data) end
#position_for_line_code(code) ⇒ Object
91 92 93 94
# File 'lib/gitlab/diff/file.rb', line 91 def position_for_line_code(code) line = line_for_line_code(code) position(line) if line end
#prev_line(index) ⇒ Object
216 217 218
# File 'lib/gitlab/diff/file.rb', line 216 def prev_line(index) diff_lines[index - 1] if index > 0 end
#raw_diff ⇒ Object
208 209 210
# File 'lib/gitlab/diff/file.rb', line 208 def raw_diff diff.diff.to_s end
#raw_size ⇒ Object
rubocop: disable CodeReuse/ActiveRecord
294 295 296
# File 'lib/gitlab/diff/file.rb', line 294 def raw_size valid_blobs.map(&:raw_size).sum end
#removed_lines ⇒ Object
238 239 240 241 242
# File 'lib/gitlab/diff/file.rb', line 238 def removed_lines strong_memoize(:removed_lines) do @stats&.deletions || diff_lines.count(&:removed?) end end
#rendered_as_text?(ignore_errors: true) ⇒ Boolean
333 334 335
# File 'lib/gitlab/diff/file.rb', line 333 def rendered_as_text?(ignore_errors: true) simple_viewer.is_a?(DiffViewer::Text) && (ignore_errors || simple_viewer.render_error.nil?) end
#rich_viewer ⇒ Object
323 324 325 326 327
# File 'lib/gitlab/diff/file.rb', line 323 def rich_viewer return @rich_viewer if defined?(@rich_viewer) @rich_viewer = rich_viewer_class&.new(self) end
#simple_viewer ⇒ Object
319 320 321
# File 'lib/gitlab/diff/file.rb', line 319 def simple_viewer @simple_viewer ||= simple_viewer_class.new(self) end
#size ⇒ Object
rubocop: disable CodeReuse/ActiveRecord
288 289 290
# File 'lib/gitlab/diff/file.rb', line 288 def size valid_blobs.map(&:size).sum end
#stored_externally? ⇒ Boolean
268 269 270
# File 'lib/gitlab/diff/file.rb', line 268 def stored_externally? try_blobs(:stored_externally?) end
#text? ⇒ Boolean
309 310 311 312 313
# File 'lib/gitlab/diff/file.rb', line 309 def text? strong_memoize(:is_text) do !binary? && !different_type? end end
#text_in_repo? ⇒ Boolean
260 261 262
# File 'lib/gitlab/diff/file.rb', line 260 def text_in_repo? !binary_in_repo? end
#unfold_diff_lines(position) ⇒ Object
Changes diff_lines according to the given position. That is, it checks whether the position requires blob lines into the diff in order to be presented.
179 180 181 182 183 184 185 186 187 188
# File 'lib/gitlab/diff/file.rb', line 179 def unfold_diff_lines(position) return unless position unfolder = Gitlab::Diff::LinesUnfolder.new(self, position) if unfolder.unfold_required? @diff_lines = unfolder.unfolded_diff_lines @unfolded = true end end
#unfolded? ⇒ Boolean
190 191 192
# File 'lib/gitlab/diff/file.rb', line 190 def unfolded? @unfolded end
#viewer ⇒ Object
315 316 317
# File 'lib/gitlab/diff/file.rb', line 315 def viewer rich_viewer || simple_viewer end