~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Martin Pool
  • Date: 2010-02-09 20:18:32 UTC
  • mto: This revision was merged to the branch mainline in revision 5063.
  • Revision ID: mbp@canonical.com-20100209201832-6e6495auzbg39gxl
Remove old, slow and messy TestHttpFetch tests.
  
These were over-specific to fetching over http, and deal only with a
now-obsolete format.
  
They also fail with bzr-svn because it does its own http probes using OPTIONS.

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
18
17
 
19
18
from bzrlib import (
20
19
    branch as _mod_branch,
27
26
    merge3,
28
27
    osutils,
29
28
    patiencediff,
 
29
    progress,
30
30
    revision as _mod_revision,
31
31
    textfile,
32
32
    trace,
36
36
    ui,
37
37
    versionedfile
38
38
    )
39
 
from bzrlib.cleanup import OperationWithCleanups
40
39
from bzrlib.symbol_versioning import (
41
40
    deprecated_in,
42
41
    deprecated_method,
46
45
 
47
46
def transform_tree(from_tree, to_tree, interesting_ids=None):
48
47
    from_tree.lock_tree_write()
49
 
    operation = OperationWithCleanups(merge_inner)
50
 
    operation.add_cleanup(from_tree.unlock)
51
 
    operation.run_simple(from_tree.branch, to_tree, from_tree,
52
 
        ignore_zero=True, interesting_ids=interesting_ids, this_tree=from_tree)
 
48
    try:
 
49
        merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
 
50
                    interesting_ids=interesting_ids, this_tree=from_tree)
 
51
    finally:
 
52
        from_tree.unlock()
53
53
 
54
54
 
55
55
class MergeHooks(hooks.Hooks):
93
93
        return ('not applicable', None)
94
94
 
95
95
 
96
 
class PerFileMerger(AbstractPerFileMerger):
97
 
    """Merge individual files when self.file_matches returns True.
98
 
 
99
 
    This class is intended to be subclassed.  The file_matches and
100
 
    merge_matching methods should be overridden with concrete implementations.
101
 
    """
102
 
 
103
 
    def file_matches(self, params):
104
 
        """Return True if merge_matching should be called on this file.
105
 
 
106
 
        Only called with merges of plain files with no clear winner.
107
 
 
108
 
        Subclasses must override this.
109
 
        """
110
 
        raise NotImplementedError(self.file_matches)
111
 
 
112
 
    def get_filename(self, params, tree):
113
 
        """Lookup the filename (i.e. basename, not path), given a Tree (e.g.
114
 
        self.merger.this_tree) and a MergeHookParams.
115
 
        """
116
 
        return osutils.basename(tree.id2path(params.file_id))
117
 
 
118
 
    def get_filepath(self, params, tree):
119
 
        """Calculate the path to the file in a tree.
120
 
 
121
 
        :param params: A MergeHookParams describing the file to merge
122
 
        :param tree: a Tree, e.g. self.merger.this_tree.
123
 
        """
124
 
        return tree.id2path(params.file_id)
125
 
 
126
 
    def merge_contents(self, params):
127
 
        """Merge the contents of a single file."""
128
 
        # Check whether this custom merge logic should be used.
129
 
        if (
130
 
            # OTHER is a straight winner, rely on default merge.
131
 
            params.winner == 'other' or
132
 
            # THIS and OTHER aren't both files.
133
 
            not params.is_file_merge() or
134
 
            # The filename doesn't match *.xml
135
 
            not self.file_matches(params)):
136
 
            return 'not_applicable', None
137
 
        return self.merge_matching(params)
138
 
 
139
 
    def merge_matching(self, params):
140
 
        """Merge the contents of a single file that has matched the criteria
141
 
        in PerFileMerger.merge_contents (is a conflict, is a file,
142
 
        self.file_matches is True).
143
 
 
144
 
        Subclasses must override this.
145
 
        """
146
 
        raise NotImplementedError(self.merge_matching)
147
 
 
148
 
 
149
 
class ConfigurableFileMerger(PerFileMerger):
 
96
class ConfigurableFileMerger(AbstractPerFileMerger):
150
97
    """Merge individual files when configured via a .conf file.
151
98
 
152
99
    This is a base class for concrete custom file merging logic. Concrete
175
122
        if self.name_prefix is None:
176
123
            raise ValueError("name_prefix must be set.")
177
124
 
178
 
    def file_matches(self, params):
 
125
    def filename_matches_config(self, params):
179
126
        """Check whether the file should call the merge hook.
180
127
 
181
128
        <name_prefix>_merge_files configuration variable is a list of files
183
130
        """
184
131
        affected_files = self.affected_files
185
132
        if affected_files is None:
186
 
            config = self.merger.this_branch.get_config()
 
133
            config = self.merger.this_tree.branch.get_config()
187
134
            # Until bzr provides a better policy for caching the config, we
188
135
            # just add the part we're interested in to the params to avoid
189
136
            # reading the config files repeatedly (bazaar.conf, location.conf,
195
142
                affected_files = self.default_files
196
143
            self.affected_files = affected_files
197
144
        if affected_files:
198
 
            filepath = self.get_filepath(params, self.merger.this_tree)
199
 
            if filepath in affected_files:
 
145
            filename = self.merger.this_tree.id2path(params.file_id)
 
146
            if filename in affected_files:
200
147
                return True
201
148
        return False
202
149
 
203
 
    def merge_matching(self, params):
 
150
    def merge_contents(self, params):
 
151
        """Merge the contents of a single file."""
 
152
        # First, check whether this custom merge logic should be used.  We
 
153
        # expect most files should not be merged by this handler.
 
154
        if (
 
155
            # OTHER is a straight winner, rely on default merge.
 
156
            params.winner == 'other' or
 
157
            # THIS and OTHER aren't both files.
 
158
            not params.is_file_merge() or
 
159
            # The filename isn't listed in the 'NAME_merge_files' config
 
160
            # option.
 
161
            not self.filename_matches_config(params)):
 
162
            return 'not_applicable', None
204
163
        return self.merge_text(params)
205
164
 
206
165
    def merge_text(self, params):
278
237
        self.interesting_files = None
279
238
        self.show_base = False
280
239
        self.reprocess = False
281
 
        if pb is not None:
282
 
            warnings.warn("pb parameter to Merger() is deprecated and ignored")
 
240
        if pb is None:
 
241
            pb = progress.DummyProgress()
 
242
        self._pb = pb
283
243
        self.pp = None
284
244
        self.recurse = recurse
285
245
        self.change_reporter = change_reporter
495
455
    def _add_parent(self):
496
456
        new_parents = self.this_tree.get_parent_ids() + [self.other_rev_id]
497
457
        new_parent_trees = []
498
 
        operation = OperationWithCleanups(self.this_tree.set_parent_trees)
499
458
        for revision_id in new_parents:
500
459
            try:
501
460
                tree = self.revision_tree(revision_id)
503
462
                tree = None
504
463
            else:
505
464
                tree.lock_read()
506
 
                operation.add_cleanup(tree.unlock)
507
465
            new_parent_trees.append((revision_id, tree))
508
 
        operation.run_simple(new_parent_trees, allow_leftmost_as_ghost=True)
 
466
        try:
 
467
            self.this_tree.set_parent_trees(new_parent_trees,
 
468
                                            allow_leftmost_as_ghost=True)
 
469
        finally:
 
470
            for _revision_id, tree in new_parent_trees:
 
471
                if tree is not None:
 
472
                    tree.unlock()
509
473
 
510
474
    def set_other(self, other_revision, possible_transports=None):
511
475
        """Set the revision and tree to merge from.
634
598
                  'other_tree': self.other_tree,
635
599
                  'interesting_ids': self.interesting_ids,
636
600
                  'interesting_files': self.interesting_files,
637
 
                  'this_branch': self.this_branch,
 
601
                  'pp': self.pp, 'this_branch': self.this_branch,
638
602
                  'do_merge': False}
639
603
        if self.merge_type.requires_base:
640
604
            kwargs['base_tree'] = self.base_tree
658
622
        if self._is_criss_cross and getattr(self.merge_type,
659
623
                                            'supports_lca_trees', False):
660
624
            kwargs['lca_trees'] = self._lca_trees
661
 
        return self.merge_type(pb=None,
 
625
        return self.merge_type(pb=self._pb,
662
626
                               change_reporter=self.change_reporter,
663
627
                               **kwargs)
664
628
 
665
 
    def _do_merge_to(self):
666
 
        merge = self.make_merger()
 
629
    def _do_merge_to(self, merge):
667
630
        if self.other_branch is not None:
668
631
            self.other_branch.update_references(self.this_branch)
669
632
        merge.do_merge()
683
646
                    sub_tree.branch.repository.revision_tree(base_revision)
684
647
                sub_merge.base_rev_id = base_revision
685
648
                sub_merge.do_merge()
686
 
        return merge
687
649
 
688
650
    def do_merge(self):
689
 
        operation = OperationWithCleanups(self._do_merge_to)
690
651
        self.this_tree.lock_tree_write()
691
 
        operation.add_cleanup(self.this_tree.unlock)
692
 
        if self.base_tree is not None:
693
 
            self.base_tree.lock_read()
694
 
            operation.add_cleanup(self.base_tree.unlock)
695
 
        if self.other_tree is not None:
696
 
            self.other_tree.lock_read()
697
 
            operation.add_cleanup(self.other_tree.unlock)
698
 
        merge = operation.run_simple()
 
652
        try:
 
653
            if self.base_tree is not None:
 
654
                self.base_tree.lock_read()
 
655
            try:
 
656
                if self.other_tree is not None:
 
657
                    self.other_tree.lock_read()
 
658
                try:
 
659
                    merge = self.make_merger()
 
660
                    self._do_merge_to(merge)
 
661
                finally:
 
662
                    if self.other_tree is not None:
 
663
                        self.other_tree.unlock()
 
664
            finally:
 
665
                if self.base_tree is not None:
 
666
                    self.base_tree.unlock()
 
667
        finally:
 
668
            self.this_tree.unlock()
699
669
        if len(merge.cooked_conflicts) == 0:
700
670
            if not self.ignore_zero and not trace.is_quiet():
701
671
                trace.note("All changes applied successfully.")
736
706
 
737
707
    def __init__(self, working_tree, this_tree, base_tree, other_tree,
738
708
                 interesting_ids=None, reprocess=False, show_base=False,
739
 
                 pb=None, pp=None, change_reporter=None,
 
709
                 pb=progress.DummyProgress(), pp=None, change_reporter=None,
740
710
                 interesting_files=None, do_merge=True,
741
711
                 cherrypick=False, lca_trees=None, this_branch=None):
742
712
        """Initialize the merger object and perform the merge.
745
715
        :param this_tree: The local tree in the merge operation
746
716
        :param base_tree: The common tree in the merge operation
747
717
        :param other_tree: The other tree to merge changes from
748
 
        :param this_branch: The branch associated with this_tree.  Defaults to
749
 
            this_tree.branch if not supplied.
 
718
        :param this_branch: The branch associated with this_tree
750
719
        :param interesting_ids: The file_ids of files that should be
751
720
            participate in the merge.  May not be combined with
752
721
            interesting_files.
753
722
        :param: reprocess If True, perform conflict-reduction processing.
754
723
        :param show_base: If True, show the base revision in text conflicts.
755
724
            (incompatible with reprocess)
756
 
        :param pb: ignored
 
725
        :param pb: A Progress bar
757
726
        :param pp: A ProgressPhase object
758
727
        :param change_reporter: An object that should report changes made
759
728
        :param interesting_files: The tree-relative paths of files that should
770
739
        if interesting_files is not None and interesting_ids is not None:
771
740
            raise ValueError(
772
741
                'specify either interesting_ids or interesting_files')
773
 
        if this_branch is None:
774
 
            this_branch = this_tree.branch
775
742
        self.interesting_ids = interesting_ids
776
743
        self.interesting_files = interesting_files
777
744
        self.this_tree = working_tree
788
755
        # making sure we haven't missed any corner cases.
789
756
        # if lca_trees is None:
790
757
        #     self._lca_trees = [self.base_tree]
 
758
        self.pb = pb
 
759
        self.pp = pp
791
760
        self.change_reporter = change_reporter
792
761
        self.cherrypick = cherrypick
 
762
        if self.pp is None:
 
763
            self.pp = progress.ProgressPhase("Merge phase", 3, self.pb)
793
764
        if do_merge:
794
765
            self.do_merge()
795
 
        if pp is not None:
796
 
            warnings.warn("pp argument to Merge3Merger is deprecated")
797
 
        if pb is not None:
798
 
            warnings.warn("pb argument to Merge3Merger is deprecated")
799
766
 
800
767
    def do_merge(self):
801
 
        operation = OperationWithCleanups(self._do_merge)
802
768
        self.this_tree.lock_tree_write()
803
 
        operation.add_cleanup(self.this_tree.unlock)
804
769
        self.base_tree.lock_read()
805
 
        operation.add_cleanup(self.base_tree.unlock)
806
770
        self.other_tree.lock_read()
807
 
        operation.add_cleanup(self.other_tree.unlock)
808
 
        operation.run()
809
 
 
810
 
    def _do_merge(self, operation):
811
 
        self.tt = transform.TreeTransform(self.this_tree, None)
812
 
        operation.add_cleanup(self.tt.finalize)
813
 
        self._compute_transform()
814
 
        results = self.tt.apply(no_conflicts=True)
815
 
        self.write_modified(results)
816
771
        try:
817
 
            self.this_tree.add_conflicts(self.cooked_conflicts)
818
 
        except errors.UnsupportedOperation:
819
 
            pass
 
772
            self.tt = transform.TreeTransform(self.this_tree, self.pb)
 
773
            try:
 
774
                self.pp.next_phase()
 
775
                self._compute_transform()
 
776
                self.pp.next_phase()
 
777
                results = self.tt.apply(no_conflicts=True)
 
778
                self.write_modified(results)
 
779
                try:
 
780
                    self.this_tree.add_conflicts(self.cooked_conflicts)
 
781
                except errors.UnsupportedOperation:
 
782
                    pass
 
783
            finally:
 
784
                self.tt.finalize()
 
785
        finally:
 
786
            self.other_tree.unlock()
 
787
            self.base_tree.unlock()
 
788
            self.this_tree.unlock()
 
789
            self.pb.clear()
820
790
 
821
791
    def make_preview_transform(self):
822
 
        operation = OperationWithCleanups(self._make_preview_transform)
823
792
        self.base_tree.lock_read()
824
 
        operation.add_cleanup(self.base_tree.unlock)
825
793
        self.other_tree.lock_read()
826
 
        operation.add_cleanup(self.other_tree.unlock)
827
 
        return operation.run_simple()
828
 
 
829
 
    def _make_preview_transform(self):
830
794
        self.tt = transform.TransformPreview(self.this_tree)
831
 
        self._compute_transform()
 
795
        try:
 
796
            self.pp.next_phase()
 
797
            self._compute_transform()
 
798
            self.pp.next_phase()
 
799
        finally:
 
800
            self.other_tree.unlock()
 
801
            self.base_tree.unlock()
 
802
            self.pb.clear()
832
803
        return self.tt
833
804
 
834
805
    def _compute_transform(self):
856
827
        finally:
857
828
            child_pb.finished()
858
829
        self.fix_root()
 
830
        self.pp.next_phase()
859
831
        child_pb = ui.ui_factory.nested_progress_bar()
860
832
        try:
861
833
            fs_conflicts = transform.resolve_conflicts(self.tt, child_pb,
1059
1031
                        continue
1060
1032
                else:
1061
1033
                    raise AssertionError('unhandled kind: %s' % other_ie.kind)
 
1034
                # XXX: We need to handle kind == 'symlink'
1062
1035
 
1063
1036
            # If we have gotten this far, that means something has changed
1064
1037
            result.append((file_id, content_changed,
1086
1059
        other_root = self.tt.trans_id_file_id(other_root_file_id)
1087
1060
        if other_root == self.tt.root:
1088
1061
            return
1089
 
        if self.other_tree.inventory.root.file_id in self.this_tree.inventory:
1090
 
            # the other tree's root is a non-root in the current tree (as when
1091
 
            # a previously unrelated branch is merged into another)
1092
 
            return
1093
1062
        try:
1094
1063
            self.tt.final_kind(other_root)
1095
 
            other_root_is_present = True
1096
1064
        except errors.NoSuchFile:
1097
 
            # other_root doesn't have a physical representation. We still need
1098
 
            # to move any references to the actual root of the tree.
1099
 
            other_root_is_present = False
1100
 
        # 'other_tree.inventory.root' is not present in this tree. We are
1101
 
        # calling adjust_path for children which *want* to be present with a
1102
 
        # correct place to go.
1103
 
        for thing, child in self.other_tree.inventory.root.children.iteritems():
 
1065
            return
 
1066
        if self.this_tree.has_id(self.other_tree.inventory.root.file_id):
 
1067
            # the other tree's root is a non-root in the current tree
 
1068
            return
 
1069
        self.reparent_children(self.other_tree.inventory.root, self.tt.root)
 
1070
        self.tt.cancel_creation(other_root)
 
1071
        self.tt.cancel_versioning(other_root)
 
1072
 
 
1073
    def reparent_children(self, ie, target):
 
1074
        for thing, child in ie.children.iteritems():
1104
1075
            trans_id = self.tt.trans_id_file_id(child.file_id)
1105
 
            if not other_root_is_present:
1106
 
                # FIXME: Make final_kind returns None instead of raising
1107
 
                # NoSuchFile to avoid the ugly construct below -- vila 20100402
1108
 
                try:
1109
 
                    self.tt.final_kind(trans_id)
1110
 
                    # The item exist in the final tree and has a defined place
1111
 
                    # to go already.
1112
 
                    continue
1113
 
                except errors.NoSuchFile, e:
1114
 
                    pass
1115
 
            # Move the item into the root
1116
 
            self.tt.adjust_path(self.tt.final_name(trans_id),
1117
 
                                self.tt.root, trans_id)
1118
 
        if other_root_is_present:
1119
 
            self.tt.cancel_creation(other_root)
1120
 
            self.tt.cancel_versioning(other_root)
 
1076
            self.tt.adjust_path(self.tt.final_name(trans_id), target, trans_id)
1121
1077
 
1122
1078
    def write_modified(self, results):
1123
1079
        modified_hashes = {}
1170
1126
 
1171
1127
    @staticmethod
1172
1128
    def _three_way(base, other, this):
 
1129
        #if base == other, either they all agree, or only THIS has changed.
1173
1130
        if base == other:
1174
 
            # if 'base == other', either they all agree, or only 'this' has
1175
 
            # changed.
1176
1131
            return 'this'
1177
1132
        elif this not in (base, other):
1178
 
            # 'this' is neither 'base' nor 'other', so both sides changed
1179
1133
            return 'conflict'
 
1134
        # "Ambiguous clean merge" -- both sides have made the same change.
1180
1135
        elif this == other:
1181
 
            # "Ambiguous clean merge" -- both sides have made the same change.
1182
1136
            return "this"
 
1137
        # this == base: only other has changed.
1183
1138
        else:
1184
 
            # this == base: only other has changed.
1185
1139
            return "other"
1186
1140
 
1187
1141
    @staticmethod
1231
1185
                # only has an lca value
1232
1186
                return 'other'
1233
1187
 
1234
 
        # At this point, the lcas disagree, and the tip disagree
 
1188
        # At this point, the lcas disagree, and the tips disagree
1235
1189
        return 'conflict'
1236
1190
 
1237
1191
    @staticmethod
1238
 
    @deprecated_method(deprecated_in((2, 2, 0)))
1239
1192
    def scalar_three_way(this_tree, base_tree, other_tree, file_id, key):
1240
1193
        """Do a three-way test on a scalar.
1241
1194
        Return "this", "other" or "conflict", depending whether a value wins.
1291
1244
                parent_id_winner = "other"
1292
1245
        if name_winner == "this" and parent_id_winner == "this":
1293
1246
            return
1294
 
        if name_winner == 'conflict' or parent_id_winner == 'conflict':
1295
 
            # Creating helpers (.OTHER or .THIS) here cause problems down the
1296
 
            # road if a ContentConflict needs to be created so we should not do
1297
 
            # that
1298
 
            trans_id = self.tt.trans_id_file_id(file_id)
1299
 
            self._raw_conflicts.append(('path conflict', trans_id, file_id,
1300
 
                                        this_parent, this_name,
1301
 
                                        other_parent, other_name))
 
1247
        if name_winner == "conflict":
 
1248
            trans_id = self.tt.trans_id_file_id(file_id)
 
1249
            self._raw_conflicts.append(('name conflict', trans_id,
 
1250
                                        this_name, other_name))
 
1251
        if parent_id_winner == "conflict":
 
1252
            trans_id = self.tt.trans_id_file_id(file_id)
 
1253
            self._raw_conflicts.append(('parent conflict', trans_id,
 
1254
                                        this_parent, other_parent))
1302
1255
        if other_name is None:
1303
1256
            # it doesn't matter whether the result was 'other' or
1304
1257
            # 'conflict'-- if there's no 'other', we leave it alone.
1305
1258
            return
 
1259
        # if we get here, name_winner and parent_winner are set to safe values.
 
1260
        trans_id = self.tt.trans_id_file_id(file_id)
1306
1261
        parent_id = parents[self.winner_idx[parent_id_winner]]
1307
1262
        if parent_id is not None:
1308
 
            # if we get here, name_winner and parent_winner are set to safe
1309
 
            # values.
 
1263
            parent_trans_id = self.tt.trans_id_file_id(parent_id)
1310
1264
            self.tt.adjust_path(names[self.winner_idx[name_winner]],
1311
 
                                self.tt.trans_id_file_id(parent_id),
1312
 
                                self.tt.trans_id_file_id(file_id))
 
1265
                                parent_trans_id, trans_id)
1313
1266
 
1314
1267
    def _do_merge_contents(self, file_id):
1315
1268
        """Performs a merge on file_id contents."""
1594
1547
 
1595
1548
    def cook_conflicts(self, fs_conflicts):
1596
1549
        """Convert all conflicts into a form that doesn't depend on trans_id"""
 
1550
        name_conflicts = {}
1597
1551
        self.cooked_conflicts.extend(transform.cook_conflicts(
1598
1552
                fs_conflicts, self.tt))
1599
1553
        fp = transform.FinalPaths(self.tt)
1600
1554
        for conflict in self._raw_conflicts:
1601
1555
            conflict_type = conflict[0]
1602
 
            if conflict_type == 'path conflict':
1603
 
                (trans_id, file_id,
1604
 
                this_parent, this_name,
1605
 
                other_parent, other_name) = conflict[1:]
1606
 
                if this_parent is None or this_name is None:
1607
 
                    this_path = '<deleted>'
1608
 
                else:
1609
 
                    parent_path =  fp.get_path(
1610
 
                        self.tt.trans_id_file_id(this_parent))
1611
 
                    this_path = osutils.pathjoin(parent_path, this_name)
1612
 
                if other_parent is None or other_name is None:
1613
 
                    other_path = '<deleted>'
1614
 
                else:
1615
 
                    parent_path =  fp.get_path(
1616
 
                        self.tt.trans_id_file_id(other_parent))
1617
 
                    other_path = osutils.pathjoin(parent_path, other_name)
1618
 
                c = _mod_conflicts.Conflict.factory(
1619
 
                    'path conflict', path=this_path,
1620
 
                    conflict_path=other_path,
1621
 
                    file_id=file_id)
1622
 
            elif conflict_type == 'contents conflict':
 
1556
            if conflict_type in ('name conflict', 'parent conflict'):
 
1557
                trans_id = conflict[1]
 
1558
                conflict_args = conflict[2:]
 
1559
                if trans_id not in name_conflicts:
 
1560
                    name_conflicts[trans_id] = {}
 
1561
                transform.unique_add(name_conflicts[trans_id], conflict_type,
 
1562
                                     conflict_args)
 
1563
            if conflict_type == 'contents conflict':
1623
1564
                for trans_id in conflict[1]:
1624
1565
                    file_id = self.tt.final_file_id(trans_id)
1625
1566
                    if file_id is not None:
1631
1572
                        break
1632
1573
                c = _mod_conflicts.Conflict.factory(conflict_type,
1633
1574
                                                    path=path, file_id=file_id)
1634
 
            elif conflict_type == 'text conflict':
 
1575
                self.cooked_conflicts.append(c)
 
1576
            if conflict_type == 'text conflict':
1635
1577
                trans_id = conflict[1]
1636
1578
                path = fp.get_path(trans_id)
1637
1579
                file_id = self.tt.final_file_id(trans_id)
1638
1580
                c = _mod_conflicts.Conflict.factory(conflict_type,
1639
1581
                                                    path=path, file_id=file_id)
 
1582
                self.cooked_conflicts.append(c)
 
1583
 
 
1584
        for trans_id, conflicts in name_conflicts.iteritems():
 
1585
            try:
 
1586
                this_parent, other_parent = conflicts['parent conflict']
 
1587
                if this_parent == other_parent:
 
1588
                    raise AssertionError()
 
1589
            except KeyError:
 
1590
                this_parent = other_parent = \
 
1591
                    self.tt.final_file_id(self.tt.final_parent(trans_id))
 
1592
            try:
 
1593
                this_name, other_name = conflicts['name conflict']
 
1594
                if this_name == other_name:
 
1595
                    raise AssertionError()
 
1596
            except KeyError:
 
1597
                this_name = other_name = self.tt.final_name(trans_id)
 
1598
            other_path = fp.get_path(trans_id)
 
1599
            if this_parent is not None and this_name is not None:
 
1600
                this_parent_path = \
 
1601
                    fp.get_path(self.tt.trans_id_file_id(this_parent))
 
1602
                this_path = osutils.pathjoin(this_parent_path, this_name)
1640
1603
            else:
1641
 
                raise AssertionError('bad conflict type: %r' % (conflict,))
 
1604
                this_path = "<deleted>"
 
1605
            file_id = self.tt.final_file_id(trans_id)
 
1606
            c = _mod_conflicts.Conflict.factory('path conflict', path=this_path,
 
1607
                                                conflict_path=other_path,
 
1608
                                                file_id=file_id)
1642
1609
            self.cooked_conflicts.append(c)
1643
1610
        self.cooked_conflicts.sort(key=_mod_conflicts.Conflict.sort_key)
1644
1611
 
1759
1726
                other_rev_id=None,
1760
1727
                interesting_files=None,
1761
1728
                this_tree=None,
1762
 
                pb=None,
 
1729
                pb=progress.DummyProgress(),
1763
1730
                change_reporter=None):
1764
1731
    """Primary interface for merging.
1765
1732