~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/conflicts.py

  • Committer: Robert Collins
  • Date: 2006-05-18 12:42:12 UTC
  • mto: (1714.1.2 integration)
  • mto: This revision was merged to the branch mainline in revision 1716.
  • Revision ID: robertc@robertcollins.net-20060518124212-ac84beb57877e18c
Combine the ignore rules into a single regex rather than looping over them
to avoid N^2 behaviour in operations like status. (Jan Hudec, Robert
Collins).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Aaron Bentley, Canonical Ltd
2
 
#
 
1
# Copyright (C) 2005 by Aaron Bentley
 
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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
#
 
7
 
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.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
20
# point down
21
21
 
22
22
import os
23
 
 
24
 
from bzrlib.lazy_import import lazy_import
25
 
lazy_import(globals(), """
26
23
import errno
27
24
 
28
 
from bzrlib import (
29
 
    builtins,
30
 
    commands,
31
 
    errors,
32
 
    osutils,
33
 
    rio,
34
 
    trace,
35
 
    )
36
 
""")
 
25
import bzrlib
 
26
from bzrlib.commands import register_command
 
27
from bzrlib.errors import BzrCommandError, NotConflicted, UnsupportedOperation
37
28
from bzrlib.option import Option
 
29
from bzrlib.osutils import rename, delete_any
 
30
from bzrlib.rio import Stanza
38
31
 
39
32
 
40
33
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
41
34
 
42
35
 
43
 
class cmd_conflicts(commands.Command):
 
36
class cmd_conflicts(bzrlib.commands.Command):
44
37
    """List files with conflicts.
45
38
 
46
39
    Merge will do its best to combine the changes in two branches, but there
48
41
    it will mark a conflict.  A conflict means that you need to fix something,
49
42
    before you should commit.
50
43
 
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
44
    Use bzr resolve when you have fixed a problem.
56
45
 
 
46
    (conflicts are determined by the presence of .BASE .TREE, and .OTHER 
 
47
    files.)
 
48
 
57
49
    See also bzr resolve.
58
50
    """
59
 
    takes_options = [Option('text', help='list text conflicts by pathname')]
60
 
 
61
 
    def run(self, text=False):
 
51
    def run(self):
62
52
        from bzrlib.workingtree import WorkingTree
63
53
        wt = WorkingTree.open_containing(u'.')[0]
64
54
        for conflict in wt.conflicts():
65
 
            if text:
66
 
                if conflict.typestring != 'text conflict':
67
 
                    continue
68
 
                self.outf.write(conflict.path + '\n')
69
 
            else:
70
 
                self.outf.write(str(conflict) + '\n')
71
 
 
72
 
 
73
 
class cmd_resolve(commands.Command):
 
55
            print conflict
 
56
 
 
57
 
 
58
class cmd_resolve(bzrlib.commands.Command):
74
59
    """Mark a conflict as resolved.
75
60
 
76
61
    Merge will do its best to combine the changes in two branches, but there
78
63
    it will mark a conflict.  A conflict means that you need to fix something,
79
64
    before you should commit.
80
65
 
81
 
    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
83
 
    resolved, or "bzr resolve --all" to mark all conflicts as resolved.
 
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
 
68
    resolved.
84
69
 
85
70
    See also bzr conflicts.
86
71
    """
91
76
        from bzrlib.workingtree import WorkingTree
92
77
        if all:
93
78
            if file_list:
94
 
                raise errors.BzrCommandError("If --all is specified,"
95
 
                                             " no FILE may be provided")
 
79
                raise BzrCommandError("If --all is specified, no FILE may be provided")
96
80
            tree = WorkingTree.open_containing('.')[0]
97
81
            resolve(tree)
98
82
        else:
99
 
            tree, file_list = builtins.tree_files(file_list)
100
83
            if file_list is None:
101
 
                un_resolved, resolved = tree.auto_resolve()
102
 
                if len(un_resolved) > 0:
103
 
                    trace.note('%d conflict(s) auto-resolved.', len(resolved))
104
 
                    trace.note('Remaining conflicts:')
105
 
                    for conflict in un_resolved:
106
 
                        trace.note(conflict)
107
 
                    return 1
108
 
                else:
109
 
                    trace.note('All conflicts resolved.')
110
 
                    return 0
111
 
            else:
112
 
                resolve(tree, file_list)
 
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)
113
88
 
114
89
 
115
90
def resolve(tree, paths=None, ignore_misses=False):
116
 
    tree.lock_tree_write()
 
91
    tree.lock_write()
117
92
    try:
118
93
        tree_conflicts = tree.conflicts()
119
94
        if paths is None:
124
99
                tree_conflicts.select_conflicts(tree, paths, ignore_misses)
125
100
        try:
126
101
            tree.set_conflicts(new_conflicts)
127
 
        except errors.UnsupportedOperation:
 
102
        except UnsupportedOperation:
128
103
            pass
129
104
        selected_conflicts.remove_files(tree)
130
105
    finally:
138
113
    """
139
114
    conflicted = False
140
115
    try:
141
 
        osutils.rename(filename + ".THIS", filename)
 
116
        rename(filename + ".THIS", filename)
142
117
        conflicted = True
143
118
    except OSError, e:
144
119
        if e.errno != errno.ENOENT:
156
131
        if e.errno != errno.ENOENT:
157
132
            raise
158
133
    if not conflicted:
159
 
        raise errors.NotConflicted(filename)
 
134
        raise NotConflicted(filename)
160
135
 
161
136
 
162
137
class ConflictList(object):
223
198
                continue
224
199
            for suffix in CONFLICT_SUFFIXES:
225
200
                try:
226
 
                    osutils.delete_any(tree.abspath(conflict.path+suffix))
 
201
                    delete_any(tree.abspath(conflict.path+suffix))
227
202
                except OSError, e:
228
203
                    if e.errno != errno.ENOENT:
229
204
                        raise
232
207
        """Select the conflicts associated with paths in a tree.
233
208
        
234
209
        File-ids are also used for this.
235
 
        :return: a pair of ConflictLists: (not_selected, selected)
236
210
        """
237
211
        path_set = set(paths)
238
212
        ids = {}
283
257
 
284
258
    def __init__(self, path, file_id=None):
285
259
        self.path = path
286
 
        # warn turned off, because the factory blindly transfers the Stanza
287
 
        # values to __init__ and Stanza is purely a Unicode api.
288
 
        self.file_id = osutils.safe_file_id(file_id, warn=False)
 
260
        self.file_id = file_id
289
261
 
290
262
    def as_stanza(self):
291
 
        s = rio.Stanza(type=self.typestring, path=self.path)
 
263
        s = Stanza(type=self.typestring, path=self.path)
292
264
        if self.file_id is not None:
293
 
            # Stanza requires Unicode apis
294
 
            s.add('file_id', self.file_id.decode('utf8'))
 
265
            s.add('file_id', self.file_id)
295
266
        return s
296
267
 
297
268
    def _cmp_list(self):
302
273
            return -1
303
274
        return cmp(self._cmp_list(), other._cmp_list())
304
275
 
305
 
    def __hash__(self):
306
 
        return hash((type(self), self.path, self.file_id))
307
 
 
308
276
    def __eq__(self, other):
309
277
        return self.__cmp__(other) == 0
310
278
 
405
373
                 conflict_file_id=None):
406
374
        HandledConflict.__init__(self, action, path, file_id)
407
375
        self.conflict_path = conflict_path 
408
 
        # warn turned off, because the factory blindly transfers the Stanza
409
 
        # values to __init__.
410
 
        self.conflict_file_id = osutils.safe_file_id(conflict_file_id,
411
 
                                                     warn=False)
 
376
        self.conflict_file_id = conflict_file_id
412
377
        
413
378
    def _cmp_list(self):
414
379
        return HandledConflict._cmp_list(self) + [self.conflict_path, 
418
383
        s = HandledConflict.as_stanza(self)
419
384
        s.add('conflict_path', self.conflict_path)
420
385
        if self.conflict_file_id is not None:
421
 
            s.add('conflict_file_id', self.conflict_file_id.decode('utf8'))
 
386
            s.add('conflict_file_id', self.conflict_file_id)
422
387
            
423
388
        return s
424
389
 
463
428
 
464
429
    typestring = 'unversioned parent'
465
430
 
466
 
    format = 'Conflict because %(path)s is not versioned, but has versioned'\
467
 
             ' children.  %(action)s.'
 
431
    format = 'Conflict adding versioned files to %(path)s.  %(action)s.'
468
432
 
469
433
 
470
434
class MissingParent(HandledConflict):
471
435
    """An attempt to add files to a directory that is not present.
472
 
    Typically, the result of a merge where THIS deleted the directory and
473
 
    the OTHER added a file to it.
474
 
    See also: DeletingParent (same situation, reversed THIS and OTHER)
 
436
    Typically, the result of a merge where one tree deleted the directory and
 
437
    the other added a file to it.
475
438
    """
476
439
 
477
440
    typestring = 'missing parent'
479
442
    format = 'Conflict adding files to %(path)s.  %(action)s.'
480
443
 
481
444
 
482
 
class DeletingParent(HandledConflict):
483
 
    """An attempt to add files to a directory that is not present.
484
 
    Typically, the result of a merge where one OTHER deleted the directory and
485
 
    the THIS added a file to it.
486
 
    """
487
 
 
488
 
    typestring = 'deleting parent'
489
 
 
490
 
    format = "Conflict: can't delete %(path)s because it is not empty.  "\
491
 
             "%(action)s."
492
 
 
493
445
 
494
446
ctype = {}
495
447
 
502
454
 
503
455
 
504
456
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
505
 
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
506
 
               DeletingParent,)
 
457
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,)