~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/conflicts.py

  • Committer: Alexander Belchenko
  • Date: 2006-07-31 16:12:57 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060731161257-91a231523255332c
new official bzr.ico

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Aaron Bentley, Canonical Ltd
 
1
# Copyright (C) 2005 by Aaron Bentley
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
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 = [
60
 
            Option('text',
61
 
                   help='List paths of files with text conflicts.'),
62
 
        ]
63
 
 
64
 
    def run(self, text=False):
 
51
    def run(self):
65
52
        from bzrlib.workingtree import WorkingTree
66
53
        wt = WorkingTree.open_containing(u'.')[0]
67
54
        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')
74
 
 
75
 
 
76
 
class cmd_resolve(commands.Command):
 
55
            print conflict
 
56
 
 
57
 
 
58
class cmd_resolve(bzrlib.commands.Command):
77
59
    """Mark a conflict as resolved.
78
60
 
79
61
    Merge will do its best to combine the changes in two branches, but there
81
63
    it will mark a conflict.  A conflict means that you need to fix something,
82
64
    before you should commit.
83
65
 
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.
 
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.
87
69
 
88
70
    See also bzr conflicts.
89
71
    """
90
72
    aliases = ['resolved']
91
73
    takes_args = ['file*']
92
 
    takes_options = [
93
 
            Option('all', help='Resolve all conflicts in this tree.'),
94
 
            ]
 
74
    takes_options = [Option('all', help='Resolve all conflicts in this tree')]
95
75
    def run(self, file_list=None, all=False):
96
76
        from bzrlib.workingtree import WorkingTree
97
77
        if all:
98
78
            if file_list:
99
 
                raise errors.BzrCommandError("If --all is specified,"
100
 
                                             " no FILE may be provided")
 
79
                raise BzrCommandError("If --all is specified, no FILE may be provided")
101
80
            tree = WorkingTree.open_containing('.')[0]
102
81
            resolve(tree)
103
82
        else:
104
 
            tree, file_list = builtins.tree_files(file_list)
105
83
            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)
 
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)
118
88
 
119
89
 
120
90
def resolve(tree, paths=None, ignore_misses=False):
121
 
    tree.lock_tree_write()
 
91
    tree.lock_write()
122
92
    try:
123
93
        tree_conflicts = tree.conflicts()
124
94
        if paths is None:
129
99
                tree_conflicts.select_conflicts(tree, paths, ignore_misses)
130
100
        try:
131
101
            tree.set_conflicts(new_conflicts)
132
 
        except errors.UnsupportedOperation:
 
102
        except UnsupportedOperation:
133
103
            pass
134
104
        selected_conflicts.remove_files(tree)
135
105
    finally:
143
113
    """
144
114
    conflicted = False
145
115
    try:
146
 
        osutils.rename(filename + ".THIS", filename)
 
116
        rename(filename + ".THIS", filename)
147
117
        conflicted = True
148
118
    except OSError, e:
149
119
        if e.errno != errno.ENOENT:
161
131
        if e.errno != errno.ENOENT:
162
132
            raise
163
133
    if not conflicted:
164
 
        raise errors.NotConflicted(filename)
 
134
        raise NotConflicted(filename)
165
135
 
166
136
 
167
137
class ConflictList(object):
228
198
                continue
229
199
            for suffix in CONFLICT_SUFFIXES:
230
200
                try:
231
 
                    osutils.delete_any(tree.abspath(conflict.path+suffix))
 
201
                    delete_any(tree.abspath(conflict.path+suffix))
232
202
                except OSError, e:
233
203
                    if e.errno != errno.ENOENT:
234
204
                        raise
235
205
 
236
 
    def select_conflicts(self, tree, paths, ignore_misses=False,
237
 
                         recurse=False):
 
206
    def select_conflicts(self, tree, paths, ignore_misses=False):
238
207
        """Select the conflicts associated with paths in a tree.
239
208
        
240
209
        File-ids are also used for this.
259
228
                if cpath in path_set:
260
229
                    selected = True
261
230
                    selected_paths.add(cpath)
262
 
                if recurse:
263
 
                    if osutils.is_inside_any(path_set, cpath):
264
 
                        selected = True
265
 
                        selected_paths.add(cpath)
266
 
 
267
231
            for key in ('file_id', 'conflict_file_id'):
268
232
                cfile_id = getattr(conflict, key, None)
269
233
                if cfile_id is None:
294
258
 
295
259
    def __init__(self, path, file_id=None):
296
260
        self.path = path
297
 
        # warn turned off, because the factory blindly transfers the Stanza
298
 
        # values to __init__ and Stanza is purely a Unicode api.
299
 
        self.file_id = osutils.safe_file_id(file_id, warn=False)
 
261
        self.file_id = file_id
300
262
 
301
263
    def as_stanza(self):
302
 
        s = rio.Stanza(type=self.typestring, path=self.path)
 
264
        s = Stanza(type=self.typestring, path=self.path)
303
265
        if self.file_id is not None:
304
 
            # Stanza requires Unicode apis
305
 
            s.add('file_id', self.file_id.decode('utf8'))
 
266
            s.add('file_id', self.file_id)
306
267
        return s
307
268
 
308
269
    def _cmp_list(self):
416
377
                 conflict_file_id=None):
417
378
        HandledConflict.__init__(self, action, path, file_id)
418
379
        self.conflict_path = conflict_path 
419
 
        # warn turned off, because the factory blindly transfers the Stanza
420
 
        # values to __init__.
421
 
        self.conflict_file_id = osutils.safe_file_id(conflict_file_id,
422
 
                                                     warn=False)
 
380
        self.conflict_file_id = conflict_file_id
423
381
        
424
382
    def _cmp_list(self):
425
383
        return HandledConflict._cmp_list(self) + [self.conflict_path, 
429
387
        s = HandledConflict.as_stanza(self)
430
388
        s.add('conflict_path', self.conflict_path)
431
389
        if self.conflict_file_id is not None:
432
 
            s.add('conflict_file_id', self.conflict_file_id.decode('utf8'))
 
390
            s.add('conflict_file_id', self.conflict_file_id)
433
391
            
434
392
        return s
435
393
 
474
432
 
475
433
    typestring = 'unversioned parent'
476
434
 
477
 
    format = 'Conflict because %(path)s is not versioned, but has versioned'\
478
 
             ' children.  %(action)s.'
 
435
    format = 'Conflict adding versioned files to %(path)s.  %(action)s.'
479
436
 
480
437
 
481
438
class MissingParent(HandledConflict):
482
439
    """An attempt to add files to a directory that is not present.
483
 
    Typically, the result of a merge where THIS deleted the directory and
484
 
    the OTHER added a file to it.
485
 
    See also: DeletingParent (same situation, reversed THIS and OTHER)
 
440
    Typically, the result of a merge where one tree deleted the directory and
 
441
    the other added a file to it.
486
442
    """
487
443
 
488
444
    typestring = 'missing parent'
490
446
    format = 'Conflict adding files to %(path)s.  %(action)s.'
491
447
 
492
448
 
493
 
class DeletingParent(HandledConflict):
494
 
    """An attempt to add files to a directory that is not present.
495
 
    Typically, the result of a merge where one OTHER deleted the directory and
496
 
    the THIS added a file to it.
497
 
    """
498
 
 
499
 
    typestring = 'deleting parent'
500
 
 
501
 
    format = "Conflict: can't delete %(path)s because it is not empty.  "\
502
 
             "%(action)s."
503
 
 
504
449
 
505
450
ctype = {}
506
451
 
513
458
 
514
459
 
515
460
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
516
 
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
517
 
               DeletingParent,)
 
461
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,)