13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
# Author: Martin Pool <mbp@canonical.com>
17
# Author: Martin Pool <mbp@canonical.com>
18
18
# Aaron Bentley <aaron.bentley@utoronto.ca>
21
import bzrlib.patiencediff
20
from difflib import SequenceMatcher
24
23
class TextMerge(object):
25
24
"""Base class for text-mergers
26
25
Subclasses must implement _merge_struct.
28
Many methods produce or consume structured merge information.
29
This is an iterable of tuples of lists of lines.
30
Each tuple may have a length of 1 - 3, depending on whether the region it
31
represents is conflicted.
33
Unconflicted region tuples have length 1.
34
Conflicted region tuples have length 2 or 3. Index 1 is text_a, e.g. THIS.
35
Index 1 is text_b, e.g. OTHER. Index 2 is optional. If present, it
38
# TODO: Show some version information (e.g. author, date) on conflicted
40
A_MARKER = '<<<<<<< \n'
41
B_MARKER = '>>>>>>> \n'
42
SPLIT_MARKER = '=======\n'
43
def __init__(self, a_marker=A_MARKER, b_marker=B_MARKER,
44
split_marker=SPLIT_MARKER):
27
def __init__(self, a_marker='<<<<<<< \n', b_marker='>>>>>>> \n',
28
split_marker='=======\n'):
45
29
self.a_marker = a_marker
46
30
self.b_marker = b_marker
47
31
self.split_marker = split_marker
49
def _merge_struct(self):
50
"""Return structured merge info. Must be implemented by subclasses.
51
See TextMerge docstring for details on the format.
53
raise NotImplementedError('_merge_struct is abstract')
55
33
def struct_to_lines(self, struct_iter):
56
34
"""Convert merge result tuples to lines"""
57
35
for lines in struct_iter:
78
56
def merge_lines(self, reprocess=False):
79
"""Produce an iterable of lines, suitable for writing to a file
80
Returns a tuple of (line iterable, conflict indicator)
81
If reprocess is True, a two-way merge will be performed on the
82
intermediate structure, to reduce conflict regions.
86
for group in self.merge_struct(reprocess):
90
return self.struct_to_lines(struct), conflicts
57
return self.struct_to_lines(self.merge_struct(reprocess))
92
59
def merge_struct(self, reprocess=False):
93
"""Produce structured merge info"""
94
60
struct_iter = self.iter_useful(self._merge_struct())
95
61
if reprocess is True:
96
62
return self.reprocess_struct(struct_iter)
101
67
def reprocess_struct(struct_iter):
102
""" Perform a two-way merge on structural merge info.
103
This reduces the size of conflict regions, but breaks the connection
104
between the BASE text and the conflict region.
106
This process may split a single conflict region into several smaller
107
ones, but will not introduce new conflicts.
109
68
for group in struct_iter:
110
69
if len(group) == 1:
117
76
class Merge2(TextMerge):
119
80
In a two way merge, common regions are shown as unconflicting, and uncommon
120
81
regions produce conflicts.
123
def __init__(self, lines_a, lines_b, a_marker=TextMerge.A_MARKER,
124
b_marker=TextMerge.B_MARKER,
125
split_marker=TextMerge.SPLIT_MARKER):
83
def __init__(self, lines_a, lines_b, a_marker='<<<<<<< \n',
84
b_marker='>>>>>>> \n', split_marker='=======\n'):
126
85
TextMerge.__init__(self, a_marker, b_marker, split_marker)
127
86
self.lines_a = lines_a
128
87
self.lines_b = lines_b
130
89
def _merge_struct(self):
131
"""Return structured merge info.
132
See TextMerge docstring.
134
sm = bzrlib.patiencediff.PatienceSequenceMatcher(None, self.lines_a, self.lines_b)
90
sm = SequenceMatcher(None, self.lines_a, self.lines_b)
137
93
for ai, bi, l in sm.get_matching_blocks():
139
95
yield(self.lines_a[pos_a:ai], self.lines_b[pos_b:bi])
141
97
yield(self.lines_a[ai:ai+l],)
144
100
# final non-matching lines
145
101
yield(self.lines_a[pos_a:-1], self.lines_b[pos_b:-1])