1
# Copyright (C) 2005 by Aaron Bentley
1
# Copyright (C) 2005 Aaron Bentley, Canonical Ltd
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
24
from bzrlib.lazy_import import lazy_import
25
lazy_import(globals(), """
26
from bzrlib.commands import register_command
27
from bzrlib.errors import BzrCommandError, NotConflicted, UnsupportedOperation
28
37
from bzrlib.option import Option
29
from bzrlib.osutils import rename, delete_any
30
from bzrlib.rio import Stanza
33
40
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
36
class cmd_conflicts(bzrlib.commands.Command):
43
class cmd_conflicts(commands.Command):
37
44
"""List files with conflicts.
39
46
Merge will do its best to combine the changes in two branches, but there
41
48
it will mark a conflict. A conflict means that you need to fix something,
42
49
before you should commit.
51
Conflicts normally are listed as short, human-readable messages. If --text
52
is supplied, the pathnames of files with text conflicts are listed,
53
instead. (This is useful for editing all files with text conflicts.)
44
55
Use bzr resolve when you have fixed a problem.
46
(conflicts are determined by the presence of .BASE .TREE, and .OTHER
49
57
See also bzr resolve.
61
help='List paths of files with text conflicts.'),
64
def run(self, text=False):
52
65
from bzrlib.workingtree import WorkingTree
53
66
wt = WorkingTree.open_containing(u'.')[0]
54
67
for conflict in wt.conflicts():
58
class cmd_resolve(bzrlib.commands.Command):
69
if conflict.typestring != 'text conflict':
71
self.outf.write(conflict.path + '\n')
73
self.outf.write(str(conflict) + '\n')
76
class cmd_resolve(commands.Command):
59
77
"""Mark a conflict as resolved.
61
79
Merge will do its best to combine the changes in two branches, but there
63
81
it will mark a conflict. A conflict means that you need to fix something,
64
82
before you should commit.
66
Once you have fixed a problem, use "bzr resolve FILE.." to mark
67
individual files as fixed, or "bzr resolve --all" to mark all conflicts as
84
Once you have fixed a problem, use "bzr resolve" to automatically mark
85
text conflicts as fixed, resolve FILE to mark a specific conflict as
86
resolved, or "bzr resolve --all" to mark all conflicts as resolved.
70
88
See also bzr conflicts.
72
90
aliases = ['resolved']
73
91
takes_args = ['file*']
74
takes_options = [Option('all', help='Resolve all conflicts in this tree')]
93
Option('all', help='Resolve all conflicts in this tree.'),
75
95
def run(self, file_list=None, all=False):
76
96
from bzrlib.workingtree import WorkingTree
79
raise BzrCommandError("If --all is specified, no FILE may be provided")
99
raise errors.BzrCommandError("If --all is specified,"
100
" no FILE may be provided")
80
101
tree = WorkingTree.open_containing('.')[0]
104
tree, file_list = builtins.tree_files(file_list)
83
105
if file_list is None:
84
raise BzrCommandError("command 'resolve' needs one or more FILE, or --all")
85
tree = WorkingTree.open_containing(file_list[0])[0]
86
to_resolve = [tree.relpath(p) for p in file_list]
87
resolve(tree, to_resolve)
106
un_resolved, resolved = tree.auto_resolve()
107
if len(un_resolved) > 0:
108
trace.note('%d conflict(s) auto-resolved.', len(resolved))
109
trace.note('Remaining conflicts:')
110
for conflict in un_resolved:
114
trace.note('All conflicts resolved.')
117
resolve(tree, file_list)
90
120
def resolve(tree, paths=None, ignore_misses=False):
121
tree.lock_tree_write()
93
123
tree_conflicts = tree.conflicts()
99
129
tree_conflicts.select_conflicts(tree, paths, ignore_misses)
101
131
tree.set_conflicts(new_conflicts)
102
except UnsupportedOperation:
132
except errors.UnsupportedOperation:
104
134
selected_conflicts.remove_files(tree)
199
229
for suffix in CONFLICT_SUFFIXES:
201
delete_any(tree.abspath(conflict.path+suffix))
231
osutils.delete_any(tree.abspath(conflict.path+suffix))
202
232
except OSError, e:
203
233
if e.errno != errno.ENOENT:
206
def select_conflicts(self, tree, paths, ignore_misses=False):
236
def select_conflicts(self, tree, paths, ignore_misses=False,
207
238
"""Select the conflicts associated with paths in a tree.
209
240
File-ids are also used for this.
228
259
if cpath in path_set:
230
261
selected_paths.add(cpath)
263
if osutils.is_inside_any(path_set, cpath):
265
selected_paths.add(cpath)
231
267
for key in ('file_id', 'conflict_file_id'):
232
268
cfile_id = getattr(conflict, key, None)
233
269
if cfile_id is None:
259
295
def __init__(self, path, file_id=None):
261
self.file_id = file_id
297
# warn turned off, because the factory blindly transfers the Stanza
298
# values to __init__ and Stanza is purely a Unicode api.
299
self.file_id = osutils.safe_file_id(file_id, warn=False)
263
301
def as_stanza(self):
264
s = Stanza(type=self.typestring, path=self.path)
302
s = rio.Stanza(type=self.typestring, path=self.path)
265
303
if self.file_id is not None:
266
s.add('file_id', self.file_id)
304
# Stanza requires Unicode apis
305
s.add('file_id', self.file_id.decode('utf8'))
269
308
def _cmp_list(self):
377
416
conflict_file_id=None):
378
417
HandledConflict.__init__(self, action, path, file_id)
379
418
self.conflict_path = conflict_path
380
self.conflict_file_id = conflict_file_id
419
# warn turned off, because the factory blindly transfers the Stanza
420
# values to __init__.
421
self.conflict_file_id = osutils.safe_file_id(conflict_file_id,
382
424
def _cmp_list(self):
383
425
return HandledConflict._cmp_list(self) + [self.conflict_path,
387
429
s = HandledConflict.as_stanza(self)
388
430
s.add('conflict_path', self.conflict_path)
389
431
if self.conflict_file_id is not None:
390
s.add('conflict_file_id', self.conflict_file_id)
432
s.add('conflict_file_id', self.conflict_file_id.decode('utf8'))
433
475
typestring = 'unversioned parent'
435
format = 'Conflict adding versioned files to %(path)s. %(action)s.'
477
format = 'Conflict because %(path)s is not versioned, but has versioned'\
478
' children. %(action)s.'
438
481
class MissingParent(HandledConflict):
439
482
"""An attempt to add files to a directory that is not present.
440
Typically, the result of a merge where one tree deleted the directory and
441
the other added a file to it.
483
Typically, the result of a merge where THIS deleted the directory and
484
the OTHER added a file to it.
485
See also: DeletingParent (same situation, reversed THIS and OTHER)
444
488
typestring = 'missing parent'
446
490
format = 'Conflict adding files to %(path)s. %(action)s.'
493
class DeletingParent(HandledConflict):
494
"""An attempt to add files to a directory that is not present.
495
Typically, the result of a merge where one OTHER deleted the directory and
496
the THIS added a file to it.
499
typestring = 'deleting parent'
501
format = "Conflict: can't delete %(path)s because it is not empty. "\
460
515
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
461
DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,)
516
DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,