~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/conflicts.py

  • Committer: John Whitley
  • Date: 2010-01-11 16:44:02 UTC
  • mto: This revision was merged to the branch mainline in revision 4981.
  • Revision ID: whitley@bangpath.org-20100111164402-9luag9p9ahpy4kmz
Terminology change: exclusion => exception.
Tweaked presentation of new logic in ExceptionGlobster

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
53
53
    instead.  (This is useful for editing all files with text conflicts.)
54
54
 
55
55
    Use bzr resolve when you have fixed a problem.
56
 
 
57
 
    See also bzr resolve.
58
56
    """
59
 
    takes_options = [Option('text', help='list text conflicts by pathname')]
 
57
    takes_options = [
 
58
            Option('text',
 
59
                   help='List paths of files with text conflicts.'),
 
60
        ]
 
61
    _see_also = ['resolve', 'conflict-types']
60
62
 
61
63
    def run(self, text=False):
62
64
        from bzrlib.workingtree import WorkingTree
79
81
    before you should commit.
80
82
 
81
83
    Once you have fixed a problem, use "bzr resolve" to automatically mark
82
 
    text conflicts as fixed, resolve FILE to mark a specific conflict as
 
84
    text conflicts as fixed, "bzr resolve FILE" to mark a specific conflict as
83
85
    resolved, or "bzr resolve --all" to mark all conflicts as resolved.
84
 
 
85
 
    See also bzr conflicts.
86
86
    """
87
87
    aliases = ['resolved']
88
88
    takes_args = ['file*']
89
 
    takes_options = [Option('all', help='Resolve all conflicts in this tree')]
 
89
    takes_options = [
 
90
            Option('all', help='Resolve all conflicts in this tree.'),
 
91
            ]
 
92
    _see_also = ['conflicts']
90
93
    def run(self, file_list=None, all=False):
91
94
        from bzrlib.workingtree import WorkingTree
92
95
        if all:
112
115
                resolve(tree, file_list)
113
116
 
114
117
 
115
 
def resolve(tree, paths=None, ignore_misses=False):
 
118
def resolve(tree, paths=None, ignore_misses=False, recursive=False):
 
119
    """Resolve some or all of the conflicts in a working tree.
 
120
 
 
121
    :param paths: If None, resolve all conflicts.  Otherwise, select only
 
122
        specified conflicts.
 
123
    :param recursive: If True, then elements of paths which are directories
 
124
        have all their children resolved, etc.  When invoked as part of
 
125
        recursive commands like revert, this should be True.  For commands
 
126
        or applications wishing finer-grained control, like the resolve
 
127
        command, this should be False.
 
128
    :ignore_misses: If False, warnings will be printed if the supplied paths
 
129
        do not have conflicts.
 
130
    """
116
131
    tree.lock_tree_write()
117
132
    try:
118
133
        tree_conflicts = tree.conflicts()
121
136
            selected_conflicts = tree_conflicts
122
137
        else:
123
138
            new_conflicts, selected_conflicts = \
124
 
                tree_conflicts.select_conflicts(tree, paths, ignore_misses)
 
139
                tree_conflicts.select_conflicts(tree, paths, ignore_misses,
 
140
                    recursive)
125
141
        try:
126
142
            tree.set_conflicts(new_conflicts)
127
143
        except errors.UnsupportedOperation:
132
148
 
133
149
 
134
150
def restore(filename):
135
 
    """\
136
 
    Restore a conflicted file to the state it was in before merging.
137
 
    Only text restoration supported at present.
 
151
    """Restore a conflicted file to the state it was in before merging.
 
152
 
 
153
    Only text restoration is supported at present.
138
154
    """
139
155
    conflicted = False
140
156
    try:
210
226
        """Generator of stanzas"""
211
227
        for conflict in self:
212
228
            yield conflict.as_stanza()
213
 
            
 
229
 
214
230
    def to_strings(self):
215
231
        """Generate strings for the provided conflicts"""
216
232
        for conflict in self:
228
244
                    if e.errno != errno.ENOENT:
229
245
                        raise
230
246
 
231
 
    def select_conflicts(self, tree, paths, ignore_misses=False):
 
247
    def select_conflicts(self, tree, paths, ignore_misses=False,
 
248
                         recurse=False):
232
249
        """Select the conflicts associated with paths in a tree.
233
 
        
 
250
 
234
251
        File-ids are also used for this.
235
252
        :return: a pair of ConflictLists: (not_selected, selected)
236
253
        """
253
270
                if cpath in path_set:
254
271
                    selected = True
255
272
                    selected_paths.add(cpath)
 
273
                if recurse:
 
274
                    if osutils.is_inside_any(path_set, cpath):
 
275
                        selected = True
 
276
                        selected_paths.add(cpath)
 
277
 
256
278
            for key in ('file_id', 'conflict_file_id'):
257
279
                cfile_id = getattr(conflict, key, None)
258
280
                if cfile_id is None:
275
297
                    print "%s is not conflicted" % path
276
298
        return new_conflicts, selected_conflicts
277
299
 
278
 
 
 
300
 
279
301
class Conflict(object):
280
302
    """Base class for all types of conflict"""
281
303
 
379
401
    """
380
402
 
381
403
    rformat = "%(class)s(%(action)r, %(path)r, %(file_id)r)"
382
 
    
 
404
 
383
405
    def __init__(self, action, path, file_id=None):
384
406
        Conflict.__init__(self, path, file_id)
385
407
        self.action = action
404
426
    def __init__(self, action, path, conflict_path, file_id=None,
405
427
                 conflict_file_id=None):
406
428
        HandledConflict.__init__(self, action, path, file_id)
407
 
        self.conflict_path = conflict_path 
 
429
        self.conflict_path = conflict_path
408
430
        # warn turned off, because the factory blindly transfers the Stanza
409
431
        # values to __init__.
410
432
        self.conflict_file_id = osutils.safe_file_id(conflict_file_id,
411
433
                                                     warn=False)
412
 
        
 
434
 
413
435
    def _cmp_list(self):
414
 
        return HandledConflict._cmp_list(self) + [self.conflict_path, 
 
436
        return HandledConflict._cmp_list(self) + [self.conflict_path,
415
437
                                                  self.conflict_file_id]
416
438
 
417
439
    def as_stanza(self):
419
441
        s.add('conflict_path', self.conflict_path)
420
442
        if self.conflict_file_id is not None:
421
443
            s.add('conflict_file_id', self.conflict_file_id.decode('utf8'))
422
 
            
 
444
 
423
445
        return s
424
446
 
425
447
 
456
478
 
457
479
 
458
480
class UnversionedParent(HandledConflict):
459
 
    """An attempt to version an file whose parent directory is not versioned.
 
481
    """An attempt to version a file whose parent directory is not versioned.
460
482
    Typically, the result of a merge where one tree unversioned the directory
461
483
    and the other added a versioned file to it.
462
484
    """
491
513
             "%(action)s."
492
514
 
493
515
 
 
516
class NonDirectoryParent(HandledConflict):
 
517
    """An attempt to add files to a directory that is not a director or
 
518
    an attempt to change the kind of a directory with files.
 
519
    """
 
520
 
 
521
    typestring = 'non-directory parent'
 
522
 
 
523
    format = "Conflict: %(path)s is not a directory, but has files in it."\
 
524
             "  %(action)s."
 
525
 
494
526
ctype = {}
495
527
 
496
528
 
503
535
 
504
536
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
505
537
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
506
 
               DeletingParent,)
 
538
               DeletingParent, NonDirectoryParent)