/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
821 by Martin Pool
- start code for built-in diff3-style resolve
1
# Copyright (C) 2004, 2005 by Canonical Ltd
2
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.
7
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.
12
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
823 by Martin Pool
quote
18
# mbp: "you know that thing where cvs gives you conflict markers?"
19
# s: "i hate that."
20
21
821 by Martin Pool
- start code for built-in diff3-style resolve
22
23
def intersect(ra, rb):
24
    """Given two ranges return the range where they intersect or None.
25
26
    >>> intersect((0, 10), (0, 6))
27
    (0, 6)
28
    >>> intersect((0, 10), (5, 15))
29
    (5, 10)
30
    >>> intersect((0, 10), (10, 15))
31
    >>> intersect((0, 9), (10, 15))
32
    >>> intersect((0, 9), (7, 15))
33
    (7, 9)
34
    """
35
    assert ra[0] <= ra[1]
36
    assert rb[0] <= rb[1]
37
    
38
    sa = max(ra[0], rb[0])
39
    sb = min(ra[1], rb[1])
40
    if sa < sb:
41
        return sa, sb
42
    else:
43
        return None
44
45
822 by Martin Pool
- Renamed merge3 test suite for easier access.
46
def threeway(baseline, aline, bline):
47
    if baseline == aline:
48
        return bline
49
    elif baseline == bline:
50
        return aline
51
    else:
52
        return [aline, bline]
53
54
55
821 by Martin Pool
- start code for built-in diff3-style resolve
56
class Merge3(object):
57
    """3-way merge of texts.
58
59
    Given BASE, OTHER, THIS, tries to produce a combined text
60
    incorporating the changes from both BASE->OTHER and BASE->THIS.
61
    All three will typically be sequences of lines."""
62
    def __init__(self, base, a, b):
63
        self.base = base
64
        self.a = a
65
        self.b = b
822 by Martin Pool
- Renamed merge3 test suite for easier access.
66
        from difflib import SequenceMatcher
67
        self.a_ops = SequenceMatcher(None, base, a).get_opcodes()
68
        self.b_ops = SequenceMatcher(None, base, b).get_opcodes()
69
70
827 by Martin Pool
- new Merge3.merge_groups feeds back the merged lines
71
72
    def merge_groups(self):
73
        """Yield sequence of line groups.  Each one is a tuple:
74
75
        'unchanged', lines
76
             Lines unchanged from base
77
78
        'a', lines
79
             Lines taken from a
80
81
        'b', lines
82
             Lines taken from b
83
84
        'conflict', base_lines, a_lines, b_lines
85
             Lines from base were changed to either a or b and conflict.
86
        """
87
        for t in self.merge_regions():
88
            what = t[0]
89
            if what == 'unchanged':
90
                yield what, self.base[t[1]:t[2]]
91
            elif what == 'a':
92
                yield what, self.a[t[1]:t[2]]
93
            elif what == 'b':
94
                yield what, self.b[t[1]:t[2]]
95
            elif what == 'conflict':
96
                yield (what,
97
                       self.base[t[1]:t[2]],
98
                       self.a[t[3]:t[4]],
99
                       self.b[t[5]:t[6]])
100
            else:
101
                raise ValueError(what)
102
103
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
104
    def merge_regions(self):
822 by Martin Pool
- Renamed merge3 test suite for easier access.
105
        """Return sequences of matching and conflicting regions.
106
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
107
        This returns tuples, where the first value says what kind we
108
        have:
109
110
        'unchanged', start, end
111
             Take a region of base[start:end]
112
113
        'a', start, end
114
             Non-clashing insertion from a[start:end]
115
822 by Martin Pool
- Renamed merge3 test suite for easier access.
116
        Method is as follows:
117
118
        The two sequences align only on regions which match the base
119
        and both descendents.  These are found by doing a two-way diff
120
        of each one against the base, and then finding the
121
        intersections between those regions.  These "sync regions"
122
        are by definition unchanged in both and easily dealt with.
123
124
        The regions in between can be in any of three cases:
125
        conflicted, or changed on only one side.
126
        """
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
127
128
        # section a[0:ia] has been disposed of, etc
129
        iz = ia = ib = 0
130
        
131
        for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
132
            matchlen = zend - zmatch
133
            assert matchlen >= 0
134
            assert matchlen == (aend - amatch)
135
            assert matchlen == (bend - bmatch)
136
            
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
137
            len_a = amatch - ia
138
            len_b = bmatch - ib
139
            len_base = zmatch - iz
140
            assert len_a >= 0
141
            assert len_b >= 0
142
            assert len_base >= 0
143
144
            if len_a or len_b:
145
                lines_base = self.base[iz:zmatch]
146
                lines_a = self.a[ia:amatch]
147
                lines_b = self.b[ib:bmatch]
148
149
                # TODO: check the len just as a shortcut
150
                equal_a = (lines_a == lines_base)
151
                equal_b = (lines_b == lines_base)
152
153
                if equal_a and not equal_b:
154
                    yield 'b', ib, bmatch
155
                elif equal_b and not equal_a:
156
                    yield 'a', ia, amatch
157
                elif not equal_a and not equal_b:
827 by Martin Pool
- new Merge3.merge_groups feeds back the merged lines
158
                    yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
159
                else:
160
                    assert 0
161
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
162
                ia = amatch
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
163
                ib = bmatch
164
            iz = zmatch
165
166
            # if the same part of the base was deleted on both sides
167
            # that's OK, we can just skip it.
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
168
169
                
170
            if matchlen > 0:
171
                assert ia == amatch
172
                assert ib == bmatch
173
                assert iz == zmatch
174
                
175
                yield 'unchanged', zmatch, zend
176
                iz = zend
177
                ia = aend
178
                ib = bend
824 by Martin Pool
- Merge3.find_sync_regions yields just a 6-tuple, not a tuple of tuples
179
        
821 by Martin Pool
- start code for built-in diff3-style resolve
180
181
        
822 by Martin Pool
- Renamed merge3 test suite for easier access.
182
    def find_sync_regions(self):
183
        """Return a list of sync regions, where both descendents match the base.
184
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
185
        Generates a list of (base1, base2, a1, a2, b1, b2).  There is
186
        always a zero-length sync region at the end of all the files.
821 by Martin Pool
- start code for built-in diff3-style resolve
187
        """
822 by Martin Pool
- Renamed merge3 test suite for easier access.
188
        from difflib import SequenceMatcher
189
        aiter = iter(SequenceMatcher(None, self.base, self.a).get_matching_blocks())
190
        biter = iter(SequenceMatcher(None, self.base, self.b).get_matching_blocks())
191
192
        abase, amatch, alen = aiter.next()
193
        bbase, bmatch, blen = biter.next()
194
195
        while aiter and biter:
196
            # there is an unconflicted block at i; how long does it
197
            # extend?  until whichever one ends earlier.
198
            i = intersect((abase, abase+alen), (bbase, bbase+blen))
199
            if i:
200
                intbase = i[0]
201
                intend = i[1]
202
                intlen = intend - intbase
203
204
                # found a match of base[i[0], i[1]]; this may be less than
205
                # the region that matches in either one
206
                assert intlen <= alen
207
                assert intlen <= blen
208
                assert abase <= intbase
209
                assert bbase <= intbase
210
211
                asub = amatch + (intbase - abase)
212
                bsub = bmatch + (intbase - bbase)
213
                aend = asub + intlen
214
                bend = bsub + intlen
215
216
                assert self.base[intbase:intend] == self.a[asub:aend], \
217
                       (self.base[intbase:intend], self.a[asub:aend])
218
                
219
                assert self.base[intbase:intend] == self.b[bsub:bend]
220
824 by Martin Pool
- Merge3.find_sync_regions yields just a 6-tuple, not a tuple of tuples
221
                yield (intbase, intend,
222
                       asub, aend,
223
                       bsub, bend)
822 by Martin Pool
- Renamed merge3 test suite for easier access.
224
225
            # advance whichever one ends first in the base text
226
            if (abase + alen) < (bbase + blen):
227
                abase, amatch, alen = aiter.next()
228
            else:
229
                bbase, bmatch, blen = biter.next()
230
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
231
        intbase = len(self.base)
232
        abase = len(self.a)
233
        bbase = len(self.b)
234
        yield (intbase, intbase, abase, abase, bbase, bbase)
235
821 by Martin Pool
- start code for built-in diff3-style resolve
236
237
238
    def find_unconflicted(self):
239
        """Return a list of ranges in base that are not conflicted."""
240
        from difflib import SequenceMatcher
241
        am = SequenceMatcher(None, self.base, self.a).get_matching_blocks()
242
        bm = SequenceMatcher(None, self.base, self.b).get_matching_blocks()
243
244
        unc = []
245
246
        while am and bm:
247
            # there is an unconflicted block at i; how long does it
248
            # extend?  until whichever one ends earlier.
249
            a1 = am[0][0]
250
            a2 = a1 + am[0][2]
251
            b1 = bm[0][0]
252
            b2 = b1 + bm[0][2]
253
            i = intersect((a1, a2), (b1, b2))
254
            if i:
255
                unc.append(i)
256
257
            if a2 < b2:
258
                del am[0]
259
            else:
260
                del bm[0]
261
                
262
        return unc