/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
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
71
    def merge_regions(self):
822 by Martin Pool
- Renamed merge3 test suite for easier access.
72
        """Return sequences of matching and conflicting regions.
73
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
74
        This returns tuples, where the first value says what kind we
75
        have:
76
77
        'unchanged', start, end
78
             Take a region of base[start:end]
79
80
        'a', start, end
81
             Non-clashing insertion from a[start:end]
82
822 by Martin Pool
- Renamed merge3 test suite for easier access.
83
        Method is as follows:
84
85
        The two sequences align only on regions which match the base
86
        and both descendents.  These are found by doing a two-way diff
87
        of each one against the base, and then finding the
88
        intersections between those regions.  These "sync regions"
89
        are by definition unchanged in both and easily dealt with.
90
91
        The regions in between can be in any of three cases:
92
        conflicted, or changed on only one side.
93
        """
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
94
95
        # section a[0:ia] has been disposed of, etc
96
        iz = ia = ib = 0
97
        
98
        for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
99
            matchlen = zend - zmatch
100
            assert matchlen >= 0
101
            assert matchlen == (aend - amatch)
102
            assert matchlen == (bend - bmatch)
103
            
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
104
            len_a = amatch - ia
105
            len_b = bmatch - ib
106
            len_base = zmatch - iz
107
            assert len_a >= 0
108
            assert len_b >= 0
109
            assert len_base >= 0
110
111
            if len_a or len_b:
112
                lines_base = self.base[iz:zmatch]
113
                lines_a = self.a[ia:amatch]
114
                lines_b = self.b[ib:bmatch]
115
116
                # TODO: check the len just as a shortcut
117
                equal_a = (lines_a == lines_base)
118
                equal_b = (lines_b == lines_base)
119
120
                if equal_a and not equal_b:
121
                    yield 'b', ib, bmatch
122
                elif equal_b and not equal_a:
123
                    yield 'a', ia, amatch
124
                elif not equal_a and not equal_b:
125
                    yield 'conflict', ia, amatch, ib, bmatch
126
                else:
127
                    assert 0
128
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
129
                ia = amatch
826 by Martin Pool
- Actually merge unsynchronized regions. Woot!
130
                ib = bmatch
131
            iz = zmatch
132
133
            # if the same part of the base was deleted on both sides
134
            # 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
135
136
                
137
            if matchlen > 0:
138
                assert ia == amatch
139
                assert ib == bmatch
140
                assert iz == zmatch
141
                
142
                yield 'unchanged', zmatch, zend
143
                iz = zend
144
                ia = aend
145
                ib = bend
824 by Martin Pool
- Merge3.find_sync_regions yields just a 6-tuple, not a tuple of tuples
146
        
821 by Martin Pool
- start code for built-in diff3-style resolve
147
148
        
822 by Martin Pool
- Renamed merge3 test suite for easier access.
149
    def find_sync_regions(self):
150
        """Return a list of sync regions, where both descendents match the base.
151
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
152
        Generates a list of (base1, base2, a1, a2, b1, b2).  There is
153
        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
154
        """
822 by Martin Pool
- Renamed merge3 test suite for easier access.
155
        from difflib import SequenceMatcher
156
        aiter = iter(SequenceMatcher(None, self.base, self.a).get_matching_blocks())
157
        biter = iter(SequenceMatcher(None, self.base, self.b).get_matching_blocks())
158
159
        abase, amatch, alen = aiter.next()
160
        bbase, bmatch, blen = biter.next()
161
162
        while aiter and biter:
163
            # there is an unconflicted block at i; how long does it
164
            # extend?  until whichever one ends earlier.
165
            i = intersect((abase, abase+alen), (bbase, bbase+blen))
166
            if i:
167
                intbase = i[0]
168
                intend = i[1]
169
                intlen = intend - intbase
170
171
                # found a match of base[i[0], i[1]]; this may be less than
172
                # the region that matches in either one
173
                assert intlen <= alen
174
                assert intlen <= blen
175
                assert abase <= intbase
176
                assert bbase <= intbase
177
178
                asub = amatch + (intbase - abase)
179
                bsub = bmatch + (intbase - bbase)
180
                aend = asub + intlen
181
                bend = bsub + intlen
182
183
                assert self.base[intbase:intend] == self.a[asub:aend], \
184
                       (self.base[intbase:intend], self.a[asub:aend])
185
                
186
                assert self.base[intbase:intend] == self.b[bsub:bend]
187
824 by Martin Pool
- Merge3.find_sync_regions yields just a 6-tuple, not a tuple of tuples
188
                yield (intbase, intend,
189
                       asub, aend,
190
                       bsub, bend)
822 by Martin Pool
- Renamed merge3 test suite for easier access.
191
192
            # advance whichever one ends first in the base text
193
            if (abase + alen) < (bbase + blen):
194
                abase, amatch, alen = aiter.next()
195
            else:
196
                bbase, bmatch, blen = biter.next()
197
825 by Martin Pool
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
198
        intbase = len(self.base)
199
        abase = len(self.a)
200
        bbase = len(self.b)
201
        yield (intbase, intbase, abase, abase, bbase, bbase)
202
821 by Martin Pool
- start code for built-in diff3-style resolve
203
204
205
    def find_unconflicted(self):
206
        """Return a list of ranges in base that are not conflicted."""
207
        from difflib import SequenceMatcher
208
        am = SequenceMatcher(None, self.base, self.a).get_matching_blocks()
209
        bm = SequenceMatcher(None, self.base, self.b).get_matching_blocks()
210
211
        unc = []
212
213
        while am and bm:
214
            # there is an unconflicted block at i; how long does it
215
            # extend?  until whichever one ends earlier.
216
            a1 = am[0][0]
217
            a2 = a1 + am[0][2]
218
            b1 = bm[0][0]
219
            b2 = b1 + bm[0][2]
220
            i = intersect((a1, a2), (b1, b2))
221
            if i:
222
                unc.append(i)
223
224
            if a2 < b2:
225
                del am[0]
226
            else:
227
                del bm[0]
228
                
229
        return unc