~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/conflicts.py

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Aaron Bentley, Canonical Ltd
 
1
# Copyright (C) 2005, 2007 Canonical Ltd
2
2
#
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
12
12
#
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
# TODO: Move this into builtins
18
18
 
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
20
20
# point down
21
21
 
22
22
import os
56
56
 
57
57
    See also bzr resolve.
58
58
    """
59
 
    takes_options = [Option('text', help='list text conflicts by pathname')]
 
59
    takes_options = [
 
60
            Option('text',
 
61
                   help='List paths of files with text conflicts.'),
 
62
        ]
60
63
 
61
64
    def run(self, text=False):
62
65
        from bzrlib.workingtree import WorkingTree
86
89
    """
87
90
    aliases = ['resolved']
88
91
    takes_args = ['file*']
89
 
    takes_options = [Option('all', help='Resolve all conflicts in this tree')]
 
92
    takes_options = [
 
93
            Option('all', help='Resolve all conflicts in this tree.'),
 
94
            ]
90
95
    def run(self, file_list=None, all=False):
91
96
        from bzrlib.workingtree import WorkingTree
92
97
        if all:
112
117
                resolve(tree, file_list)
113
118
 
114
119
 
115
 
def resolve(tree, paths=None, ignore_misses=False):
 
120
def resolve(tree, paths=None, ignore_misses=False, recursive=False):
 
121
    """Resolve some or all of the conflicts in a working tree.
 
122
 
 
123
    :param paths: If None, resolve all conflicts.  Otherwise, select only
 
124
        specified conflicts.
 
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.
 
132
    """
116
133
    tree.lock_tree_write()
117
134
    try:
118
135
        tree_conflicts = tree.conflicts()
121
138
            selected_conflicts = tree_conflicts
122
139
        else:
123
140
            new_conflicts, selected_conflicts = \
124
 
                tree_conflicts.select_conflicts(tree, paths, ignore_misses)
 
141
                tree_conflicts.select_conflicts(tree, paths, ignore_misses,
 
142
                    recursive)
125
143
        try:
126
144
            tree.set_conflicts(new_conflicts)
127
145
        except errors.UnsupportedOperation:
210
228
        """Generator of stanzas"""
211
229
        for conflict in self:
212
230
            yield conflict.as_stanza()
213
 
            
 
231
 
214
232
    def to_strings(self):
215
233
        """Generate strings for the provided conflicts"""
216
234
        for conflict in self:
228
246
                    if e.errno != errno.ENOENT:
229
247
                        raise
230
248
 
231
 
    def select_conflicts(self, tree, paths, ignore_misses=False):
 
249
    def select_conflicts(self, tree, paths, ignore_misses=False,
 
250
                         recurse=False):
232
251
        """Select the conflicts associated with paths in a tree.
233
 
        
 
252
 
234
253
        File-ids are also used for this.
235
254
        :return: a pair of ConflictLists: (not_selected, selected)
236
255
        """
253
272
                if cpath in path_set:
254
273
                    selected = True
255
274
                    selected_paths.add(cpath)
 
275
                if recurse:
 
276
                    if osutils.is_inside_any(path_set, cpath):
 
277
                        selected = True
 
278
                        selected_paths.add(cpath)
 
279
 
256
280
            for key in ('file_id', 'conflict_file_id'):
257
281
                cfile_id = getattr(conflict, key, None)
258
282
                if cfile_id is None:
275
299
                    print "%s is not conflicted" % path
276
300
        return new_conflicts, selected_conflicts
277
301
 
278
 
 
 
302
 
279
303
class Conflict(object):
280
304
    """Base class for all types of conflict"""
281
305
 
379
403
    """
380
404
 
381
405
    rformat = "%(class)s(%(action)r, %(path)r, %(file_id)r)"
382
 
    
 
406
 
383
407
    def __init__(self, action, path, file_id=None):
384
408
        Conflict.__init__(self, path, file_id)
385
409
        self.action = action
404
428
    def __init__(self, action, path, conflict_path, file_id=None,
405
429
                 conflict_file_id=None):
406
430
        HandledConflict.__init__(self, action, path, file_id)
407
 
        self.conflict_path = conflict_path 
 
431
        self.conflict_path = conflict_path
408
432
        # warn turned off, because the factory blindly transfers the Stanza
409
433
        # values to __init__.
410
434
        self.conflict_file_id = osutils.safe_file_id(conflict_file_id,
411
435
                                                     warn=False)
412
 
        
 
436
 
413
437
    def _cmp_list(self):
414
 
        return HandledConflict._cmp_list(self) + [self.conflict_path, 
 
438
        return HandledConflict._cmp_list(self) + [self.conflict_path,
415
439
                                                  self.conflict_file_id]
416
440
 
417
441
    def as_stanza(self):
419
443
        s.add('conflict_path', self.conflict_path)
420
444
        if self.conflict_file_id is not None:
421
445
            s.add('conflict_file_id', self.conflict_file_id.decode('utf8'))
422
 
            
 
446
 
423
447
        return s
424
448
 
425
449
 
491
515
             "%(action)s."
492
516
 
493
517
 
 
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.
 
521
    """
 
522
 
 
523
    typestring = 'non-directory parent'
 
524
 
 
525
    format = "Conflict: %(path)s is not a directory, but has files in it."\
 
526
             "  %(action)s."
 
527
 
494
528
ctype = {}
495
529
 
496
530
 
503
537
 
504
538
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
505
539
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
506
 
               DeletingParent,)
 
540
               DeletingParent, NonDirectoryParent)