diff options
author | Sean McGivern <sean@gitlab.com> | 2016-07-25 19:47:09 +0100 |
---|---|---|
committer | Fatih Acet <acetfatih@gmail.com> | 2016-08-12 23:24:43 +0300 |
commit | df2ed097b730c8ba0b79cac8cc3dbfcb0cf587cb (patch) | |
tree | cf81b693a81e9bca1a5044706a4ba12c041bf55f /lib/gitlab/conflict | |
parent | ff15fbf10c8c32fde876e5c9578fdc2161b7905c (diff) | |
download | gitlab-ce-df2ed097b730c8ba0b79cac8cc3dbfcb0cf587cb.tar.gz |
Add backend for merge conflicts reading
Diffstat (limited to 'lib/gitlab/conflict')
-rw-r--r-- | lib/gitlab/conflict/file.rb | 91 | ||||
-rw-r--r-- | lib/gitlab/conflict/parser.rb | 59 |
2 files changed, 150 insertions, 0 deletions
diff --git a/lib/gitlab/conflict/file.rb b/lib/gitlab/conflict/file.rb new file mode 100644 index 00000000000..84d3e6f4e03 --- /dev/null +++ b/lib/gitlab/conflict/file.rb @@ -0,0 +1,91 @@ +module Gitlab + module Conflict + class File + CONTEXT_LINES = 3 + + attr_reader :merge_file, :their_path, :their_ref, :our_path, :our_ref, :repository + + def initialize(merge_file, conflict, their_ref, our_ref, repository) + @merge_file = merge_file + @their_path = conflict[:theirs][:path] + @our_path = conflict[:ours][:path] + @their_ref = their_ref + @our_ref = our_ref + @repository = repository + end + + # Array of Gitlab::Diff::Line objects + def lines + @lines ||= Gitlab::Conflict::Parser.new.parse(merge_file[:data], their_path, our_path) + end + + def highlighted_lines + return @highlighted_lines if @highlighted_lines + + their_highlight = Gitlab::Highlight.highlight_lines(repository, their_ref, their_path) + our_highlight = Gitlab::Highlight.highlight_lines(repository, our_ref, our_path) + + @highlighted_lines = lines.map do |line| + line = line.dup + if line.type == 'old' + line.rich_text = their_highlight[line.old_line - 1].delete("\n") + else + line.rich_text = our_highlight[line.new_line - 1].delete("\n") + end + line + end + end + + def sections + return @sections if @sections + + chunked_lines = highlighted_lines.chunk { |line| line.type.nil? } + match_line = nil + + @sections = chunked_lines.flat_map.with_index do |(no_conflict, lines), i| + section = nil + + 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 + 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 + match_line.text = "@@ -#{match_line.old_pos},#{lines.last.old_pos} +#{match_line.new_pos},#{lines.last.new_pos} @@" + end + + section || { conflict: !no_conflict, lines: lines } + end + end + + def as_json(opts = nil) + { + old_path: their_path, + new_path: our_path, + sections: sections + } + end + end + end +end diff --git a/lib/gitlab/conflict/parser.rb b/lib/gitlab/conflict/parser.rb new file mode 100644 index 00000000000..a233c268070 --- /dev/null +++ b/lib/gitlab/conflict/parser.rb @@ -0,0 +1,59 @@ +module Gitlab + module Conflict + class Parser + class UnexpectedDelimiter < StandardError + end + + class MissingEndDelimiter < StandardError + end + + def parse(text, their_path, our_path) + return [] if text.blank? + + 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) + else + lines << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new) + 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 + + def empty? + @lines.empty? + end + end + end +end |