BigW Consortium Gitlab

position.rb 4.02 KB
Newer Older
Douwe Maan committed
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
# Defines a specific location, identified by paths and line numbers,
# within a specific diff, identified by start, head and base commit ids.
module Gitlab
  module Diff
    class Position
      attr_reader :old_path
      attr_reader :new_path
      attr_reader :old_line
      attr_reader :new_line
      attr_reader :base_sha
      attr_reader :start_sha
      attr_reader :head_sha

      def initialize(attrs = {})
        @old_path = attrs[:old_path]
        @new_path = attrs[:new_path]
        @old_line = attrs[:old_line]
        @new_line = attrs[:new_line]

        if attrs[:diff_refs]
          @base_sha  = attrs[:diff_refs].base_sha
          @start_sha = attrs[:diff_refs].start_sha
          @head_sha  = attrs[:diff_refs].head_sha
        else
          @base_sha  = attrs[:base_sha]
          @start_sha = attrs[:start_sha]
          @head_sha  = attrs[:head_sha]
        end
      end

31 32 33 34 35
      # `Gitlab::Diff::Position` objects are stored as serialized attributes in
      # `DiffNote`, which use YAML to encode and decode objects.
      # `#init_with` and `#encode_with` can be used to customize the en/decoding
      # behavior. In this case, we override these to prevent memoized instance
      # variables like `@diff_file` and `@diff_line` from being serialized.
Douwe Maan committed
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
      def init_with(coder)
        initialize(coder['attributes'])

        self
      end

      def encode_with(coder)
        coder['attributes'] = self.to_h
      end

      def key
        @key ||= [base_sha, start_sha, head_sha, Digest::SHA1.hexdigest(old_path || ""), Digest::SHA1.hexdigest(new_path || ""), old_line, new_line]
      end

      def ==(other)
        other.is_a?(self.class) && key == other.key
      end

      def to_h
        {
          old_path: old_path,
          new_path: new_path,
          old_line: old_line,
          new_line: new_line,
          base_sha:  base_sha,
          start_sha: start_sha,
          head_sha:  head_sha
        }
      end

      def inspect
        %(#<#{self.class}:#{object_id} #{to_h}>)
      end

      def complete?
        file_path.present? &&
          (old_line || new_line) &&
          diff_refs.complete?
      end

76 77
      def to_json(opts = nil)
        JSON.generate(self.to_h, opts)
Douwe Maan committed
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 138 139 140 141
      end

      def type
        if old_line && new_line
          nil
        elsif new_line
          'new'
        else
          'old'
        end
      end

      def unchanged?
        type.nil?
      end

      def added?
        type == 'new'
      end

      def removed?
        type == 'old'
      end

      def paths
        [old_path, new_path].compact.uniq
      end

      def file_path
        new_path.presence || old_path
      end

      def diff_refs
        @diff_refs ||= DiffRefs.new(base_sha: base_sha, start_sha: start_sha, head_sha: head_sha)
      end

      def diff_file(repository)
        @diff_file ||= begin
          if RequestStore.active?
            key = {
              project_id: repository.project.id,
              start_sha: start_sha,
              head_sha: head_sha,
              path: file_path
            }

            RequestStore.fetch(key) { find_diff_file(repository) }
          else
            find_diff_file(repository)
          end
        end
      end

      def diff_line(repository)
        @diff_line ||= diff_file(repository).line_for_position(self)
      end

      def line_code(repository)
        @line_code ||= diff_file(repository).line_code_for_position(self)
      end

      private

      def find_diff_file(repository)
142 143 144 145 146 147 148 149 150 151 152 153
        # We're at the initial commit, so just get that as we can't compare to anything.
        if Gitlab::Git.blank_ref?(start_sha)
          compare = Gitlab::Git::Commit.find(repository.raw_repository, head_sha)
        else
          compare = Gitlab::Git::Compare.new(
            repository.raw_repository,
            start_sha,
            head_sha
          )
        end

        diff = compare.diffs(paths: paths).first
Douwe Maan committed
154 155 156 157 158 159 160 161

        return unless diff

        Gitlab::Diff::File.new(diff, repository: repository, diff_refs: diff_refs)
      end
    end
  end
end