~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/conflicts.py

  • Committer: Patch Queue Manager
  • Date: 2011-09-22 14:12:18 UTC
  • mfrom: (6155.3.1 jam)
  • Revision ID: pqm@pqm.ubuntu.com-20110922141218-86s4uu6nqvourw4f
(jameinel) Cleanup comments bzrlib/smart/__init__.py (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2007, 2009 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2009, 2010, 2011 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
18
18
# point down
19
19
 
20
20
import os
21
 
import re
22
21
 
23
22
from bzrlib.lazy_import import lazy_import
24
23
lazy_import(globals(), """
25
24
import errno
26
25
 
27
26
from bzrlib import (
28
 
    builtins,
29
 
    commands,
 
27
    cleanup,
30
28
    errors,
31
29
    osutils,
32
30
    rio,
33
31
    trace,
 
32
    transform,
34
33
    workingtree,
35
34
    )
 
35
from bzrlib.i18n import gettext, ngettext
36
36
""")
37
37
from bzrlib import (
 
38
    commands,
38
39
    option,
39
40
    registry,
40
41
    )
44
45
 
45
46
 
46
47
class cmd_conflicts(commands.Command):
47
 
    """List files with conflicts.
 
48
    __doc__ = """List files with conflicts.
48
49
 
49
50
    Merge will do its best to combine the changes in two branches, but there
50
51
    are some kinds of problems only a human can fix.  When it encounters those,
58
59
    Use bzr resolve when you have fixed a problem.
59
60
    """
60
61
    takes_options = [
 
62
            'directory',
61
63
            option.Option('text',
62
64
                          help='List paths of files with text conflicts.'),
63
65
        ]
64
 
    _see_also = ['resolve']
 
66
    _see_also = ['resolve', 'conflict-types']
65
67
 
66
 
    def run(self, text=False):
67
 
        wt = workingtree.WorkingTree.open_containing(u'.')[0]
 
68
    def run(self, text=False, directory=u'.'):
 
69
        wt = workingtree.WorkingTree.open_containing(directory)[0]
68
70
        for conflict in wt.conflicts():
69
71
            if text:
70
72
                if conflict.typestring != 'text conflict':
71
73
                    continue
72
74
                self.outf.write(conflict.path + '\n')
73
75
            else:
74
 
                self.outf.write(str(conflict) + '\n')
 
76
                self.outf.write(unicode(conflict) + '\n')
75
77
 
76
78
 
77
79
resolve_action_registry = registry.Registry()
80
82
resolve_action_registry.register(
81
83
    'done', 'done', 'Marks the conflict as resolved' )
82
84
resolve_action_registry.register(
83
 
    'keep-mine', 'keep_mine',
 
85
    'take-this', 'take_this',
84
86
    'Resolve the conflict preserving the version in the working tree' )
85
87
resolve_action_registry.register(
86
 
    'take-their', 'take_their',
 
88
    'take-other', 'take_other',
87
89
    'Resolve the conflict taking the merged version into account' )
88
90
resolve_action_registry.default_key = 'done'
89
91
 
97
99
 
98
100
 
99
101
class cmd_resolve(commands.Command):
100
 
    """Mark a conflict as resolved.
 
102
    __doc__ = """Mark a conflict as resolved.
101
103
 
102
104
    Merge will do its best to combine the changes in two branches, but there
103
105
    are some kinds of problems only a human can fix.  When it encounters those,
111
113
    aliases = ['resolved']
112
114
    takes_args = ['file*']
113
115
    takes_options = [
 
116
            'directory',
114
117
            option.Option('all', help='Resolve all conflicts in this tree.'),
115
118
            ResolveActionOption(),
116
119
            ]
117
120
    _see_also = ['conflicts']
118
 
    def run(self, file_list=None, all=False, action=None):
 
121
    def run(self, file_list=None, all=False, action=None, directory=None):
119
122
        if all:
120
123
            if file_list:
121
 
                raise errors.BzrCommandError("If --all is specified,"
122
 
                                             " no FILE may be provided")
123
 
            tree = workingtree.WorkingTree.open_containing('.')[0]
 
124
                raise errors.BzrCommandError(gettext("If --all is specified,"
 
125
                                             " no FILE may be provided"))
 
126
            if directory is None:
 
127
                directory = u'.'
 
128
            tree = workingtree.WorkingTree.open_containing(directory)[0]
124
129
            if action is None:
125
130
                action = 'done'
126
131
        else:
127
 
            tree, file_list = builtins.tree_files(file_list)
 
132
            tree, file_list = workingtree.WorkingTree.open_containing_paths(
 
133
                file_list, directory)
128
134
            if file_list is None:
129
135
                if action is None:
130
136
                    # FIXME: There is a special case here related to the option
140
146
            if file_list is None:
141
147
                un_resolved, resolved = tree.auto_resolve()
142
148
                if len(un_resolved) > 0:
143
 
                    trace.note('%d conflict(s) auto-resolved.', len(resolved))
144
 
                    trace.note('Remaining conflicts:')
 
149
                    trace.note(ngettext('%d conflict auto-resolved.',
 
150
                        '%d conflicts auto-resolved.', len(resolved)),
 
151
                        len(resolved))
 
152
                    trace.note(gettext('Remaining conflicts:'))
145
153
                    for conflict in un_resolved:
146
 
                        trace.note(conflict)
 
154
                        trace.note(unicode(conflict))
147
155
                    return 1
148
156
                else:
149
 
                    trace.note('All conflicts resolved.')
 
157
                    trace.note(gettext('All conflicts resolved.'))
150
158
                    return 0
151
159
            else:
152
160
                # FIXME: This can never occur but the block above needs some
154
162
                # conflict.auto(tree) --vila 091242
155
163
                pass
156
164
        else:
157
 
            resolve(tree, file_list, action=action)
 
165
            before, after = resolve(tree, file_list, action=action)
 
166
            trace.note(ngettext('{0} conflict resolved, {1} remaining',
 
167
                                '{0} conflicts resolved, {1} remaining',
 
168
                                before-after).format(before - after, after))
158
169
 
159
170
 
160
171
def resolve(tree, paths=None, ignore_misses=False, recursive=False,
173
184
    :param action: How the conflict should be resolved,
174
185
    """
175
186
    tree.lock_tree_write()
 
187
    nb_conflicts_after = None
176
188
    try:
177
189
        tree_conflicts = tree.conflicts()
 
190
        nb_conflicts_before = len(tree_conflicts)
178
191
        if paths is None:
179
192
            new_conflicts = ConflictList()
180
193
            to_process = tree_conflicts
188
201
            except NotImplementedError:
189
202
                new_conflicts.append(conflict)
190
203
        try:
 
204
            nb_conflicts_after = len(new_conflicts)
191
205
            tree.set_conflicts(new_conflicts)
192
206
        except errors.UnsupportedOperation:
193
207
            pass
194
208
    finally:
195
209
        tree.unlock()
 
210
    if nb_conflicts_after is None:
 
211
        nb_conflicts_after = nb_conflicts_before
 
212
    return nb_conflicts_before, nb_conflicts_after
196
213
 
197
214
 
198
215
def restore(filename):
278
295
    def to_strings(self):
279
296
        """Generate strings for the provided conflicts"""
280
297
        for conflict in self:
281
 
            yield str(conflict)
 
298
            yield unicode(conflict)
282
299
 
283
300
    def remove_files(self, tree):
284
301
        """Remove the THIS, BASE and OTHER files for listed conflicts"""
377
394
    def __ne__(self, other):
378
395
        return not self.__eq__(other)
379
396
 
380
 
    def __str__(self):
 
397
    def __unicode__(self):
381
398
        return self.format % self.__dict__
382
399
 
383
400
    def __repr__(self):
406
423
 
407
424
        :param tree: The tree passed as a parameter to the method.
408
425
        """
409
 
        meth = getattr(self, action, None)
 
426
        meth = getattr(self, 'action_%s' % action, None)
410
427
        if meth is None:
411
428
            raise NotImplementedError(self.__class__.__name__ + '.' + action)
412
429
        meth(tree)
413
430
 
 
431
    def associated_filenames(self):
 
432
        """The names of the files generated to help resolve the conflict."""
 
433
        raise NotImplementedError(self.associated_filenames)
 
434
 
414
435
    def cleanup(self, tree):
415
 
        raise NotImplementedError(self.cleanup)
 
436
        for fname in self.associated_filenames():
 
437
            try:
 
438
                osutils.delete_any(tree.abspath(fname))
 
439
            except OSError, e:
 
440
                if e.errno != errno.ENOENT:
 
441
                    raise
416
442
 
417
 
    def done(self, tree):
 
443
    def action_done(self, tree):
418
444
        """Mark the conflict as solved once it has been handled."""
419
445
        # This method does nothing but simplifies the design of upper levels.
420
446
        pass
421
447
 
422
 
    def keep_mine(self, tree):
423
 
        raise NotImplementedError(self.keep_mine)
424
 
 
425
 
    def take_their(self, tree):
426
 
        raise NotImplementedError(self.take_their)
 
448
    def action_take_this(self, tree):
 
449
        raise NotImplementedError(self.action_take_this)
 
450
 
 
451
    def action_take_other(self, tree):
 
452
        raise NotImplementedError(self.action_take_other)
 
453
 
 
454
    def _resolve_with_cleanups(self, tree, *args, **kwargs):
 
455
        tt = transform.TreeTransform(tree)
 
456
        op = cleanup.OperationWithCleanups(self._resolve)
 
457
        op.add_cleanup(tt.finalize)
 
458
        op.run_simple(tt, *args, **kwargs)
427
459
 
428
460
 
429
461
class PathConflict(Conflict):
445
477
            s.add('conflict_path', self.conflict_path)
446
478
        return s
447
479
 
448
 
    def cleanup(self, tree):
 
480
    def associated_filenames(self):
449
481
        # No additional files have been generated here
450
 
        pass
451
 
 
452
 
    def keep_mine(self, tree):
453
 
        tree.rename_one(self.conflict_path, self.path)
454
 
 
455
 
    def take_their(self, tree):
456
 
        # just acccept bzr proposal
457
 
        pass
 
482
        return []
 
483
 
 
484
    def _resolve(self, tt, file_id, path, winner):
 
485
        """Resolve the conflict.
 
486
 
 
487
        :param tt: The TreeTransform where the conflict is resolved.
 
488
        :param file_id: The retained file id.
 
489
        :param path: The retained path.
 
490
        :param winner: 'this' or 'other' indicates which side is the winner.
 
491
        """
 
492
        path_to_create = None
 
493
        if winner == 'this':
 
494
            if self.path == '<deleted>':
 
495
                return # Nothing to do
 
496
            if self.conflict_path == '<deleted>':
 
497
                path_to_create = self.path
 
498
                revid = tt._tree.get_parent_ids()[0]
 
499
        elif winner == 'other':
 
500
            if self.conflict_path == '<deleted>':
 
501
                return  # Nothing to do
 
502
            if self.path == '<deleted>':
 
503
                path_to_create = self.conflict_path
 
504
                # FIXME: If there are more than two parents we may need to
 
505
                # iterate. Taking the last parent is the safer bet in the mean
 
506
                # time. -- vila 20100309
 
507
                revid = tt._tree.get_parent_ids()[-1]
 
508
        else:
 
509
            # Programmer error
 
510
            raise AssertionError('bad winner: %r' % (winner,))
 
511
        if path_to_create is not None:
 
512
            tid = tt.trans_id_tree_path(path_to_create)
 
513
            transform.create_from_tree(
 
514
                tt, tid, self._revision_tree(tt._tree, revid), file_id)
 
515
            tt.version_file(file_id, tid)
 
516
        else:
 
517
            tid = tt.trans_id_file_id(file_id)
 
518
        # Adjust the path for the retained file id
 
519
        parent_tid = tt.get_tree_parent(tid)
 
520
        tt.adjust_path(osutils.basename(path), parent_tid, tid)
 
521
        tt.apply()
 
522
 
 
523
    def _revision_tree(self, tree, revid):
 
524
        return tree.branch.repository.revision_tree(revid)
 
525
 
 
526
    def _infer_file_id(self, tree):
 
527
        # Prior to bug #531967, file_id wasn't always set, there may still be
 
528
        # conflict files in the wild so we need to cope with them
 
529
        # Establish which path we should use to find back the file-id
 
530
        possible_paths = []
 
531
        for p in (self.path, self.conflict_path):
 
532
            if p == '<deleted>':
 
533
                # special hard-coded path 
 
534
                continue
 
535
            if p is not None:
 
536
                possible_paths.append(p)
 
537
        # Search the file-id in the parents with any path available
 
538
        file_id = None
 
539
        for revid in tree.get_parent_ids():
 
540
            revtree = self._revision_tree(tree, revid)
 
541
            for p in possible_paths:
 
542
                file_id = revtree.path2id(p)
 
543
                if file_id is not None:
 
544
                    return revtree, file_id
 
545
        return None, None
 
546
 
 
547
    def action_take_this(self, tree):
 
548
        if self.file_id is not None:
 
549
            self._resolve_with_cleanups(tree, self.file_id, self.path,
 
550
                                        winner='this')
 
551
        else:
 
552
            # Prior to bug #531967 we need to find back the file_id and restore
 
553
            # the content from there
 
554
            revtree, file_id = self._infer_file_id(tree)
 
555
            tree.revert([revtree.id2path(file_id)],
 
556
                        old_tree=revtree, backups=False)
 
557
 
 
558
    def action_take_other(self, tree):
 
559
        if self.file_id is not None:
 
560
            self._resolve_with_cleanups(tree, self.file_id,
 
561
                                        self.conflict_path,
 
562
                                        winner='other')
 
563
        else:
 
564
            # Prior to bug #531967 we need to find back the file_id and restore
 
565
            # the content from there
 
566
            revtree, file_id = self._infer_file_id(tree)
 
567
            tree.revert([revtree.id2path(file_id)],
 
568
                        old_tree=revtree, backups=False)
458
569
 
459
570
 
460
571
class ContentsConflict(PathConflict):
461
 
    """The files are of different types, or not present"""
 
572
    """The files are of different types (or both binary), or not present"""
462
573
 
463
574
    has_files = True
464
575
 
466
577
 
467
578
    format = 'Contents conflict in %(path)s'
468
579
 
469
 
    def cleanup(self, tree):
470
 
        for suffix in ('.BASE', '.OTHER'):
471
 
            try:
472
 
                osutils.delete_any(tree.abspath(self.path + suffix))
473
 
            except OSError, e:
474
 
                if e.errno != errno.ENOENT:
475
 
                    raise
476
 
 
477
 
    # FIXME: I smell something weird here and it seems we should be able to be
478
 
    # more coherent with some other conflict ? bzr *did* a choice there but
479
 
    # neither keep_mine nor take_their reflect that... -- vila 091224
480
 
    def keep_mine(self, tree):
481
 
        tree.remove([self.path + '.OTHER'], force=True, keep_files=False)
482
 
 
483
 
    def take_their(self, tree):
484
 
        tree.remove([self.path], force=True, keep_files=False)
485
 
 
486
 
 
487
 
 
488
 
# FIXME: TextConflict is about a single file-id, there never is a conflict_path
489
 
# attribute so we shouldn't inherit from PathConflict but simply from Conflict
 
580
    def associated_filenames(self):
 
581
        return [self.path + suffix for suffix in ('.BASE', '.OTHER')]
 
582
 
 
583
    def _resolve(self, tt, suffix_to_remove):
 
584
        """Resolve the conflict.
 
585
 
 
586
        :param tt: The TreeTransform where the conflict is resolved.
 
587
        :param suffix_to_remove: Either 'THIS' or 'OTHER'
 
588
 
 
589
        The resolution is symmetric: when taking THIS, OTHER is deleted and
 
590
        item.THIS is renamed into item and vice-versa.
 
591
        """
 
592
        try:
 
593
            # Delete 'item.THIS' or 'item.OTHER' depending on
 
594
            # suffix_to_remove
 
595
            tt.delete_contents(
 
596
                tt.trans_id_tree_path(self.path + '.' + suffix_to_remove))
 
597
        except errors.NoSuchFile:
 
598
            # There are valid cases where 'item.suffix_to_remove' either
 
599
            # never existed or was already deleted (including the case
 
600
            # where the user deleted it)
 
601
            pass
 
602
        try:
 
603
            this_path = tt._tree.id2path(self.file_id)
 
604
        except errors.NoSuchId:
 
605
            # The file is not present anymore. This may happen if the user
 
606
            # deleted the file either manually or when resolving a conflict on
 
607
            # the parent.  We may raise some exception to indicate that the
 
608
            # conflict doesn't exist anymore and as such doesn't need to be
 
609
            # resolved ? -- vila 20110615 
 
610
            this_tid = None
 
611
        else:
 
612
            this_tid = tt.trans_id_tree_path(this_path)
 
613
        if this_tid is not None:
 
614
            # Rename 'item.suffix_to_remove' (note that if
 
615
            # 'item.suffix_to_remove' has been deleted, this is a no-op)
 
616
            parent_tid = tt.get_tree_parent(this_tid)
 
617
            tt.adjust_path(osutils.basename(self.path), parent_tid, this_tid)
 
618
            tt.apply()
 
619
 
 
620
    def action_take_this(self, tree):
 
621
        self._resolve_with_cleanups(tree, 'OTHER')
 
622
 
 
623
    def action_take_other(self, tree):
 
624
        self._resolve_with_cleanups(tree, 'THIS')
 
625
 
490
626
 
491
627
# TODO: There should be a base revid attribute to better inform the user about
492
628
# how the conflicts were generated.
493
 
class TextConflict(PathConflict):
 
629
class TextConflict(Conflict):
494
630
    """The merge algorithm could not resolve all differences encountered."""
495
631
 
496
632
    has_files = True
499
635
 
500
636
    format = 'Text conflict in %(path)s'
501
637
 
502
 
    def cleanup(self, tree):
503
 
        for suffix in CONFLICT_SUFFIXES:
504
 
            try:
505
 
                osutils.delete_any(tree.abspath(self.path+suffix))
506
 
            except OSError, e:
507
 
                if e.errno != errno.ENOENT:
508
 
                    raise
 
638
    rformat = '%(class)s(%(path)r, %(file_id)r)'
 
639
 
 
640
    def associated_filenames(self):
 
641
        return [self.path + suffix for suffix in CONFLICT_SUFFIXES]
 
642
 
 
643
    def _resolve(self, tt, winner_suffix):
 
644
        """Resolve the conflict by copying one of .THIS or .OTHER into file.
 
645
 
 
646
        :param tt: The TreeTransform where the conflict is resolved.
 
647
        :param winner_suffix: Either 'THIS' or 'OTHER'
 
648
 
 
649
        The resolution is symmetric, when taking THIS, item.THIS is renamed
 
650
        into item and vice-versa. This takes one of the files as a whole
 
651
        ignoring every difference that could have been merged cleanly.
 
652
        """
 
653
        # To avoid useless copies, we switch item and item.winner_suffix, only
 
654
        # item will exist after the conflict has been resolved anyway.
 
655
        item_tid = tt.trans_id_file_id(self.file_id)
 
656
        item_parent_tid = tt.get_tree_parent(item_tid)
 
657
        winner_path = self.path + '.' + winner_suffix
 
658
        winner_tid = tt.trans_id_tree_path(winner_path)
 
659
        winner_parent_tid = tt.get_tree_parent(winner_tid)
 
660
        # Switch the paths to preserve the content
 
661
        tt.adjust_path(osutils.basename(self.path),
 
662
                       winner_parent_tid, winner_tid)
 
663
        tt.adjust_path(osutils.basename(winner_path), item_parent_tid, item_tid)
 
664
        # Associate the file_id to the right content
 
665
        tt.unversion_file(item_tid)
 
666
        tt.version_file(self.file_id, winner_tid)
 
667
        tt.apply()
 
668
 
 
669
    def action_take_this(self, tree):
 
670
        self._resolve_with_cleanups(tree, 'THIS')
 
671
 
 
672
    def action_take_other(self, tree):
 
673
        self._resolve_with_cleanups(tree, 'OTHER')
509
674
 
510
675
 
511
676
class HandledConflict(Conflict):
527
692
        s.add('action', self.action)
528
693
        return s
529
694
 
530
 
    def cleanup(self, tree):
531
 
        """Nothing to cleanup."""
532
 
        pass
 
695
    def associated_filenames(self):
 
696
        # Nothing has been generated here
 
697
        return []
533
698
 
534
699
 
535
700
class HandledPathConflict(HandledConflict):
577
742
 
578
743
    format = 'Conflict adding file %(conflict_path)s.  %(action)s %(path)s.'
579
744
 
580
 
    def keep_mine(self, tree):
 
745
    def action_take_this(self, tree):
581
746
        tree.remove([self.conflict_path], force=True, keep_files=False)
582
747
        tree.rename_one(self.path, self.conflict_path)
583
748
 
584
 
    def take_their(self, tree):
 
749
    def action_take_other(self, tree):
585
750
        tree.remove([self.path], force=True, keep_files=False)
586
751
 
587
752
 
598
763
 
599
764
    typestring = 'parent loop'
600
765
 
601
 
    format = 'Conflict moving %(conflict_path)s into %(path)s.  %(action)s.'
 
766
    format = 'Conflict moving %(path)s into %(conflict_path)s. %(action)s.'
602
767
 
603
 
    def keep_mine(self, tree):
 
768
    def action_take_this(self, tree):
604
769
        # just acccept bzr proposal
605
770
        pass
606
771
 
607
 
    def take_their(self, tree):
608
 
        # FIXME: We shouldn't have to manipulate so many paths here (and there
609
 
        # is probably a bug or two...)
610
 
        conflict_base_path = osutils.basename(self.conflict_path)
611
 
        base_path = osutils.basename(self.path)
612
 
        tree.rename_one(self.conflict_path, conflict_base_path)
613
 
        tree.rename_one(self.path,
614
 
                        osutils.joinpath([conflict_base_path, base_path]))
 
772
    def action_take_other(self, tree):
 
773
        tt = transform.TreeTransform(tree)
 
774
        try:
 
775
            p_tid = tt.trans_id_file_id(self.file_id)
 
776
            parent_tid = tt.get_tree_parent(p_tid)
 
777
            cp_tid = tt.trans_id_file_id(self.conflict_file_id)
 
778
            cparent_tid = tt.get_tree_parent(cp_tid)
 
779
            tt.adjust_path(osutils.basename(self.path), cparent_tid, cp_tid)
 
780
            tt.adjust_path(osutils.basename(self.conflict_path),
 
781
                           parent_tid, p_tid)
 
782
            tt.apply()
 
783
        finally:
 
784
            tt.finalize()
615
785
 
616
786
 
617
787
class UnversionedParent(HandledConflict):
628
798
    # FIXME: We silently do nothing to make tests pass, but most probably the
629
799
    # conflict shouldn't exist (the long story is that the conflict is
630
800
    # generated with another one that can be resolved properly) -- vila 091224
631
 
    def keep_mine(self, tree):
 
801
    def action_take_this(self, tree):
632
802
        pass
633
803
 
634
 
    def take_their(self, tree):
 
804
    def action_take_other(self, tree):
635
805
        pass
636
806
 
637
807
 
646
816
 
647
817
    format = 'Conflict adding files to %(path)s.  %(action)s.'
648
818
 
649
 
    def keep_mine(self, tree):
 
819
    def action_take_this(self, tree):
650
820
        tree.remove([self.path], force=True, keep_files=False)
651
821
 
652
 
    def take_their(self, tree):
 
822
    def action_take_other(self, tree):
653
823
        # just acccept bzr proposal
654
824
        pass
655
825
 
668
838
    # FIXME: It's a bit strange that the default action is not coherent with
669
839
    # MissingParent from the *user* pov.
670
840
 
671
 
    def keep_mine(self, tree):
 
841
    def action_take_this(self, tree):
672
842
        # just acccept bzr proposal
673
843
        pass
674
844
 
675
 
    def take_their(self, tree):
 
845
    def action_take_other(self, tree):
676
846
        tree.remove([self.path], force=True, keep_files=False)
677
847
 
678
848
 
686
856
    format = "Conflict: %(path)s is not a directory, but has files in it."\
687
857
             "  %(action)s."
688
858
 
689
 
    def keep_mine(self, tree):
 
859
    # FIXME: .OTHER should be used instead of .new when the conflict is created
 
860
 
 
861
    def action_take_this(self, tree):
690
862
        # FIXME: we should preserve that path when the conflict is generated !
691
863
        if self.path.endswith('.new'):
692
864
            conflict_path = self.path[:-(len('.new'))]
693
865
            tree.remove([self.path], force=True, keep_files=False)
694
866
            tree.add(conflict_path)
695
867
        else:
696
 
            raise NotImplementedError(self.keep_mine)
 
868
            raise NotImplementedError(self.action_take_this)
697
869
 
698
 
    def take_their(self, tree):
 
870
    def action_take_other(self, tree):
699
871
        # FIXME: we should preserve that path when the conflict is generated !
700
872
        if self.path.endswith('.new'):
701
873
            conflict_path = self.path[:-(len('.new'))]
702
874
            tree.remove([conflict_path], force=True, keep_files=False)
703
875
            tree.rename_one(self.path, conflict_path)
704
876
        else:
705
 
            raise NotImplementedError(self.take_their)
 
877
            raise NotImplementedError(self.action_take_other)
706
878
 
707
879
 
708
880
ctype = {}