1
# Copyright (C) 2004, 2005 by Canonical Ltd
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.
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.
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
18
# mbp: "you know that thing where cvs gives you conflict markers?"
23
def intersect(ra, rb):
24
"""Given two ranges return the range where they intersect or None.
26
>>> intersect((0, 10), (0, 6))
28
>>> intersect((0, 10), (5, 15))
30
>>> intersect((0, 10), (10, 15))
31
>>> intersect((0, 9), (10, 15))
32
>>> intersect((0, 9), (7, 15))
38
sa = max(ra[0], rb[0])
39
sb = min(ra[1], rb[1])
46
def threeway(baseline, aline, bline):
49
elif baseline == bline:
57
"""3-way merge of texts.
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):
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()
72
"""Return sequences of matching and conflicting regions.
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.
82
The regions in between can be in any of three cases:
83
conflicted, or changed on only one side.
87
def find_sync_regions(self):
88
"""Return a list of sync regions, where both descendents match the base.
90
Generates a list of ((base1, base2), (a1, a2), (b1, b2)).
92
from difflib import SequenceMatcher
93
aiter = iter(SequenceMatcher(None, self.base, self.a).get_matching_blocks())
94
biter = iter(SequenceMatcher(None, self.base, self.b).get_matching_blocks())
96
abase, amatch, alen = aiter.next()
97
bbase, bmatch, blen = biter.next()
99
while aiter and biter:
100
# there is an unconflicted block at i; how long does it
101
# extend? until whichever one ends earlier.
102
i = intersect((abase, abase+alen), (bbase, bbase+blen))
106
intlen = intend - intbase
108
# found a match of base[i[0], i[1]]; this may be less than
109
# the region that matches in either one
110
assert intlen <= alen
111
assert intlen <= blen
112
assert abase <= intbase
113
assert bbase <= intbase
115
asub = amatch + (intbase - abase)
116
bsub = bmatch + (intbase - bbase)
120
assert self.base[intbase:intend] == self.a[asub:aend], \
121
(self.base[intbase:intend], self.a[asub:aend])
123
assert self.base[intbase:intend] == self.b[bsub:bend]
125
yield ((intbase, intend),
129
# advance whichever one ends first in the base text
130
if (abase + alen) < (bbase + blen):
131
abase, amatch, alen = aiter.next()
133
bbase, bmatch, blen = biter.next()
137
def find_unconflicted(self):
138
"""Return a list of ranges in base that are not conflicted."""
139
from difflib import SequenceMatcher
140
am = SequenceMatcher(None, self.base, self.a).get_matching_blocks()
141
bm = SequenceMatcher(None, self.base, self.b).get_matching_blocks()
146
# there is an unconflicted block at i; how long does it
147
# extend? until whichever one ends earlier.
152
i = intersect((a1, a2), (b1, b2))