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