legacy_diff_note.rb 2.81 KB
Newer Older
1 2
# frozen_string_literal: true

Douwe Maan's avatar
Douwe Maan committed
3
# A note on merge request or commit diffs, using the legacy implementation.
4
#
Douwe Maan's avatar
Douwe Maan committed
5 6
# All new diff notes are of the type `DiffNote`, but any diff notes created
# before the introduction of the new implementation still use `LegacyDiffNote`.
7 8
#
# A note of this type is never resolvable.
9
class LegacyDiffNote < Note
10 11
  include NoteOnDiff

12
  serialize :st_diff # rubocop:disable Cop/ActiveRecordSerialize
13 14 15 16 17

  validates :line_code, presence: true, line_code: true

  before_create :set_diff

18 19
  def discussion_class(*)
    LegacyDiffDiscussion
20 21
  end

22
  def project_repository
23
    Gitlab::SafeRequestStore.fetch("project:#{project_id}:repository") { self.project.repository }
24 25
  end

26 27
  def diff_file_hash
    line_code.split('_')[0] if line_code
28 29 30 31 32 33
  end

  def diff
    @diff ||= Gitlab::Git::Diff.new(st_diff) if st_diff.respond_to?(:map)
  end

34
  def diff_file
35
    @diff_file ||= Gitlab::Diff::File.new(diff, repository: project_repository) if diff
36 37 38
  end

  def diff_line
39
    @diff_line ||= diff_file&.line_for_line_code(self.line_code)
40 41
  end

42 43 44 45
  def original_line_code
    self.line_code
  end

46 47 48 49 50 51 52
  # Check if this note is part of an "active" discussion
  #
  # This will always return true for anything except MergeRequest noteables,
  # which have special logic.
  #
  # If the note's current diff cannot be matched in the MergeRequest's current
  # diff, it's considered inactive.
53
  def active?(diff_refs = nil)
54
    return @active if defined?(@active)
55
    return true if for_commit?
56
    return true unless diff_line
57
    return false unless noteable
58
    return false if diff_refs && diff_refs != noteable.diff_refs
59 60 61 62 63 64

    noteable_diff = find_noteable_diff

    if noteable_diff
      parsed_lines = Gitlab::Diff::Parser.new.parse(noteable_diff.diff.each_line)

65
      @active = parsed_lines.any? { |line_obj| line_obj.text == diff_line.text }
66 67 68 69 70 71 72
    else
      @active = false
    end

    @active
  end

73
  private
74

75
  def find_diff
76
    return unless noteable
77
    return @diff if defined?(@diff)
78

79
    @diff = noteable.raw_diffs(Commit.max_diff_options).find do |d|
80
      d.new_path && Digest::SHA1.hexdigest(d.new_path) == diff_file_hash
81 82 83
    end
  end

84 85 86 87 88
  def set_diff
    # First lets find notes with same diff
    # before iterating over all mr diffs
    diff = diff_for_line_code unless for_merge_request?
    diff ||= find_diff
89

90
    self.st_diff = diff.to_hash if diff
91 92
  end

93 94 95 96 97
  def diff_for_line_code
    attributes = {
      noteable_type: noteable_type,
      line_code: line_code
    }
98

99 100 101 102
    if for_commit?
      attributes[:commit_id] = commit_id
    else
      attributes[:noteable_id] = noteable_id
103 104
    end

105
    self.class.where(attributes).last.try(:diff)
106 107 108 109
  end

  # Find the diff on noteable that matches our own
  def find_noteable_diff
110
    diffs = noteable.raw_diffs(Commit.max_diff_options)
111 112 113
    diffs.find { |d| d.new_path == self.diff.new_path }
  end
end