2018-11-09 13:39:43 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-07-12 10:31:36 -04:00
|
|
|
# Gitaly note: JV: no RPC's here.
|
|
|
|
|
2017-01-04 13:43:06 -05:00
|
|
|
module Gitlab
|
|
|
|
module Git
|
|
|
|
class DiffCollection
|
|
|
|
include Enumerable
|
|
|
|
|
2017-07-13 18:22:09 -04:00
|
|
|
attr_reader :limits
|
|
|
|
|
2021-11-23 13:12:49 -05:00
|
|
|
def self.default_limits
|
|
|
|
{ max_files: ::Commit.diff_safe_max_files, max_lines: ::Commit.diff_safe_max_lines }
|
2020-09-21 11:09:44 -04:00
|
|
|
end
|
|
|
|
|
2018-09-06 16:56:06 -04:00
|
|
|
def self.limits(options = {})
|
2017-07-13 18:22:09 -04:00
|
|
|
limits = {}
|
2021-11-23 13:12:49 -05:00
|
|
|
defaults = default_limits
|
2020-09-21 11:09:44 -04:00
|
|
|
limits[:max_files] = options.fetch(:max_files, defaults[:max_files])
|
|
|
|
limits[:max_lines] = options.fetch(:max_lines, defaults[:max_lines])
|
2017-07-13 18:22:09 -04:00
|
|
|
limits[:max_bytes] = limits[:max_files] * 5.kilobytes # Average 5 KB per file
|
2020-09-21 11:09:44 -04:00
|
|
|
|
|
|
|
limits[:safe_max_files] = [limits[:max_files], defaults[:max_files]].min
|
|
|
|
limits[:safe_max_lines] = [limits[:max_lines], defaults[:max_lines]].min
|
2017-07-13 18:22:09 -04:00
|
|
|
limits[:safe_max_bytes] = limits[:safe_max_files] * 5.kilobytes # Average 5 KB per file
|
2018-09-24 11:30:49 -04:00
|
|
|
limits[:max_patch_bytes] = Gitlab::Git::Diff.patch_hard_limit_bytes
|
2022-04-01 05:10:01 -04:00
|
|
|
limits
|
2017-07-13 18:22:09 -04:00
|
|
|
end
|
|
|
|
|
2017-01-04 13:43:06 -05:00
|
|
|
def initialize(iterator, options = {})
|
|
|
|
@iterator = iterator
|
2018-09-06 16:56:06 -04:00
|
|
|
@limits = self.class.limits(options)
|
2017-05-31 15:41:25 -04:00
|
|
|
@enforce_limits = !!options.fetch(:limits, true)
|
2017-05-26 19:27:30 -04:00
|
|
|
@expanded = !!options.fetch(:expanded, true)
|
2019-10-09 17:06:24 -04:00
|
|
|
@offset_index = options.fetch(:offset_index, 0)
|
2017-01-04 13:43:06 -05:00
|
|
|
|
|
|
|
@line_count = 0
|
|
|
|
@byte_count = 0
|
|
|
|
@overflow = false
|
2017-05-23 10:14:52 -04:00
|
|
|
@empty = true
|
2020-09-02 11:10:54 -04:00
|
|
|
@array = []
|
2017-01-04 13:43:06 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def each(&block)
|
2017-07-13 18:22:09 -04:00
|
|
|
@array.each(&block)
|
|
|
|
|
|
|
|
return if @overflow
|
|
|
|
return if @iterator.nil?
|
|
|
|
|
2018-07-06 06:01:15 -04:00
|
|
|
if @iterator.is_a?(Gitlab::GitalyClient::DiffStitcher)
|
|
|
|
each_gitaly_patch(&block)
|
|
|
|
else
|
|
|
|
each_serialized_patch(&block)
|
2017-01-04 13:43:06 -05:00
|
|
|
end
|
2017-07-13 18:22:09 -04:00
|
|
|
|
|
|
|
@populated = true
|
|
|
|
|
|
|
|
# Allow iterator to be garbage-collected. It cannot be reused anyway.
|
|
|
|
@iterator = nil
|
2017-01-04 13:43:06 -05:00
|
|
|
end
|
|
|
|
|
2020-12-14 19:10:07 -05:00
|
|
|
def sort(&block)
|
|
|
|
@array = @array.sort(&block)
|
|
|
|
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2017-01-04 13:43:06 -05:00
|
|
|
def empty?
|
2017-05-23 10:14:52 -04:00
|
|
|
any? # Make sure the iterator has been exercised
|
|
|
|
@empty
|
2017-01-04 13:43:06 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def overflow?
|
|
|
|
populate!
|
|
|
|
!!@overflow
|
|
|
|
end
|
|
|
|
|
2021-04-14 11:09:04 -04:00
|
|
|
def overflow_max_lines?
|
|
|
|
!!@overflow_max_lines
|
|
|
|
end
|
|
|
|
|
|
|
|
def overflow_max_bytes?
|
|
|
|
!!@overflow_max_bytes
|
|
|
|
end
|
|
|
|
|
|
|
|
def overflow_max_files?
|
|
|
|
!!@overflow_max_files
|
|
|
|
end
|
|
|
|
|
|
|
|
def collapsed_safe_lines?
|
|
|
|
!!@collapsed_safe_lines
|
|
|
|
end
|
|
|
|
|
|
|
|
def collapsed_safe_files?
|
|
|
|
!!@collapsed_safe_files
|
|
|
|
end
|
|
|
|
|
|
|
|
def collapsed_safe_bytes?
|
|
|
|
!!@collapsed_safe_bytes
|
|
|
|
end
|
|
|
|
|
2017-01-04 13:43:06 -05:00
|
|
|
def size
|
|
|
|
@size ||= count # forces a loop using each method
|
|
|
|
end
|
|
|
|
|
|
|
|
def real_size
|
|
|
|
populate!
|
|
|
|
|
|
|
|
if @overflow
|
|
|
|
"#{size}+"
|
|
|
|
else
|
|
|
|
size.to_s
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-08-13 04:26:16 -04:00
|
|
|
def line_count
|
|
|
|
populate!
|
|
|
|
|
|
|
|
@line_count
|
|
|
|
end
|
|
|
|
|
2017-01-04 13:43:06 -05:00
|
|
|
def decorate!
|
2021-04-19 11:09:08 -04:00
|
|
|
each_with_index do |element, i|
|
2017-01-04 13:43:06 -05:00
|
|
|
@array[i] = yield(element)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-05-15 13:18:19 -04:00
|
|
|
alias_method :to_ary, :to_a
|
|
|
|
|
2017-01-04 13:43:06 -05:00
|
|
|
private
|
|
|
|
|
|
|
|
def populate!
|
|
|
|
return if @populated
|
|
|
|
|
2020-01-27 16:08:47 -05:00
|
|
|
each {} # force a loop through all diffs
|
2017-01-04 13:43:06 -05:00
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
|
|
|
def over_safe_limits?(files)
|
2022-04-01 05:10:01 -04:00
|
|
|
if files >= limits[:safe_max_files]
|
2021-04-14 11:09:04 -04:00
|
|
|
@collapsed_safe_files = true
|
2022-04-01 05:10:01 -04:00
|
|
|
elsif @line_count > limits[:safe_max_lines]
|
2021-04-14 11:09:04 -04:00
|
|
|
@collapsed_safe_lines = true
|
2022-04-01 05:10:01 -04:00
|
|
|
elsif @byte_count >= limits[:safe_max_bytes]
|
2021-04-14 11:09:04 -04:00
|
|
|
@collapsed_safe_bytes = true
|
|
|
|
end
|
|
|
|
|
|
|
|
@collapsed_safe_files || @collapsed_safe_lines || @collapsed_safe_bytes
|
2017-01-04 13:43:06 -05:00
|
|
|
end
|
|
|
|
|
2020-10-15 23:08:29 -04:00
|
|
|
def expand_diff?
|
|
|
|
# Force single-entry diff collections to always present as expanded
|
|
|
|
#
|
|
|
|
@iterator.size == 1 || !@enforce_limits || @expanded
|
|
|
|
end
|
|
|
|
|
2017-07-13 18:22:09 -04:00
|
|
|
def each_gitaly_patch
|
|
|
|
i = @array.length
|
|
|
|
|
|
|
|
@iterator.each do |raw|
|
2020-10-15 23:08:29 -04:00
|
|
|
diff = Gitlab::Git::Diff.new(raw, expanded: expand_diff?)
|
2017-07-13 18:22:09 -04:00
|
|
|
|
|
|
|
if raw.overflow_marker
|
|
|
|
@overflow = true
|
|
|
|
break
|
|
|
|
end
|
|
|
|
|
|
|
|
yield @array[i] = diff
|
2017-05-23 10:14:52 -04:00
|
|
|
i += 1
|
|
|
|
end
|
2017-07-13 18:22:09 -04:00
|
|
|
end
|
2017-05-23 10:14:52 -04:00
|
|
|
|
2018-07-06 06:01:15 -04:00
|
|
|
def each_serialized_patch
|
2017-07-13 18:22:09 -04:00
|
|
|
i = @array.length
|
2017-01-04 13:43:06 -05:00
|
|
|
|
2019-10-09 17:06:24 -04:00
|
|
|
@iterator.each_with_index do |raw, iterator_index|
|
2017-05-23 10:14:52 -04:00
|
|
|
@empty = false
|
2017-01-04 13:43:06 -05:00
|
|
|
|
2022-04-01 05:10:01 -04:00
|
|
|
if @enforce_limits && i >= limits[:max_files]
|
2017-01-04 13:43:06 -05:00
|
|
|
@overflow = true
|
2021-04-14 11:09:04 -04:00
|
|
|
@overflow_max_files = true
|
2017-01-04 13:43:06 -05:00
|
|
|
break
|
|
|
|
end
|
|
|
|
|
2020-10-15 23:08:29 -04:00
|
|
|
diff = Gitlab::Git::Diff.new(raw, expanded: expand_diff?)
|
2017-01-04 13:43:06 -05:00
|
|
|
|
2020-10-15 23:08:29 -04:00
|
|
|
if !expand_diff? && over_safe_limits?(i) && diff.line_count > 0
|
2017-05-26 19:27:30 -04:00
|
|
|
diff.collapse!
|
2017-01-04 13:43:06 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
@line_count += diff.line_count
|
|
|
|
@byte_count += diff.diff.bytesize
|
|
|
|
|
2022-04-01 05:10:01 -04:00
|
|
|
if @enforce_limits && @line_count >= limits[:max_lines]
|
2021-04-14 11:09:04 -04:00
|
|
|
# This last Diff instance pushes us over the lines limit. We stop and
|
|
|
|
# discard it.
|
|
|
|
@overflow = true
|
|
|
|
@overflow_max_lines = true
|
|
|
|
break
|
|
|
|
end
|
|
|
|
|
2022-04-01 05:10:01 -04:00
|
|
|
if @enforce_limits && @byte_count >= limits[:max_bytes]
|
2017-01-04 13:43:06 -05:00
|
|
|
# This last Diff instance pushes us over the lines limit. We stop and
|
|
|
|
# discard it.
|
|
|
|
@overflow = true
|
2021-04-14 11:09:04 -04:00
|
|
|
@overflow_max_bytes = true
|
2017-01-04 13:43:06 -05:00
|
|
|
break
|
|
|
|
end
|
|
|
|
|
2019-10-09 17:06:24 -04:00
|
|
|
# We should not yield / memoize diffs before the offset index. Though,
|
|
|
|
# we still consider the limit buffers for diffs before it.
|
|
|
|
if iterator_index >= @offset_index
|
|
|
|
yield @array[i] = diff
|
|
|
|
i += 1
|
|
|
|
end
|
2017-01-04 13:43:06 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|