/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
828 by Martin Pool
- code to represent merges in regular text conflict form
72
    def merge_lines(self,
73
                    start_marker='<<<<<<<<\n',
74
                    mid_marker='========\n',
75
                    end_marker='>>>>>>>>\n'):
76
        """Return merge in cvs-like form.
77
        """
78
        for t in self.merge_regions():
79
            what = t[0]
80
            if what == 'unchanged':
81
                for i in range(t[1], t[2]):
82
                    yield self.base[i]
83
            elif what == 'a':
84
                for i in range(t[1], t[2]):
85
                    yield self.a[i]
86
            elif what == 'b':
87
                for i in range(t[1], t[2]):
88
                    yield self.b[i]
89
            elif what == 'conflict':
90
                yield start_marker
91
                for i in range(t[3], t[4]):
92
                    yield self.a[i]
93
                yield mid_marker
94
                for i in range(t[5], t[6]):
95
                    yield self.b[i]
96
                yield end_marker
97
            else:
98
                raise ValueError(what)
99
        
100
        
101
102
103
827 by Martin Pool
- new Merge3.merge_groups feeds back the merged lines
104
    def merge_groups(self):
105
        """Yield sequence of line groups.  Each one is a tuple:
106
107
        'unchanged', lines
108
             Lines unchanged from base
109
110
        'a', lines
111
             Lines taken from a
112
113
        'b', lines
114
             Lines taken from b
115
116
        'conflict', base_lines, a_lines, b_lines
117
             Lines from base were changed to either a or b and conflict.
118
        """
119
        for t in self.merge_regions():
120
            what = t[0]
121
            if what == 'unchanged':
122
                yield what, self.base[t[1]:t[2]]
123
            elif what == 'a':
124
                yield what, self.a[t[1]:t[2]]
125
            elif what == 'b':
126
                yield what, self.b[t[1]:t[2]]
127
            elif what == 'conflict':
128
                yield (what,
129
                       self.base[t[1]:t[2]],
130
                       self.a[t[3]:t[4]],
131
                       self.b[t[5]:t[6]])
132
            else:
133
                raise ValueError(what)
134
135
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
136
    def merge_regions(self):
822 by Martin Pool
- Renamed merge3 test suite for easier access.
137
        """Return sequences of matching and conflicting regions.
138
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
139
        This returns tuples, where the first value says what kind we
140
        have:
141
142
        'unchanged', start, end
143
             Take a region of base[start:end]
144
145
        'a', start, end
146
             Non-clashing insertion from a[start:end]
147
822 by Martin Pool
- Renamed merge3 test suite for easier access.
148
        Method is as follows:
149
150
        The two sequences align only on regions which match the base
151
        and both descendents.  These are found by doing a two-way diff
152
        of each one against the base, and then finding the
153
        intersections between those regions.  These "sync regions"
154
        are by definition unchanged in both and easily dealt with.
155
156
        The regions in between can be in any of three cases:
157
        conflicted, or changed on only one side.
158
        """
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
159
160
        # section a[0:ia] has been disposed of, etc
161
        iz = ia = ib = 0
162
        
163
        for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
164
            matchlen = zend - zmatch
165
            assert matchlen >= 0
166
            assert matchlen == (aend - amatch)
167
            assert matchlen == (bend - bmatch)
168
            
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
169
            len_a = amatch - ia
170
            len_b = bmatch - ib
171
            len_base = zmatch - iz
172
            assert len_a >= 0
173
            assert len_b >= 0
174
            assert len_base >= 0
175
176
            if len_a or len_b:
177
                lines_base = self.base[iz:zmatch]
178
                lines_a = self.a[ia:amatch]
179
                lines_b = self.b[ib:bmatch]
180
181
                # TODO: check the len just as a shortcut
182
                equal_a = (lines_a == lines_base)
183
                equal_b = (lines_b == lines_base)
184
185
                if equal_a and not equal_b:
186
                    yield 'b', ib, bmatch
187
                elif equal_b and not equal_a:
188
                    yield 'a', ia, amatch
189
                elif not equal_a and not equal_b:
827 by Martin Pool
- new Merge3.merge_groups feeds back the merged lines
190
                    yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
191
                else:
192
                    assert 0
193
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
194
                ia = amatch
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
195
                ib = bmatch
196
            iz = zmatch
197
198
            # if the same part of the base was deleted on both sides
199
            # 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
200
201
                
202
            if matchlen > 0:
203
                assert ia == amatch
204
                assert ib == bmatch
205
                assert iz == zmatch
206
                
207
                yield 'unchanged', zmatch, zend
208
                iz = zend
209
                ia = aend
210
                ib = bend
824 by Martin Pool
- Merge3.find_sync_regions yields just a 6-tuple, not a tuple of tuples
211
        
821 by Martin Pool
- start code for built-in diff3-style resolve
212
213
        
822 by Martin Pool
- Renamed merge3 test suite for easier access.
214
    def find_sync_regions(self):
215
        """Return a list of sync regions, where both descendents match the base.
216
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
217
        Generates a list of (base1, base2, a1, a2, b1, b2).  There is
218
        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
219
        """
822 by Martin Pool
- Renamed merge3 test suite for easier access.
220
        from difflib import SequenceMatcher
221
        aiter = iter(SequenceMatcher(None, self.base, self.a).get_matching_blocks())
222
        biter = iter(SequenceMatcher(None, self.base, self.b).get_matching_blocks())
223
224
        abase, amatch, alen = aiter.next()
225
        bbase, bmatch, blen = biter.next()
226
227
        while aiter and biter:
228
            # there is an unconflicted block at i; how long does it
229
            # extend?  until whichever one ends earlier.
230
            i = intersect((abase, abase+alen), (bbase, bbase+blen))
231
            if i:
232
                intbase = i[0]
233
                intend = i[1]
234
                intlen = intend - intbase
235
236
                # found a match of base[i[0], i[1]]; this may be less than
237
                # the region that matches in either one
238
                assert intlen <= alen
239
                assert intlen <= blen
240
                assert abase <= intbase
241
                assert bbase <= intbase
242
243
                asub = amatch + (intbase - abase)
244
                bsub = bmatch + (intbase - bbase)
245
                aend = asub + intlen
246
                bend = bsub + intlen
247
248
                assert self.base[intbase:intend] == self.a[asub:aend], \
249
                       (self.base[intbase:intend], self.a[asub:aend])
250
                
251
                assert self.base[intbase:intend] == self.b[bsub:bend]
252
824 by Martin Pool
- Merge3.find_sync_regions yields just a 6-tuple, not a tuple of tuples
253
                yield (intbase, intend,
254
                       asub, aend,
255
                       bsub, bend)
822 by Martin Pool
- Renamed merge3 test suite for easier access.
256
257
            # advance whichever one ends first in the base text
258
            if (abase + alen) < (bbase + blen):
259
                abase, amatch, alen = aiter.next()
260
            else:
261
                bbase, bmatch, blen = biter.next()
262
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
263
        intbase = len(self.base)
264
        abase = len(self.a)
265
        bbase = len(self.b)
266
        yield (intbase, intbase, abase, abase, bbase, bbase)
267
821 by Martin Pool
- start code for built-in diff3-style resolve
268
269
270
    def find_unconflicted(self):
271
        """Return a list of ranges in base that are not conflicted."""
272
        from difflib import SequenceMatcher
273
        am = SequenceMatcher(None, self.base, self.a).get_matching_blocks()
274
        bm = SequenceMatcher(None, self.base, self.b).get_matching_blocks()
275
276
        unc = []
277
278
        while am and bm:
279
            # there is an unconflicted block at i; how long does it
280
            # extend?  until whichever one ends earlier.
281
            a1 = am[0][0]
282
            a2 = a1 + am[0][2]
283
            b1 = bm[0][0]
284
            b2 = b1 + bm[0][2]
285
            i = intersect((a1, a2), (b1, b2))
286
            if i:
287
                unc.append(i)
288
289
            if a2 < b2:
290
                del am[0]
291
            else:
292
                del bm[0]
293
                
294
        return unc