/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: John Arbash Meinel
  • Date: 2008-06-05 16:27:16 UTC
  • mfrom: (3475 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3476.
  • Revision ID: john@arbash-meinel.com-20080605162716-a3hn238tnctbfd8j
merge bzr.dev, resolve NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
68
68
                 this_tree=None, pb=DummyProgress(), change_reporter=None,
69
69
                 recurse='down', revision_graph=None):
70
70
        object.__init__(self)
71
 
        assert this_tree is not None, "this_tree is required"
72
71
        self.this_branch = this_branch
73
72
        self.this_basis = _mod_revision.ensure_null(
74
73
            this_branch.last_revision())
116
115
 
117
116
    def _get_base_is_other_ancestor(self):
118
117
        if self._base_is_other_ancestor is None:
119
 
            self.base_is_other_ancestor = self.revision_graph.is_ancestor(
 
118
            if self.other_basis is None:
 
119
                return True
 
120
            self._base_is_other_ancestor = self.revision_graph.is_ancestor(
120
121
                self.base_rev_id, self.other_basis)
121
122
        return self._base_is_other_ancestor
122
123
 
150
151
        base_revision_id, other_revision_id, verified =\
151
152
            mergeable.get_merge_request(tree.branch.repository)
152
153
        revision_graph = tree.branch.repository.get_graph()
153
 
        if (base_revision_id != _mod_revision.NULL_REVISION and
154
 
            revision_graph.is_ancestor(
155
 
            base_revision_id, tree.branch.last_revision())):
156
 
            base_revision_id = None
157
 
        else:
158
 
            warning('Performing cherrypick')
 
154
        if base_revision_id is not None:
 
155
            if (base_revision_id != _mod_revision.NULL_REVISION and
 
156
                revision_graph.is_ancestor(
 
157
                base_revision_id, tree.branch.last_revision())):
 
158
                base_revision_id = None
 
159
            else:
 
160
                warning('Performing cherrypick')
159
161
        merger = klass.from_revision_ids(pb, tree, other_revision_id,
160
162
                                         base_revision_id, revision_graph=
161
163
                                         revision_graph)
162
164
        return merger, verified
163
165
 
164
166
    @staticmethod
165
 
    def from_revision_ids(pb, this, other, base=None, other_branch=None,
 
167
    def from_revision_ids(pb, tree, other, base=None, other_branch=None,
166
168
                          base_branch=None, revision_graph=None):
167
169
        """Return a Merger for revision-ids.
168
170
 
171
173
        :param base: The revision-id to use as BASE.  If not specified, will
172
174
            be auto-selected.
173
175
        :param other_branch: A branch containing the other revision-id.  If
174
 
            not supplied, this.branch is used.
 
176
            not supplied, tree.branch is used.
175
177
        :param base_branch: A branch containing the base revision-id.  If
176
 
            not supplied, other_branch or this.branch will be used.
 
178
            not supplied, other_branch or tree.branch will be used.
 
179
        :param revision_graph: If you have a revision_graph precomputed, pass
 
180
            it in, otherwise it will be created for you.
177
181
        :param pb: A progress indicator
178
182
        """
179
 
        merger = Merger(this.branch, this_tree=this, pb=pb,
 
183
        merger = Merger(tree.branch, this_tree=tree, pb=pb,
180
184
                        revision_graph=revision_graph)
181
185
        if other_branch is None:
182
 
            other_branch = this.branch
 
186
            other_branch = tree.branch
183
187
        merger.set_other_revision(other, other_branch)
184
188
        if base is None:
185
189
            merger.find_base()
232
236
        self.ensure_revision_trees()
233
237
        def get_id(tree, file_id):
234
238
            revision_id = tree.inventory[file_id].revision
235
 
            assert revision_id is not None
236
239
            return revision_id
237
240
        if self.this_rev_id is None:
238
241
            if self.this_basis_tree.get_file_sha1(file_id) != \
399
402
        if (not getattr(self.merge_type, 'supports_reverse_cherrypick', True)
400
403
            and not self.base_is_other_ancestor):
401
404
            raise errors.CannotReverseCherrypick()
402
 
        if self.merge_type.history_based:
 
405
        if self.merge_type.supports_cherrypick:
403
406
            kwargs['cherrypick'] = (not self.base_is_ancestor or
404
407
                                    not self.base_is_other_ancestor)
405
408
        return self.merge_type(pb=self._pb,
407
410
                               **kwargs)
408
411
 
409
412
    def do_merge(self):
410
 
        merge = self.make_merger()
411
413
        self.this_tree.lock_tree_write()
412
414
        if self.base_tree is not None:
413
415
            self.base_tree.lock_read()
414
416
        if self.other_tree is not None:
415
417
            self.other_tree.lock_read()
416
418
        try:
 
419
            merge = self.make_merger()
417
420
            merge.do_merge()
418
421
            if self.recurse == 'down':
419
422
                for path, file_id in self.this_tree.iter_references():
430
433
                    base_revision = self.base_tree.get_reference_revision(file_id)
431
434
                    sub_merge.base_tree = \
432
435
                        sub_tree.branch.repository.revision_tree(base_revision)
 
436
                    sub_merge.base_rev_id = base_revision
433
437
                    sub_merge.do_merge()
434
438
 
435
439
        finally:
453
457
    supports_reprocess = True
454
458
    supports_show_base = True
455
459
    history_based = False
 
460
    supports_cherrypick = True
456
461
    supports_reverse_cherrypick = True
457
462
    winner_idx = {"this": 2, "other": 1, "conflict": 1}
458
463
 
459
464
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
460
465
                 interesting_ids=None, reprocess=False, show_base=False,
461
466
                 pb=DummyProgress(), pp=None, change_reporter=None,
462
 
                 interesting_files=None, do_merge=True):
 
467
                 interesting_files=None, do_merge=True,
 
468
                 cherrypick=False):
463
469
        """Initialize the merger object and perform the merge.
464
470
 
465
471
        :param working_tree: The working tree to apply the merge to
483
489
            merge.
484
490
        """
485
491
        object.__init__(self)
486
 
        if interesting_files is not None:
487
 
            assert interesting_ids is None
 
492
        if interesting_files is not None and interesting_ids is not None:
 
493
            raise ValueError(
 
494
                'specify either interesting_ids or interesting_files')
488
495
        self.interesting_ids = interesting_ids
489
496
        self.interesting_files = interesting_files
490
497
        self.this_tree = working_tree
497
504
        self.pb = pb
498
505
        self.pp = pp
499
506
        self.change_reporter = change_reporter
 
507
        self.cherrypick = cherrypick
500
508
        if self.pp is None:
501
509
            self.pp = ProgressPhase("Merge phase", 3, self.pb)
502
510
        if do_merge:
565
573
        if self.change_reporter is not None:
566
574
            from bzrlib import delta
567
575
            delta.report_changes(
568
 
                self.tt._iter_changes(), self.change_reporter)
 
576
                self.tt.iter_changes(), self.change_reporter)
569
577
        self.cook_conflicts(fs_conflicts)
570
578
        for conflict in self.cooked_conflicts:
571
579
            warning(conflict)
580
588
        executable3 is a tuple of execute-bit values for base, other and this.
581
589
        """
582
590
        result = []
583
 
        iterator = self.other_tree._iter_changes(self.base_tree,
 
591
        iterator = self.other_tree.iter_changes(self.base_tree,
584
592
                include_unchanged=True, specific_files=self.interesting_files,
585
593
                extra_trees=[self.this_tree])
586
594
        for (file_id, paths, changed, versioned, parents, names, kind,
704
712
        if key_base == key_other:
705
713
            return "this"
706
714
        key_this = key(this_tree, file_id)
707
 
        if key_this not in (key_base, key_other):
 
715
        # "Ambiguous clean merge"
 
716
        if key_this == key_other:
 
717
            return "this"
 
718
        elif key_this == key_base:
 
719
            return "other"
 
720
        else:
708
721
            return "conflict"
709
 
        # "Ambiguous clean merge"
710
 
        elif key_this == key_other:
711
 
            return "this"
712
 
        else:
713
 
            assert key_this == key_base
714
 
            return "other"
715
722
 
716
723
    def merge_names(self, file_id):
717
724
        def get_entry(tree):
865
872
            base_lines = []
866
873
        other_lines = self.get_lines(self.other_tree, file_id)
867
874
        this_lines = self.get_lines(self.this_tree, file_id)
868
 
        m3 = Merge3(base_lines, this_lines, other_lines)
 
875
        m3 = Merge3(base_lines, this_lines, other_lines,
 
876
                    is_cherrypick=self.cherrypick)
869
877
        start_marker = "!START OF MERGE CONFLICT!" + "I HOPE THIS IS UNIQUE"
870
878
        if self.show_base is True:
871
879
            base_marker = '|' * 7
941
949
        base_executable, other_executable, this_executable = executable
942
950
        if file_status == "deleted":
943
951
            return
944
 
        trans_id = self.tt.trans_id_file_id(file_id)
945
 
        try:
946
 
            if self.tt.final_kind(trans_id) != "file":
947
 
                return
948
 
        except NoSuchFile:
949
 
            return
950
952
        winner = self._three_way(*executable)
951
953
        if winner == "conflict":
952
954
        # There must be a None in here, if we have a conflict, but we
955
957
                winner = "this"
956
958
            else:
957
959
                winner = "other"
 
960
        if winner == 'this' and file_status != "modified":
 
961
            return
 
962
        trans_id = self.tt.trans_id_file_id(file_id)
 
963
        try:
 
964
            if self.tt.final_kind(trans_id) != "file":
 
965
                return
 
966
        except NoSuchFile:
 
967
            return
958
968
        if winner == "this":
959
 
            if file_status == "modified":
960
 
                executability = this_executable
961
 
                if executability is not None:
962
 
                    trans_id = self.tt.trans_id_file_id(file_id)
963
 
                    self.tt.set_executability(executability, trans_id)
 
969
            executability = this_executable
964
970
        else:
965
 
            assert winner == "other"
966
971
            if file_id in self.other_tree:
967
972
                executability = other_executable
968
973
            elif file_id in self.this_tree:
969
974
                executability = this_executable
970
975
            elif file_id in self.base_tree:
971
976
                executability = base_executable
972
 
            if executability is not None:
973
 
                trans_id = self.tt.trans_id_file_id(file_id)
974
 
                self.tt.set_executability(executability, trans_id)
 
977
        if executability is not None:
 
978
            trans_id = self.tt.trans_id_file_id(file_id)
 
979
            self.tt.set_executability(executability, trans_id)
975
980
 
976
981
    def cook_conflicts(self, fs_conflicts):
977
982
        """Convert all conflicts into a form that doesn't depend on trans_id"""
1010
1015
        for trans_id, conflicts in name_conflicts.iteritems():
1011
1016
            try:
1012
1017
                this_parent, other_parent = conflicts['parent conflict']
1013
 
                assert this_parent != other_parent
 
1018
                if this_parent == other_parent:
 
1019
                    raise AssertionError()
1014
1020
            except KeyError:
1015
1021
                this_parent = other_parent = \
1016
1022
                    self.tt.final_file_id(self.tt.final_parent(trans_id))
1017
1023
            try:
1018
1024
                this_name, other_name = conflicts['name conflict']
1019
 
                assert this_name != other_name
 
1025
                if this_name == other_name:
 
1026
                    raise AssertionError()
1020
1027
            except KeyError:
1021
1028
                this_name = other_name = self.tt.final_name(trans_id)
1022
1029
            other_path = fp.get_path(trans_id)
1040
1047
    supports_reverse_cherrypick = False
1041
1048
    history_based = True
1042
1049
 
1043
 
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
1044
 
                 interesting_ids=None, pb=DummyProgress(), pp=None,
1045
 
                 reprocess=False, change_reporter=None,
1046
 
                 interesting_files=None, cherrypick=False, do_merge=True):
1047
 
        self.cherrypick = cherrypick
1048
 
        super(WeaveMerger, self).__init__(working_tree, this_tree, 
1049
 
                                          base_tree, other_tree, 
1050
 
                                          interesting_files=interesting_files,
1051
 
                                          interesting_ids=interesting_ids, 
1052
 
                                          pb=pb, pp=pp, reprocess=reprocess,
1053
 
                                          change_reporter=change_reporter,
1054
 
                                          do_merge=do_merge)
1055
 
 
1056
1050
    def _merged_lines(self, file_id):
1057
1051
        """Generate the merged lines.
1058
1052
        There is no distinction between lines that are meant to contain <<<<<<<
1188
1182
    merger.interesting_ids = interesting_ids
1189
1183
    merger.ignore_zero = ignore_zero
1190
1184
    if interesting_files:
1191
 
        assert not interesting_ids, ('Only supply interesting_ids'
1192
 
                                     ' or interesting_files')
 
1185
        if interesting_ids:
 
1186
            raise ValueError('Only supply interesting_ids'
 
1187
                             ' or interesting_files')
1193
1188
        merger.interesting_files = interesting_files
1194
1189
    merger.show_base = show_base
1195
1190
    merger.reprocess = reprocess
1196
1191
    merger.other_rev_id = other_rev_id
1197
1192
    merger.other_basis = other_rev_id
 
1193
    get_revision_id = getattr(base_tree, 'get_revision_id', None)
 
1194
    if get_revision_id is None:
 
1195
        get_revision_id = base_tree.last_revision
 
1196
    merger.set_base_revision(get_revision_id(), this_branch)
1198
1197
    return merger.do_merge()
1199
1198
 
1200
1199
def get_merge_type_registry():
1233
1232
            yield status_a(revision, text)
1234
1233
        for revision, text in annotated_b[b_cur:bi]:
1235
1234
            yield status_b(revision, text)
1236
 
 
1237
1235
        # and now the matched section
1238
1236
        a_cur = ai + l
1239
1237
        b_cur = bi + l
1240
 
        for text_a, text_b in zip(plain_a[ai:a_cur], plain_b[bi:b_cur]):
1241
 
            assert text_a == text_b
 
1238
        for text_a in plain_a[ai:a_cur]:
1242
1239
            yield "unchanged", text_a
1243
1240
 
1244
1241
 
1403
1400
        """
1404
1401
        if version_id not in self.uncommon:
1405
1402
            return set()
1406
 
        parents = self.vf.get_parents(version_id)
 
1403
        parents = self.vf.get_parent_map([version_id])[version_id]
1407
1404
        if len(parents) == 0:
1408
1405
            return set(range(len(self.vf.get_lines(version_id))))
1409
1406
        new = None
1437
1434
        _PlanMergeBase.__init__(self, a_rev, b_rev, vf)
1438
1435
        self.lcas = graph.find_lca(a_rev, b_rev)
1439
1436
        for lca in self.lcas:
1440
 
            lca_lines = self.vf.get_lines(lca)
 
1437
            if _mod_revision.is_null(lca):
 
1438
                lca_lines = []
 
1439
            else:
 
1440
                lca_lines = self.vf.get_lines(lca)
1441
1441
            matcher = patiencediff.PatienceSequenceMatcher(None, self.lines_a,
1442
1442
                                                           lca_lines)
1443
1443
            blocks = list(matcher.get_matching_blocks())