2016-07-25 18:47:09 +00:00
|
|
|
module Gitlab
|
|
|
|
module Conflict
|
|
|
|
class File
|
2016-07-27 16:54:04 +00:00
|
|
|
class MissingResolution < StandardError
|
|
|
|
end
|
|
|
|
|
2016-07-25 18:47:09 +00:00
|
|
|
CONTEXT_LINES = 3
|
|
|
|
|
2016-07-27 11:42:18 +00:00
|
|
|
attr_reader :merge_file_result, :their_path, :their_ref, :our_path, :our_ref, :repository
|
2016-07-25 18:47:09 +00:00
|
|
|
|
2016-07-27 11:42:18 +00:00
|
|
|
def initialize(merge_file_result, conflict, diff_refs:, repository:)
|
|
|
|
@merge_file_result = merge_file_result
|
2016-07-25 18:47:09 +00:00
|
|
|
@their_path = conflict[:theirs][:path]
|
|
|
|
@our_path = conflict[:ours][:path]
|
2016-07-27 11:42:18 +00:00
|
|
|
@their_ref = diff_refs.start_sha
|
|
|
|
@our_ref = diff_refs.head_sha
|
2016-07-25 18:47:09 +00:00
|
|
|
@repository = repository
|
|
|
|
end
|
|
|
|
|
|
|
|
# Array of Gitlab::Diff::Line objects
|
|
|
|
def lines
|
2016-07-27 11:42:18 +00:00
|
|
|
@lines ||= Gitlab::Conflict::Parser.new.parse(merge_file_result[:data],
|
|
|
|
our_path: our_path,
|
2016-07-29 12:29:14 +00:00
|
|
|
their_path: their_path,
|
|
|
|
parent: self)
|
2016-07-25 18:47:09 +00:00
|
|
|
end
|
|
|
|
|
2016-07-27 16:54:04 +00:00
|
|
|
def resolve!(resolution, index:, rugged:)
|
|
|
|
new_file = resolve_lines(resolution).map(&:text).join("\n")
|
|
|
|
|
|
|
|
oid = rugged.write(new_file, :blob)
|
|
|
|
our_mode = index.conflict_get(our_path)[:ours][:mode]
|
|
|
|
index.add(path: our_path, oid: oid, mode: our_mode)
|
|
|
|
index.conflict_remove(our_path)
|
|
|
|
end
|
|
|
|
|
|
|
|
def resolve_lines(resolution)
|
2016-07-29 13:51:11 +00:00
|
|
|
section_id = nil
|
2016-07-27 16:54:04 +00:00
|
|
|
|
|
|
|
lines.map do |line|
|
|
|
|
unless line.type
|
2016-07-29 13:51:11 +00:00
|
|
|
section_id = nil
|
2016-07-27 16:54:04 +00:00
|
|
|
next line
|
|
|
|
end
|
|
|
|
|
2016-07-29 13:51:11 +00:00
|
|
|
section_id ||= line_code(line)
|
2016-07-27 16:54:04 +00:00
|
|
|
|
2016-07-29 13:51:11 +00:00
|
|
|
case resolution[section_id]
|
2016-07-27 16:54:04 +00:00
|
|
|
when 'ours'
|
|
|
|
next unless line.type == 'new'
|
|
|
|
when 'theirs'
|
|
|
|
next unless line.type == 'old'
|
|
|
|
else
|
2016-07-29 13:51:11 +00:00
|
|
|
raise MissingResolution, "Missing resolution for section ID: #{section_id}"
|
2016-07-27 16:54:04 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
line
|
|
|
|
end.compact
|
|
|
|
end
|
|
|
|
|
2016-07-29 12:29:14 +00:00
|
|
|
def highlight_lines!
|
2016-07-25 18:47:09 +00:00
|
|
|
their_highlight = Gitlab::Highlight.highlight_lines(repository, their_ref, their_path)
|
|
|
|
our_highlight = Gitlab::Highlight.highlight_lines(repository, our_ref, our_path)
|
|
|
|
|
2016-07-29 12:29:14 +00:00
|
|
|
lines.each do |line|
|
2016-07-25 18:47:09 +00:00
|
|
|
if line.type == 'old'
|
2016-07-27 11:42:18 +00:00
|
|
|
line.rich_text = their_highlight[line.old_line - 1]
|
2016-07-25 18:47:09 +00:00
|
|
|
else
|
2016-07-27 11:42:18 +00:00
|
|
|
line.rich_text = our_highlight[line.new_line - 1]
|
2016-07-25 18:47:09 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def sections
|
|
|
|
return @sections if @sections
|
|
|
|
|
2016-07-29 12:29:14 +00:00
|
|
|
chunked_lines = lines.chunk { |line| line.type.nil? }
|
2016-07-29 15:37:51 +00:00
|
|
|
last_candidate_match_header = nil
|
|
|
|
match_line_header = nil
|
2016-07-25 18:47:09 +00:00
|
|
|
match_line = nil
|
|
|
|
|
|
|
|
@sections = chunked_lines.flat_map.with_index do |(no_conflict, lines), i|
|
|
|
|
section = nil
|
|
|
|
|
2016-07-29 15:37:51 +00:00
|
|
|
lines.each do |line|
|
|
|
|
last_candidate_match_header = " #{line.text}" if line.text.match(/\A[A-Za-z$_]/)
|
|
|
|
end
|
|
|
|
|
2016-07-25 18:47:09 +00:00
|
|
|
if no_conflict
|
|
|
|
conflict_before = i > 0
|
|
|
|
conflict_after = chunked_lines.peek
|
|
|
|
|
|
|
|
if conflict_before && conflict_after
|
|
|
|
if lines.length > CONTEXT_LINES * 2
|
|
|
|
tail_lines = lines.last(CONTEXT_LINES)
|
|
|
|
first_tail_line = tail_lines.first
|
2016-07-29 15:37:51 +00:00
|
|
|
match_line_header = last_candidate_match_header
|
2016-07-25 18:47:09 +00:00
|
|
|
match_line = Gitlab::Diff::Line.new('',
|
|
|
|
'match',
|
|
|
|
first_tail_line.index,
|
|
|
|
first_tail_line.old_pos,
|
|
|
|
first_tail_line.new_pos)
|
|
|
|
|
|
|
|
section = [
|
|
|
|
{ conflict: false, lines: lines.first(CONTEXT_LINES) },
|
|
|
|
{ conflict: false, lines: tail_lines.unshift(match_line) }
|
|
|
|
]
|
|
|
|
end
|
|
|
|
elsif conflict_after
|
|
|
|
lines = lines.last(CONTEXT_LINES)
|
|
|
|
elsif conflict_before
|
|
|
|
lines = lines.first(CONTEXT_LINES)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if match_line && !section
|
2016-07-29 15:37:51 +00:00
|
|
|
match_line.text = "@@ -#{match_line.old_pos},#{lines.last.old_pos} +#{match_line.new_pos},#{lines.last.new_pos} @@#{match_line_header}"
|
2016-07-25 18:47:09 +00:00
|
|
|
end
|
|
|
|
|
2016-07-27 16:54:04 +00:00
|
|
|
section ||= { conflict: !no_conflict, lines: lines }
|
|
|
|
section[:id] = line_code(lines.first) unless no_conflict
|
|
|
|
section
|
2016-07-25 18:47:09 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-07-27 16:54:04 +00:00
|
|
|
def line_code(line)
|
|
|
|
Gitlab::Diff::LineCode.generate(our_path, line.new_pos, line.old_pos)
|
|
|
|
end
|
|
|
|
|
2016-07-25 18:47:09 +00:00
|
|
|
def as_json(opts = nil)
|
|
|
|
{
|
|
|
|
old_path: their_path,
|
|
|
|
new_path: our_path,
|
|
|
|
sections: sections
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|