~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/conflicts.py

  • Committer: wang
  • Date: 2006-10-29 13:41:32 UTC
  • mto: (2104.4.1 wang_65714)
  • mto: This revision was merged to the branch mainline in revision 2109.
  • Revision ID: wang@ubuntu-20061029134132-3d7f4216f20c4aef
Replace python's difflib by patiencediff because the worst case 
performance is cubic for difflib and people commiting large data 
files are often hurt by this. The worst case performance of patience is 
quadratic. Fix bug 65714.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2007 Canonical Ltd
 
1
# Copyright (C) 2005 Aaron Bentley, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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
26
26
import errno
27
27
 
28
28
from bzrlib import (
29
 
    builtins,
30
29
    commands,
31
30
    errors,
32
31
    osutils,
33
32
    rio,
34
 
    trace,
35
33
    )
36
34
""")
37
35
from bzrlib.option import Option
48
46
    it will mark a conflict.  A conflict means that you need to fix something,
49
47
    before you should commit.
50
48
 
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.)
54
 
 
55
49
    Use bzr resolve when you have fixed a problem.
56
50
 
 
51
    (conflicts are determined by the presence of .BASE .TREE, and .OTHER 
 
52
    files.)
 
53
 
57
54
    See also bzr resolve.
58
55
    """
59
 
    takes_options = [
60
 
            Option('text',
61
 
                   help='List paths of files with text conflicts.'),
62
 
        ]
63
 
 
64
 
    def run(self, text=False):
 
56
    def run(self):
65
57
        from bzrlib.workingtree import WorkingTree
66
58
        wt = WorkingTree.open_containing(u'.')[0]
67
59
        for conflict in wt.conflicts():
68
 
            if text:
69
 
                if conflict.typestring != 'text conflict':
70
 
                    continue
71
 
                self.outf.write(conflict.path + '\n')
72
 
            else:
73
 
                self.outf.write(str(conflict) + '\n')
 
60
            print conflict
74
61
 
75
62
 
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.
83
70
 
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
 
73
    resolved.
87
74
 
88
75
    See also bzr conflicts.
89
76
    """
90
77
    aliases = ['resolved']
91
78
    takes_args = ['file*']
92
 
    takes_options = [
93
 
            Option('all', help='Resolve all conflicts in this tree.'),
94
 
            ]
 
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
97
82
        if all:
101
86
            tree = WorkingTree.open_containing('.')[0]
102
87
            resolve(tree)
103
88
        else:
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:
111
 
                        trace.note(conflict)
112
 
                    return 1
113
 
                else:
114
 
                    trace.note('All conflicts resolved.')
115
 
                    return 0
116
 
            else:
117
 
                resolve(tree, file_list)
118
 
 
119
 
 
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
 
    """
 
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)
 
95
 
 
96
 
 
97
def resolve(tree, paths=None, ignore_misses=False):
133
98
    tree.lock_tree_write()
134
99
    try:
135
100
        tree_conflicts = tree.conflicts()
138
103
            selected_conflicts = tree_conflicts
139
104
        else:
140
105
            new_conflicts, selected_conflicts = \
141
 
                tree_conflicts.select_conflicts(tree, paths, ignore_misses,
142
 
                    recursive)
 
106
                tree_conflicts.select_conflicts(tree, paths, ignore_misses)
143
107
        try:
144
108
            tree.set_conflicts(new_conflicts)
145
109
        except errors.UnsupportedOperation:
228
192
        """Generator of stanzas"""
229
193
        for conflict in self:
230
194
            yield conflict.as_stanza()
231
 
 
 
195
            
232
196
    def to_strings(self):
233
197
        """Generate strings for the provided conflicts"""
234
198
        for conflict in self:
246
210
                    if e.errno != errno.ENOENT:
247
211
                        raise
248
212
 
249
 
    def select_conflicts(self, tree, paths, ignore_misses=False,
250
 
                         recurse=False):
 
213
    def select_conflicts(self, tree, paths, ignore_misses=False):
251
214
        """Select the conflicts associated with paths in a tree.
252
 
 
 
215
        
253
216
        File-ids are also used for this.
254
217
        :return: a pair of ConflictLists: (not_selected, selected)
255
218
        """
272
235
                if cpath in path_set:
273
236
                    selected = True
274
237
                    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
 
 
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:
299
257
                    print "%s is not conflicted" % path
300
258
        return new_conflicts, selected_conflicts
301
259
 
302
 
 
 
260
 
303
261
class Conflict(object):
304
262
    """Base class for all types of conflict"""
305
263
 
307
265
 
308
266
    def __init__(self, path, file_id=None):
309
267
        self.path = path
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
313
269
 
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)
319
274
        return s
320
275
 
321
276
    def _cmp_list(self):
403
358
    """
404
359
 
405
360
    rformat = "%(class)s(%(action)r, %(path)r, %(file_id)r)"
406
 
 
 
361
    
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,
435
 
                                                     warn=False)
436
 
 
 
386
        self.conflict_path = conflict_path 
 
387
        self.conflict_file_id = conflict_file_id
 
388
        
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]
440
392
 
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'))
446
 
 
 
397
            s.add('conflict_file_id', self.conflict_file_id)
 
398
            
447
399
        return s
448
400
 
449
401
 
515
467
             "%(action)s."
516
468
 
517
469
 
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
 
 
528
470
ctype = {}
529
471
 
530
472
 
537
479
 
538
480
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
539
481
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
540
 
               DeletingParent, NonDirectoryParent)
 
482
               DeletingParent,)