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