~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/conflicts.py

  • Committer: Martin Pool
  • Date: 2010-03-15 06:54:44 UTC
  • mto: This revision was merged to the branch mainline in revision 5095.
  • Revision ID: mbp@canonical.com-20100315065444-gfs7vp8te4ez5rc9
Fix typo in ReadVFile.readline (thanks mnordhoff)

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
# point down
19
19
 
20
20
import os
 
21
import re
21
22
 
22
23
from bzrlib.lazy_import import lazy_import
23
24
lazy_import(globals(), """
45
46
 
46
47
 
47
48
class cmd_conflicts(commands.Command):
48
 
    __doc__ = """List files with conflicts.
 
49
    """List files with conflicts.
49
50
 
50
51
    Merge will do its best to combine the changes in two branches, but there
51
52
    are some kinds of problems only a human can fix.  When it encounters those,
98
99
 
99
100
 
100
101
class cmd_resolve(commands.Command):
101
 
    __doc__ = """Mark a conflict as resolved.
 
102
    """Mark a conflict as resolved.
102
103
 
103
104
    Merge will do its best to combine the changes in two branches, but there
104
105
    are some kinds of problems only a human can fix.  When it encounters those,
435
436
    def action_take_other(self, tree):
436
437
        raise NotImplementedError(self.action_take_other)
437
438
 
438
 
    def _resolve_with_cleanups(self, tree, *args, **kwargs):
439
 
        tt = transform.TreeTransform(tree)
440
 
        op = cleanup.OperationWithCleanups(self._resolve)
441
 
        op.add_cleanup(tt.finalize)
442
 
        op.run_simple(tt, *args, **kwargs)
443
 
 
444
439
 
445
440
class PathConflict(Conflict):
446
441
    """A conflict was encountered merging file paths"""
465
460
        # No additional files have been generated here
466
461
        return []
467
462
 
468
 
    def _resolve(self, tt, file_id, path, winner):
469
 
        """Resolve the conflict.
470
 
 
471
 
        :param tt: The TreeTransform where the conflict is resolved.
472
 
        :param file_id: The retained file id.
473
 
        :param path: The retained path.
474
 
        :param winner: 'this' or 'other' indicates which side is the winner.
475
 
        """
476
 
        path_to_create = None
477
 
        if winner == 'this':
478
 
            if self.path == '<deleted>':
479
 
                return # Nothing to do
480
 
            if self.conflict_path == '<deleted>':
481
 
                path_to_create = self.path
482
 
                revid = tt._tree.get_parent_ids()[0]
483
 
        elif winner == 'other':
484
 
            if self.conflict_path == '<deleted>':
485
 
                return  # Nothing to do
486
 
            if self.path == '<deleted>':
487
 
                path_to_create = self.conflict_path
488
 
                # FIXME: If there are more than two parents we may need to
489
 
                # iterate. Taking the last parent is the safer bet in the mean
490
 
                # time. -- vila 20100309
491
 
                revid = tt._tree.get_parent_ids()[-1]
492
 
        else:
493
 
            # Programmer error
494
 
            raise AssertionError('bad winner: %r' % (winner,))
495
 
        if path_to_create is not None:
496
 
            tid = tt.trans_id_tree_path(path_to_create)
497
 
            transform.create_from_tree(
498
 
                tt, tt.trans_id_tree_path(path_to_create),
499
 
                self._revision_tree(tt._tree, revid), file_id)
500
 
            tt.version_file(file_id, tid)
501
 
 
502
 
        # Adjust the path for the retained file id
503
 
        tid = tt.trans_id_file_id(file_id)
504
 
        parent_tid = tt.get_tree_parent(tid)
505
 
        tt.adjust_path(path, parent_tid, tid)
506
 
        tt.apply()
507
 
 
508
 
    def _revision_tree(self, tree, revid):
509
 
        return tree.branch.repository.revision_tree(revid)
510
 
 
511
 
    def _infer_file_id(self, tree):
512
 
        # Prior to bug #531967, file_id wasn't always set, there may still be
513
 
        # conflict files in the wild so we need to cope with them
514
 
        # Establish which path we should use to find back the file-id
515
 
        possible_paths = []
516
 
        for p in (self.path, self.conflict_path):
517
 
            if p == '<deleted>':
518
 
                # special hard-coded path 
519
 
                continue
520
 
            if p is not None:
521
 
                possible_paths.append(p)
522
 
        # Search the file-id in the parents with any path available
523
 
        file_id = None
524
 
        for revid in tree.get_parent_ids():
525
 
            revtree = self._revision_tree(tree, revid)
526
 
            for p in possible_paths:
527
 
                file_id = revtree.path2id(p)
528
 
                if file_id is not None:
529
 
                    return revtree, file_id
530
 
        return None, None
531
 
 
532
463
    def action_take_this(self, tree):
533
 
        if self.file_id is not None:
534
 
            self._resolve_with_cleanups(tree, self.file_id, self.path,
535
 
                                        winner='this')
536
 
        else:
537
 
            # Prior to bug #531967 we need to find back the file_id and restore
538
 
            # the content from there
539
 
            revtree, file_id = self._infer_file_id(tree)
540
 
            tree.revert([revtree.id2path(file_id)],
541
 
                        old_tree=revtree, backups=False)
 
464
        tree.rename_one(self.conflict_path, self.path)
542
465
 
543
466
    def action_take_other(self, tree):
544
 
        if self.file_id is not None:
545
 
            self._resolve_with_cleanups(tree, self.file_id,
546
 
                                        self.conflict_path,
547
 
                                        winner='other')
548
 
        else:
549
 
            # Prior to bug #531967 we need to find back the file_id and restore
550
 
            # the content from there
551
 
            revtree, file_id = self._infer_file_id(tree)
552
 
            tree.revert([revtree.id2path(file_id)],
553
 
                        old_tree=revtree, backups=False)
 
467
        # just acccept bzr proposal
 
468
        pass
554
469
 
555
470
 
556
471
class ContentsConflict(PathConflict):
557
 
    """The files are of different types (or both binary), or not present"""
 
472
    """The files are of different types, or not present"""
558
473
 
559
474
    has_files = True
560
475
 
565
480
    def associated_filenames(self):
566
481
        return [self.path + suffix for suffix in ('.BASE', '.OTHER')]
567
482
 
568
 
    def _resolve(self, tt, suffix_to_remove):
 
483
    def _take_it(self, tt, suffix_to_remove):
569
484
        """Resolve the conflict.
570
485
 
571
486
        :param tt: The TreeTransform where the conflict is resolved.
591
506
        tt.adjust_path(self.path, parent_tid, this_tid)
592
507
        tt.apply()
593
508
 
 
509
    def _take_it_with_cleanups(self, tree, suffix_to_remove):
 
510
        tt = transform.TreeTransform(tree)
 
511
        op = cleanup.OperationWithCleanups(self._take_it)
 
512
        op.add_cleanup(tt.finalize)
 
513
        op.run_simple(tt, suffix_to_remove)
 
514
 
594
515
    def action_take_this(self, tree):
595
 
        self._resolve_with_cleanups(tree, 'OTHER')
 
516
        self._take_it_with_cleanups(tree, 'OTHER')
596
517
 
597
518
    def action_take_other(self, tree):
598
 
        self._resolve_with_cleanups(tree, 'THIS')
 
519
        self._take_it_with_cleanups(tree, 'THIS')
599
520
 
600
521
 
601
522
# FIXME: TextConflict is about a single file-id, there never is a conflict_path
706
627
 
707
628
    typestring = 'parent loop'
708
629
 
709
 
    format = 'Conflict moving %(path)s into %(conflict_path)s. %(action)s.'
 
630
    format = 'Conflict moving %(conflict_path)s into %(path)s.  %(action)s.'
710
631
 
711
632
    def action_take_this(self, tree):
712
633
        # just acccept bzr proposal