summaryrefslogtreecommitdiff
path: root/lib/gitlab/conflict
diff options
context:
space:
mode:
authorSean McGivern <sean@gitlab.com>2016-07-25 19:47:09 +0100
committerFatih Acet <acetfatih@gmail.com>2016-08-12 23:24:43 +0300
commitdf2ed097b730c8ba0b79cac8cc3dbfcb0cf587cb (patch)
treecf81b693a81e9bca1a5044706a4ba12c041bf55f /lib/gitlab/conflict
parentff15fbf10c8c32fde876e5c9578fdc2161b7905c (diff)
downloadgitlab-ce-df2ed097b730c8ba0b79cac8cc3dbfcb0cf587cb.tar.gz
Add backend for merge conflicts reading
Diffstat (limited to 'lib/gitlab/conflict')
-rw-r--r--lib/gitlab/conflict/file.rb91
-rw-r--r--lib/gitlab/conflict/parser.rb59
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