/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():
200
            matchlen = zend - zmatch
201
            assert matchlen >= 0
202
            assert matchlen == (aend - amatch)
203
            assert matchlen == (bend - bmatch)
204
            
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
205
            len_a = amatch - ia
206
            len_b = bmatch - ib
207
            len_base = zmatch - iz
208
            assert len_a >= 0
209
            assert len_b >= 0
210
            assert len_base >= 0
211
212
            if len_a or len_b:
213
                lines_base = self.base[iz:zmatch]
214
                lines_a = self.a[ia:amatch]
215
                lines_b = self.b[ib:bmatch]
216
217
                # TODO: check the len just as a shortcut
218
                equal_a = (lines_a == lines_base)
219
                equal_b = (lines_b == lines_base)
830 by Martin Pool
- handle chunks which differ from the base but agree
220
                same = lines_a == lines_b
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
221
830 by Martin Pool
- handle chunks which differ from the base but agree
222
                if same:
223
                    yield 'same', ia, amatch
224
                elif equal_a and not equal_b:
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
225
                    yield 'b', ib, bmatch
226
                elif equal_b and not equal_a:
227
                    yield 'a', ia, amatch
228
                elif not equal_a and not equal_b:
827 by Martin Pool
- new Merge3.merge_groups feeds back the merged lines
229
                    yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
230
                else:
231
                    assert 0
232
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
233
                ia = amatch
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
234
                ib = bmatch
235
            iz = zmatch
236
237
            # if the same part of the base was deleted on both sides
238
            # 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
239
240
                
241
            if matchlen > 0:
242
                assert ia == amatch
243
                assert ib == bmatch
244
                assert iz == zmatch
245
                
246
                yield 'unchanged', zmatch, zend
247
                iz = zend
248
                ia = aend
249
                ib = bend
824 by Martin Pool
- Merge3.find_sync_regions yields just a 6-tuple, not a tuple of tuples
250
        
821 by Martin Pool
- start code for built-in diff3-style resolve
251
252
        
822 by Martin Pool
- Renamed merge3 test suite for easier access.
253
    def find_sync_regions(self):
254
        """Return a list of sync regions, where both descendents match the base.
255
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
256
        Generates a list of (base1, base2, a1, a2, b1, b2).  There is
257
        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
258
        """
822 by Martin Pool
- Renamed merge3 test suite for easier access.
259
        from difflib import SequenceMatcher
260
        aiter = iter(SequenceMatcher(None, self.base, self.a).get_matching_blocks())
261
        biter = iter(SequenceMatcher(None, self.base, self.b).get_matching_blocks())
262
263
        abase, amatch, alen = aiter.next()
264
        bbase, bmatch, blen = biter.next()
265
266
        while aiter and biter:
267
            # there is an unconflicted block at i; how long does it
268
            # extend?  until whichever one ends earlier.
269
            i = intersect((abase, abase+alen), (bbase, bbase+blen))
270
            if i:
271
                intbase = i[0]
272
                intend = i[1]
273
                intlen = intend - intbase
274
275
                # found a match of base[i[0], i[1]]; this may be less than
276
                # the region that matches in either one
277
                assert intlen <= alen
278
                assert intlen <= blen
279
                assert abase <= intbase
280
                assert bbase <= intbase
281
282
                asub = amatch + (intbase - abase)
283
                bsub = bmatch + (intbase - bbase)
284
                aend = asub + intlen
285
                bend = bsub + intlen
286
287
                assert self.base[intbase:intend] == self.a[asub:aend], \
288
                       (self.base[intbase:intend], self.a[asub:aend])
289
                
290
                assert self.base[intbase:intend] == self.b[bsub:bend]
291
824 by Martin Pool
- Merge3.find_sync_regions yields just a 6-tuple, not a tuple of tuples
292
                yield (intbase, intend,
293
                       asub, aend,
294
                       bsub, bend)
822 by Martin Pool
- Renamed merge3 test suite for easier access.
295
296
            # advance whichever one ends first in the base text
297
            if (abase + alen) < (bbase + blen):
298
                abase, amatch, alen = aiter.next()
299
            else:
300
                bbase, bmatch, blen = biter.next()
301
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
302
        intbase = len(self.base)
303
        abase = len(self.a)
304
        bbase = len(self.b)
305
        yield (intbase, intbase, abase, abase, bbase, bbase)
306
821 by Martin Pool
- start code for built-in diff3-style resolve
307
308
309
    def find_unconflicted(self):
310
        """Return a list of ranges in base that are not conflicted."""
311
        from difflib import SequenceMatcher
833 by Martin Pool
- don't sync up on blank or hash-only lines
312
313
        import re
314
315
        # don't sync-up on lines containing only blanks or pounds
316
        junk_re = re.compile(r'^[ \t#]*$')
317
        
318
        am = SequenceMatcher(junk_re.match, self.base, self.a).get_matching_blocks()
319
        bm = SequenceMatcher(junk_re.match, self.base, self.b).get_matching_blocks()
821 by Martin Pool
- start code for built-in diff3-style resolve
320
321
        unc = []
322
323
        while am and bm:
324
            # there is an unconflicted block at i; how long does it
325
            # extend?  until whichever one ends earlier.
326
            a1 = am[0][0]
327
            a2 = a1 + am[0][2]
328
            b1 = bm[0][0]
329
            b2 = b1 + bm[0][2]
330
            i = intersect((a1, a2), (b1, b2))
331
            if i:
332
                unc.append(i)
333
334
            if a2 < b2:
335
                del am[0]
336
            else:
337
                del bm[0]
338
                
339
        return unc
829 by Martin Pool
- More merge3 cvs-form stuff
340
341
342
def main(argv):
830 by Martin Pool
- handle chunks which differ from the base but agree
343
    # as for diff3 and meld the syntax is "MINE BASE OTHER"
344
    a = file(argv[1], 'rt').readlines()
345
    base = file(argv[2], 'rt').readlines()
829 by Martin Pool
- More merge3 cvs-form stuff
346
    b = file(argv[3], 'rt').readlines()
347
348
    m3 = Merge3(base, a, b)
349
832 by Martin Pool
- New Merge3.merge_annotated method for debugging.
350
    # sys.stdout.writelines(m3.merge_lines(name_a=argv[1], name_b=argv[3]))
351
    sys.stdout.writelines(m3.merge_annotated())
829 by Martin Pool
- More merge3 cvs-form stuff
352
353
354
if __name__ == '__main__':
355
    import sys
356
    sys.exit(main(sys.argv))