~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-06-25 10:36:36 UTC
  • mfrom: (3350.6.12 versionedfiles)
  • Revision ID: pqm@pqm.ubuntu.com-20080625103636-6kxh4e1gmyn82f50
(mbp for robertc) VersionedFiles refactoring

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
 
18
import errno
 
19
from itertools import chain
18
20
import os
19
 
import errno
20
21
import warnings
21
22
 
22
23
from bzrlib import (
42
43
                           WorkingTreeNotRevision,
43
44
                           BinaryFile,
44
45
                           )
 
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
257
259
    def compare_basis(self):
258
260
        try:
259
261
            basis_tree = self.revision_tree(self.this_tree.last_revision())
260
 
        except errors.RevisionNotPresent:
 
262
        except errors.NoSuchRevision:
261
263
            basis_tree = self.this_tree.basis_tree()
262
264
        changes = self.this_tree.changes_from(basis_tree)
263
265
        if not changes.has_changed():
277
279
        for revision_id in new_parents:
278
280
            try:
279
281
                tree = self.revision_tree(revision_id)
280
 
            except errors.RevisionNotPresent:
 
282
            except errors.NoSuchRevision:
281
283
                tree = None
282
284
            else:
283
285
                tree.lock_read()
1240
1242
 
1241
1243
class _PlanMergeBase(object):
1242
1244
 
1243
 
    def __init__(self, a_rev, b_rev, vf):
 
1245
    def __init__(self, a_rev, b_rev, vf, key_prefix):
1244
1246
        """Contructor.
1245
1247
 
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 key_prefix: A prefix for accessing keys in vf, typically
 
1252
            (file_id,).
1249
1253
        """
1250
1254
        self.a_rev = a_rev
1251
1255
        self.b_rev = b_rev
1252
 
        self.lines_a = vf.get_lines(a_rev)
1253
 
        self.lines_b = vf.get_lines(b_rev)
1254
1256
        self.vf = vf
1255
1257
        self._last_lines = None
1256
1258
        self._last_lines_revision_id = None
1257
1259
        self._cached_matching_blocks = {}
 
1260
        self._key_prefix = key_prefix
 
1261
        lines = self.get_lines([a_rev, b_rev])
 
1262
        self.lines_a = lines[a_rev]
 
1263
        self.lines_b = lines[b_rev]
 
1264
 
 
1265
    def get_lines(self, revisions):
 
1266
        """Get lines for revisions from the backing VersionedFiles.
 
1267
        
 
1268
        :raises RevisionNotPresent: on absent texts.
 
1269
        """
 
1270
        keys = [(self._key_prefix + (rev,)) for rev in revisions]
 
1271
        result = {}
 
1272
        for record in self.vf.get_record_stream(keys, 'unordered', True):
 
1273
            if record.storage_kind == 'absent':
 
1274
                raise errors.RevisionNotPresent(record.key, self.vf)
 
1275
            result[record.key[-1]] = osutils.split_lines(
 
1276
                record.get_bytes_as('fulltext'))
 
1277
        return result
1258
1278
 
1259
1279
    def plan_merge(self):
1260
1280
        """Generate a 'plan' for merging the two revisions.
1308
1328
            return cached
1309
1329
        if self._last_lines_revision_id == left_revision:
1310
1330
            left_lines = self._last_lines
 
1331
            right_lines = self.get_lines([right_revision])[right_revision]
1311
1332
        else:
1312
 
            left_lines = self.vf.get_lines(left_revision)
1313
 
        right_lines = self.vf.get_lines(right_revision)
 
1333
            lines = self.get_lines([left_revision, right_revision])
 
1334
            left_lines = lines[left_revision]
 
1335
            right_lines = lines[right_revision]
1314
1336
        self._last_lines = right_lines
1315
1337
        self._last_lines_revision_id = right_revision
1316
1338
        matcher = patiencediff.PatienceSequenceMatcher(None, left_lines,
1369
1391
class _PlanMerge(_PlanMergeBase):
1370
1392
    """Plan an annotate merge using on-the-fly annotation"""
1371
1393
 
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)
 
1394
    def __init__(self, a_rev, b_rev, vf, key_prefix):
 
1395
        _PlanMergeBase.__init__(self, a_rev, b_rev, vf, key_prefix)
 
1396
        graph = Graph(vf)
 
1397
        # XXX: There is probably a better API to use to examine less history.
 
1398
        a_ancestry = set(chain(*graph._make_breadth_first_searcher(
 
1399
            [key_prefix + (a_rev,)])))
 
1400
        b_ancestry = set(chain(*graph._make_breadth_first_searcher(
 
1401
            [key_prefix + (b_rev,)])))
 
1402
        self.uncommon = set(key[-1] for key in
 
1403
            a_ancestry.symmetric_difference(b_ancestry))
1377
1404
 
1378
1405
    def _determine_status(self, revision_id, unique_line_numbers):
1379
1406
        """Determines the status unique lines versus all lcas.
1399
1426
        """
1400
1427
        if version_id not in self.uncommon:
1401
1428
            return set()
1402
 
        parents = self.vf.get_parent_map([version_id])[version_id]
 
1429
        key = self._key_prefix + (version_id,)
 
1430
        parent_map = self.vf.get_parent_map([key])
 
1431
        parents = tuple(parent[-1] for parent in parent_map[key])
1403
1432
        if len(parents) == 0:
1404
 
            return set(range(len(self.vf.get_lines(version_id))))
 
1433
            return set(range(len(self.get_lines([version_id])[version_id])))
1405
1434
        new = None
1406
1435
        for parent in parents:
1407
1436
            blocks = self._get_matching_blocks(version_id, parent)
1429
1458
    This is faster, and hopefully produces more useful output.
1430
1459
    """
1431
1460
 
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)
 
1461
    def __init__(self, a_rev, b_rev, vf, key_prefix, graph):
 
1462
        _PlanMergeBase.__init__(self, a_rev, b_rev, vf, key_prefix)
 
1463
        lcas = graph.find_lca(key_prefix + (a_rev,), key_prefix + (b_rev,))
 
1464
        self.lcas = set()
 
1465
        for lca in lcas:
 
1466
            if lca == NULL_REVISION:
 
1467
                self.lcas.add(lca)
 
1468
            else:
 
1469
                self.lcas.add(lca[-1])
1435
1470
        for lca in self.lcas:
1436
1471
            if _mod_revision.is_null(lca):
1437
1472
                lca_lines = []
1438
1473
            else:
1439
 
                lca_lines = self.vf.get_lines(lca)
 
1474
                lca_lines = self.get_lines([lca])[lca]
1440
1475
            matcher = patiencediff.PatienceSequenceMatcher(None, self.lines_a,
1441
1476
                                                           lca_lines)
1442
1477
            blocks = list(matcher.get_matching_blocks())