1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
module Gitlab
module Conflict
class File
class MissingResolution < StandardError
end
CONTEXT_LINES = 3
attr_reader :merge_file_result, :their_path, :their_ref, :our_path, :our_ref, :repository
def initialize(merge_file_result, conflict, diff_refs:, repository:)
@merge_file_result = merge_file_result
@their_path = conflict[:theirs][:path]
@our_path = conflict[:ours][:path]
@their_ref = diff_refs.start_sha
@our_ref = diff_refs.head_sha
@repository = repository
end
# Array of Gitlab::Diff::Line objects
def lines
@lines ||= Gitlab::Conflict::Parser.new.parse(merge_file_result[:data],
our_path: our_path,
their_path: their_path)
end
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)
current_section = nil
lines.map do |line|
unless line.type
current_section = nil
next line
end
current_section ||= resolution[line_code(line)]
case current_section
when 'ours'
next unless line.type == 'new'
when 'theirs'
next unless line.type == 'old'
else
raise MissingResolution
end
line
end.compact
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]
else
line.rich_text = our_highlight[line.new_line - 1]
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 }
section[:id] = line_code(lines.first) unless no_conflict
section
end
end
def line_code(line)
Gitlab::Diff::LineCode.generate(our_path, line.new_pos, line.old_pos)
end
def as_json(opts = nil)
{
old_path: their_path,
new_path: our_path,
sections: sections
}
end
end
end
end
|