/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
47
821 by Martin Pool
- start code for built-in diff3-style resolve
48
class Merge3(object):
49
    """3-way merge of texts.
50
51
    Given BASE, OTHER, THIS, tries to produce a combined text
52
    incorporating the changes from both BASE->OTHER and BASE->THIS.
53
    All three will typically be sequences of lines."""
54
    def __init__(self, base, a, b):
55
        self.base = base
56
        self.a = a
57
        self.b = b
822 by Martin Pool
- Renamed merge3 test suite for easier access.
58
        from difflib import SequenceMatcher
59
        self.a_ops = SequenceMatcher(None, base, a).get_opcodes()
60
        self.b_ops = SequenceMatcher(None, base, b).get_opcodes()
61
62
827 by Martin Pool
- new Merge3.merge_groups feeds back the merged lines
63
828 by Martin Pool
- code to represent merges in regular text conflict form
64
    def merge_lines(self,
829 by Martin Pool
- More merge3 cvs-form stuff
65
                    name_a=None,
66
                    name_b=None,
67
                    start_marker='<<<<<<<<',
68
                    mid_marker='========',
69
                    end_marker='>>>>>>>>',
70
                    show_base=False):
828 by Martin Pool
- code to represent merges in regular text conflict form
71
        """Return merge in cvs-like form.
72
        """
829 by Martin Pool
- More merge3 cvs-form stuff
73
        if name_a:
74
            start_marker = start_marker + ' ' + name_a
75
        if name_b:
76
            end_marker = end_marker + ' ' + name_b
77
            
828 by Martin Pool
- code to represent merges in regular text conflict form
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]
830 by Martin Pool
- handle chunks which differ from the base but agree
83
            elif what == 'a' or what == 'same':
828 by Martin Pool
- code to represent merges in regular text conflict form
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':
829 by Martin Pool
- More merge3 cvs-form stuff
90
                yield start_marker + '\n'
828 by Martin Pool
- code to represent merges in regular text conflict form
91
                for i in range(t[3], t[4]):
92
                    yield self.a[i]
829 by Martin Pool
- More merge3 cvs-form stuff
93
                yield mid_marker + '\n'
828 by Martin Pool
- code to represent merges in regular text conflict form
94
                for i in range(t[5], t[6]):
95
                    yield self.b[i]
829 by Martin Pool
- More merge3 cvs-form stuff
96
                yield end_marker + '\n'
828 by Martin Pool
- code to represent merges in regular text conflict form
97
            else:
98
                raise ValueError(what)
99
        
100
        
101
102
103
832 by Martin Pool
- New Merge3.merge_annotated method for debugging.
104
    def merge_annotated(self):
105
        """Return merge with conflicts, showing origin of lines.
106
107
        Most useful for debugging merge.        
108
        """
109
        for t in self.merge_regions():
110
            what = t[0]
111
            if what == 'unchanged':
112
                for i in range(t[1], t[2]):
113
                    yield 'u | ' + self.base[i]
114
            elif what == 'a' or what == 'same':
115
                for i in range(t[1], t[2]):
116
                    yield what[0] + ' | ' + self.a[i]
117
            elif what == 'b':
118
                for i in range(t[1], t[2]):
119
                    yield 'b | ' + self.b[i]
120
            elif what == 'conflict':
121
                yield '<<<<\n'
122
                for i in range(t[3], t[4]):
123
                    yield 'A | ' + self.a[i]
124
                yield '----\n'
125
                for i in range(t[5], t[6]):
126
                    yield 'B | ' + self.b[i]
127
                yield '>>>>\n'
128
            else:
129
                raise ValueError(what)
130
        
131
        
132
133
134
827 by Martin Pool
- new Merge3.merge_groups feeds back the merged lines
135
    def merge_groups(self):
136
        """Yield sequence of line groups.  Each one is a tuple:
137
138
        'unchanged', lines
139
             Lines unchanged from base
140
141
        'a', lines
142
             Lines taken from a
143
830 by Martin Pool
- handle chunks which differ from the base but agree
144
        'same', lines
145
             Lines taken from a (and equal to b)
146
827 by Martin Pool
- new Merge3.merge_groups feeds back the merged lines
147
        'b', lines
148
             Lines taken from b
149
150
        'conflict', base_lines, a_lines, b_lines
151
             Lines from base were changed to either a or b and conflict.
152
        """
153
        for t in self.merge_regions():
154
            what = t[0]
155
            if what == 'unchanged':
156
                yield what, self.base[t[1]:t[2]]
830 by Martin Pool
- handle chunks which differ from the base but agree
157
            elif what == 'a' or what == 'same':
827 by Martin Pool
- new Merge3.merge_groups feeds back the merged lines
158
                yield what, self.a[t[1]:t[2]]
159
            elif what == 'b':
160
                yield what, self.b[t[1]:t[2]]
161
            elif what == 'conflict':
162
                yield (what,
163
                       self.base[t[1]:t[2]],
164
                       self.a[t[3]:t[4]],
165
                       self.b[t[5]:t[6]])
166
            else:
167
                raise ValueError(what)
168
169
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
170
    def merge_regions(self):
822 by Martin Pool
- Renamed merge3 test suite for easier access.
171
        """Return sequences of matching and conflicting regions.
172
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
173
        This returns tuples, where the first value says what kind we
174
        have:
175
176
        'unchanged', start, end
177
             Take a region of base[start:end]
178
830 by Martin Pool
- handle chunks which differ from the base but agree
179
        'same', astart, aend
180
             b and a are different from base but give the same result
181
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
182
        'a', start, end
183
             Non-clashing insertion from a[start:end]
184
822 by Martin Pool
- Renamed merge3 test suite for easier access.
185
        Method is as follows:
186
187
        The two sequences align only on regions which match the base
188
        and both descendents.  These are found by doing a two-way diff
189
        of each one against the base, and then finding the
190
        intersections between those regions.  These "sync regions"
191
        are by definition unchanged in both and easily dealt with.
192
193
        The regions in between can be in any of three cases:
194
        conflicted, or changed on only one side.
195
        """
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
196
197
        # section a[0:ia] has been disposed of, etc
198
        iz = ia = ib = 0
199
        
200
        for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
201
            matchlen = zend - zmatch
202
            assert matchlen >= 0
203
            assert matchlen == (aend - amatch)
204
            assert matchlen == (bend - bmatch)
205
            
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
206
            len_a = amatch - ia
207
            len_b = bmatch - ib
208
            len_base = zmatch - iz
209
            assert len_a >= 0
210
            assert len_b >= 0
211
            assert len_base >= 0
212
213
            if len_a or len_b:
214
                lines_base = self.base[iz:zmatch]
215
                lines_a = self.a[ia:amatch]
216
                lines_b = self.b[ib:bmatch]
217
218
                # TODO: check the len just as a shortcut
219
                equal_a = (lines_a == lines_base)
220
                equal_b = (lines_b == lines_base)
830 by Martin Pool
- handle chunks which differ from the base but agree
221
                same = lines_a == lines_b
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
222
830 by Martin Pool
- handle chunks which differ from the base but agree
223
                if same:
224
                    yield 'same', ia, amatch
225
                elif equal_a and not equal_b:
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
226
                    yield 'b', ib, bmatch
227
                elif equal_b and not equal_a:
228
                    yield 'a', ia, amatch
229
                elif not equal_a and not equal_b:
827 by Martin Pool
- new Merge3.merge_groups feeds back the merged lines
230
                    yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
231
                else:
232
                    assert 0
233
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
234
                ia = amatch
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
235
                ib = bmatch
236
            iz = zmatch
237
238
            # if the same part of the base was deleted on both sides
239
            # 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
240
241
                
242
            if matchlen > 0:
243
                assert ia == amatch
244
                assert ib == bmatch
245
                assert iz == zmatch
246
                
247
                yield 'unchanged', zmatch, zend
248
                iz = zend
249
                ia = aend
250
                ib = bend
824 by Martin Pool
- Merge3.find_sync_regions yields just a 6-tuple, not a tuple of tuples
251
        
821 by Martin Pool
- start code for built-in diff3-style resolve
252
253
        
822 by Martin Pool
- Renamed merge3 test suite for easier access.
254
    def find_sync_regions(self):
255
        """Return a list of sync regions, where both descendents match the base.
256
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
257
        Generates a list of (base1, base2, a1, a2, b1, b2).  There is
258
        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
259
        """
822 by Martin Pool
- Renamed merge3 test suite for easier access.
260
        from difflib import SequenceMatcher
261
        aiter = iter(SequenceMatcher(None, self.base, self.a).get_matching_blocks())
262
        biter = iter(SequenceMatcher(None, self.base, self.b).get_matching_blocks())
263
264
        abase, amatch, alen = aiter.next()
265
        bbase, bmatch, blen = biter.next()
266
267
        while aiter and biter:
268
            # there is an unconflicted block at i; how long does it
269
            # extend?  until whichever one ends earlier.
270
            i = intersect((abase, abase+alen), (bbase, bbase+blen))
271
            if i:
272
                intbase = i[0]
273
                intend = i[1]
274
                intlen = intend - intbase
275
276
                # found a match of base[i[0], i[1]]; this may be less than
277
                # the region that matches in either one
278
                assert intlen <= alen
279
                assert intlen <= blen
280
                assert abase <= intbase
281
                assert bbase <= intbase
282
283
                asub = amatch + (intbase - abase)
284
                bsub = bmatch + (intbase - bbase)
285
                aend = asub + intlen
286
                bend = bsub + intlen
287
288
                assert self.base[intbase:intend] == self.a[asub:aend], \
289
                       (self.base[intbase:intend], self.a[asub:aend])
290
                
291
                assert self.base[intbase:intend] == self.b[bsub:bend]
292
824 by Martin Pool
- Merge3.find_sync_regions yields just a 6-tuple, not a tuple of tuples
293
                yield (intbase, intend,
294
                       asub, aend,
295
                       bsub, bend)
822 by Martin Pool
- Renamed merge3 test suite for easier access.
296
297
            # advance whichever one ends first in the base text
298
            if (abase + alen) < (bbase + blen):
299
                abase, amatch, alen = aiter.next()
300
            else:
301
                bbase, bmatch, blen = biter.next()
302
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
303
        intbase = len(self.base)
304
        abase = len(self.a)
305
        bbase = len(self.b)
306
        yield (intbase, intbase, abase, abase, bbase, bbase)
307
821 by Martin Pool
- start code for built-in diff3-style resolve
308
309
310
    def find_unconflicted(self):
311
        """Return a list of ranges in base that are not conflicted."""
312
        from difflib import SequenceMatcher
313
        am = SequenceMatcher(None, self.base, self.a).get_matching_blocks()
314
        bm = SequenceMatcher(None, self.base, self.b).get_matching_blocks()
315
316
        unc = []
317
318
        while am and bm:
319
            # there is an unconflicted block at i; how long does it
320
            # extend?  until whichever one ends earlier.
321
            a1 = am[0][0]
322
            a2 = a1 + am[0][2]
323
            b1 = bm[0][0]
324
            b2 = b1 + bm[0][2]
325
            i = intersect((a1, a2), (b1, b2))
326
            if i:
327
                unc.append(i)
328
329
            if a2 < b2:
330
                del am[0]
331
            else:
332
                del bm[0]
333
                
334
        return unc
829 by Martin Pool
- More merge3 cvs-form stuff
335
336
337
def main(argv):
830 by Martin Pool
- handle chunks which differ from the base but agree
338
    # as for diff3 and meld the syntax is "MINE BASE OTHER"
339
    a = file(argv[1], 'rt').readlines()
340
    base = file(argv[2], 'rt').readlines()
829 by Martin Pool
- More merge3 cvs-form stuff
341
    b = file(argv[3], 'rt').readlines()
342
343
    m3 = Merge3(base, a, b)
344
832 by Martin Pool
- New Merge3.merge_annotated method for debugging.
345
    # sys.stdout.writelines(m3.merge_lines(name_a=argv[1], name_b=argv[3]))
346
    sys.stdout.writelines(m3.merge_annotated())
829 by Martin Pool
- More merge3 cvs-form stuff
347
348
349
if __name__ == '__main__':
350
    import sys
351
    sys.exit(main(sys.argv))