/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 breezy/conflicts.py

  • Committer: Jelmer Vernooij
  • Date: 2018-06-14 17:59:16 UTC
  • mto: This revision was merged to the branch mainline in revision 7065.
  • Revision ID: jelmer@jelmer.uk-20180614175916-a2e2xh5k533guq1x
Move breezy.plugins.git to breezy.git.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
# TODO: 'brz resolve' should accept a directory name and work from that
18
18
# point down
19
19
 
 
20
from __future__ import absolute_import
 
21
 
20
22
import os
21
 
import re
22
23
 
23
24
from .lazy_import import lazy_import
24
25
lazy_import(globals(), """
25
26
import errno
26
27
 
27
28
from breezy import (
 
29
    cleanup,
 
30
    errors,
28
31
    osutils,
29
32
    rio,
30
33
    trace,
35
38
""")
36
39
from . import (
37
40
    cache_utf8,
38
 
    errors,
39
41
    commands,
40
42
    option,
41
43
    registry,
42
44
    )
 
45
from .sixish import text_type
43
46
 
44
47
 
45
48
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
60
63
    Use brz resolve when you have fixed a problem.
61
64
    """
62
65
    takes_options = [
63
 
        'directory',
64
 
        option.Option('text',
65
 
                      help='List paths of files with text conflicts.'),
 
66
            'directory',
 
67
            option.Option('text',
 
68
                          help='List paths of files with text conflicts.'),
66
69
        ]
67
70
    _see_also = ['resolve', 'conflict-types']
68
71
 
74
77
                    continue
75
78
                self.outf.write(conflict.path + '\n')
76
79
            else:
77
 
                self.outf.write(str(conflict) + '\n')
 
80
                self.outf.write(text_type(conflict) + '\n')
78
81
 
79
82
 
80
83
resolve_action_registry = registry.Registry()
81
84
 
82
85
 
83
86
resolve_action_registry.register(
84
 
    'auto', 'auto', 'Detect whether conflict has been resolved by user.')
85
 
resolve_action_registry.register(
86
87
    'done', 'done', 'Marks the conflict as resolved.')
87
88
resolve_action_registry.register(
88
89
    'take-this', 'take_this',
92
93
    'Resolve the conflict taking the merged version into account.')
93
94
resolve_action_registry.default_key = 'done'
94
95
 
95
 
 
96
96
class ResolveActionOption(option.RegistryOption):
97
97
 
98
98
    def __init__(self):
117
117
    aliases = ['resolved']
118
118
    takes_args = ['file*']
119
119
    takes_options = [
120
 
        'directory',
121
 
        option.Option('all', help='Resolve all conflicts in this tree.'),
122
 
        ResolveActionOption(),
123
 
        ]
 
120
            'directory',
 
121
            option.Option('all', help='Resolve all conflicts in this tree.'),
 
122
            ResolveActionOption(),
 
123
            ]
124
124
    _see_also = ['conflicts']
125
 
 
126
125
    def run(self, file_list=None, all=False, action=None, directory=None):
127
126
        if all:
128
127
            if file_list:
129
 
                raise errors.CommandError(gettext("If --all is specified,"
130
 
                                                     " no FILE may be provided"))
 
128
                raise errors.BzrCommandError(gettext("If --all is specified,"
 
129
                                             " no FILE may be provided"))
131
130
            if directory is None:
132
131
                directory = u'.'
133
132
            tree = workingtree.WorkingTree.open_containing(directory)[0]
136
135
        else:
137
136
            tree, file_list = workingtree.WorkingTree.open_containing_paths(
138
137
                file_list, directory)
139
 
            if action is None:
140
 
                if file_list is None:
 
138
            if file_list is None:
 
139
                if action is None:
 
140
                    # FIXME: There is a special case here related to the option
 
141
                    # handling that could be clearer and easier to discover by
 
142
                    # providing an --auto action (bug #344013 and #383396) and
 
143
                    # make it mandatory instead of implicit and active only
 
144
                    # when no file_list is provided -- vila 091229
141
145
                    action = 'auto'
142
 
                else:
 
146
            else:
 
147
                if action is None:
143
148
                    action = 'done'
144
 
        before, after = resolve(tree, file_list, action=action)
145
 
        # GZ 2012-07-27: Should unify UI below now that auto is less magical.
146
 
        if action == 'auto' and file_list is None:
147
 
            if after > 0:
148
 
                trace.note(
149
 
                    ngettext('%d conflict auto-resolved.',
150
 
                             '%d conflicts auto-resolved.', before - after),
151
 
                    before - after)
152
 
                trace.note(gettext('Remaining conflicts:'))
153
 
                for conflict in tree.conflicts():
154
 
                    trace.note(str(conflict))
155
 
                return 1
 
149
        if action == 'auto':
 
150
            if file_list is None:
 
151
                un_resolved, resolved = tree.auto_resolve()
 
152
                if len(un_resolved) > 0:
 
153
                    trace.note(ngettext('%d conflict auto-resolved.',
 
154
                        '%d conflicts auto-resolved.', len(resolved)),
 
155
                        len(resolved))
 
156
                    trace.note(gettext('Remaining conflicts:'))
 
157
                    for conflict in un_resolved:
 
158
                        trace.note(text_type(conflict))
 
159
                    return 1
 
160
                else:
 
161
                    trace.note(gettext('All conflicts resolved.'))
 
162
                    return 0
156
163
            else:
157
 
                trace.note(gettext('All conflicts resolved.'))
158
 
                return 0
 
164
                # FIXME: This can never occur but the block above needs some
 
165
                # refactoring to transfer tree.auto_resolve() to
 
166
                # conflict.auto(tree) --vila 091242
 
167
                pass
159
168
        else:
 
169
            before, after = resolve(tree, file_list, action=action)
160
170
            trace.note(ngettext('{0} conflict resolved, {1} remaining',
161
171
                                '{0} conflicts resolved, {1} remaining',
162
 
                                before - after).format(before - after, after))
 
172
                                before-after).format(before - after, after))
163
173
 
164
174
 
165
175
def resolve(tree, paths=None, ignore_misses=False, recursive=False,
286
296
    def to_strings(self):
287
297
        """Generate strings for the provided conflicts"""
288
298
        for conflict in self:
289
 
            yield str(conflict)
 
299
            yield text_type(conflict)
290
300
 
291
301
    def remove_files(self, tree):
292
302
        """Remove the THIS, BASE and OTHER files for listed conflicts"""
359
369
        self.path = path
360
370
        # the factory blindly transfers the Stanza values to __init__ and
361
371
        # Stanza is purely a Unicode api.
362
 
        if isinstance(file_id, str):
 
372
        if isinstance(file_id, text_type):
363
373
            file_id = cache_utf8.encode(file_id)
364
 
        self.file_id = file_id
 
374
        self.file_id = osutils.safe_file_id(file_id)
365
375
 
366
376
    def as_stanza(self):
367
377
        s = rio.Stanza(type=self.typestring, path=self.path)
376
386
    def __cmp__(self, other):
377
387
        if getattr(other, "_cmp_list", None) is None:
378
388
            return -1
379
 
        x = self._cmp_list()
380
 
        y = other._cmp_list()
381
 
        return (x > y) - (x < y)
 
389
        return cmp(self._cmp_list(), other._cmp_list())
382
390
 
383
391
    def __hash__(self):
384
392
        return hash((type(self), self.path, self.file_id))
390
398
        return not self.__eq__(other)
391
399
 
392
400
    def __unicode__(self):
393
 
        return self.describe()
394
 
 
395
 
    def __str__(self):
396
 
        return self.describe()
397
 
 
398
 
    def describe(self):
399
401
        return self.format % self.__dict__
400
402
 
401
403
    def __repr__(self):
441
443
                if e.errno != errno.ENOENT:
442
444
                    raise
443
445
 
444
 
    def action_auto(self, tree):
445
 
        raise NotImplementedError(self.action_auto)
446
 
 
447
446
    def action_done(self, tree):
448
447
        """Mark the conflict as solved once it has been handled."""
449
448
        # This method does nothing but simplifies the design of upper levels.
456
455
        raise NotImplementedError(self.action_take_other)
457
456
 
458
457
    def _resolve_with_cleanups(self, tree, *args, **kwargs):
459
 
        with tree.transform() as tt:
460
 
            self._resolve(tt, *args, **kwargs)
 
458
        tt = transform.TreeTransform(tree)
 
459
        op = cleanup.OperationWithCleanups(self._resolve)
 
460
        op.add_cleanup(tt.finalize)
 
461
        op.run_simple(tt, *args, **kwargs)
461
462
 
462
463
 
463
464
class PathConflict(Conflict):
494
495
        path_to_create = None
495
496
        if winner == 'this':
496
497
            if self.path == '<deleted>':
497
 
                return  # Nothing to do
 
498
                return # Nothing to do
498
499
            if self.conflict_path == '<deleted>':
499
500
                path_to_create = self.path
500
501
                revid = tt._tree.get_parent_ids()[0]
514
515
            tid = tt.trans_id_tree_path(path_to_create)
515
516
            tree = self._revision_tree(tt._tree, revid)
516
517
            transform.create_from_tree(
517
 
                tt, tid, tree, tree.id2path(file_id))
518
 
            tt.version_file(tid, file_id=file_id)
 
518
                tt, tid, tree, tree.id2path(file_id), file_id=file_id)
 
519
            tt.version_file(file_id, tid)
519
520
        else:
520
521
            tid = tt.trans_id_file_id(file_id)
521
522
        # Adjust the path for the retained file id
533
534
        possible_paths = []
534
535
        for p in (self.path, self.conflict_path):
535
536
            if p == '<deleted>':
536
 
                # special hard-coded path
 
537
                # special hard-coded path 
537
538
                continue
538
539
            if p is not None:
539
540
                possible_paths.append(p)
609
610
            # deleted the file either manually or when resolving a conflict on
610
611
            # the parent.  We may raise some exception to indicate that the
611
612
            # conflict doesn't exist anymore and as such doesn't need to be
612
 
            # resolved ? -- vila 20110615
 
613
            # resolved ? -- vila 20110615 
613
614
            this_tid = None
614
615
        else:
615
616
            this_tid = tt.trans_id_tree_path(this_path)
640
641
 
641
642
    rformat = '%(class)s(%(path)r, %(file_id)r)'
642
643
 
643
 
    _conflict_re = re.compile(b'^(<{7}|={7}|>{7})')
644
 
 
645
644
    def associated_filenames(self):
646
645
        return [self.path + suffix for suffix in CONFLICT_SUFFIXES]
647
646
 
665
664
        # Switch the paths to preserve the content
666
665
        tt.adjust_path(osutils.basename(self.path),
667
666
                       winner_parent_tid, winner_tid)
668
 
        tt.adjust_path(osutils.basename(winner_path),
669
 
                       item_parent_tid, item_tid)
 
667
        tt.adjust_path(osutils.basename(winner_path), item_parent_tid, item_tid)
670
668
        # Associate the file_id to the right content
671
669
        tt.unversion_file(item_tid)
672
 
        tt.version_file(winner_tid, file_id=self.file_id)
 
670
        tt.version_file(self.file_id, winner_tid)
673
671
        tt.apply()
674
672
 
675
 
    def action_auto(self, tree):
676
 
        # GZ 2012-07-27: Using NotImplementedError to signal that a conflict
677
 
        #                can't be auto resolved does not seem ideal.
678
 
        try:
679
 
            kind = tree.kind(self.path)
680
 
        except errors.NoSuchFile:
681
 
            return
682
 
        if kind != 'file':
683
 
            raise NotImplementedError("Conflict is not a file")
684
 
        conflict_markers_in_line = self._conflict_re.search
685
 
        # GZ 2012-07-27: What if not tree.has_id(self.file_id) due to removal?
686
 
        with tree.get_file(self.path) as f:
687
 
            for line in f:
688
 
                if conflict_markers_in_line(line):
689
 
                    raise NotImplementedError("Conflict markers present")
690
 
 
691
673
    def action_take_this(self, tree):
692
674
        self._resolve_with_cleanups(tree, 'THIS')
693
675
 
733
715
        self.conflict_path = conflict_path
734
716
        # the factory blindly transfers the Stanza values to __init__,
735
717
        # so they can be unicode.
736
 
        if isinstance(conflict_file_id, str):
 
718
        if isinstance(conflict_file_id, text_type):
737
719
            conflict_file_id = cache_utf8.encode(conflict_file_id)
738
 
        self.conflict_file_id = conflict_file_id
 
720
        self.conflict_file_id = osutils.safe_file_id(conflict_file_id)
739
721
 
740
722
    def _cmp_list(self):
741
723
        return HandledConflict._cmp_list(self) + [self.conflict_path,
793
775
        pass
794
776
 
795
777
    def action_take_other(self, tree):
796
 
        with tree.transform() as tt:
 
778
        tt = transform.TreeTransform(tree)
 
779
        try:
797
780
            p_tid = tt.trans_id_file_id(self.file_id)
798
781
            parent_tid = tt.get_tree_parent(p_tid)
799
782
            cp_tid = tt.trans_id_file_id(self.conflict_file_id)
802
785
            tt.adjust_path(osutils.basename(self.conflict_path),
803
786
                           parent_tid, p_tid)
804
787
            tt.apply()
 
788
        finally:
 
789
            tt.finalize()
805
790
 
806
791
 
807
792
class UnversionedParent(HandledConflict):
906
891
    for conflict_type in conflict_types:
907
892
        ctype[conflict_type.typestring] = conflict_type
908
893
 
909
 
 
910
894
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
911
895
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
912
896
               DeletingParent, NonDirectoryParent)