~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

(jelmer) Deprecate Repository.iter_reverse_revision_history(). (Jelmer
 Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
38
38
    versionedfile,
39
39
    workingtree,
40
40
    )
41
 
from bzrlib.i18n import gettext
42
41
""")
43
42
from bzrlib import (
44
43
    decorators,
45
44
    errors,
46
45
    hooks,
47
 
    registry,
48
46
    )
49
47
from bzrlib.symbol_versioning import (
50
48
    deprecated_in,
140
138
            params.winner == 'other' or
141
139
            # THIS and OTHER aren't both files.
142
140
            not params.is_file_merge() or
143
 
            # The filename doesn't match
 
141
            # The filename doesn't match *.xml
144
142
            not self.file_matches(params)):
145
143
            return 'not_applicable', None
146
144
        return self.merge_matching(params)
593
591
                else:
594
592
                    self.base_rev_id = self.revision_graph.find_unique_lca(
595
593
                                            *lcas)
596
 
                sorted_lca_keys = self.revision_graph.find_merge_order(
 
594
                sorted_lca_keys = self.revision_graph.find_merge_order(                
597
595
                    revisions[0], lcas)
598
596
                if self.base_rev_id == _mod_revision.NULL_REVISION:
599
597
                    self.base_rev_id = sorted_lca_keys[0]
600
 
 
 
598
                
601
599
            if self.base_rev_id == _mod_revision.NULL_REVISION:
602
600
                raise errors.UnrelatedBranches()
603
601
            if self._is_criss_cross:
606
604
                trace.mutter('Criss-cross lcas: %r' % lcas)
607
605
                if self.base_rev_id in lcas:
608
606
                    trace.mutter('Unable to find unique lca. '
609
 
                                 'Fallback %r as best option.'
610
 
                                 % self.base_rev_id)
 
607
                                 'Fallback %r as best option.' % self.base_rev_id)
611
608
                interesting_revision_ids = set(lcas)
612
609
                interesting_revision_ids.add(self.base_rev_id)
613
610
                interesting_trees = dict((t.get_revision_id(), t)
692
689
                    continue
693
690
                sub_merge = Merger(sub_tree.branch, this_tree=sub_tree)
694
691
                sub_merge.merge_type = self.merge_type
695
 
                other_branch = self.other_branch.reference_parent(file_id,
696
 
                                                                  relpath)
 
692
                other_branch = self.other_branch.reference_parent(file_id, relpath)
697
693
                sub_merge.set_other_revision(other_revision, other_branch)
698
694
                base_revision = self.base_tree.get_reference_revision(file_id)
699
695
                sub_merge.base_tree = \
715
711
        merge = operation.run_simple()
716
712
        if len(merge.cooked_conflicts) == 0:
717
713
            if not self.ignore_zero and not trace.is_quiet():
718
 
                trace.note(gettext("All changes applied successfully."))
 
714
                trace.note("All changes applied successfully.")
719
715
        else:
720
 
            trace.note(gettext("%d conflicts encountered.")
 
716
            trace.note("%d conflicts encountered."
721
717
                       % len(merge.cooked_conflicts))
722
718
 
723
719
        return len(merge.cooked_conflicts)
855
851
        else:
856
852
            entries = self._entries_lca()
857
853
            resolver = self._lca_multi_way
858
 
        # Prepare merge hooks
859
 
        factories = Merger.hooks['merge_file_content']
860
 
        # One hook for each registered one plus our default merger
861
 
        hooks = [factory(self) for factory in factories] + [self]
862
 
        self.active_hooks = [hook for hook in hooks if hook is not None]
863
854
        child_pb = ui.ui_factory.nested_progress_bar()
864
855
        try:
 
856
            factories = Merger.hooks['merge_file_content']
 
857
            hooks = [factory(self) for factory in factories] + [self]
 
858
            self.active_hooks = [hook for hook in hooks if hook is not None]
865
859
            for num, (file_id, changed, parents3, names3,
866
860
                      executable3) in enumerate(entries):
867
 
                # Try merging each entry
868
 
                child_pb.update(gettext('Preparing file merge'),
869
 
                                num, len(entries))
 
861
                child_pb.update('Preparing file merge', num, len(entries))
870
862
                self._merge_names(file_id, parents3, names3, resolver=resolver)
871
863
                if changed:
872
864
                    file_status = self._do_merge_contents(file_id)
998
990
                else:
999
991
                    lca_entries.append(lca_ie)
1000
992
 
1001
 
            if base_inventory.has_id(file_id):
 
993
            if file_id in base_inventory:
1002
994
                base_ie = base_inventory[file_id]
1003
995
            else:
1004
996
                base_ie = _none_entry
1005
997
 
1006
 
            if this_inventory.has_id(file_id):
 
998
            if file_id in this_inventory:
1007
999
                this_ie = this_inventory[file_id]
1008
1000
            else:
1009
1001
                this_ie = _none_entry
1114
1106
        other_root = self.tt.trans_id_file_id(other_root_file_id)
1115
1107
        if other_root == self.tt.root:
1116
1108
            return
1117
 
        if self.this_tree.inventory.has_id(
1118
 
            self.other_tree.inventory.root.file_id):
1119
 
            # the other tree's root is a non-root in the current tree (as
1120
 
            # when a previously unrelated branch is merged into another)
 
1109
        if self.other_tree.inventory.root.file_id in self.this_tree.inventory:
 
1110
            # the other tree's root is a non-root in the current tree (as when
 
1111
            # a previously unrelated branch is merged into another)
1121
1112
            return
1122
1113
        if self.tt.final_kind(other_root) is not None:
1123
1114
            other_root_is_present = True
1175
1166
    @staticmethod
1176
1167
    def contents_sha1(tree, file_id):
1177
1168
        """Determine the sha1 of the file contents (used as a key method)."""
1178
 
        if not tree.has_id(file_id):
 
1169
        if file_id not in tree:
1179
1170
            return None
1180
1171
        return tree.get_file_sha1(file_id)
1181
1172
 
1351
1342
    def _do_merge_contents(self, file_id):
1352
1343
        """Performs a merge on file_id contents."""
1353
1344
        def contents_pair(tree):
1354
 
            if not tree.has_id(file_id):
 
1345
            if file_id not in tree:
1355
1346
                return (None, None)
1356
1347
            kind = tree.kind(file_id)
1357
1348
            if kind == "file":
1395
1386
            if hook_status != 'not_applicable':
1396
1387
                # Don't try any more hooks, this one applies.
1397
1388
                break
1398
 
        # If the merge ends up replacing the content of the file, we get rid of
1399
 
        # it at the end of this method (this variable is used to track the
1400
 
        # exceptions to this rule).
1401
 
        keep_this = False
1402
1389
        result = "modified"
1403
1390
        if hook_status == 'not_applicable':
1404
 
            # No merge hook was able to resolve the situation. Two cases exist:
1405
 
            # a content conflict or a duplicate one.
 
1391
            # This is a contents conflict, because none of the available
 
1392
            # functions could merge it.
1406
1393
            result = None
1407
1394
            name = self.tt.final_name(trans_id)
1408
1395
            parent_id = self.tt.final_parent(trans_id)
1409
 
            duplicate = False
1410
 
            inhibit_content_conflict = False
1411
 
            if params.this_kind is None: # file_id is not in THIS
1412
 
                # Is the name used for a different file_id ?
1413
 
                dupe_path = self.other_tree.id2path(file_id)
1414
 
                this_id = self.this_tree.path2id(dupe_path)
1415
 
                if this_id is not None:
1416
 
                    # Two entries for the same path
1417
 
                    keep_this = True
1418
 
                    # versioning the merged file will trigger a duplicate
1419
 
                    # conflict
1420
 
                    self.tt.version_file(file_id, trans_id)
1421
 
                    transform.create_from_tree(
1422
 
                        self.tt, trans_id, self.other_tree, file_id,
1423
 
                        filter_tree_path=self._get_filter_tree_path(file_id))
1424
 
                    inhibit_content_conflict = True
1425
 
            elif params.other_kind is None: # file_id is not in OTHER
1426
 
                # Is the name used for a different file_id ?
1427
 
                dupe_path = self.this_tree.id2path(file_id)
1428
 
                other_id = self.other_tree.path2id(dupe_path)
1429
 
                if other_id is not None:
1430
 
                    # Two entries for the same path again, but here, the other
1431
 
                    # entry will also be merged.  We simply inhibit the
1432
 
                    # 'content' conflict creation because we know OTHER will
1433
 
                    # create (or has already created depending on ordering) an
1434
 
                    # entry at the same path. This will trigger a 'duplicate'
1435
 
                    # conflict later.
1436
 
                    keep_this = True
1437
 
                    inhibit_content_conflict = True
1438
 
            if not inhibit_content_conflict:
1439
 
                if params.this_kind is not None:
1440
 
                    self.tt.unversion_file(trans_id)
1441
 
                # This is a contents conflict, because none of the available
1442
 
                # functions could merge it.
1443
 
                file_group = self._dump_conflicts(name, parent_id, file_id,
1444
 
                                                  set_version=True)
1445
 
                self._raw_conflicts.append(('contents conflict', file_group))
 
1396
            if self.this_tree.has_id(file_id):
 
1397
                self.tt.unversion_file(trans_id)
 
1398
            file_group = self._dump_conflicts(name, parent_id, file_id,
 
1399
                                              set_version=True)
 
1400
            self._raw_conflicts.append(('contents conflict', file_group))
1446
1401
        elif hook_status == 'success':
1447
1402
            self.tt.create_file(lines, trans_id)
1448
1403
        elif hook_status == 'conflicted':
1464
1419
            raise AssertionError('unknown hook_status: %r' % (hook_status,))
1465
1420
        if not self.this_tree.has_id(file_id) and result == "modified":
1466
1421
            self.tt.version_file(file_id, trans_id)
1467
 
        if not keep_this:
1468
 
            # The merge has been performed and produced a new content, so the
1469
 
            # old contents should not be retained.
1470
 
            self.tt.delete_contents(trans_id)
 
1422
        # The merge has been performed, so the old contents should not be
 
1423
        # retained.
 
1424
        self.tt.delete_contents(trans_id)
1471
1425
        return result
1472
1426
 
1473
1427
    def _default_other_winner_merge(self, merge_hook_params):
1474
1428
        """Replace this contents with other."""
1475
1429
        file_id = merge_hook_params.file_id
1476
1430
        trans_id = merge_hook_params.trans_id
 
1431
        file_in_this = self.this_tree.has_id(file_id)
1477
1432
        if self.other_tree.has_id(file_id):
1478
1433
            # OTHER changed the file
1479
 
            transform.create_from_tree(
1480
 
                self.tt, trans_id, self.other_tree, file_id,
1481
 
                filter_tree_path=self._get_filter_tree_path(file_id))
 
1434
            wt = self.this_tree
 
1435
            if wt.supports_content_filtering():
 
1436
                # We get the path from the working tree if it exists.
 
1437
                # That fails though when OTHER is adding a file, so
 
1438
                # we fall back to the other tree to find the path if
 
1439
                # it doesn't exist locally.
 
1440
                try:
 
1441
                    filter_tree_path = wt.id2path(file_id)
 
1442
                except errors.NoSuchId:
 
1443
                    filter_tree_path = self.other_tree.id2path(file_id)
 
1444
            else:
 
1445
                # Skip the id2path lookup for older formats
 
1446
                filter_tree_path = None
 
1447
            transform.create_from_tree(self.tt, trans_id,
 
1448
                             self.other_tree, file_id,
 
1449
                             filter_tree_path=filter_tree_path)
1482
1450
            return 'done', None
1483
 
        elif self.this_tree.has_id(file_id):
 
1451
        elif file_in_this:
1484
1452
            # OTHER deleted the file
1485
1453
            return 'delete', None
1486
1454
        else:
1560
1528
                                              other_lines)
1561
1529
            file_group.append(trans_id)
1562
1530
 
1563
 
 
1564
 
    def _get_filter_tree_path(self, file_id):
1565
 
        if self.this_tree.supports_content_filtering():
1566
 
            # We get the path from the working tree if it exists.
1567
 
            # That fails though when OTHER is adding a file, so
1568
 
            # we fall back to the other tree to find the path if
1569
 
            # it doesn't exist locally.
1570
 
            try:
1571
 
                return self.this_tree.id2path(file_id)
1572
 
            except errors.NoSuchId:
1573
 
                return self.other_tree.id2path(file_id)
1574
 
        # Skip the id2path lookup for older formats
1575
 
        return None
1576
 
 
1577
1531
    def _dump_conflicts(self, name, parent_id, file_id, this_lines=None,
1578
1532
                        base_lines=None, other_lines=None, set_version=False,
1579
1533
                        no_base=False):
1662
1616
 
1663
1617
    def cook_conflicts(self, fs_conflicts):
1664
1618
        """Convert all conflicts into a form that doesn't depend on trans_id"""
1665
 
        content_conflict_file_ids = set()
1666
 
        cooked_conflicts = transform.cook_conflicts(fs_conflicts, self.tt)
 
1619
        self.cooked_conflicts.extend(transform.cook_conflicts(
 
1620
                fs_conflicts, self.tt))
1667
1621
        fp = transform.FinalPaths(self.tt)
1668
1622
        for conflict in self._raw_conflicts:
1669
1623
            conflict_type = conflict[0]
1680
1634
                if other_parent is None or other_name is None:
1681
1635
                    other_path = '<deleted>'
1682
1636
                else:
1683
 
                    if other_parent == self.other_tree.get_root_id():
1684
 
                        # The tree transform doesn't know about the other root,
1685
 
                        # so we special case here to avoid a NoFinalPath
1686
 
                        # exception
1687
 
                        parent_path = ''
1688
 
                    else:
1689
 
                        parent_path =  fp.get_path(
1690
 
                            self.tt.trans_id_file_id(other_parent))
 
1637
                    parent_path =  fp.get_path(
 
1638
                        self.tt.trans_id_file_id(other_parent))
1691
1639
                    other_path = osutils.pathjoin(parent_path, other_name)
1692
1640
                c = _mod_conflicts.Conflict.factory(
1693
1641
                    'path conflict', path=this_path,
1697
1645
                for trans_id in conflict[1]:
1698
1646
                    file_id = self.tt.final_file_id(trans_id)
1699
1647
                    if file_id is not None:
1700
 
                        # Ok we found the relevant file-id
1701
1648
                        break
1702
1649
                path = fp.get_path(trans_id)
1703
1650
                for suffix in ('.BASE', '.THIS', '.OTHER'):
1704
1651
                    if path.endswith(suffix):
1705
 
                        # Here is the raw path
1706
1652
                        path = path[:-len(suffix)]
1707
1653
                        break
1708
1654
                c = _mod_conflicts.Conflict.factory(conflict_type,
1709
1655
                                                    path=path, file_id=file_id)
1710
 
                content_conflict_file_ids.add(file_id)
1711
1656
            elif conflict_type == 'text conflict':
1712
1657
                trans_id = conflict[1]
1713
1658
                path = fp.get_path(trans_id)
1716
1661
                                                    path=path, file_id=file_id)
1717
1662
            else:
1718
1663
                raise AssertionError('bad conflict type: %r' % (conflict,))
1719
 
            cooked_conflicts.append(c)
1720
 
 
1721
 
        self.cooked_conflicts = []
1722
 
        # We want to get rid of path conflicts when a corresponding contents
1723
 
        # conflict exists. This can occur when one branch deletes a file while
1724
 
        # the other renames *and* modifies it. In this case, the content
1725
 
        # conflict is enough.
1726
 
        for c in cooked_conflicts:
1727
 
            if (c.typestring == 'path conflict'
1728
 
                and c.file_id in content_conflict_file_ids):
1729
 
                continue
1730
1664
            self.cooked_conflicts.append(c)
1731
1665
        self.cooked_conflicts.sort(key=_mod_conflicts.Conflict.sort_key)
1732
1666
 
1953
1887
            entries = self._entries_to_incorporate()
1954
1888
            entries = list(entries)
1955
1889
            for num, (entry, parent_id) in enumerate(entries):
1956
 
                child_pb.update(gettext('Preparing file merge'), num, len(entries))
 
1890
                child_pb.update('Preparing file merge', num, len(entries))
1957
1891
                parent_trans_id = self.tt.trans_id_file_id(parent_id)
1958
1892
                trans_id = transform.new_by_entry(self.tt, entry,
1959
1893
                    parent_trans_id, self.other_tree)
1977
1911
        name_in_target = osutils.basename(self._target_subdir)
1978
1912
        merge_into_root = subdir.copy()
1979
1913
        merge_into_root.name = name_in_target
1980
 
        if self.this_tree.inventory.has_id(merge_into_root.file_id):
 
1914
        if merge_into_root.file_id in self.this_tree.inventory:
1981
1915
            # Give the root a new file-id.
1982
1916
            # This can happen fairly easily if the directory we are
1983
1917
            # incorporating is the root, and both trees have 'TREE_ROOT' as
2020
1954
    """
2021
1955
    if this_tree is None:
2022
1956
        raise errors.BzrError("bzrlib.merge.merge_inner requires a this_tree "
2023
 
                              "parameter")
 
1957
                              "parameter as of bzrlib version 0.8.")
2024
1958
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree,
2025
1959
                    pb=pb, change_reporter=change_reporter)
2026
1960
    merger.backup_files = backup_files
2043
1977
    merger.set_base_revision(get_revision_id(), this_branch)
2044
1978
    return merger.do_merge()
2045
1979
 
2046
 
 
2047
 
merge_type_registry = registry.Registry()
2048
 
merge_type_registry.register('diff3', Diff3Merger,
2049
 
                             "Merge using external diff3.")
2050
 
merge_type_registry.register('lca', LCAMerger,
2051
 
                             "LCA-newness merge.")
2052
 
merge_type_registry.register('merge3', Merge3Merger,
2053
 
                             "Native diff3-style merge.")
2054
 
merge_type_registry.register('weave', WeaveMerger,
2055
 
                             "Weave-based merge.")
2056
 
 
2057
 
 
2058
1980
def get_merge_type_registry():
2059
 
    """Merge type registry was previously in bzrlib.option
 
1981
    """Merge type registry is in bzrlib.option to avoid circular imports.
2060
1982
 
2061
 
    This method provides a backwards compatible way to retrieve it.
 
1983
    This method provides a sanctioned way to retrieve it.
2062
1984
    """
2063
 
    return merge_type_registry
 
1985
    from bzrlib import option
 
1986
    return option._merge_type_registry
2064
1987
 
2065
1988
 
2066
1989
def _plan_annotate_merge(annotated_a, annotated_b, ancestors_a, ancestors_b):