/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
71
    def merge(self):
72
        """Return sequences of matching and conflicting regions.
73
74
        Method is as follows:
75
76
        The two sequences align only on regions which match the base
77
        and both descendents.  These are found by doing a two-way diff
78
        of each one against the base, and then finding the
79
        intersections between those regions.  These "sync regions"
80
        are by definition unchanged in both and easily dealt with.
81
82
        The regions in between can be in any of three cases:
83
        conflicted, or changed on only one side.
84
        """
824 by Martin Pool
- Merge3.find_sync_regions yields just a 6-tuple, not a tuple of tuples
85
        
821 by Martin Pool
- start code for built-in diff3-style resolve
86
87
        
822 by Martin Pool
- Renamed merge3 test suite for easier access.
88
    def find_sync_regions(self):
89
        """Return a list of sync regions, where both descendents match the base.
90
824 by Martin Pool
- Merge3.find_sync_regions yields just a 6-tuple, not a tuple of tuples
91
        Generates a list of (base1, base2, a1, a2, b1, b2). 
821 by Martin Pool
- start code for built-in diff3-style resolve
92
        """
822 by Martin Pool
- Renamed merge3 test suite for easier access.
93
        from difflib import SequenceMatcher
94
        aiter = iter(SequenceMatcher(None, self.base, self.a).get_matching_blocks())
95
        biter = iter(SequenceMatcher(None, self.base, self.b).get_matching_blocks())
96
97
        abase, amatch, alen = aiter.next()
98
        bbase, bmatch, blen = biter.next()
99
100
        while aiter and biter:
101
            # there is an unconflicted block at i; how long does it
102
            # extend?  until whichever one ends earlier.
103
            i = intersect((abase, abase+alen), (bbase, bbase+blen))
104
            if i:
105
                intbase = i[0]
106
                intend = i[1]
107
                intlen = intend - intbase
108
109
                # found a match of base[i[0], i[1]]; this may be less than
110
                # the region that matches in either one
111
                assert intlen <= alen
112
                assert intlen <= blen
113
                assert abase <= intbase
114
                assert bbase <= intbase
115
116
                asub = amatch + (intbase - abase)
117
                bsub = bmatch + (intbase - bbase)
118
                aend = asub + intlen
119
                bend = bsub + intlen
120
121
                assert self.base[intbase:intend] == self.a[asub:aend], \
122
                       (self.base[intbase:intend], self.a[asub:aend])
123
                
124
                assert self.base[intbase:intend] == self.b[bsub:bend]
125
824 by Martin Pool
- Merge3.find_sync_regions yields just a 6-tuple, not a tuple of tuples
126
                yield (intbase, intend,
127
                       asub, aend,
128
                       bsub, bend)
822 by Martin Pool
- Renamed merge3 test suite for easier access.
129
130
            # advance whichever one ends first in the base text
131
            if (abase + alen) < (bbase + blen):
132
                abase, amatch, alen = aiter.next()
133
            else:
134
                bbase, bmatch, blen = biter.next()
135
821 by Martin Pool
- start code for built-in diff3-style resolve
136
137
138
    def find_unconflicted(self):
139
        """Return a list of ranges in base that are not conflicted."""
140
        from difflib import SequenceMatcher
141
        am = SequenceMatcher(None, self.base, self.a).get_matching_blocks()
142
        bm = SequenceMatcher(None, self.base, self.b).get_matching_blocks()
143
144
        unc = []
145
146
        while am and bm:
147
            # there is an unconflicted block at i; how long does it
148
            # extend?  until whichever one ends earlier.
149
            a1 = am[0][0]
150
            a2 = a1 + am[0][2]
151
            b1 = bm[0][0]
152
            b2 = b1 + bm[0][2]
153
            i = intersect((a1, a2), (b1, b2))
154
            if i:
155
                unc.append(i)
156
157
            if a2 < b2:
158
                del am[0]
159
            else:
160
                del bm[0]
161
                
162
        return unc