~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Danny van Heumen
  • Date: 2010-03-09 21:42:11 UTC
  • mto: (4634.139.5 2.0)
  • mto: This revision was merged to the branch mainline in revision 5160.
  • Revision ID: danny@dannyvanheumen.nl-20100309214211-iqh42x6qcikgd9p3
Reverted now-useless TODO list.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
 
18
18
import errno
23
23
from bzrlib import (
24
24
    debug,
25
25
    errors,
 
26
    graph as _mod_graph,
26
27
    osutils,
27
28
    patiencediff,
28
29
    registry,
29
30
    revision as _mod_revision,
 
31
    tree as _mod_tree,
 
32
    tsort,
30
33
    )
31
34
from bzrlib.branch import Branch
32
35
from bzrlib.conflicts import ConflictList, Conflict
52
55
from bzrlib.trace import mutter, warning, note, is_quiet
53
56
from bzrlib.transform import (TransformPreview, TreeTransform,
54
57
                              resolve_conflicts, cook_conflicts,
55
 
                              conflict_pass, FinalPaths, create_by_entry,
 
58
                              conflict_pass, FinalPaths, create_from_tree,
56
59
                              unique_add, ROOT_PARENT)
57
60
from bzrlib.versionedfile import PlanWeaveMerge
58
61
from bzrlib import ui
61
64
 
62
65
 
63
66
def transform_tree(from_tree, to_tree, interesting_ids=None):
64
 
    merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
65
 
                interesting_ids=interesting_ids, this_tree=from_tree)
 
67
    from_tree.lock_tree_write()
 
68
    try:
 
69
        merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
 
70
                    interesting_ids=interesting_ids, this_tree=from_tree)
 
71
    finally:
 
72
        from_tree.unlock()
66
73
 
67
74
 
68
75
class Merger(object):
69
76
    def __init__(self, this_branch, other_tree=None, base_tree=None,
70
 
                 this_tree=None, pb=DummyProgress(), change_reporter=None,
 
77
                 this_tree=None, pb=None, change_reporter=None,
71
78
                 recurse='down', revision_graph=None):
72
79
        object.__init__(self)
73
80
        self.this_branch = this_branch
86
93
        self.interesting_files = None
87
94
        self.show_base = False
88
95
        self.reprocess = False
 
96
        if pb is None:
 
97
            pb = DummyProgress()
89
98
        self._pb = pb
90
99
        self.pp = None
91
100
        self.recurse = recurse
94
103
        self._revision_graph = revision_graph
95
104
        self._base_is_ancestor = None
96
105
        self._base_is_other_ancestor = None
 
106
        self._is_criss_cross = None
 
107
        self._lca_trees = None
 
108
 
 
109
    def cache_trees_with_revision_ids(self, trees):
 
110
        """Cache any tree in trees if it has a revision_id."""
 
111
        for maybe_tree in trees:
 
112
            if maybe_tree is None:
 
113
                continue
 
114
            try:
 
115
                rev_id = maybe_tree.get_revision_id()
 
116
            except AttributeError:
 
117
                continue
 
118
            self._cached_trees[rev_id] = maybe_tree
97
119
 
98
120
    @property
99
121
    def revision_graph(self):
127
149
                                      _set_base_is_other_ancestor)
128
150
 
129
151
    @staticmethod
130
 
    def from_uncommitted(tree, other_tree, pb):
 
152
    def from_uncommitted(tree, other_tree, pb=None, base_tree=None):
131
153
        """Return a Merger for uncommitted changes in other_tree.
132
154
 
133
155
        :param tree: The tree to merge into
134
156
        :param other_tree: The tree to get uncommitted changes from
135
157
        :param pb: A progress indicator
 
158
        :param base_tree: The basis to use for the merge.  If unspecified,
 
159
            other_tree.basis_tree() will be used.
136
160
        """
137
 
        merger = Merger(tree.branch, other_tree, other_tree.basis_tree(), tree,
138
 
                        pb)
 
161
        if base_tree is None:
 
162
            base_tree = other_tree.basis_tree()
 
163
        merger = Merger(tree.branch, other_tree, base_tree, tree, pb)
139
164
        merger.base_rev_id = merger.base_tree.get_revision_id()
140
165
        merger.other_rev_id = None
141
166
        merger.other_basis = merger.base_rev_id
167
192
 
168
193
    @staticmethod
169
194
    def from_revision_ids(pb, tree, other, base=None, other_branch=None,
170
 
                          base_branch=None, revision_graph=None):
 
195
                          base_branch=None, revision_graph=None,
 
196
                          tree_branch=None):
171
197
        """Return a Merger for revision-ids.
172
198
 
 
199
        :param pb: A progress indicator
173
200
        :param tree: The tree to merge changes into
174
201
        :param other: The revision-id to use as OTHER
175
202
        :param base: The revision-id to use as BASE.  If not specified, will
180
207
            not supplied, other_branch or tree.branch will be used.
181
208
        :param revision_graph: If you have a revision_graph precomputed, pass
182
209
            it in, otherwise it will be created for you.
183
 
        :param pb: A progress indicator
 
210
        :param tree_branch: The branch associated with tree.  If not supplied,
 
211
            tree.branch will be used.
184
212
        """
185
 
        merger = Merger(tree.branch, this_tree=tree, pb=pb,
 
213
        if tree_branch is None:
 
214
            tree_branch = tree.branch
 
215
        merger = Merger(tree_branch, this_tree=tree, pb=pb,
186
216
                        revision_graph=revision_graph)
187
217
        if other_branch is None:
188
218
            other_branch = tree.branch
228
258
 
229
259
        if self.other_rev_id is None:
230
260
            other_basis_tree = self.revision_tree(self.other_basis)
231
 
            changes = other_basis_tree.changes_from(self.other_tree)
232
 
            if changes.has_changed():
 
261
            if other_basis_tree.has_changes(self.other_tree):
233
262
                raise WorkingTreeNotRevision(self.this_tree)
234
263
            other_rev_id = self.other_basis
235
264
            self.other_tree = other_basis_tree
261
290
            basis_tree = self.revision_tree(self.this_tree.last_revision())
262
291
        except errors.NoSuchRevision:
263
292
            basis_tree = self.this_tree.basis_tree()
264
 
        changes = self.this_tree.changes_from(basis_tree)
265
 
        if not changes.has_changed():
 
293
        if not self.this_tree.has_changes(basis_tree):
266
294
            self.this_rev_id = self.this_basis
267
295
 
268
296
    def set_interesting_files(self, file_list):
351
379
                     ensure_null(self.other_basis)]
352
380
        if NULL_REVISION in revisions:
353
381
            self.base_rev_id = NULL_REVISION
 
382
            self.base_tree = self.revision_tree(self.base_rev_id)
 
383
            self._is_criss_cross = False
354
384
        else:
355
 
            self.base_rev_id, steps = self.revision_graph.find_unique_lca(
356
 
                revisions[0], revisions[1], count_steps=True)
 
385
            lcas = self.revision_graph.find_lca(revisions[0], revisions[1])
 
386
            self._is_criss_cross = False
 
387
            if len(lcas) == 0:
 
388
                self.base_rev_id = NULL_REVISION
 
389
            elif len(lcas) == 1:
 
390
                self.base_rev_id = list(lcas)[0]
 
391
            else: # len(lcas) > 1
 
392
                if len(lcas) > 2:
 
393
                    # find_unique_lca can only handle 2 nodes, so we have to
 
394
                    # start back at the beginning. It is a shame to traverse
 
395
                    # the graph again, but better than re-implementing
 
396
                    # find_unique_lca.
 
397
                    self.base_rev_id = self.revision_graph.find_unique_lca(
 
398
                                            revisions[0], revisions[1])
 
399
                else:
 
400
                    self.base_rev_id = self.revision_graph.find_unique_lca(
 
401
                                            *lcas)
 
402
                self._is_criss_cross = True
357
403
            if self.base_rev_id == NULL_REVISION:
358
404
                raise UnrelatedBranches()
359
 
            if steps > 1:
 
405
            if self._is_criss_cross:
360
406
                warning('Warning: criss-cross merge encountered.  See bzr'
361
407
                        ' help criss-cross.')
362
 
        self.base_tree = self.revision_tree(self.base_rev_id)
 
408
                mutter('Criss-cross lcas: %r' % lcas)
 
409
                interesting_revision_ids = [self.base_rev_id]
 
410
                interesting_revision_ids.extend(lcas)
 
411
                interesting_trees = dict((t.get_revision_id(), t)
 
412
                    for t in self.this_branch.repository.revision_trees(
 
413
                        interesting_revision_ids))
 
414
                self._cached_trees.update(interesting_trees)
 
415
                self.base_tree = interesting_trees.pop(self.base_rev_id)
 
416
                sorted_lca_keys = self.revision_graph.find_merge_order(
 
417
                    revisions[0], lcas)
 
418
                self._lca_trees = [interesting_trees[key]
 
419
                                   for key in sorted_lca_keys]
 
420
            else:
 
421
                self.base_tree = self.revision_tree(self.base_rev_id)
363
422
        self.base_is_ancestor = True
364
423
        self.base_is_other_ancestor = True
 
424
        mutter('Base revid: %r' % self.base_rev_id)
365
425
 
366
426
    def set_base(self, base_revision):
367
427
        """Set the base revision to use for the merge.
407
467
        if self.merge_type.supports_cherrypick:
408
468
            kwargs['cherrypick'] = (not self.base_is_ancestor or
409
469
                                    not self.base_is_other_ancestor)
 
470
        if self._is_criss_cross and getattr(self.merge_type,
 
471
                                            'supports_lca_trees', False):
 
472
            kwargs['lca_trees'] = self._lca_trees
410
473
        return self.merge_type(pb=self._pb,
411
474
                               change_reporter=self.change_reporter,
412
475
                               **kwargs)
413
476
 
 
477
    def _do_merge_to(self, merge):
 
478
        if self.other_branch is not None:
 
479
            self.other_branch.update_references(self.this_branch)
 
480
        merge.do_merge()
 
481
        if self.recurse == 'down':
 
482
            for relpath, file_id in self.this_tree.iter_references():
 
483
                sub_tree = self.this_tree.get_nested_tree(file_id, relpath)
 
484
                other_revision = self.other_tree.get_reference_revision(
 
485
                    file_id, relpath)
 
486
                if  other_revision == sub_tree.last_revision():
 
487
                    continue
 
488
                sub_merge = Merger(sub_tree.branch, this_tree=sub_tree)
 
489
                sub_merge.merge_type = self.merge_type
 
490
                other_branch = self.other_branch.reference_parent(file_id, relpath)
 
491
                sub_merge.set_other_revision(other_revision, other_branch)
 
492
                base_revision = self.base_tree.get_reference_revision(file_id)
 
493
                sub_merge.base_tree = \
 
494
                    sub_tree.branch.repository.revision_tree(base_revision)
 
495
                sub_merge.base_rev_id = base_revision
 
496
                sub_merge.do_merge()
 
497
 
414
498
    def do_merge(self):
415
499
        self.this_tree.lock_tree_write()
416
 
        if self.base_tree is not None:
417
 
            self.base_tree.lock_read()
418
 
        if self.other_tree is not None:
419
 
            self.other_tree.lock_read()
420
500
        try:
421
 
            merge = self.make_merger()
422
 
            merge.do_merge()
423
 
            if self.recurse == 'down':
424
 
                for relpath, file_id in self.this_tree.iter_references():
425
 
                    sub_tree = self.this_tree.get_nested_tree(file_id, relpath)
426
 
                    other_revision = self.other_tree.get_reference_revision(
427
 
                        file_id, relpath)
428
 
                    if  other_revision == sub_tree.last_revision():
429
 
                        continue
430
 
                    sub_merge = Merger(sub_tree.branch, this_tree=sub_tree)
431
 
                    sub_merge.merge_type = self.merge_type
432
 
                    other_branch = self.other_branch.reference_parent(file_id, relpath)
433
 
                    sub_merge.set_other_revision(other_revision, other_branch)
434
 
                    base_revision = self.base_tree.get_reference_revision(file_id)
435
 
                    sub_merge.base_tree = \
436
 
                        sub_tree.branch.repository.revision_tree(base_revision)
437
 
                    sub_merge.base_rev_id = base_revision
438
 
                    sub_merge.do_merge()
439
 
 
440
 
        finally:
441
 
            if self.other_tree is not None:
442
 
                self.other_tree.unlock()
443
501
            if self.base_tree is not None:
444
 
                self.base_tree.unlock()
 
502
                self.base_tree.lock_read()
 
503
            try:
 
504
                if self.other_tree is not None:
 
505
                    self.other_tree.lock_read()
 
506
                try:
 
507
                    merge = self.make_merger()
 
508
                    self._do_merge_to(merge)
 
509
                finally:
 
510
                    if self.other_tree is not None:
 
511
                        self.other_tree.unlock()
 
512
            finally:
 
513
                if self.base_tree is not None:
 
514
                    self.base_tree.unlock()
 
515
        finally:
445
516
            self.this_tree.unlock()
446
517
        if len(merge.cooked_conflicts) == 0:
447
518
            if not self.ignore_zero and not is_quiet():
452
523
        return len(merge.cooked_conflicts)
453
524
 
454
525
 
 
526
class _InventoryNoneEntry(object):
 
527
    """This represents an inventory entry which *isn't there*.
 
528
 
 
529
    It simplifies the merging logic if we always have an InventoryEntry, even
 
530
    if it isn't actually present
 
531
    """
 
532
    executable = None
 
533
    kind = None
 
534
    name = None
 
535
    parent_id = None
 
536
    revision = None
 
537
    symlink_target = None
 
538
    text_sha1 = None
 
539
 
 
540
_none_entry = _InventoryNoneEntry()
 
541
 
 
542
 
455
543
class Merge3Merger(object):
456
544
    """Three-way merger that uses the merge3 text merger"""
457
545
    requires_base = True
461
549
    supports_cherrypick = True
462
550
    supports_reverse_cherrypick = True
463
551
    winner_idx = {"this": 2, "other": 1, "conflict": 1}
 
552
    supports_lca_trees = True
464
553
 
465
 
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
 
554
    def __init__(self, working_tree, this_tree, base_tree, other_tree,
466
555
                 interesting_ids=None, reprocess=False, show_base=False,
467
556
                 pb=DummyProgress(), pp=None, change_reporter=None,
468
557
                 interesting_files=None, do_merge=True,
469
 
                 cherrypick=False):
 
558
                 cherrypick=False, lca_trees=None):
470
559
        """Initialize the merger object and perform the merge.
471
560
 
472
561
        :param working_tree: The working tree to apply the merge to
473
562
        :param this_tree: The local tree in the merge operation
474
563
        :param base_tree: The common tree in the merge operation
475
 
        :param other_tree: The other other tree to merge changes from
 
564
        :param other_tree: The other tree to merge changes from
476
565
        :param interesting_ids: The file_ids of files that should be
477
566
            participate in the merge.  May not be combined with
478
567
            interesting_files.
488
577
            be combined with interesting_ids.  If neither interesting_files nor
489
578
            interesting_ids is specified, all files may participate in the
490
579
            merge.
 
580
        :param lca_trees: Can be set to a dictionary of {revision_id:rev_tree}
 
581
            if the ancestry was found to include a criss-cross merge.
 
582
            Otherwise should be None.
491
583
        """
492
584
        object.__init__(self)
493
585
        if interesting_files is not None and interesting_ids is not None:
502
594
        self.cooked_conflicts = []
503
595
        self.reprocess = reprocess
504
596
        self.show_base = show_base
 
597
        self._lca_trees = lca_trees
 
598
        # Uncommenting this will change the default algorithm to always use
 
599
        # _entries_lca. This can be useful for running the test suite and
 
600
        # making sure we haven't missed any corner cases.
 
601
        # if lca_trees is None:
 
602
        #     self._lca_trees = [self.base_tree]
505
603
        self.pb = pb
506
604
        self.pp = pp
507
605
        self.change_reporter = change_reporter
515
613
        self.this_tree.lock_tree_write()
516
614
        self.base_tree.lock_read()
517
615
        self.other_tree.lock_read()
518
 
        self.tt = TreeTransform(self.this_tree, self.pb)
519
616
        try:
520
 
            self.pp.next_phase()
521
 
            self._compute_transform()
522
 
            self.pp.next_phase()
523
 
            results = self.tt.apply(no_conflicts=True)
524
 
            self.write_modified(results)
 
617
            self.tt = TreeTransform(self.this_tree, self.pb)
525
618
            try:
526
 
                self.this_tree.add_conflicts(self.cooked_conflicts)
527
 
            except UnsupportedOperation:
528
 
                pass
 
619
                self.pp.next_phase()
 
620
                self._compute_transform()
 
621
                self.pp.next_phase()
 
622
                results = self.tt.apply(no_conflicts=True)
 
623
                self.write_modified(results)
 
624
                try:
 
625
                    self.this_tree.add_conflicts(self.cooked_conflicts)
 
626
                except UnsupportedOperation:
 
627
                    pass
 
628
            finally:
 
629
                self.tt.finalize()
529
630
        finally:
530
 
            self.tt.finalize()
531
631
            self.other_tree.unlock()
532
632
            self.base_tree.unlock()
533
633
            self.this_tree.unlock()
548
648
        return self.tt
549
649
 
550
650
    def _compute_transform(self):
551
 
        entries = self._entries3()
 
651
        if self._lca_trees is None:
 
652
            entries = self._entries3()
 
653
            resolver = self._three_way
 
654
        else:
 
655
            entries = self._entries_lca()
 
656
            resolver = self._lca_multi_way
552
657
        child_pb = ui.ui_factory.nested_progress_bar()
553
658
        try:
554
659
            for num, (file_id, changed, parents3, names3,
555
660
                      executable3) in enumerate(entries):
556
661
                child_pb.update('Preparing file merge', num, len(entries))
557
 
                self._merge_names(file_id, parents3, names3)
 
662
                self._merge_names(file_id, parents3, names3, resolver=resolver)
558
663
                if changed:
559
664
                    file_status = self.merge_contents(file_id)
560
665
                else:
561
666
                    file_status = 'unmodified'
562
667
                self._merge_executable(file_id,
563
 
                    executable3, file_status)
 
668
                    executable3, file_status, resolver=resolver)
564
669
        finally:
565
670
            child_pb.finished()
566
671
        self.fix_root()
592
697
        iterator = self.other_tree.iter_changes(self.base_tree,
593
698
                include_unchanged=True, specific_files=self.interesting_files,
594
699
                extra_trees=[self.this_tree])
 
700
        this_entries = dict((e.file_id, e) for p, e in
 
701
                            self.this_tree.iter_entries_by_dir(
 
702
                            self.interesting_ids))
595
703
        for (file_id, paths, changed, versioned, parents, names, kind,
596
704
             executable) in iterator:
597
705
            if (self.interesting_ids is not None and
598
706
                file_id not in self.interesting_ids):
599
707
                continue
600
 
            if file_id in self.this_tree.inventory:
601
 
                entry = self.this_tree.inventory[file_id]
 
708
            entry = this_entries.get(file_id)
 
709
            if entry is not None:
602
710
                this_name = entry.name
603
711
                this_parent = entry.parent_id
604
712
                this_executable = entry.executable
612
720
            result.append((file_id, changed, parents3, names3, executable3))
613
721
        return result
614
722
 
 
723
    def _entries_lca(self):
 
724
        """Gather data about files modified between multiple trees.
 
725
 
 
726
        This compares OTHER versus all LCA trees, and for interesting entries,
 
727
        it then compares with THIS and BASE.
 
728
 
 
729
        For the multi-valued entries, the format will be (BASE, [lca1, lca2])
 
730
        :return: [(file_id, changed, parents, names, executable)]
 
731
            file_id     Simple file_id of the entry
 
732
            changed     Boolean, True if the kind or contents changed
 
733
                        else False
 
734
            parents     ((base, [parent_id, in, lcas]), parent_id_other,
 
735
                         parent_id_this)
 
736
            names       ((base, [name, in, lcas]), name_in_other, name_in_this)
 
737
            executable  ((base, [exec, in, lcas]), exec_in_other, exec_in_this)
 
738
        """
 
739
        if self.interesting_files is not None:
 
740
            lookup_trees = [self.this_tree, self.base_tree]
 
741
            lookup_trees.extend(self._lca_trees)
 
742
            # I think we should include the lca trees as well
 
743
            interesting_ids = self.other_tree.paths2ids(self.interesting_files,
 
744
                                                        lookup_trees)
 
745
        else:
 
746
            interesting_ids = self.interesting_ids
 
747
        result = []
 
748
        walker = _mod_tree.MultiWalker(self.other_tree, self._lca_trees)
 
749
 
 
750
        base_inventory = self.base_tree.inventory
 
751
        this_inventory = self.this_tree.inventory
 
752
        for path, file_id, other_ie, lca_values in walker.iter_all():
 
753
            # Is this modified at all from any of the other trees?
 
754
            if other_ie is None:
 
755
                other_ie = _none_entry
 
756
            if interesting_ids is not None and file_id not in interesting_ids:
 
757
                continue
 
758
 
 
759
            # If other_revision is found in any of the lcas, that means this
 
760
            # node is uninteresting. This is because when merging, if there are
 
761
            # multiple heads(), we have to create a new node. So if we didn't,
 
762
            # we know that the ancestry is linear, and that OTHER did not
 
763
            # modify anything
 
764
            # See doc/developers/lca_merge_resolution.txt for details
 
765
            other_revision = other_ie.revision
 
766
            if other_revision is not None:
 
767
                # We can't use this shortcut when other_revision is None,
 
768
                # because it may be None because things are WorkingTrees, and
 
769
                # not because it is *actually* None.
 
770
                is_unmodified = False
 
771
                for lca_path, ie in lca_values:
 
772
                    if ie is not None and ie.revision == other_revision:
 
773
                        is_unmodified = True
 
774
                        break
 
775
                if is_unmodified:
 
776
                    continue
 
777
 
 
778
            lca_entries = []
 
779
            for lca_path, lca_ie in lca_values:
 
780
                if lca_ie is None:
 
781
                    lca_entries.append(_none_entry)
 
782
                else:
 
783
                    lca_entries.append(lca_ie)
 
784
 
 
785
            if file_id in base_inventory:
 
786
                base_ie = base_inventory[file_id]
 
787
            else:
 
788
                base_ie = _none_entry
 
789
 
 
790
            if file_id in this_inventory:
 
791
                this_ie = this_inventory[file_id]
 
792
            else:
 
793
                this_ie = _none_entry
 
794
 
 
795
            lca_kinds = []
 
796
            lca_parent_ids = []
 
797
            lca_names = []
 
798
            lca_executable = []
 
799
            for lca_ie in lca_entries:
 
800
                lca_kinds.append(lca_ie.kind)
 
801
                lca_parent_ids.append(lca_ie.parent_id)
 
802
                lca_names.append(lca_ie.name)
 
803
                lca_executable.append(lca_ie.executable)
 
804
 
 
805
            kind_winner = self._lca_multi_way(
 
806
                (base_ie.kind, lca_kinds),
 
807
                other_ie.kind, this_ie.kind)
 
808
            parent_id_winner = self._lca_multi_way(
 
809
                (base_ie.parent_id, lca_parent_ids),
 
810
                other_ie.parent_id, this_ie.parent_id)
 
811
            name_winner = self._lca_multi_way(
 
812
                (base_ie.name, lca_names),
 
813
                other_ie.name, this_ie.name)
 
814
 
 
815
            content_changed = True
 
816
            if kind_winner == 'this':
 
817
                # No kind change in OTHER, see if there are *any* changes
 
818
                if other_ie.kind == 'directory':
 
819
                    if parent_id_winner == 'this' and name_winner == 'this':
 
820
                        # No change for this directory in OTHER, skip
 
821
                        continue
 
822
                    content_changed = False
 
823
                elif other_ie.kind is None or other_ie.kind == 'file':
 
824
                    def get_sha1(ie, tree):
 
825
                        if ie.kind != 'file':
 
826
                            return None
 
827
                        return tree.get_file_sha1(file_id)
 
828
                    base_sha1 = get_sha1(base_ie, self.base_tree)
 
829
                    lca_sha1s = [get_sha1(ie, tree) for ie, tree
 
830
                                 in zip(lca_entries, self._lca_trees)]
 
831
                    this_sha1 = get_sha1(this_ie, self.this_tree)
 
832
                    other_sha1 = get_sha1(other_ie, self.other_tree)
 
833
                    sha1_winner = self._lca_multi_way(
 
834
                        (base_sha1, lca_sha1s), other_sha1, this_sha1,
 
835
                        allow_overriding_lca=False)
 
836
                    exec_winner = self._lca_multi_way(
 
837
                        (base_ie.executable, lca_executable),
 
838
                        other_ie.executable, this_ie.executable)
 
839
                    if (parent_id_winner == 'this' and name_winner == 'this'
 
840
                        and sha1_winner == 'this' and exec_winner == 'this'):
 
841
                        # No kind, parent, name, exec, or content change for
 
842
                        # OTHER, so this node is not considered interesting
 
843
                        continue
 
844
                    if sha1_winner == 'this':
 
845
                        content_changed = False
 
846
                elif other_ie.kind == 'symlink':
 
847
                    def get_target(ie, tree):
 
848
                        if ie.kind != 'symlink':
 
849
                            return None
 
850
                        return tree.get_symlink_target(file_id)
 
851
                    base_target = get_target(base_ie, self.base_tree)
 
852
                    lca_targets = [get_target(ie, tree) for ie, tree
 
853
                                   in zip(lca_entries, self._lca_trees)]
 
854
                    this_target = get_target(this_ie, self.this_tree)
 
855
                    other_target = get_target(other_ie, self.other_tree)
 
856
                    target_winner = self._lca_multi_way(
 
857
                        (base_target, lca_targets),
 
858
                        other_target, this_target)
 
859
                    if (parent_id_winner == 'this' and name_winner == 'this'
 
860
                        and target_winner == 'this'):
 
861
                        # No kind, parent, name, or symlink target change
 
862
                        # not interesting
 
863
                        continue
 
864
                    if target_winner == 'this':
 
865
                        content_changed = False
 
866
                elif other_ie.kind == 'tree-reference':
 
867
                    # The 'changed' information seems to be handled at a higher
 
868
                    # level. At least, _entries3 returns False for content
 
869
                    # changed, even when at a new revision_id.
 
870
                    content_changed = False
 
871
                    if (parent_id_winner == 'this' and name_winner == 'this'):
 
872
                        # Nothing interesting
 
873
                        continue
 
874
                else:
 
875
                    raise AssertionError('unhandled kind: %s' % other_ie.kind)
 
876
                # XXX: We need to handle kind == 'symlink'
 
877
 
 
878
            # If we have gotten this far, that means something has changed
 
879
            result.append((file_id, content_changed,
 
880
                           ((base_ie.parent_id, lca_parent_ids),
 
881
                            other_ie.parent_id, this_ie.parent_id),
 
882
                           ((base_ie.name, lca_names),
 
883
                            other_ie.name, this_ie.name),
 
884
                           ((base_ie.executable, lca_executable),
 
885
                            other_ie.executable, this_ie.executable)
 
886
                          ))
 
887
        return result
 
888
 
 
889
 
615
890
    def fix_root(self):
616
891
        try:
617
892
            self.tt.final_kind(self.tt.root)
618
893
        except NoSuchFile:
619
894
            self.tt.cancel_deletion(self.tt.root)
620
895
        if self.tt.final_file_id(self.tt.root) is None:
621
 
            self.tt.version_file(self.tt.tree_file_id(self.tt.root), 
 
896
            self.tt.version_file(self.tt.tree_file_id(self.tt.root),
622
897
                                 self.tt.root)
623
 
        if self.other_tree.inventory.root is None:
624
 
            return
625
898
        other_root_file_id = self.other_tree.get_root_id()
 
899
        if other_root_file_id is None:
 
900
            return
626
901
        other_root = self.tt.trans_id_file_id(other_root_file_id)
627
902
        if other_root == self.tt.root:
628
903
            return
630
905
            self.tt.final_kind(other_root)
631
906
        except NoSuchFile:
632
907
            return
 
908
        if self.other_tree.inventory.root.file_id in self.this_tree.inventory:
 
909
            # the other tree's root is a non-root in the current tree
 
910
            return
633
911
        self.reparent_children(self.other_tree.inventory.root, self.tt.root)
634
912
        self.tt.cancel_creation(other_root)
635
913
        self.tt.cancel_versioning(other_root)
664
942
        if entry is None:
665
943
            return None
666
944
        return entry.name
667
 
    
 
945
 
668
946
    @staticmethod
669
947
    def contents_sha1(tree, file_id):
670
948
        """Determine the sha1 of the file contents (used as a key method)."""
703
981
            return "other"
704
982
 
705
983
    @staticmethod
 
984
    def _lca_multi_way(bases, other, this, allow_overriding_lca=True):
 
985
        """Consider LCAs when determining whether a change has occurred.
 
986
 
 
987
        If LCAS are all identical, this is the same as a _three_way comparison.
 
988
 
 
989
        :param bases: value in (BASE, [LCAS])
 
990
        :param other: value in OTHER
 
991
        :param this: value in THIS
 
992
        :param allow_overriding_lca: If there is more than one unique lca
 
993
            value, allow OTHER to override THIS if it has a new value, and
 
994
            THIS only has an lca value, or vice versa. This is appropriate for
 
995
            truly scalar values, not as much for non-scalars.
 
996
        :return: 'this', 'other', or 'conflict' depending on whether an entry
 
997
            changed or not.
 
998
        """
 
999
        # See doc/developers/lca_tree_merging.txt for details about this
 
1000
        # algorithm.
 
1001
        if other == this:
 
1002
            # Either Ambiguously clean, or nothing was actually changed. We
 
1003
            # don't really care
 
1004
            return 'this'
 
1005
        base_val, lca_vals = bases
 
1006
        # Remove 'base_val' from the lca_vals, because it is not interesting
 
1007
        filtered_lca_vals = [lca_val for lca_val in lca_vals
 
1008
                                      if lca_val != base_val]
 
1009
        if len(filtered_lca_vals) == 0:
 
1010
            return Merge3Merger._three_way(base_val, other, this)
 
1011
 
 
1012
        unique_lca_vals = set(filtered_lca_vals)
 
1013
        if len(unique_lca_vals) == 1:
 
1014
            return Merge3Merger._three_way(unique_lca_vals.pop(), other, this)
 
1015
 
 
1016
        if allow_overriding_lca:
 
1017
            if other in unique_lca_vals:
 
1018
                if this in unique_lca_vals:
 
1019
                    # Each side picked a different lca, conflict
 
1020
                    return 'conflict'
 
1021
                else:
 
1022
                    # This has a value which supersedes both lca values, and
 
1023
                    # other only has an lca value
 
1024
                    return 'this'
 
1025
            elif this in unique_lca_vals:
 
1026
                # OTHER has a value which supersedes both lca values, and this
 
1027
                # only has an lca value
 
1028
                return 'other'
 
1029
 
 
1030
        # At this point, the lcas disagree, and the tips disagree
 
1031
        return 'conflict'
 
1032
 
 
1033
    @staticmethod
706
1034
    def scalar_three_way(this_tree, base_tree, other_tree, file_id, key):
707
1035
        """Do a three-way test on a scalar.
708
1036
        Return "this", "other" or "conflict", depending whether a value wins.
740
1068
            else:
741
1069
                names.append(entry.name)
742
1070
                parents.append(entry.parent_id)
743
 
        return self._merge_names(file_id, parents, names)
 
1071
        return self._merge_names(file_id, parents, names,
 
1072
                                 resolver=self._three_way)
744
1073
 
745
 
    def _merge_names(self, file_id, parents, names):
 
1074
    def _merge_names(self, file_id, parents, names, resolver):
746
1075
        """Perform a merge on file_id names and parents"""
747
1076
        base_name, other_name, this_name = names
748
1077
        base_parent, other_parent, this_parent = parents
749
1078
 
750
 
        name_winner = self._three_way(*names)
 
1079
        name_winner = resolver(*names)
751
1080
 
752
 
        parent_id_winner = self._three_way(*parents)
 
1081
        parent_id_winner = resolver(*parents)
753
1082
        if this_name is None:
754
1083
            if name_winner == "this":
755
1084
                name_winner = "other"
759
1088
            return
760
1089
        if name_winner == "conflict":
761
1090
            trans_id = self.tt.trans_id_file_id(file_id)
762
 
            self._raw_conflicts.append(('name conflict', trans_id, 
 
1091
            self._raw_conflicts.append(('name conflict', trans_id,
763
1092
                                        this_name, other_name))
764
1093
        if parent_id_winner == "conflict":
765
1094
            trans_id = self.tt.trans_id_file_id(file_id)
766
 
            self._raw_conflicts.append(('parent conflict', trans_id, 
 
1095
            self._raw_conflicts.append(('parent conflict', trans_id,
767
1096
                                        this_parent, other_parent))
768
1097
        if other_name is None:
769
 
            # it doesn't matter whether the result was 'other' or 
 
1098
            # it doesn't matter whether the result was 'other' or
770
1099
            # 'conflict'-- if there's no 'other', we leave it alone.
771
1100
            return
772
1101
        # if we get here, name_winner and parent_winner are set to safe values.
778
1107
                                parent_trans_id, trans_id)
779
1108
 
780
1109
    def merge_contents(self, file_id):
781
 
        """Performa a merge on file_id contents."""
 
1110
        """Performs a merge on file_id contents."""
782
1111
        def contents_pair(tree):
783
1112
            if file_id not in tree:
784
1113
                return (None, None)
799
1128
                self.tt.unversion_file(trans_id)
800
1129
                if file_id in self.this_tree:
801
1130
                    self.tt.delete_contents(trans_id)
802
 
            file_group = self._dump_conflicts(name, parent_id, file_id, 
 
1131
            file_group = self._dump_conflicts(name, parent_id, file_id,
803
1132
                                              set_version=True)
804
1133
            self._raw_conflicts.append(('contents conflict', file_group))
805
1134
 
808
1137
        # file kind...
809
1138
        base_pair = contents_pair(self.base_tree)
810
1139
        other_pair = contents_pair(self.other_tree)
811
 
        if base_pair == other_pair:
812
 
            # OTHER introduced no changes
813
 
            return "unmodified"
814
 
        this_pair = contents_pair(self.this_tree)
815
 
        if this_pair == other_pair:
816
 
            # THIS and OTHER introduced the same changes
817
 
            return "unmodified"
818
 
        else:
819
 
            trans_id = self.tt.trans_id_file_id(file_id)
820
 
            if this_pair == base_pair:
821
 
                # only OTHER introduced changes
822
 
                if file_id in self.this_tree:
823
 
                    # Remove any existing contents
824
 
                    self.tt.delete_contents(trans_id)
825
 
                if file_id in self.other_tree:
826
 
                    # OTHER changed the file
827
 
                    create_by_entry(self.tt, 
828
 
                                    self.other_tree.inventory[file_id], 
829
 
                                    self.other_tree, trans_id)
830
 
                    if file_id not in self.this_tree.inventory:
831
 
                        self.tt.version_file(file_id, trans_id)
832
 
                    return "modified"
833
 
                elif file_id in self.this_tree.inventory:
834
 
                    # OTHER deleted the file
835
 
                    self.tt.unversion_file(trans_id)
836
 
                    return "deleted"
837
 
            #BOTH THIS and OTHER introduced changes; scalar conflict
838
 
            elif this_pair[0] == "file" and other_pair[0] == "file":
 
1140
        if self._lca_trees:
 
1141
            this_pair = contents_pair(self.this_tree)
 
1142
            lca_pairs = [contents_pair(tree) for tree in self._lca_trees]
 
1143
            winner = self._lca_multi_way((base_pair, lca_pairs), other_pair,
 
1144
                                         this_pair, allow_overriding_lca=False)
 
1145
        else:
 
1146
            if base_pair == other_pair:
 
1147
                winner = 'this'
 
1148
            else:
 
1149
                # We delayed evaluating this_pair as long as we can to avoid
 
1150
                # unnecessary sha1 calculation
 
1151
                this_pair = contents_pair(self.this_tree)
 
1152
                winner = self._three_way(base_pair, other_pair, this_pair)
 
1153
        if winner == 'this':
 
1154
            # No interesting changes introduced by OTHER
 
1155
            return "unmodified"
 
1156
        trans_id = self.tt.trans_id_file_id(file_id)
 
1157
        if winner == 'other':
 
1158
            # OTHER is a straight winner, so replace this contents with other
 
1159
            file_in_this = file_id in self.this_tree
 
1160
            if file_in_this:
 
1161
                # Remove any existing contents
 
1162
                self.tt.delete_contents(trans_id)
 
1163
            if file_id in self.other_tree:
 
1164
                # OTHER changed the file
 
1165
                wt = self.this_tree
 
1166
                if wt.supports_content_filtering():
 
1167
                    # We get the path from the working tree if it exists.
 
1168
                    # That fails though when OTHER is adding a file, so
 
1169
                    # we fall back to the other tree to find the path if
 
1170
                    # it doesn't exist locally.
 
1171
                    try:
 
1172
                        filter_tree_path = wt.id2path(file_id)
 
1173
                    except errors.NoSuchId:
 
1174
                        filter_tree_path = self.other_tree.id2path(file_id)
 
1175
                else:
 
1176
                    # Skip the id2path lookup for older formats
 
1177
                    filter_tree_path = None
 
1178
                create_from_tree(self.tt, trans_id,
 
1179
                                 self.other_tree, file_id,
 
1180
                                 filter_tree_path=filter_tree_path)
 
1181
                if not file_in_this:
 
1182
                    self.tt.version_file(file_id, trans_id)
 
1183
                return "modified"
 
1184
            elif file_in_this:
 
1185
                # OTHER deleted the file
 
1186
                self.tt.unversion_file(trans_id)
 
1187
                return "deleted"
 
1188
        else:
 
1189
            # We have a hypothetical conflict, but if we have files, then we
 
1190
            # can try to merge the content
 
1191
            if this_pair[0] == 'file' and other_pair[0] == 'file':
839
1192
                # THIS and OTHER are both files, so text merge.  Either
840
1193
                # BASE is a file, or both converted to files, so at least we
841
1194
                # have agreement that output should be a file.
843
1196
                    self.text_merge(file_id, trans_id)
844
1197
                except BinaryFile:
845
1198
                    return contents_conflict()
846
 
                if file_id not in self.this_tree.inventory:
 
1199
                if file_id not in self.this_tree:
847
1200
                    self.tt.version_file(file_id, trans_id)
848
1201
                try:
849
1202
                    self.tt.tree_kind(trans_id)
852
1205
                    pass
853
1206
                return "modified"
854
1207
            else:
855
 
                # Scalar conflict, can't text merge.  Dump conflicts
856
1208
                return contents_conflict()
857
1209
 
858
1210
    def get_lines(self, tree, file_id):
883
1235
 
884
1236
        def iter_merge3(retval):
885
1237
            retval["text_conflicts"] = False
886
 
            for line in m3.merge_lines(name_a = "TREE", 
887
 
                                       name_b = "MERGE-SOURCE", 
 
1238
            for line in m3.merge_lines(name_a = "TREE",
 
1239
                                       name_b = "MERGE-SOURCE",
888
1240
                                       name_base = "BASE-REVISION",
889
 
                                       start_marker=start_marker, 
 
1241
                                       start_marker=start_marker,
890
1242
                                       base_marker=base_marker,
891
1243
                                       reprocess=self.reprocess):
892
1244
                if line.startswith(start_marker):
901
1253
            self._raw_conflicts.append(('text conflict', trans_id))
902
1254
            name = self.tt.final_name(trans_id)
903
1255
            parent_id = self.tt.final_parent(trans_id)
904
 
            file_group = self._dump_conflicts(name, parent_id, file_id, 
 
1256
            file_group = self._dump_conflicts(name, parent_id, file_id,
905
1257
                                              this_lines, base_lines,
906
1258
                                              other_lines)
907
1259
            file_group.append(trans_id)
908
1260
 
909
 
    def _dump_conflicts(self, name, parent_id, file_id, this_lines=None, 
 
1261
    def _dump_conflicts(self, name, parent_id, file_id, this_lines=None,
910
1262
                        base_lines=None, other_lines=None, set_version=False,
911
1263
                        no_base=False):
912
1264
        """Emit conflict files.
914
1266
        determined automatically.  If set_version is true, the .OTHER, .THIS
915
1267
        or .BASE (in that order) will be created as versioned files.
916
1268
        """
917
 
        data = [('OTHER', self.other_tree, other_lines), 
 
1269
        data = [('OTHER', self.other_tree, other_lines),
918
1270
                ('THIS', self.this_tree, this_lines)]
919
1271
        if not no_base:
920
1272
            data.append(('BASE', self.base_tree, base_lines))
 
1273
 
 
1274
        # We need to use the actual path in the working tree of the file here,
 
1275
        # ignoring the conflict suffixes
 
1276
        wt = self.this_tree
 
1277
        if wt.supports_content_filtering():
 
1278
            try:
 
1279
                filter_tree_path = wt.id2path(file_id)
 
1280
            except errors.NoSuchId:
 
1281
                # file has been deleted
 
1282
                filter_tree_path = None
 
1283
        else:
 
1284
            # Skip the id2path lookup for older formats
 
1285
            filter_tree_path = None
 
1286
 
921
1287
        versioned = False
922
1288
        file_group = []
923
1289
        for suffix, tree, lines in data:
924
1290
            if file_id in tree:
925
1291
                trans_id = self._conflict_file(name, parent_id, tree, file_id,
926
 
                                               suffix, lines)
 
1292
                                               suffix, lines, filter_tree_path)
927
1293
                file_group.append(trans_id)
928
1294
                if set_version and not versioned:
929
1295
                    self.tt.version_file(file_id, trans_id)
930
1296
                    versioned = True
931
1297
        return file_group
932
 
           
933
 
    def _conflict_file(self, name, parent_id, tree, file_id, suffix, 
934
 
                       lines=None):
 
1298
 
 
1299
    def _conflict_file(self, name, parent_id, tree, file_id, suffix,
 
1300
                       lines=None, filter_tree_path=None):
935
1301
        """Emit a single conflict file."""
936
1302
        name = name + '.' + suffix
937
1303
        trans_id = self.tt.create_path(name, parent_id)
938
 
        entry = tree.inventory[file_id]
939
 
        create_by_entry(self.tt, entry, tree, trans_id, lines)
 
1304
        create_from_tree(self.tt, trans_id, tree, file_id, lines,
 
1305
            filter_tree_path)
940
1306
        return trans_id
941
1307
 
942
1308
    def merge_executable(self, file_id, file_status):
943
1309
        """Perform a merge on the execute bit."""
944
1310
        executable = [self.executable(t, file_id) for t in (self.base_tree,
945
1311
                      self.other_tree, self.this_tree)]
946
 
        self._merge_executable(file_id, executable, file_status)
 
1312
        self._merge_executable(file_id, executable, file_status,
 
1313
                               resolver=self._three_way)
947
1314
 
948
 
    def _merge_executable(self, file_id, executable, file_status):
 
1315
    def _merge_executable(self, file_id, executable, file_status,
 
1316
                          resolver):
949
1317
        """Perform a merge on the execute bit."""
950
1318
        base_executable, other_executable, this_executable = executable
951
1319
        if file_status == "deleted":
952
1320
            return
953
 
        winner = self._three_way(*executable)
 
1321
        winner = resolver(*executable)
954
1322
        if winner == "conflict":
955
1323
        # There must be a None in here, if we have a conflict, but we
956
1324
        # need executability since file status was not deleted.
992
1360
                conflict_args = conflict[2:]
993
1361
                if trans_id not in name_conflicts:
994
1362
                    name_conflicts[trans_id] = {}
995
 
                unique_add(name_conflicts[trans_id], conflict_type, 
 
1363
                unique_add(name_conflicts[trans_id], conflict_type,
996
1364
                           conflict_args)
997
1365
            if conflict_type == 'contents conflict':
998
1366
                for trans_id in conflict[1]:
1076
1444
        """
1077
1445
        lines, conflicts = self._merged_lines(file_id)
1078
1446
        lines = list(lines)
1079
 
        # Note we're checking whether the OUTPUT is binary in this case, 
 
1447
        # Note we're checking whether the OUTPUT is binary in this case,
1080
1448
        # because we don't want to get into weave merge guts.
1081
1449
        check_text_lines(lines)
1082
1450
        self.tt.create_file(lines, trans_id)
1084
1452
            self._raw_conflicts.append(('text conflict', trans_id))
1085
1453
            name = self.tt.final_name(trans_id)
1086
1454
            parent_id = self.tt.final_parent(trans_id)
1087
 
            file_group = self._dump_conflicts(name, parent_id, file_id, 
 
1455
            file_group = self._dump_conflicts(name, parent_id, file_id,
1088
1456
                                              no_base=True)
1089
1457
            file_group.append(trans_id)
1090
1458
 
1167
1535
                this_tree=None,
1168
1536
                pb=DummyProgress(),
1169
1537
                change_reporter=None):
1170
 
    """Primary interface for merging. 
 
1538
    """Primary interface for merging.
1171
1539
 
1172
 
        typical use is probably 
 
1540
        typical use is probably
1173
1541
        'merge_inner(branch, branch.get_revision_tree(other_revision),
1174
1542
                     branch.get_revision_tree(base_revision))'
1175
1543
        """
1194
1562
    get_revision_id = getattr(base_tree, 'get_revision_id', None)
1195
1563
    if get_revision_id is None:
1196
1564
        get_revision_id = base_tree.last_revision
 
1565
    merger.cache_trees_with_revision_ids([other_tree, base_tree, this_tree])
1197
1566
    merger.set_base_revision(get_revision_id(), this_branch)
1198
1567
    return merger.do_merge()
1199
1568
 
1258
1627
        self._last_lines_revision_id = None
1259
1628
        self._cached_matching_blocks = {}
1260
1629
        self._key_prefix = key_prefix
1261
 
        lines = self.get_lines([a_rev, b_rev])
1262
 
        self.lines_a = lines[a_rev]
1263
 
        self.lines_b = lines[b_rev]
 
1630
        self._precache_tip_lines()
 
1631
 
 
1632
    def _precache_tip_lines(self):
 
1633
        lines = self.get_lines([self.a_rev, self.b_rev])
 
1634
        self.lines_a = lines[self.a_rev]
 
1635
        self.lines_b = lines[self.b_rev]
1264
1636
 
1265
1637
    def get_lines(self, revisions):
1266
1638
        """Get lines for revisions from the backing VersionedFiles.
1267
 
        
 
1639
 
1268
1640
        :raises RevisionNotPresent: on absent texts.
1269
1641
        """
1270
1642
        keys = [(self._key_prefix + (rev,)) for rev in revisions]
1272
1644
        for record in self.vf.get_record_stream(keys, 'unordered', True):
1273
1645
            if record.storage_kind == 'absent':
1274
1646
                raise errors.RevisionNotPresent(record.key, self.vf)
1275
 
            result[record.key[-1]] = osutils.split_lines(
1276
 
                record.get_bytes_as('fulltext'))
 
1647
            result[record.key[-1]] = osutils.chunks_to_lines(
 
1648
                record.get_bytes_as('chunked'))
1277
1649
        return result
1278
1650
 
1279
1651
    def plan_merge(self):
1392
1764
    """Plan an annotate merge using on-the-fly annotation"""
1393
1765
 
1394
1766
    def __init__(self, a_rev, b_rev, vf, key_prefix):
1395
 
        _PlanMergeBase.__init__(self, a_rev, b_rev, vf, key_prefix)
1396
 
        graph = Graph(vf)
1397
 
        # XXX: There is probably a better API to use to examine less history.
1398
 
        a_ancestry = set(chain(*graph._make_breadth_first_searcher(
1399
 
            [key_prefix + (a_rev,)])))
1400
 
        b_ancestry = set(chain(*graph._make_breadth_first_searcher(
1401
 
            [key_prefix + (b_rev,)])))
1402
 
        self.uncommon = set(key[-1] for key in
1403
 
            a_ancestry.symmetric_difference(b_ancestry))
1404
 
 
1405
 
    def _determine_status(self, revision_id, unique_line_numbers):
1406
 
        """Determines the status unique lines versus all lcas.
1407
 
 
1408
 
        Basically, determines why the line is unique to this revision.
1409
 
 
1410
 
        A line may be determined new or killed, but not both.
1411
 
 
1412
 
        :param revision_id: The id of the revision in which the lines are
1413
 
            unique
1414
 
        :param unique_line_numbers: The line numbers of unique lines.
1415
 
        :return a tuple of (new_this, killed_other):
1416
 
        """
1417
 
        new = self._find_new(revision_id)
1418
 
        killed = set(unique_line_numbers).difference(new)
1419
 
        return new, killed
1420
 
 
1421
 
    def _find_new(self, version_id):
1422
 
        """Determine which lines are new in the ancestry of this version.
1423
 
 
1424
 
        If a lines is present in this version, and not present in any
1425
 
        common ancestor, it is considered new.
1426
 
        """
1427
 
        if version_id not in self.uncommon:
1428
 
            return set()
1429
 
        key = self._key_prefix + (version_id,)
1430
 
        parent_map = self.vf.get_parent_map([key])
1431
 
        parents = tuple(parent[-1] for parent in parent_map[key])
1432
 
        if len(parents) == 0:
1433
 
            return set(range(len(self.get_lines([version_id])[version_id])))
1434
 
        new = None
1435
 
        for parent in parents:
1436
 
            blocks = self._get_matching_blocks(version_id, parent)
1437
 
            result, unused = self._unique_lines(blocks)
1438
 
            parent_new = self._find_new(parent)
1439
 
            for i, j, n in blocks:
1440
 
                for ii, jj in [(i+r, j+r) for r in range(n)]:
1441
 
                    if jj in parent_new:
1442
 
                        result.append(ii)
1443
 
            if new is None:
1444
 
                new = set(result)
1445
 
            else:
1446
 
                new.intersection_update(result)
1447
 
        return new
 
1767
        super(_PlanMerge, self).__init__(a_rev, b_rev, vf, key_prefix)
 
1768
        self.a_key = self._key_prefix + (self.a_rev,)
 
1769
        self.b_key = self._key_prefix + (self.b_rev,)
 
1770
        self.graph = Graph(self.vf)
 
1771
        heads = self.graph.heads((self.a_key, self.b_key))
 
1772
        if len(heads) == 1:
 
1773
            # one side dominates, so we can just return its values, yay for
 
1774
            # per-file graphs
 
1775
            # Ideally we would know that before we get this far
 
1776
            self._head_key = heads.pop()
 
1777
            if self._head_key == self.a_key:
 
1778
                other = b_rev
 
1779
            else:
 
1780
                other = a_rev
 
1781
            mutter('found dominating revision for %s\n%s > %s', self.vf,
 
1782
                   self._head_key[-1], other)
 
1783
            self._weave = None
 
1784
        else:
 
1785
            self._head_key = None
 
1786
            self._build_weave()
 
1787
 
 
1788
    def _precache_tip_lines(self):
 
1789
        # Turn this into a no-op, because we will do this later
 
1790
        pass
 
1791
 
 
1792
    def _find_recursive_lcas(self):
 
1793
        """Find all the ancestors back to a unique lca"""
 
1794
        cur_ancestors = (self.a_key, self.b_key)
 
1795
        # graph.find_lca(uncommon, keys) now returns plain NULL_REVISION,
 
1796
        # rather than a key tuple. We will just map that directly to no common
 
1797
        # ancestors.
 
1798
        parent_map = {}
 
1799
        while True:
 
1800
            next_lcas = self.graph.find_lca(*cur_ancestors)
 
1801
            # Map a plain NULL_REVISION to a simple no-ancestors
 
1802
            if next_lcas == set([NULL_REVISION]):
 
1803
                next_lcas = ()
 
1804
            # Order the lca's based on when they were merged into the tip
 
1805
            # While the actual merge portion of weave merge uses a set() of
 
1806
            # active revisions, the order of insertion *does* effect the
 
1807
            # implicit ordering of the texts.
 
1808
            for rev_key in cur_ancestors:
 
1809
                ordered_parents = tuple(self.graph.find_merge_order(rev_key,
 
1810
                                                                    next_lcas))
 
1811
                parent_map[rev_key] = ordered_parents
 
1812
            if len(next_lcas) == 0:
 
1813
                break
 
1814
            elif len(next_lcas) == 1:
 
1815
                parent_map[list(next_lcas)[0]] = ()
 
1816
                break
 
1817
            elif len(next_lcas) > 2:
 
1818
                # More than 2 lca's, fall back to grabbing all nodes between
 
1819
                # this and the unique lca.
 
1820
                mutter('More than 2 LCAs, falling back to all nodes for:'
 
1821
                       ' %s, %s\n=> %s', self.a_key, self.b_key, cur_ancestors)
 
1822
                cur_lcas = next_lcas
 
1823
                while len(cur_lcas) > 1:
 
1824
                    cur_lcas = self.graph.find_lca(*cur_lcas)
 
1825
                if len(cur_lcas) == 0:
 
1826
                    # No common base to find, use the full ancestry
 
1827
                    unique_lca = None
 
1828
                else:
 
1829
                    unique_lca = list(cur_lcas)[0]
 
1830
                    if unique_lca == NULL_REVISION:
 
1831
                        # find_lca will return a plain 'NULL_REVISION' rather
 
1832
                        # than a key tuple when there is no common ancestor, we
 
1833
                        # prefer to just use None, because it doesn't confuse
 
1834
                        # _get_interesting_texts()
 
1835
                        unique_lca = None
 
1836
                parent_map.update(self._find_unique_parents(next_lcas,
 
1837
                                                            unique_lca))
 
1838
                break
 
1839
            cur_ancestors = next_lcas
 
1840
        return parent_map
 
1841
 
 
1842
    def _find_unique_parents(self, tip_keys, base_key):
 
1843
        """Find ancestors of tip that aren't ancestors of base.
 
1844
 
 
1845
        :param tip_keys: Nodes that are interesting
 
1846
        :param base_key: Cull all ancestors of this node
 
1847
        :return: The parent map for all revisions between tip_keys and
 
1848
            base_key. base_key will be included. References to nodes outside of
 
1849
            the ancestor set will also be removed.
 
1850
        """
 
1851
        # TODO: this would be simpler if find_unique_ancestors took a list
 
1852
        #       instead of a single tip, internally it supports it, but it
 
1853
        #       isn't a "backwards compatible" api change.
 
1854
        if base_key is None:
 
1855
            parent_map = dict(self.graph.iter_ancestry(tip_keys))
 
1856
            # We remove NULL_REVISION because it isn't a proper tuple key, and
 
1857
            # thus confuses things like _get_interesting_texts, and our logic
 
1858
            # to add the texts into the memory weave.
 
1859
            if NULL_REVISION in parent_map:
 
1860
                parent_map.pop(NULL_REVISION)
 
1861
        else:
 
1862
            interesting = set()
 
1863
            for tip in tip_keys:
 
1864
                interesting.update(
 
1865
                    self.graph.find_unique_ancestors(tip, [base_key]))
 
1866
            parent_map = self.graph.get_parent_map(interesting)
 
1867
            parent_map[base_key] = ()
 
1868
        culled_parent_map, child_map, tails = self._remove_external_references(
 
1869
            parent_map)
 
1870
        # Remove all the tails but base_key
 
1871
        if base_key is not None:
 
1872
            tails.remove(base_key)
 
1873
            self._prune_tails(culled_parent_map, child_map, tails)
 
1874
        # Now remove all the uninteresting 'linear' regions
 
1875
        simple_map = _mod_graph.collapse_linear_regions(culled_parent_map)
 
1876
        return simple_map
 
1877
 
 
1878
    @staticmethod
 
1879
    def _remove_external_references(parent_map):
 
1880
        """Remove references that go outside of the parent map.
 
1881
 
 
1882
        :param parent_map: Something returned from Graph.get_parent_map(keys)
 
1883
        :return: (filtered_parent_map, child_map, tails)
 
1884
            filtered_parent_map is parent_map without external references
 
1885
            child_map is the {parent_key: [child_keys]} mapping
 
1886
            tails is a list of nodes that do not have any parents in the map
 
1887
        """
 
1888
        # TODO: The basic effect of this function seems more generic than
 
1889
        #       _PlanMerge. But the specific details of building a child_map,
 
1890
        #       and computing tails seems very specific to _PlanMerge.
 
1891
        #       Still, should this be in Graph land?
 
1892
        filtered_parent_map = {}
 
1893
        child_map = {}
 
1894
        tails = []
 
1895
        for key, parent_keys in parent_map.iteritems():
 
1896
            culled_parent_keys = [p for p in parent_keys if p in parent_map]
 
1897
            if not culled_parent_keys:
 
1898
                tails.append(key)
 
1899
            for parent_key in culled_parent_keys:
 
1900
                child_map.setdefault(parent_key, []).append(key)
 
1901
            # TODO: Do we want to do this, it adds overhead for every node,
 
1902
            #       just to say that the node has no children
 
1903
            child_map.setdefault(key, [])
 
1904
            filtered_parent_map[key] = culled_parent_keys
 
1905
        return filtered_parent_map, child_map, tails
 
1906
 
 
1907
    @staticmethod
 
1908
    def _prune_tails(parent_map, child_map, tails_to_remove):
 
1909
        """Remove tails from the parent map.
 
1910
 
 
1911
        This will remove the supplied revisions until no more children have 0
 
1912
        parents.
 
1913
 
 
1914
        :param parent_map: A dict of {child: [parents]}, this dictionary will
 
1915
            be modified in place.
 
1916
        :param tails_to_remove: A list of tips that should be removed,
 
1917
            this list will be consumed
 
1918
        :param child_map: The reverse dict of parent_map ({parent: [children]})
 
1919
            this dict will be modified
 
1920
        :return: None, parent_map will be modified in place.
 
1921
        """
 
1922
        while tails_to_remove:
 
1923
            next = tails_to_remove.pop()
 
1924
            parent_map.pop(next)
 
1925
            children = child_map.pop(next)
 
1926
            for child in children:
 
1927
                child_parents = parent_map[child]
 
1928
                child_parents.remove(next)
 
1929
                if len(child_parents) == 0:
 
1930
                    tails_to_remove.append(child)
 
1931
 
 
1932
    def _get_interesting_texts(self, parent_map):
 
1933
        """Return a dict of texts we are interested in.
 
1934
 
 
1935
        Note that the input is in key tuples, but the output is in plain
 
1936
        revision ids.
 
1937
 
 
1938
        :param parent_map: The output from _find_recursive_lcas
 
1939
        :return: A dict of {'revision_id':lines} as returned by
 
1940
            _PlanMergeBase.get_lines()
 
1941
        """
 
1942
        all_revision_keys = set(parent_map)
 
1943
        all_revision_keys.add(self.a_key)
 
1944
        all_revision_keys.add(self.b_key)
 
1945
 
 
1946
        # Everything else is in 'keys' but get_lines is in 'revision_ids'
 
1947
        all_texts = self.get_lines([k[-1] for k in all_revision_keys])
 
1948
        return all_texts
 
1949
 
 
1950
    def _build_weave(self):
 
1951
        from bzrlib import weave
 
1952
        self._weave = weave.Weave(weave_name='in_memory_weave',
 
1953
                                  allow_reserved=True)
 
1954
        parent_map = self._find_recursive_lcas()
 
1955
 
 
1956
        all_texts = self._get_interesting_texts(parent_map)
 
1957
 
 
1958
        # Note: Unfortunately, the order given by topo_sort will effect the
 
1959
        # ordering resolution in the output. Specifically, if you add A then B,
 
1960
        # then in the output text A lines will show up before B lines. And, of
 
1961
        # course, topo_sort doesn't guarantee any real ordering.
 
1962
        # So we use merge_sort, and add a fake node on the tip.
 
1963
        # This ensures that left-hand parents will always be inserted into the
 
1964
        # weave before right-hand parents.
 
1965
        tip_key = self._key_prefix + (_mod_revision.CURRENT_REVISION,)
 
1966
        parent_map[tip_key] = (self.a_key, self.b_key)
 
1967
 
 
1968
        for seq_num, key, depth, eom in reversed(tsort.merge_sort(parent_map,
 
1969
                                                                  tip_key)):
 
1970
            if key == tip_key:
 
1971
                continue
 
1972
        # for key in tsort.topo_sort(parent_map):
 
1973
            parent_keys = parent_map[key]
 
1974
            revision_id = key[-1]
 
1975
            parent_ids = [k[-1] for k in parent_keys]
 
1976
            self._weave.add_lines(revision_id, parent_ids,
 
1977
                                  all_texts[revision_id])
 
1978
 
 
1979
    def plan_merge(self):
 
1980
        """Generate a 'plan' for merging the two revisions.
 
1981
 
 
1982
        This involves comparing their texts and determining the cause of
 
1983
        differences.  If text A has a line and text B does not, then either the
 
1984
        line was added to text A, or it was deleted from B.  Once the causes
 
1985
        are combined, they are written out in the format described in
 
1986
        VersionedFile.plan_merge
 
1987
        """
 
1988
        if self._head_key is not None: # There was a single head
 
1989
            if self._head_key == self.a_key:
 
1990
                plan = 'new-a'
 
1991
            else:
 
1992
                if self._head_key != self.b_key:
 
1993
                    raise AssertionError('There was an invalid head: %s != %s'
 
1994
                                         % (self.b_key, self._head_key))
 
1995
                plan = 'new-b'
 
1996
            head_rev = self._head_key[-1]
 
1997
            lines = self.get_lines([head_rev])[head_rev]
 
1998
            return ((plan, line) for line in lines)
 
1999
        return self._weave.plan_merge(self.a_rev, self.b_rev)
1448
2000
 
1449
2001
 
1450
2002
class _PlanLCAMerge(_PlanMergeBase):