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