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