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

  • Committer: Richard Wilbur
  • Date: 2016-02-04 19:07:28 UTC
  • mto: This revision was merged to the branch mainline in revision 6618.
  • Revision ID: richard.wilbur@gmail.com-20160204190728-p0zvfii6zase0fw7
Update COPYING.txt from the original http://www.gnu.org/licenses/gpl-2.0.txt  (Only differences were in whitespace.)  Thanks to Petr Stodulka for pointing out the discrepancy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
# TODO: 'brz resolve' should accept a directory name and work from that
 
17
# TODO: 'bzr 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
 
from .lazy_import import lazy_import
 
24
from bzrlib.lazy_import import lazy_import
24
25
lazy_import(globals(), """
25
26
import errno
26
27
 
27
 
from breezy import (
 
28
from bzrlib import (
 
29
    cleanup,
 
30
    errors,
28
31
    osutils,
29
32
    rio,
30
33
    trace,
31
34
    transform,
32
35
    workingtree,
33
36
    )
34
 
from breezy.i18n import gettext, ngettext
 
37
from bzrlib.i18n import gettext, ngettext
35
38
""")
36
 
from . import (
37
 
    cache_utf8,
38
 
    errors,
 
39
from bzrlib import (
39
40
    commands,
40
41
    option,
41
42
    registry,
57
58
    is supplied, the pathnames of files with text conflicts are listed,
58
59
    instead.  (This is useful for editing all files with text conflicts.)
59
60
 
60
 
    Use brz resolve when you have fixed a problem.
 
61
    Use bzr resolve when you have fixed a problem.
61
62
    """
62
63
    takes_options = [
63
 
        'directory',
64
 
        option.Option('text',
65
 
                      help='List paths of files with text conflicts.'),
 
64
            'directory',
 
65
            option.Option('text',
 
66
                          help='List paths of files with text conflicts.'),
66
67
        ]
67
68
    _see_also = ['resolve', 'conflict-types']
68
69
 
74
75
                    continue
75
76
                self.outf.write(conflict.path + '\n')
76
77
            else:
77
 
                self.outf.write(str(conflict) + '\n')
 
78
                self.outf.write(unicode(conflict) + '\n')
78
79
 
79
80
 
80
81
resolve_action_registry = registry.Registry()
81
82
 
82
83
 
83
84
resolve_action_registry.register(
84
 
    'auto', 'auto', 'Detect whether conflict has been resolved by user.')
85
 
resolve_action_registry.register(
86
85
    'done', 'done', 'Marks the conflict as resolved.')
87
86
resolve_action_registry.register(
88
87
    'take-this', 'take_this',
92
91
    'Resolve the conflict taking the merged version into account.')
93
92
resolve_action_registry.default_key = 'done'
94
93
 
95
 
 
96
94
class ResolveActionOption(option.RegistryOption):
97
95
 
98
96
    def __init__(self):
110
108
    it will mark a conflict.  A conflict means that you need to fix something,
111
109
    before you can commit.
112
110
 
113
 
    Once you have fixed a problem, use "brz resolve" to automatically mark
114
 
    text conflicts as fixed, "brz resolve FILE" to mark a specific conflict as
115
 
    resolved, or "brz resolve --all" to mark all conflicts as resolved.
 
111
    Once you have fixed a problem, use "bzr resolve" to automatically mark
 
112
    text conflicts as fixed, "bzr resolve FILE" to mark a specific conflict as
 
113
    resolved, or "bzr resolve --all" to mark all conflicts as resolved.
116
114
    """
117
115
    aliases = ['resolved']
118
116
    takes_args = ['file*']
119
117
    takes_options = [
120
 
        'directory',
121
 
        option.Option('all', help='Resolve all conflicts in this tree.'),
122
 
        ResolveActionOption(),
123
 
        ]
 
118
            'directory',
 
119
            option.Option('all', help='Resolve all conflicts in this tree.'),
 
120
            ResolveActionOption(),
 
121
            ]
124
122
    _see_also = ['conflicts']
125
 
 
126
123
    def run(self, file_list=None, all=False, action=None, directory=None):
127
124
        if all:
128
125
            if file_list:
129
 
                raise errors.CommandError(gettext("If --all is specified,"
130
 
                                                     " no FILE may be provided"))
 
126
                raise errors.BzrCommandError(gettext("If --all is specified,"
 
127
                                             " no FILE may be provided"))
131
128
            if directory is None:
132
129
                directory = u'.'
133
130
            tree = workingtree.WorkingTree.open_containing(directory)[0]
136
133
        else:
137
134
            tree, file_list = workingtree.WorkingTree.open_containing_paths(
138
135
                file_list, directory)
139
 
            if action is None:
140
 
                if file_list is None:
 
136
            if file_list is None:
 
137
                if action is None:
 
138
                    # FIXME: There is a special case here related to the option
 
139
                    # handling that could be clearer and easier to discover by
 
140
                    # providing an --auto action (bug #344013 and #383396) and
 
141
                    # make it mandatory instead of implicit and active only
 
142
                    # when no file_list is provided -- vila 091229
141
143
                    action = 'auto'
142
 
                else:
 
144
            else:
 
145
                if action is None:
143
146
                    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
 
147
        if action == 'auto':
 
148
            if file_list is None:
 
149
                un_resolved, resolved = tree.auto_resolve()
 
150
                if len(un_resolved) > 0:
 
151
                    trace.note(ngettext('%d conflict auto-resolved.',
 
152
                        '%d conflicts auto-resolved.', len(resolved)),
 
153
                        len(resolved))
 
154
                    trace.note(gettext('Remaining conflicts:'))
 
155
                    for conflict in un_resolved:
 
156
                        trace.note(unicode(conflict))
 
157
                    return 1
 
158
                else:
 
159
                    trace.note(gettext('All conflicts resolved.'))
 
160
                    return 0
156
161
            else:
157
 
                trace.note(gettext('All conflicts resolved.'))
158
 
                return 0
 
162
                # FIXME: This can never occur but the block above needs some
 
163
                # refactoring to transfer tree.auto_resolve() to
 
164
                # conflict.auto(tree) --vila 091242
 
165
                pass
159
166
        else:
 
167
            before, after = resolve(tree, file_list, action=action)
160
168
            trace.note(ngettext('{0} conflict resolved, {1} remaining',
161
169
                                '{0} conflicts resolved, {1} remaining',
162
 
                                before - after).format(before - after, after))
 
170
                                before-after).format(before - after, after))
163
171
 
164
172
 
165
173
def resolve(tree, paths=None, ignore_misses=False, recursive=False,
177
185
        paths do not have conflicts.
178
186
    :param action: How the conflict should be resolved,
179
187
    """
 
188
    tree.lock_tree_write()
180
189
    nb_conflicts_after = None
181
 
    with tree.lock_tree_write():
 
190
    try:
182
191
        tree_conflicts = tree.conflicts()
183
192
        nb_conflicts_before = len(tree_conflicts)
184
193
        if paths is None:
198
207
            tree.set_conflicts(new_conflicts)
199
208
        except errors.UnsupportedOperation:
200
209
            pass
 
210
    finally:
 
211
        tree.unlock()
201
212
    if nb_conflicts_after is None:
202
213
        nb_conflicts_after = nb_conflicts_before
203
214
    return nb_conflicts_before, nb_conflicts_after
212
223
    try:
213
224
        osutils.rename(filename + ".THIS", filename)
214
225
        conflicted = True
215
 
    except OSError as e:
 
226
    except OSError, e:
216
227
        if e.errno != errno.ENOENT:
217
228
            raise
218
229
    try:
219
230
        os.unlink(filename + ".BASE")
220
231
        conflicted = True
221
 
    except OSError as e:
 
232
    except OSError, e:
222
233
        if e.errno != errno.ENOENT:
223
234
            raise
224
235
    try:
225
236
        os.unlink(filename + ".OTHER")
226
237
        conflicted = True
227
 
    except OSError as e:
 
238
    except OSError, e:
228
239
        if e.errno != errno.ENOENT:
229
240
            raise
230
241
    if not conflicted:
286
297
    def to_strings(self):
287
298
        """Generate strings for the provided conflicts"""
288
299
        for conflict in self:
289
 
            yield str(conflict)
 
300
            yield unicode(conflict)
290
301
 
291
302
    def remove_files(self, tree):
292
303
        """Remove the THIS, BASE and OTHER files for listed conflicts"""
343
354
        if ignore_misses is not True:
344
355
            for path in [p for p in paths if p not in selected_paths]:
345
356
                if not os.path.exists(tree.abspath(path)):
346
 
                    print("%s does not exist" % path)
 
357
                    print "%s does not exist" % path
347
358
                else:
348
 
                    print("%s is not conflicted" % path)
 
359
                    print "%s is not conflicted" % path
349
360
        return new_conflicts, selected_conflicts
350
361
 
351
362
 
357
368
 
358
369
    def __init__(self, path, file_id=None):
359
370
        self.path = path
360
 
        # the factory blindly transfers the Stanza values to __init__ and
361
 
        # Stanza is purely a Unicode api.
362
 
        if isinstance(file_id, str):
363
 
            file_id = cache_utf8.encode(file_id)
364
 
        self.file_id = file_id
 
371
        # warn turned off, because the factory blindly transfers the Stanza
 
372
        # values to __init__ and Stanza is purely a Unicode api.
 
373
        self.file_id = osutils.safe_file_id(file_id, warn=False)
365
374
 
366
375
    def as_stanza(self):
367
376
        s = rio.Stanza(type=self.typestring, path=self.path)
376
385
    def __cmp__(self, other):
377
386
        if getattr(other, "_cmp_list", None) is None:
378
387
            return -1
379
 
        x = self._cmp_list()
380
 
        y = other._cmp_list()
381
 
        return (x > y) - (x < y)
 
388
        return cmp(self._cmp_list(), other._cmp_list())
382
389
 
383
390
    def __hash__(self):
384
391
        return hash((type(self), self.path, self.file_id))
390
397
        return not self.__eq__(other)
391
398
 
392
399
    def __unicode__(self):
393
 
        return self.describe()
394
 
 
395
 
    def __str__(self):
396
 
        return self.describe()
397
 
 
398
 
    def describe(self):
399
400
        return self.format % self.__dict__
400
401
 
401
402
    def __repr__(self):
437
438
        for fname in self.associated_filenames():
438
439
            try:
439
440
                osutils.delete_any(tree.abspath(fname))
440
 
            except OSError as e:
 
441
            except OSError, e:
441
442
                if e.errno != errno.ENOENT:
442
443
                    raise
443
444
 
444
 
    def action_auto(self, tree):
445
 
        raise NotImplementedError(self.action_auto)
446
 
 
447
445
    def action_done(self, tree):
448
446
        """Mark the conflict as solved once it has been handled."""
449
447
        # This method does nothing but simplifies the design of upper levels.
456
454
        raise NotImplementedError(self.action_take_other)
457
455
 
458
456
    def _resolve_with_cleanups(self, tree, *args, **kwargs):
459
 
        with tree.transform() as tt:
460
 
            self._resolve(tt, *args, **kwargs)
 
457
        tt = transform.TreeTransform(tree)
 
458
        op = cleanup.OperationWithCleanups(self._resolve)
 
459
        op.add_cleanup(tt.finalize)
 
460
        op.run_simple(tt, *args, **kwargs)
461
461
 
462
462
 
463
463
class PathConflict(Conflict):
494
494
        path_to_create = None
495
495
        if winner == 'this':
496
496
            if self.path == '<deleted>':
497
 
                return  # Nothing to do
 
497
                return # Nothing to do
498
498
            if self.conflict_path == '<deleted>':
499
499
                path_to_create = self.path
500
500
                revid = tt._tree.get_parent_ids()[0]
512
512
            raise AssertionError('bad winner: %r' % (winner,))
513
513
        if path_to_create is not None:
514
514
            tid = tt.trans_id_tree_path(path_to_create)
515
 
            tree = self._revision_tree(tt._tree, revid)
516
515
            transform.create_from_tree(
517
 
                tt, tid, tree, tree.id2path(file_id))
518
 
            tt.version_file(tid, file_id=file_id)
 
516
                tt, tid, self._revision_tree(tt._tree, revid), file_id)
 
517
            tt.version_file(file_id, tid)
519
518
        else:
520
519
            tid = tt.trans_id_file_id(file_id)
521
520
        # Adjust the path for the retained file id
533
532
        possible_paths = []
534
533
        for p in (self.path, self.conflict_path):
535
534
            if p == '<deleted>':
536
 
                # special hard-coded path
 
535
                # special hard-coded path 
537
536
                continue
538
537
            if p is not None:
539
538
                possible_paths.append(p)
609
608
            # deleted the file either manually or when resolving a conflict on
610
609
            # the parent.  We may raise some exception to indicate that the
611
610
            # conflict doesn't exist anymore and as such doesn't need to be
612
 
            # resolved ? -- vila 20110615
 
611
            # resolved ? -- vila 20110615 
613
612
            this_tid = None
614
613
        else:
615
614
            this_tid = tt.trans_id_tree_path(this_path)
640
639
 
641
640
    rformat = '%(class)s(%(path)r, %(file_id)r)'
642
641
 
643
 
    _conflict_re = re.compile(b'^(<{7}|={7}|>{7})')
644
 
 
645
642
    def associated_filenames(self):
646
643
        return [self.path + suffix for suffix in CONFLICT_SUFFIXES]
647
644
 
665
662
        # Switch the paths to preserve the content
666
663
        tt.adjust_path(osutils.basename(self.path),
667
664
                       winner_parent_tid, winner_tid)
668
 
        tt.adjust_path(osutils.basename(winner_path),
669
 
                       item_parent_tid, item_tid)
 
665
        tt.adjust_path(osutils.basename(winner_path), item_parent_tid, item_tid)
670
666
        # Associate the file_id to the right content
671
667
        tt.unversion_file(item_tid)
672
 
        tt.version_file(winner_tid, file_id=self.file_id)
 
668
        tt.version_file(self.file_id, winner_tid)
673
669
        tt.apply()
674
670
 
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
671
    def action_take_this(self, tree):
692
672
        self._resolve_with_cleanups(tree, 'THIS')
693
673
 
731
711
                 conflict_file_id=None):
732
712
        HandledConflict.__init__(self, action, path, file_id)
733
713
        self.conflict_path = conflict_path
734
 
        # the factory blindly transfers the Stanza values to __init__,
735
 
        # so they can be unicode.
736
 
        if isinstance(conflict_file_id, str):
737
 
            conflict_file_id = cache_utf8.encode(conflict_file_id)
738
 
        self.conflict_file_id = conflict_file_id
 
714
        # warn turned off, because the factory blindly transfers the Stanza
 
715
        # values to __init__.
 
716
        self.conflict_file_id = osutils.safe_file_id(conflict_file_id,
 
717
                                                     warn=False)
739
718
 
740
719
    def _cmp_list(self):
741
720
        return HandledConflict._cmp_list(self) + [self.conflict_path,
789
768
    format = 'Conflict moving %(path)s into %(conflict_path)s. %(action)s.'
790
769
 
791
770
    def action_take_this(self, tree):
792
 
        # just acccept brz proposal
 
771
        # just acccept bzr proposal
793
772
        pass
794
773
 
795
774
    def action_take_other(self, tree):
796
 
        with tree.transform() as tt:
 
775
        tt = transform.TreeTransform(tree)
 
776
        try:
797
777
            p_tid = tt.trans_id_file_id(self.file_id)
798
778
            parent_tid = tt.get_tree_parent(p_tid)
799
779
            cp_tid = tt.trans_id_file_id(self.conflict_file_id)
802
782
            tt.adjust_path(osutils.basename(self.conflict_path),
803
783
                           parent_tid, p_tid)
804
784
            tt.apply()
 
785
        finally:
 
786
            tt.finalize()
805
787
 
806
788
 
807
789
class UnversionedParent(HandledConflict):
840
822
        tree.remove([self.path], force=True, keep_files=False)
841
823
 
842
824
    def action_take_other(self, tree):
843
 
        # just acccept brz proposal
 
825
        # just acccept bzr proposal
844
826
        pass
845
827
 
846
828
 
859
841
    # MissingParent from the *user* pov.
860
842
 
861
843
    def action_take_this(self, tree):
862
 
        # just acccept brz proposal
 
844
        # just acccept bzr proposal
863
845
        pass
864
846
 
865
847
    def action_take_other(self, tree):
906
888
    for conflict_type in conflict_types:
907
889
        ctype[conflict_type.typestring] = conflict_type
908
890
 
909
 
 
910
891
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
911
892
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
912
893
               DeletingParent, NonDirectoryParent)