15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
from itertools import chain
22
23
from bzrlib import (
42
43
WorkingTreeNotRevision,
46
from bzrlib.graph import Graph
45
47
from bzrlib.merge3 import Merge3
46
48
from bzrlib.osutils import rename, pathjoin
47
49
from progress import DummyProgress, ProgressPhase
256
258
def compare_basis(self):
258
260
basis_tree = self.revision_tree(self.this_tree.last_revision())
259
except errors.RevisionNotPresent:
261
except errors.NoSuchRevision:
260
262
basis_tree = self.this_tree.basis_tree()
261
263
changes = self.this_tree.changes_from(basis_tree)
262
264
if not changes.has_changed():
1241
1243
class _PlanMergeBase(object):
1243
def __init__(self, a_rev, b_rev, vf):
1245
def __init__(self, a_rev, b_rev, vf, prefix):
1246
1248
:param a_rev: Revision-id of one revision to merge
1247
1249
:param b_rev: Revision-id of the other revision to merge
1248
:param vf: A versionedfile containing both revisions
1250
:param vf: A VersionedFiles containing both revisions
1251
:param prefix: A prefix for accessing keys in vf.
1250
1253
self.a_rev = a_rev
1251
1254
self.b_rev = b_rev
1252
self.lines_a = vf.get_lines(a_rev)
1253
self.lines_b = vf.get_lines(b_rev)
1255
1256
self._last_lines = None
1256
1257
self._last_lines_revision_id = None
1257
1258
self._cached_matching_blocks = {}
1259
self._prefix = prefix
1260
lines = self.get_lines([a_rev, b_rev])
1261
self.lines_a = lines[a_rev]
1262
self.lines_b = lines[b_rev]
1264
def get_lines(self, revisions):
1265
"""Get lines for revisions from the backing VersionedFiles.
1267
:raises RevisionNotPresent on absent texts.
1269
keys = dict((self._prefix + (rev,), rev) for rev in revisions)
1271
for record in self.vf.get_record_stream(keys, 'unordered', True):
1272
if record.storage_kind == 'absent':
1273
raise errors.RevisionNotPresent(record.key, self.vf)
1274
result[keys[record.key]] = osutils.split_lines(
1275
record.get_bytes_as('fulltext'))
1259
1278
def plan_merge(self):
1260
1279
"""Generate a 'plan' for merging the two revisions.
1309
1328
if self._last_lines_revision_id == left_revision:
1310
1329
left_lines = self._last_lines
1330
right_lines = self.get_lines([right_revision])[right_revision]
1312
left_lines = self.vf.get_lines(left_revision)
1313
right_lines = self.vf.get_lines(right_revision)
1332
lines = self.get_lines([left_revision, right_revision])
1333
left_lines = lines[left_revision]
1334
right_lines = lines[right_revision]
1314
1335
self._last_lines = right_lines
1315
1336
self._last_lines_revision_id = right_revision
1316
1337
matcher = patiencediff.PatienceSequenceMatcher(None, left_lines,
1369
1390
class _PlanMerge(_PlanMergeBase):
1370
1391
"""Plan an annotate merge using on-the-fly annotation"""
1372
def __init__(self, a_rev, b_rev, vf):
1373
_PlanMergeBase.__init__(self, a_rev, b_rev, vf)
1374
a_ancestry = set(vf.get_ancestry(a_rev, topo_sorted=False))
1375
b_ancestry = set(vf.get_ancestry(b_rev, topo_sorted=False))
1376
self.uncommon = a_ancestry.symmetric_difference(b_ancestry)
1393
def __init__(self, a_rev, b_rev, vf, prefix):
1394
_PlanMergeBase.__init__(self, a_rev, b_rev, vf, prefix)
1396
# XXX: There is probably a better API to use to examine less history.
1397
a_ancestry = set(chain(*graph._make_breadth_first_searcher(
1398
[prefix + (a_rev,)])))
1399
b_ancestry = set(chain(*graph._make_breadth_first_searcher(
1400
[prefix + (b_rev,)])))
1401
self.uncommon = set(key[-1] for key in
1402
a_ancestry.symmetric_difference(b_ancestry))
1378
1404
def _determine_status(self, revision_id, unique_line_numbers):
1379
1405
"""Determines the status unique lines versus all lcas.
1400
1426
if version_id not in self.uncommon:
1402
parents = self.vf.get_parent_map([version_id])[version_id]
1428
key = self._prefix + (version_id,)
1429
parent_map = self.vf.get_parent_map([key])
1430
parents = tuple(parent[-1] for parent in parent_map[key])
1403
1431
if len(parents) == 0:
1404
return set(range(len(self.vf.get_lines(version_id))))
1432
return set(range(len(self.get_lines([version_id])[version_id])))
1406
1434
for parent in parents:
1407
1435
blocks = self._get_matching_blocks(version_id, parent)
1429
1457
This is faster, and hopefully produces more useful output.
1432
def __init__(self, a_rev, b_rev, vf, graph):
1433
_PlanMergeBase.__init__(self, a_rev, b_rev, vf)
1434
self.lcas = graph.find_lca(a_rev, b_rev)
1460
def __init__(self, a_rev, b_rev, vf, prefix, graph):
1461
_PlanMergeBase.__init__(self, a_rev, b_rev, vf, prefix)
1462
lcas = graph.find_lca(prefix + (a_rev,), prefix + (b_rev,))
1463
self.lcas = set(lca[-1] for lca in lcas)
1435
1464
for lca in self.lcas:
1436
lca_lines = self.vf.get_lines(lca)
1465
lca_lines = self.get_lines([lca])[lca]
1437
1466
matcher = patiencediff.PatienceSequenceMatcher(None, self.lines_a,
1439
1468
blocks = list(matcher.get_matching_blocks())