gitlab-org--gitlab-foss/lib/gitlab/conflict/parser.rb
Sean McGivern 96c77bf775 Allow resolving conflicts with non-ASCII chars
We wanted to check that the text could be encoded as JSON, because
conflict resolutions are passed back and forth in that format, so the
file itself must be UTF-8. However, all strings from the repository come
back without an encoding from Rugged, making them ASCII_8BIT.

We force to UTF-8, and reject if it's invalid. This still leaves the
problem of a file that 'looks like' UTF-8 (contains valid UTF-8 byte
sequences), but isn't. However:

1. If the conflicts contain the problem bytes, the user will see that
   the file isn't displayed correctly.
2. If the problem bytes are outside of the conflict area, then we will
   write back the same bytes when we resolve the conflicts, even though
   we though the encoding was UTF-8.
2017-03-15 11:18:29 +00:00

64 lines
2 KiB
Ruby

module Gitlab
module Conflict
class Parser
UnresolvableError = Class.new(StandardError)
UnmergeableFile = Class.new(UnresolvableError)
UnsupportedEncoding = Class.new(UnresolvableError)
# Recoverable errors - the conflict can be resolved in an editor, but not with
# sections.
ParserError = Class.new(StandardError)
UnexpectedDelimiter = Class.new(ParserError)
MissingEndDelimiter = Class.new(ParserError)
def parse(text, our_path:, their_path:, parent_file: nil)
raise UnmergeableFile if text.blank? # Typically a binary file
raise UnmergeableFile if text.length > 200.kilobytes
text.force_encoding('UTF-8')
raise UnsupportedEncoding unless text.valid_encoding?
line_obj_index = 0
line_old = 1
line_new = 1
type = nil
lines = []
conflict_start = "<<<<<<< #{our_path}"
conflict_middle = '======='
conflict_end = ">>>>>>> #{their_path}"
text.each_line.map do |line|
full_line = line.delete("\n")
if full_line == conflict_start
raise UnexpectedDelimiter unless type.nil?
type = 'new'
elsif full_line == conflict_middle
raise UnexpectedDelimiter unless type == 'new'
type = 'old'
elsif full_line == conflict_end
raise UnexpectedDelimiter unless type == 'old'
type = nil
elsif line[0] == '\\'
type = 'nonewline'
lines << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new, parent_file: parent_file)
else
lines << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new, parent_file: parent_file)
line_old += 1 if type != 'new'
line_new += 1 if type != 'old'
line_obj_index += 1
end
end
raise MissingEndDelimiter unless type.nil?
lines
end
end
end
end