/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: Aaron Bentley
  • Date: 2006-05-20 17:51:13 UTC
  • mfrom: (1718 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1727.
  • Revision ID: aaron.bentley@utoronto.ca-20060520175113-4549e0023f9210bf
Merge from bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
2
 
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
17
17
 
18
18
import os
19
19
import errno
20
 
from shutil import rmtree
21
20
from tempfile import mkdtemp
22
21
 
23
22
import bzrlib
24
23
from bzrlib.branch import Branch
 
24
from bzrlib.conflicts import ConflictList, Conflict
25
25
from bzrlib.delta import compare_trees
26
26
from bzrlib.errors import (BzrCommandError,
27
27
                           BzrError,
32
32
                           NotBranchError,
33
33
                           NotVersionedError,
34
34
                           UnrelatedBranches,
 
35
                           UnsupportedOperation,
35
36
                           WorkingTreeNotRevision,
 
37
                           BinaryFile,
36
38
                           )
37
39
from bzrlib.merge3 import Merge3
38
40
import bzrlib.osutils
39
 
from bzrlib.osutils import rename, pathjoin
 
41
from bzrlib.osutils import rename, pathjoin, rmtree
40
42
from progress import DummyProgress, ProgressPhase
41
43
from bzrlib.revision import common_ancestor, is_ancestor, NULL_REVISION
42
44
from bzrlib.symbol_versioning import *
 
45
from bzrlib.textfile import check_text_lines
43
46
from bzrlib.trace import mutter, warning, note
44
47
from bzrlib.transform import (TreeTransform, resolve_conflicts, cook_conflicts,
45
 
                              conflicts_strings, FinalPaths, create_by_entry,
46
 
                              unique_add)
 
48
                              FinalPaths, create_by_entry, unique_add)
 
49
from bzrlib.versionedfile import WeaveMerge
47
50
import bzrlib.ui
48
51
 
49
52
# TODO: Report back as changes are merged in
243
246
        if self.merge_type.supports_reprocess:
244
247
            kwargs['reprocess'] = self.reprocess
245
248
        elif self.reprocess:
246
 
            raise BzrError("Reprocess is not supported for this merge"
247
 
                                  " type. %s" % merge_type)
 
249
            raise BzrError("Conflict reduction is not supported for merge"
 
250
                                  " type %s." % self.merge_type)
248
251
        if self.merge_type.supports_show_base:
249
252
            kwargs['show_base'] = self.show_base
250
253
        elif self.show_base:
281
284
        for file_id in old_entries:
282
285
            entry = old_entries[file_id]
283
286
            path = id2path(file_id)
 
287
            if file_id in self.base_tree.inventory:
 
288
                executable = getattr(self.base_tree.inventory[file_id], 'executable', False)
 
289
            else:
 
290
                executable = getattr(entry, 'executable', False)
284
291
            new_inventory[file_id] = (path, file_id, entry.parent_id, 
285
 
                                      entry.kind)
 
292
                                      entry.kind, executable)
 
293
                                      
286
294
            by_path[path] = file_id
287
295
        
288
296
        deletions = 0
305
313
                parent = by_path[os.path.dirname(path)]
306
314
            abspath = pathjoin(self.this_tree.basedir, path)
307
315
            kind = bzrlib.osutils.file_kind(abspath)
308
 
            new_inventory[file_id] = (path, file_id, parent, kind)
 
316
            if file_id in self.base_tree.inventory:
 
317
                executable = getattr(self.base_tree.inventory[file_id], 'executable', False)
 
318
            else:
 
319
                executable = False
 
320
            new_inventory[file_id] = (path, file_id, parent, kind, executable)
309
321
            by_path[path] = file_id 
310
322
 
311
323
        # Get a list in insertion order
370
382
            finally:
371
383
                child_pb.finished()
372
384
            self.cook_conflicts(fs_conflicts)
373
 
            for line in conflicts_strings(self.cooked_conflicts):
374
 
                warning(line)
 
385
            for conflict in self.cooked_conflicts:
 
386
                warning(conflict)
375
387
            self.pp.next_phase()
376
388
            results = self.tt.apply()
377
389
            self.write_modified(results)
 
390
            try:
 
391
                working_tree.set_conflicts(ConflictList(self.cooked_conflicts))
 
392
            except UnsupportedOperation:
 
393
                pass
378
394
        finally:
379
395
            try:
380
396
                self.tt.finalize()
512
528
            else:
513
529
                contents = None
514
530
            return kind, contents
 
531
 
 
532
        def contents_conflict():
 
533
            trans_id = self.tt.trans_id_file_id(file_id)
 
534
            name = self.tt.final_name(trans_id)
 
535
            parent_id = self.tt.final_parent(trans_id)
 
536
            if file_id in self.this_tree.inventory:
 
537
                self.tt.unversion_file(trans_id)
 
538
                self.tt.delete_contents(trans_id)
 
539
            file_group = self._dump_conflicts(name, parent_id, file_id, 
 
540
                                              set_version=True)
 
541
            self._raw_conflicts.append(('contents conflict', file_group))
 
542
 
515
543
        # See SPOT run.  run, SPOT, run.
516
544
        # So we're not QUITE repeating ourselves; we do tricky things with
517
545
        # file kind...
548
576
                # THIS and OTHER are both files, so text merge.  Either
549
577
                # BASE is a file, or both converted to files, so at least we
550
578
                # have agreement that output should be a file.
 
579
                try:
 
580
                    self.text_merge(file_id, trans_id)
 
581
                except BinaryFile:
 
582
                    return contents_conflict()
551
583
                if file_id not in self.this_tree.inventory:
552
584
                    self.tt.version_file(file_id, trans_id)
553
 
                self.text_merge(file_id, trans_id)
554
585
                try:
555
586
                    self.tt.tree_kind(trans_id)
556
587
                    self.tt.delete_contents(trans_id)
559
590
                return "modified"
560
591
            else:
561
592
                # Scalar conflict, can't text merge.  Dump conflicts
562
 
                trans_id = self.tt.trans_id_file_id(file_id)
563
 
                name = self.tt.final_name(trans_id)
564
 
                parent_id = self.tt.final_parent(trans_id)
565
 
                if file_id in self.this_tree.inventory:
566
 
                    self.tt.unversion_file(trans_id)
567
 
                    self.tt.delete_contents(trans_id)
568
 
                file_group = self._dump_conflicts(name, parent_id, file_id, 
569
 
                                                  set_version=True)
570
 
                self._raw_conflicts.append(('contents conflict', file_group))
 
593
                return contents_conflict()
571
594
 
572
595
    def get_lines(self, tree, file_id):
573
596
        """Return the lines in a file, or an empty list."""
692
715
 
693
716
    def cook_conflicts(self, fs_conflicts):
694
717
        """Convert all conflicts into a form that doesn't depend on trans_id"""
 
718
        from conflicts import Conflict
695
719
        name_conflicts = {}
696
720
        self.cooked_conflicts.extend(cook_conflicts(fs_conflicts, self.tt))
697
721
        fp = FinalPaths(self.tt)
714
738
                    if path.endswith(suffix):
715
739
                        path = path[:-len(suffix)]
716
740
                        break
717
 
                self.cooked_conflicts.append((conflict_type, file_id, path))
 
741
                c = Conflict.factory(conflict_type, path=path, file_id=file_id)
 
742
                self.cooked_conflicts.append(c)
718
743
            if conflict_type == 'text conflict':
719
744
                trans_id = conflict[1]
720
745
                path = fp.get_path(trans_id)
721
746
                file_id = self.tt.final_file_id(trans_id)
722
 
                self.cooked_conflicts.append((conflict_type, file_id, path))
 
747
                c = Conflict.factory(conflict_type, path=path, file_id=file_id)
 
748
                self.cooked_conflicts.append(c)
723
749
 
724
750
        for trans_id, conflicts in name_conflicts.iteritems():
725
751
            try:
741
767
            else:
742
768
                this_path = "<deleted>"
743
769
            file_id = self.tt.final_file_id(trans_id)
744
 
            self.cooked_conflicts.append(('path conflict', file_id, this_path, 
745
 
                                         other_path))
 
770
            c = Conflict.factory('path conflict', path=this_path,
 
771
                                 conflict_path=other_path, file_id=file_id)
 
772
            self.cooked_conflicts.append(c)
 
773
        self.cooked_conflicts.sort(key=Conflict.sort_key)
746
774
 
747
775
 
748
776
class WeaveMerger(Merge3Merger):
749
777
    """Three-way tree merger, text weave merger."""
750
 
    supports_reprocess = False
 
778
    supports_reprocess = True
751
779
    supports_show_base = False
752
780
 
753
781
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
754
 
                 interesting_ids=None, pb=DummyProgress(), pp=None):
 
782
                 interesting_ids=None, pb=DummyProgress(), pp=None,
 
783
                 reprocess=False):
755
784
        self.this_revision_tree = self._get_revision_tree(this_tree)
756
785
        self.other_revision_tree = self._get_revision_tree(other_tree)
757
786
        super(WeaveMerger, self).__init__(working_tree, this_tree, 
758
787
                                          base_tree, other_tree, 
759
788
                                          interesting_ids=interesting_ids, 
760
 
                                          pb=pb, pp=pp)
 
789
                                          pb=pb, pp=pp, reprocess=reprocess)
761
790
 
762
791
    def _get_revision_tree(self, tree):
763
792
        """Return a revision tree releated to this tree.
787
816
        this_revision_id = self.this_revision_tree.inventory[file_id].revision
788
817
        other_revision_id = \
789
818
            self.other_revision_tree.inventory[file_id].revision
790
 
        plan =  weave.plan_merge(this_revision_id, other_revision_id)
791
 
        return weave.weave_merge(plan, '<<<<<<< TREE\n', 
792
 
                                       '>>>>>>> MERGE-SOURCE\n')
 
819
        wm = WeaveMerge(weave, this_revision_id, other_revision_id, 
 
820
                        '<<<<<<< TREE\n', '>>>>>>> MERGE-SOURCE\n')
 
821
        return wm.merge_lines(self.reprocess)
793
822
 
794
823
    def text_merge(self, file_id, trans_id):
795
824
        """Perform a (weave) text merge for a given file and file-id.
797
826
        and a conflict will be noted.
798
827
        """
799
828
        self._check_file(file_id)
800
 
        lines = list(self._merged_lines(file_id))
801
 
        conflicts = '<<<<<<< TREE\n' in lines
 
829
        lines, conflicts = self._merged_lines(file_id)
 
830
        lines = list(lines)
 
831
        # Note we're checking whether the OUTPUT is binary in this case, 
 
832
        # because we don't want to get into weave merge guts.
 
833
        check_text_lines(lines)
802
834
        self.tt.create_file(lines, trans_id)
803
835
        if conflicts:
804
836
            self._raw_conflicts.append(('text conflict', trans_id))