1
# Copyright (C) 2005, 2007 Canonical Ltd
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
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
# TODO: Move this into builtins
19
# TODO: 'bzr resolve' should accept a directory name and work from that
19
# TODO: 'bzr resolve' should accept a directory name and work from that
48
46
it will mark a conflict. A conflict means that you need to fix something,
49
47
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.)
55
49
Use bzr resolve when you have fixed a problem.
51
(conflicts are determined by the presence of .BASE .TREE, and .OTHER
57
54
See also bzr resolve.
61
help='List paths of files with text conflicts.'),
64
def run(self, text=False):
65
57
from bzrlib.workingtree import WorkingTree
66
58
wt = WorkingTree.open_containing(u'.')[0]
67
59
for conflict in wt.conflicts():
69
if conflict.typestring != 'text conflict':
71
self.outf.write(conflict.path + '\n')
73
self.outf.write(str(conflict) + '\n')
76
63
class cmd_resolve(commands.Command):
81
68
it will mark a conflict. A conflict means that you need to fix something,
82
69
before you should commit.
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.
71
Once you have fixed a problem, use "bzr resolve FILE.." to mark
72
individual files as fixed, or "bzr resolve --all" to mark all conflicts as
88
75
See also bzr conflicts.
90
77
aliases = ['resolved']
91
78
takes_args = ['file*']
93
Option('all', help='Resolve all conflicts in this tree.'),
79
takes_options = [Option('all', help='Resolve all conflicts in this tree')]
95
80
def run(self, file_list=None, all=False):
96
81
from bzrlib.workingtree import WorkingTree
101
86
tree = WorkingTree.open_containing('.')[0]
104
tree, file_list = builtins.tree_files(file_list)
105
89
if file_list is None:
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)
120
def resolve(tree, paths=None, ignore_misses=False, recursive=False):
121
"""Resolve some or all of the conflicts in a working tree.
123
:param paths: If None, resolve all conflicts. Otherwise, select only
125
:param recursive: If True, then elements of paths which are directories
126
have all their children resolved, etc. When invoked as part of
127
recursive commands like revert, this should be True. For commands
128
or applications wishing finer-grained control, like the resolve
129
command, this should be False.
130
:ignore_misses: If False, warnings will be printed if the supplied paths
131
do not have conflicts.
90
raise errors.BzrCommandError("command 'resolve' needs one or"
91
" more FILE, or --all")
92
tree = WorkingTree.open_containing(file_list[0])[0]
93
to_resolve = [tree.relpath(p) for p in file_list]
94
resolve(tree, to_resolve)
97
def resolve(tree, paths=None, ignore_misses=False):
133
98
tree.lock_tree_write()
135
100
tree_conflicts = tree.conflicts()
138
103
selected_conflicts = tree_conflicts
140
105
new_conflicts, selected_conflicts = \
141
tree_conflicts.select_conflicts(tree, paths, ignore_misses,
106
tree_conflicts.select_conflicts(tree, paths, ignore_misses)
144
108
tree.set_conflicts(new_conflicts)
145
109
except errors.UnsupportedOperation:
246
210
if e.errno != errno.ENOENT:
249
def select_conflicts(self, tree, paths, ignore_misses=False,
213
def select_conflicts(self, tree, paths, ignore_misses=False):
251
214
"""Select the conflicts associated with paths in a tree.
253
216
File-ids are also used for this.
254
217
:return: a pair of ConflictLists: (not_selected, selected)
272
235
if cpath in path_set:
274
237
selected_paths.add(cpath)
276
if osutils.is_inside_any(path_set, cpath):
278
selected_paths.add(cpath)
280
238
for key in ('file_id', 'conflict_file_id'):
281
239
cfile_id = getattr(conflict, key, None)
282
240
if cfile_id is None:
308
266
def __init__(self, path, file_id=None):
310
# warn turned off, because the factory blindly transfers the Stanza
311
# values to __init__ and Stanza is purely a Unicode api.
312
self.file_id = osutils.safe_file_id(file_id, warn=False)
268
self.file_id = file_id
314
270
def as_stanza(self):
315
271
s = rio.Stanza(type=self.typestring, path=self.path)
316
272
if self.file_id is not None:
317
# Stanza requires Unicode apis
318
s.add('file_id', self.file_id.decode('utf8'))
273
s.add('file_id', self.file_id)
321
276
def _cmp_list(self):
405
360
rformat = "%(class)s(%(action)r, %(path)r, %(file_id)r)"
407
362
def __init__(self, action, path, file_id=None):
408
363
Conflict.__init__(self, path, file_id)
409
364
self.action = action
428
383
def __init__(self, action, path, conflict_path, file_id=None,
429
384
conflict_file_id=None):
430
385
HandledConflict.__init__(self, action, path, file_id)
431
self.conflict_path = conflict_path
432
# warn turned off, because the factory blindly transfers the Stanza
433
# values to __init__.
434
self.conflict_file_id = osutils.safe_file_id(conflict_file_id,
386
self.conflict_path = conflict_path
387
self.conflict_file_id = conflict_file_id
437
389
def _cmp_list(self):
438
return HandledConflict._cmp_list(self) + [self.conflict_path,
390
return HandledConflict._cmp_list(self) + [self.conflict_path,
439
391
self.conflict_file_id]
441
393
def as_stanza(self):
442
394
s = HandledConflict.as_stanza(self)
443
395
s.add('conflict_path', self.conflict_path)
444
396
if self.conflict_file_id is not None:
445
s.add('conflict_file_id', self.conflict_file_id.decode('utf8'))
397
s.add('conflict_file_id', self.conflict_file_id)
518
class NonDirectoryParent(HandledConflict):
519
"""An attempt to add files to a directory that is not a director or
520
an attempt to change the kind of a directory with files.
523
typestring = 'non-directory parent'
525
format = "Conflict: %(path)s is not a directory, but has files in it."\
538
480
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
539
481
DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
540
DeletingParent, NonDirectoryParent)