1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
3
 
# This program is free software; you can redistribute it and/or modify
 
4
 
# it under the terms of the GNU General Public License as published by
 
5
 
# the Free Software Foundation; either version 2 of the License, or
 
6
 
# (at your option) any later version.
 
8
 
# This program is distributed in the hope that it will be useful,
 
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 
# GNU General Public License for more details.
 
13
 
# You should have received a copy of the GNU General Public License
 
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
 
17
 
# Author: Martin Pool <mbp@canonical.com>
 
18
 
#         Aaron Bentley <aaron.bentley@utoronto.ca>
 
21
 
import bzrlib.patiencediff
 
24
 
class TextMerge(object):
 
25
 
    """Base class for text-mergers
 
26
 
    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):
 
45
 
        self.a_marker = a_marker
 
46
 
        self.b_marker = b_marker
 
47
 
        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
 
    def struct_to_lines(self, struct_iter):
 
56
 
        """Convert merge result tuples to lines"""
 
57
 
        for lines in struct_iter:
 
65
 
                yield self.split_marker
 
70
 
    def iter_useful(self, struct_iter):
 
71
 
        """Iterate through input tuples, skipping empty ones."""
 
72
 
        for group in struct_iter:
 
75
 
            elif len(group) > 1 and len(group[1]) > 0:
 
78
 
    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
 
92
 
    def merge_struct(self, reprocess=False):
 
93
 
        """Produce structured merge info"""
 
94
 
        struct_iter = self.iter_useful(self._merge_struct())
 
96
 
            return self.reprocess_struct(struct_iter)
 
101
 
    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
 
        for group in struct_iter:
 
113
 
                for newgroup in Merge2(group[0], group[1]).merge_struct():
 
117
 
class Merge2(TextMerge):
 
119
 
    In a two way merge, common regions are shown as unconflicting, and uncommon
 
120
 
    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):
 
126
 
        TextMerge.__init__(self, a_marker, b_marker, split_marker)
 
127
 
        self.lines_a = lines_a
 
128
 
        self.lines_b = lines_b
 
130
 
    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)
 
137
 
        for ai, bi, l in sm.get_matching_blocks():
 
139
 
            yield(self.lines_a[pos_a:ai], self.lines_b[pos_b:bi])
 
141
 
            yield(self.lines_a[ai:ai+l],)
 
144
 
        # final non-matching lines
 
145
 
        yield(self.lines_a[pos_a:-1], self.lines_b[pos_b:-1])