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
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
20
from __future__ import absolute_import
23
from .lazy_import import lazy_import
24
from bzrlib.lazy_import import lazy_import
24
25
lazy_import(globals(), """
34
from breezy.i18n import gettext, ngettext
37
from bzrlib.i18n import gettext, ngettext
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.)
60
Use brz resolve when you have fixed a problem.
61
Use bzr resolve when you have fixed a problem.
65
help='List paths of files with text conflicts.'),
66
help='List paths of files with text conflicts.'),
67
68
_see_also = ['resolve', 'conflict-types']
75
76
self.outf.write(conflict.path + '\n')
77
self.outf.write(str(conflict) + '\n')
78
self.outf.write(unicode(conflict) + '\n')
80
81
resolve_action_registry = registry.Registry()
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',
110
108
it will mark a conflict. A conflict means that you need to fix something,
111
109
before you can commit.
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.
117
115
aliases = ['resolved']
118
116
takes_args = ['file*']
119
117
takes_options = [
121
option.Option('all', help='Resolve all conflicts in this tree.'),
122
ResolveActionOption(),
119
option.Option('all', help='Resolve all conflicts in this tree.'),
120
ResolveActionOption(),
124
122
_see_also = ['conflicts']
126
123
def run(self, file_list=None, all=False, action=None, directory=None):
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:
133
130
tree = workingtree.WorkingTree.open_containing(directory)[0]
137
134
tree, file_list = workingtree.WorkingTree.open_containing_paths(
138
135
file_list, directory)
140
if file_list is None:
136
if file_list 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
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:
149
ngettext('%d conflict auto-resolved.',
150
'%d conflicts auto-resolved.', before - after),
152
trace.note(gettext('Remaining conflicts:'))
153
for conflict in tree.conflicts():
154
trace.note(str(conflict))
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)),
154
trace.note(gettext('Remaining conflicts:'))
155
for conflict in un_resolved:
156
trace.note(unicode(conflict))
159
trace.note(gettext('All conflicts resolved.'))
157
trace.note(gettext('All conflicts resolved.'))
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
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))
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,
188
tree.lock_tree_write()
180
189
nb_conflicts_after = None
181
with tree.lock_tree_write():
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:
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
213
224
osutils.rename(filename + ".THIS", filename)
214
225
conflicted = True
216
227
if e.errno != errno.ENOENT:
219
230
os.unlink(filename + ".BASE")
220
231
conflicted = True
222
233
if e.errno != errno.ENOENT:
225
236
os.unlink(filename + ".OTHER")
226
237
conflicted = True
228
239
if e.errno != errno.ENOENT:
230
241
if not conflicted:
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
348
print("%s is not conflicted" % path)
359
print "%s is not conflicted" % path
349
360
return new_conflicts, selected_conflicts
358
369
def __init__(self, path, file_id=None):
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)
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:
380
y = other._cmp_list()
381
return (x > y) - (x < y)
388
return cmp(self._cmp_list(), other._cmp_list())
383
390
def __hash__(self):
384
391
return hash((type(self), self.path, self.file_id))
437
438
for fname in self.associated_filenames():
439
440
osutils.delete_any(tree.abspath(fname))
441
442
if e.errno != errno.ENOENT:
444
def action_auto(self, tree):
445
raise NotImplementedError(self.action_auto)
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)
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)
463
463
class PathConflict(Conflict):
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)
520
519
tid = tt.trans_id_file_id(file_id)
521
520
# Adjust the path for the retained file id
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
615
614
this_tid = tt.trans_id_tree_path(this_path)
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)
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.
679
kind = tree.kind(self.path)
680
except errors.NoSuchFile:
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:
688
if conflict_markers_in_line(line):
689
raise NotImplementedError("Conflict markers present")
691
671
def action_take_this(self, tree):
692
672
self._resolve_with_cleanups(tree, 'THIS')
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,
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.'
791
770
def action_take_this(self, tree):
792
# just acccept brz proposal
771
# just acccept bzr proposal
795
774
def action_take_other(self, tree):
796
with tree.transform() as tt:
775
tt = transform.TreeTransform(tree)
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)
840
822
tree.remove([self.path], force=True, keep_files=False)
842
824
def action_take_other(self, tree):
843
# just acccept brz proposal
825
# just acccept bzr proposal
859
841
# MissingParent from the *user* pov.
861
843
def action_take_this(self, tree):
862
# just acccept brz proposal
844
# just acccept bzr proposal
865
847
def action_take_other(self, tree):
906
888
for conflict_type in conflict_types:
907
889
ctype[conflict_type.typestring] = conflict_type
910
891
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
911
892
DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
912
893
DeletingParent, NonDirectoryParent)