Method: Refinement::Changeset.parse_raw_diff

Defined in:
lib/refinement/changeset.rb

.parse_raw_diff(diff, repository:, base_revision:) ⇒ Array<FileModification>

Parses the raw diff into FileModification objects

Parameters:

  • diff (String)

    a diff generated by ‘git diff –raw -z`

  • repository (Pathname)

    the path to the repository

  • base_revision (String)

    the base revision the diff was constructed agains

Returns:



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/refinement/changeset.rb', line 163

def self.parse_raw_diff(diff, repository:, base_revision:)
  # since we're null separating the chunks (to avoid dealing with path escaping) we have to reconstruct
  # the chunks into individual diff entries. entries always start with a colon so we can use that to signal if
  # we're on a new entry
  parsed_lines = diff.split("\0").each_with_object([]) do |chunk, lines|
    lines << [] if chunk.start_with?(':')
    lines.last << chunk
  end

  parsed_lines.map do |split_line|
    # change chunk (letter + optional similarity percentage) will always be the last part of first line chunk
    change_chunk = split_line[0].split(/\s/).last

    new_path = Pathname(split_line[2]).freeze if split_line[2]
    old_path = Pathname(split_line[1]).freeze
    prior_path = old_path if new_path
    # new path if one exists, else existing path. new path only exists for rename and copy
    changed_path = new_path || old_path

    change_character = change_chunk[0]
    # returns 0 when a similarity percentage isn't specified by git.
    _similarity = change_chunk[1..3].to_i

    FileModification.new(
      path: changed_path,
      type: CHANGE_CHARACTERS[change_character],
      prior_path: prior_path,
      contents_reader: -> { repo.join(changed_path).read },
      prior_contents_reader: lambda {
        git!('show', "#{base_revision}:#{prior_path || changed_path}", chdir: repository)
      }
    )
  end
end