~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Robert Collins
  • Date: 2010-04-08 04:34:03 UTC
  • mfrom: (5138 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5139.
  • Revision ID: robertc@robertcollins.net-20100408043403-56z0d07vdqrx7f3t
Update bugfix for 528114 to trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
import warnings
17
18
 
18
19
from bzrlib import (
19
20
    branch as _mod_branch,
26
27
    merge3,
27
28
    osutils,
28
29
    patiencediff,
29
 
    progress,
30
30
    revision as _mod_revision,
31
31
    textfile,
32
32
    trace,
237
237
        self.interesting_files = None
238
238
        self.show_base = False
239
239
        self.reprocess = False
240
 
        if pb is None:
241
 
            pb = progress.DummyProgress()
242
 
        self._pb = pb
 
240
        if pb is not None:
 
241
            warnings.warn("pb parameter to Merger() is deprecated and ignored")
243
242
        self.pp = None
244
243
        self.recurse = recurse
245
244
        self.change_reporter = change_reporter
594
593
                  'other_tree': self.other_tree,
595
594
                  'interesting_ids': self.interesting_ids,
596
595
                  'interesting_files': self.interesting_files,
597
 
                  'pp': self.pp, 'this_branch': self.this_branch,
 
596
                  'this_branch': self.this_branch,
598
597
                  'do_merge': False}
599
598
        if self.merge_type.requires_base:
600
599
            kwargs['base_tree'] = self.base_tree
618
617
        if self._is_criss_cross and getattr(self.merge_type,
619
618
                                            'supports_lca_trees', False):
620
619
            kwargs['lca_trees'] = self._lca_trees
621
 
        return self.merge_type(pb=self._pb,
 
620
        return self.merge_type(pb=None,
622
621
                               change_reporter=self.change_reporter,
623
622
                               **kwargs)
624
623
 
696
695
 
697
696
    def __init__(self, working_tree, this_tree, base_tree, other_tree,
698
697
                 interesting_ids=None, reprocess=False, show_base=False,
699
 
                 pb=progress.DummyProgress(), pp=None, change_reporter=None,
 
698
                 pb=None, pp=None, change_reporter=None,
700
699
                 interesting_files=None, do_merge=True,
701
700
                 cherrypick=False, lca_trees=None, this_branch=None):
702
701
        """Initialize the merger object and perform the merge.
712
711
        :param: reprocess If True, perform conflict-reduction processing.
713
712
        :param show_base: If True, show the base revision in text conflicts.
714
713
            (incompatible with reprocess)
715
 
        :param pb: A Progress bar
 
714
        :param pb: ignored
716
715
        :param pp: A ProgressPhase object
717
716
        :param change_reporter: An object that should report changes made
718
717
        :param interesting_files: The tree-relative paths of files that should
745
744
        # making sure we haven't missed any corner cases.
746
745
        # if lca_trees is None:
747
746
        #     self._lca_trees = [self.base_tree]
748
 
        self.pb = pb
749
 
        self.pp = pp
750
747
        self.change_reporter = change_reporter
751
748
        self.cherrypick = cherrypick
752
 
        if self.pp is None:
753
 
            self.pp = progress.ProgressPhase("Merge phase", 3, self.pb)
754
749
        if do_merge:
755
750
            self.do_merge()
 
751
        if pp is not None:
 
752
            warnings.warn("pp argument to Merge3Merger is deprecated")
 
753
        if pb is not None:
 
754
            warnings.warn("pb argument to Merge3Merger is deprecated")
756
755
 
757
756
    def do_merge(self):
758
757
        operation = OperationWithCleanups(self._do_merge)
759
 
        operation.add_cleanup(self.pb.clear)
760
758
        self.this_tree.lock_tree_write()
761
759
        operation.add_cleanup(self.this_tree.unlock)
762
760
        self.base_tree.lock_read()
766
764
        operation.run()
767
765
 
768
766
    def _do_merge(self, operation):
769
 
        self.tt = transform.TreeTransform(self.this_tree, self.pb)
 
767
        self.tt = transform.TreeTransform(self.this_tree, None)
770
768
        operation.add_cleanup(self.tt.finalize)
771
 
        self.pp.next_phase()
772
769
        self._compute_transform()
773
 
        self.pp.next_phase()
774
770
        results = self.tt.apply(no_conflicts=True)
775
771
        self.write_modified(results)
776
772
        try:
780
776
 
781
777
    def make_preview_transform(self):
782
778
        operation = OperationWithCleanups(self._make_preview_transform)
783
 
        operation.add_cleanup(self.pb.clear)
784
779
        self.base_tree.lock_read()
785
780
        operation.add_cleanup(self.base_tree.unlock)
786
781
        self.other_tree.lock_read()
789
784
 
790
785
    def _make_preview_transform(self):
791
786
        self.tt = transform.TransformPreview(self.this_tree)
792
 
        self.pp.next_phase()
793
787
        self._compute_transform()
794
 
        self.pp.next_phase()
795
788
        return self.tt
796
789
 
797
790
    def _compute_transform(self):
819
812
        finally:
820
813
            child_pb.finished()
821
814
        self.fix_root()
822
 
        self.pp.next_phase()
823
815
        child_pb = ui.ui_factory.nested_progress_bar()
824
816
        try:
825
817
            fs_conflicts = transform.resolve_conflicts(self.tt, child_pb,
1023
1015
                        continue
1024
1016
                else:
1025
1017
                    raise AssertionError('unhandled kind: %s' % other_ie.kind)
1026
 
                # XXX: We need to handle kind == 'symlink'
1027
1018
 
1028
1019
            # If we have gotten this far, that means something has changed
1029
1020
            result.append((file_id, content_changed,
1118
1109
 
1119
1110
    @staticmethod
1120
1111
    def _three_way(base, other, this):
1121
 
        #if base == other, either they all agree, or only THIS has changed.
1122
1112
        if base == other:
 
1113
            # if 'base == other', either they all agree, or only 'this' has
 
1114
            # changed.
1123
1115
            return 'this'
1124
1116
        elif this not in (base, other):
 
1117
            # 'this' is neither 'base' nor 'other', so both sides changed
1125
1118
            return 'conflict'
1126
 
        # "Ambiguous clean merge" -- both sides have made the same change.
1127
1119
        elif this == other:
 
1120
            # "Ambiguous clean merge" -- both sides have made the same change.
1128
1121
            return "this"
1129
 
        # this == base: only other has changed.
1130
1122
        else:
 
1123
            # this == base: only other has changed.
1131
1124
            return "other"
1132
1125
 
1133
1126
    @staticmethod
1177
1170
                # only has an lca value
1178
1171
                return 'other'
1179
1172
 
1180
 
        # At this point, the lcas disagree, and the tips disagree
 
1173
        # At this point, the lcas disagree, and the tip disagree
1181
1174
        return 'conflict'
1182
1175
 
1183
1176
    @staticmethod
 
1177
    @deprecated_method(deprecated_in((2, 2, 0)))
1184
1178
    def scalar_three_way(this_tree, base_tree, other_tree, file_id, key):
1185
1179
        """Do a three-way test on a scalar.
1186
1180
        Return "this", "other" or "conflict", depending whether a value wins.
1236
1230
                parent_id_winner = "other"
1237
1231
        if name_winner == "this" and parent_id_winner == "this":
1238
1232
            return
1239
 
        if name_winner == "conflict":
1240
 
            trans_id = self.tt.trans_id_file_id(file_id)
1241
 
            self._raw_conflicts.append(('name conflict', trans_id,
1242
 
                                        this_name, other_name))
1243
 
        if parent_id_winner == "conflict":
1244
 
            trans_id = self.tt.trans_id_file_id(file_id)
1245
 
            self._raw_conflicts.append(('parent conflict', trans_id,
1246
 
                                        this_parent, other_parent))
 
1233
        if name_winner == 'conflict' or parent_id_winner == 'conflict':
 
1234
            # Creating helpers (.OTHER or .THIS) here cause problems down the
 
1235
            # road if a ContentConflict needs to be created so we should not do
 
1236
            # that
 
1237
            trans_id = self.tt.trans_id_file_id(file_id)
 
1238
            self._raw_conflicts.append(('path conflict', trans_id, file_id,
 
1239
                                        this_parent, this_name,
 
1240
                                        other_parent, other_name))
1247
1241
        if other_name is None:
1248
1242
            # it doesn't matter whether the result was 'other' or
1249
1243
            # 'conflict'-- if there's no 'other', we leave it alone.
1250
1244
            return
1251
 
        # if we get here, name_winner and parent_winner are set to safe values.
1252
 
        trans_id = self.tt.trans_id_file_id(file_id)
1253
1245
        parent_id = parents[self.winner_idx[parent_id_winner]]
1254
1246
        if parent_id is not None:
1255
 
            parent_trans_id = self.tt.trans_id_file_id(parent_id)
 
1247
            # if we get here, name_winner and parent_winner are set to safe
 
1248
            # values.
1256
1249
            self.tt.adjust_path(names[self.winner_idx[name_winner]],
1257
 
                                parent_trans_id, trans_id)
 
1250
                                self.tt.trans_id_file_id(parent_id),
 
1251
                                self.tt.trans_id_file_id(file_id))
1258
1252
 
1259
1253
    def _do_merge_contents(self, file_id):
1260
1254
        """Performs a merge on file_id contents."""
1539
1533
 
1540
1534
    def cook_conflicts(self, fs_conflicts):
1541
1535
        """Convert all conflicts into a form that doesn't depend on trans_id"""
1542
 
        name_conflicts = {}
1543
1536
        self.cooked_conflicts.extend(transform.cook_conflicts(
1544
1537
                fs_conflicts, self.tt))
1545
1538
        fp = transform.FinalPaths(self.tt)
1546
1539
        for conflict in self._raw_conflicts:
1547
1540
            conflict_type = conflict[0]
1548
 
            if conflict_type in ('name conflict', 'parent conflict'):
1549
 
                trans_id = conflict[1]
1550
 
                conflict_args = conflict[2:]
1551
 
                if trans_id not in name_conflicts:
1552
 
                    name_conflicts[trans_id] = {}
1553
 
                transform.unique_add(name_conflicts[trans_id], conflict_type,
1554
 
                                     conflict_args)
1555
 
            if conflict_type == 'contents conflict':
 
1541
            if conflict_type == 'path conflict':
 
1542
                (trans_id, file_id,
 
1543
                this_parent, this_name,
 
1544
                other_parent, other_name) = conflict[1:]
 
1545
                if this_parent is None or this_name is None:
 
1546
                    this_path = '<deleted>'
 
1547
                else:
 
1548
                    parent_path =  fp.get_path(
 
1549
                        self.tt.trans_id_file_id(this_parent))
 
1550
                    this_path = osutils.pathjoin(parent_path, this_name)
 
1551
                if other_parent is None or other_name is None:
 
1552
                    other_path = '<deleted>'
 
1553
                else:
 
1554
                    parent_path =  fp.get_path(
 
1555
                        self.tt.trans_id_file_id(other_parent))
 
1556
                    other_path = osutils.pathjoin(parent_path, other_name)
 
1557
                c = _mod_conflicts.Conflict.factory(
 
1558
                    'path conflict', path=this_path,
 
1559
                    conflict_path=other_path,
 
1560
                    file_id=file_id)
 
1561
            elif conflict_type == 'contents conflict':
1556
1562
                for trans_id in conflict[1]:
1557
1563
                    file_id = self.tt.final_file_id(trans_id)
1558
1564
                    if file_id is not None:
1564
1570
                        break
1565
1571
                c = _mod_conflicts.Conflict.factory(conflict_type,
1566
1572
                                                    path=path, file_id=file_id)
1567
 
                self.cooked_conflicts.append(c)
1568
 
            if conflict_type == 'text conflict':
 
1573
            elif conflict_type == 'text conflict':
1569
1574
                trans_id = conflict[1]
1570
1575
                path = fp.get_path(trans_id)
1571
1576
                file_id = self.tt.final_file_id(trans_id)
1572
1577
                c = _mod_conflicts.Conflict.factory(conflict_type,
1573
1578
                                                    path=path, file_id=file_id)
1574
 
                self.cooked_conflicts.append(c)
1575
 
 
1576
 
        for trans_id, conflicts in name_conflicts.iteritems():
1577
 
            try:
1578
 
                this_parent, other_parent = conflicts['parent conflict']
1579
 
                if this_parent == other_parent:
1580
 
                    raise AssertionError()
1581
 
            except KeyError:
1582
 
                this_parent = other_parent = \
1583
 
                    self.tt.final_file_id(self.tt.final_parent(trans_id))
1584
 
            try:
1585
 
                this_name, other_name = conflicts['name conflict']
1586
 
                if this_name == other_name:
1587
 
                    raise AssertionError()
1588
 
            except KeyError:
1589
 
                this_name = other_name = self.tt.final_name(trans_id)
1590
 
            other_path = fp.get_path(trans_id)
1591
 
            if this_parent is not None and this_name is not None:
1592
 
                this_parent_path = \
1593
 
                    fp.get_path(self.tt.trans_id_file_id(this_parent))
1594
 
                this_path = osutils.pathjoin(this_parent_path, this_name)
1595
1579
            else:
1596
 
                this_path = "<deleted>"
1597
 
            file_id = self.tt.final_file_id(trans_id)
1598
 
            c = _mod_conflicts.Conflict.factory('path conflict', path=this_path,
1599
 
                                                conflict_path=other_path,
1600
 
                                                file_id=file_id)
 
1580
                raise AssertionError('bad conflict type: %r' % (conflict,))
1601
1581
            self.cooked_conflicts.append(c)
1602
1582
        self.cooked_conflicts.sort(key=_mod_conflicts.Conflict.sort_key)
1603
1583
 
1718
1698
                other_rev_id=None,
1719
1699
                interesting_files=None,
1720
1700
                this_tree=None,
1721
 
                pb=progress.DummyProgress(),
 
1701
                pb=None,
1722
1702
                change_reporter=None):
1723
1703
    """Primary interface for merging.
1724
1704