~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
256
258
    def compare_basis(self):
257
259
        try:
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():
276
278
        for revision_id in new_parents:
277
279
            try:
278
280
                tree = self.revision_tree(revision_id)
279
 
            except errors.RevisionNotPresent:
 
281
            except errors.NoSuchRevision:
280
282
                tree = None
281
283
            else:
282
284
                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, 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 prefix: A prefix for accessing keys in vf.
1249
1252
        """
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)
1254
1255
        self.vf = vf
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]
 
1263
 
 
1264
    def get_lines(self, revisions):
 
1265
        """Get lines for revisions from the backing VersionedFiles.
 
1266
        
 
1267
        :raises RevisionNotPresent on absent texts.
 
1268
        """
 
1269
        keys = dict((self._prefix + (rev,), rev) for rev in revisions)
 
1270
        result = {}
 
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'))
 
1276
        return result
1258
1277
 
1259
1278
    def plan_merge(self):
1260
1279
        """Generate a 'plan' for merging the two revisions.
1308
1327
            return cached
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]
1311
1331
        else:
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"""
1371
1392
 
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)
 
1395
        graph = Graph(vf)
 
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))
1377
1403
 
1378
1404
    def _determine_status(self, revision_id, unique_line_numbers):
1379
1405
        """Determines the status unique lines versus all lcas.
1399
1425
        """
1400
1426
        if version_id not in self.uncommon:
1401
1427
            return set()
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])))
1405
1433
        new = None
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.
1430
1458
    """
1431
1459
 
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,
1438
1467
                                                           lca_lines)
1439
1468
            blocks = list(matcher.get_matching_blocks())