1
# Copyright (C) 2005 by Aaron Bentley
1
# Copyright (C) 2005, 2007 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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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)
90
def resolve(tree, paths=None, ignore_misses=False):
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.
133
tree.lock_tree_write()
93
135
tree_conflicts = tree.conflicts()
96
138
selected_conflicts = tree_conflicts
98
140
new_conflicts, selected_conflicts = \
99
tree_conflicts.select_conflicts(tree, paths, ignore_misses)
141
tree_conflicts.select_conflicts(tree, paths, ignore_misses,
101
144
tree.set_conflicts(new_conflicts)
102
except UnsupportedOperation:
145
except errors.UnsupportedOperation:
104
147
selected_conflicts.remove_files(tree)
199
242
for suffix in CONFLICT_SUFFIXES:
201
delete_any(tree.abspath(conflict.path+suffix))
244
osutils.delete_any(tree.abspath(conflict.path+suffix))
202
245
except OSError, e:
203
246
if e.errno != errno.ENOENT:
206
def select_conflicts(self, tree, paths, ignore_misses=False):
249
def select_conflicts(self, tree, paths, ignore_misses=False,
207
251
"""Select the conflicts associated with paths in a tree.
209
253
File-ids are also used for this.
254
:return: a pair of ConflictLists: (not_selected, selected)
211
256
path_set = set(paths)
258
308
def __init__(self, path, file_id=None):
260
self.file_id = file_id
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)
262
314
def as_stanza(self):
263
s = Stanza(type=self.typestring, path=self.path)
315
s = rio.Stanza(type=self.typestring, path=self.path)
264
316
if self.file_id is not None:
265
s.add('file_id', self.file_id)
317
# Stanza requires Unicode apis
318
s.add('file_id', self.file_id.decode('utf8'))
268
321
def _cmp_list(self):
373
429
conflict_file_id=None):
374
430
HandledConflict.__init__(self, action, path, file_id)
375
431
self.conflict_path = conflict_path
376
self.conflict_file_id = conflict_file_id
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,
378
437
def _cmp_list(self):
379
438
return HandledConflict._cmp_list(self) + [self.conflict_path,
429
488
typestring = 'unversioned parent'
431
format = 'Conflict adding versioned files to %(path)s. %(action)s.'
490
format = 'Conflict because %(path)s is not versioned, but has versioned'\
491
' children. %(action)s.'
434
494
class MissingParent(HandledConflict):
435
495
"""An attempt to add files to a directory that is not present.
436
Typically, the result of a merge where one tree deleted the directory and
437
the other added a file to it.
496
Typically, the result of a merge where THIS deleted the directory and
497
the OTHER added a file to it.
498
See also: DeletingParent (same situation, reversed THIS and OTHER)
440
501
typestring = 'missing parent'
442
503
format = 'Conflict adding files to %(path)s. %(action)s.'
506
class DeletingParent(HandledConflict):
507
"""An attempt to add files to a directory that is not present.
508
Typically, the result of a merge where one OTHER deleted the directory and
509
the THIS added a file to it.
512
typestring = 'deleting parent'
514
format = "Conflict: can't delete %(path)s because it is not empty. "\
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."\
456
538
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
457
DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,)
539
DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
540
DeletingParent, NonDirectoryParent)