/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/conflicts.py

(John Arbash Meinel)  Fix bug #158333,
        make sure that Repository.fetch(self) is properly a no-op for all
        Repository implementations.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Aaron Bentley
2
 
 
 
1
# Copyright (C) 2005 Aaron Bentley, Canonical Ltd
 
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(), """
23
26
import errno
24
27
 
25
 
import bzrlib
26
 
from bzrlib.commands import register_command
27
 
from bzrlib.errors import BzrCommandError, NotConflicted, UnsupportedOperation
 
28
from bzrlib import (
 
29
    builtins,
 
30
    commands,
 
31
    errors,
 
32
    osutils,
 
33
    rio,
 
34
    trace,
 
35
    )
 
36
""")
28
37
from bzrlib.option import Option
29
 
from bzrlib.osutils import rename, delete_any
30
 
from bzrlib.rio import Stanza
31
38
 
32
39
 
33
40
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
34
41
 
35
42
 
36
 
class cmd_conflicts(bzrlib.commands.Command):
 
43
class cmd_conflicts(commands.Command):
37
44
    """List files with conflicts.
38
45
 
39
46
    Merge will do its best to combine the changes in two branches, but there
41
48
    it will mark a conflict.  A conflict means that you need to fix something,
42
49
    before you should commit.
43
50
 
 
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
 
44
55
    Use bzr resolve when you have fixed a problem.
45
56
 
46
 
    (conflicts are determined by the presence of .BASE .TREE, and .OTHER 
47
 
    files.)
48
 
 
49
57
    See also bzr resolve.
50
58
    """
51
 
    def run(self):
 
59
    takes_options = [
 
60
            Option('text',
 
61
                   help='List paths of files with text conflicts.'),
 
62
        ]
 
63
 
 
64
    def run(self, text=False):
52
65
        from bzrlib.workingtree import WorkingTree
53
66
        wt = WorkingTree.open_containing(u'.')[0]
54
67
        for conflict in wt.conflicts():
55
 
            print conflict
56
 
 
57
 
 
58
 
class cmd_resolve(bzrlib.commands.Command):
 
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):
59
77
    """Mark a conflict as resolved.
60
78
 
61
79
    Merge will do its best to combine the changes in two branches, but there
63
81
    it will mark a conflict.  A conflict means that you need to fix something,
64
82
    before you should commit.
65
83
 
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
    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.
69
87
 
70
88
    See also bzr conflicts.
71
89
    """
72
90
    aliases = ['resolved']
73
91
    takes_args = ['file*']
74
 
    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
            ]
75
95
    def run(self, file_list=None, all=False):
76
96
        from bzrlib.workingtree import WorkingTree
77
97
        if all:
78
98
            if file_list:
79
 
                raise BzrCommandError("If --all is specified, no FILE may be provided")
 
99
                raise errors.BzrCommandError("If --all is specified,"
 
100
                                             " no FILE may be provided")
80
101
            tree = WorkingTree.open_containing('.')[0]
81
102
            resolve(tree)
82
103
        else:
 
104
            tree, file_list = builtins.tree_files(file_list)
83
105
            if file_list is None:
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)
 
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)
88
118
 
89
119
 
90
120
def resolve(tree, paths=None, ignore_misses=False):
91
 
    tree.lock_write()
 
121
    tree.lock_tree_write()
92
122
    try:
93
123
        tree_conflicts = tree.conflicts()
94
124
        if paths is None:
99
129
                tree_conflicts.select_conflicts(tree, paths, ignore_misses)
100
130
        try:
101
131
            tree.set_conflicts(new_conflicts)
102
 
        except UnsupportedOperation:
 
132
        except errors.UnsupportedOperation:
103
133
            pass
104
134
        selected_conflicts.remove_files(tree)
105
135
    finally:
113
143
    """
114
144
    conflicted = False
115
145
    try:
116
 
        rename(filename + ".THIS", filename)
 
146
        osutils.rename(filename + ".THIS", filename)
117
147
        conflicted = True
118
148
    except OSError, e:
119
149
        if e.errno != errno.ENOENT:
131
161
        if e.errno != errno.ENOENT:
132
162
            raise
133
163
    if not conflicted:
134
 
        raise NotConflicted(filename)
 
164
        raise errors.NotConflicted(filename)
135
165
 
136
166
 
137
167
class ConflictList(object):
198
228
                continue
199
229
            for suffix in CONFLICT_SUFFIXES:
200
230
                try:
201
 
                    delete_any(tree.abspath(conflict.path+suffix))
 
231
                    osutils.delete_any(tree.abspath(conflict.path+suffix))
202
232
                except OSError, e:
203
233
                    if e.errno != errno.ENOENT:
204
234
                        raise
205
235
 
206
 
    def select_conflicts(self, tree, paths, ignore_misses=False):
 
236
    def select_conflicts(self, tree, paths, ignore_misses=False,
 
237
                         recurse=False):
207
238
        """Select the conflicts associated with paths in a tree.
208
239
        
209
240
        File-ids are also used for this.
 
241
        :return: a pair of ConflictLists: (not_selected, selected)
210
242
        """
211
243
        path_set = set(paths)
212
244
        ids = {}
227
259
                if cpath in path_set:
228
260
                    selected = True
229
261
                    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
 
230
267
            for key in ('file_id', 'conflict_file_id'):
231
268
                cfile_id = getattr(conflict, key, None)
232
269
                if cfile_id is None:
257
294
 
258
295
    def __init__(self, path, file_id=None):
259
296
        self.path = path
260
 
        self.file_id = file_id
 
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
300
 
262
301
    def as_stanza(self):
263
 
        s = Stanza(type=self.typestring, path=self.path)
 
302
        s = rio.Stanza(type=self.typestring, path=self.path)
264
303
        if self.file_id is not None:
265
 
            s.add('file_id', self.file_id)
 
304
            # Stanza requires Unicode apis
 
305
            s.add('file_id', self.file_id.decode('utf8'))
266
306
        return s
267
307
 
268
308
    def _cmp_list(self):
273
313
            return -1
274
314
        return cmp(self._cmp_list(), other._cmp_list())
275
315
 
 
316
    def __hash__(self):
 
317
        return hash((type(self), self.path, self.file_id))
 
318
 
276
319
    def __eq__(self, other):
277
320
        return self.__cmp__(other) == 0
278
321
 
292
335
        global ctype
293
336
        return ctype[type](**kwargs)
294
337
 
 
338
    @staticmethod
 
339
    def sort_key(conflict):
 
340
        if conflict.path is not None:
 
341
            return conflict.path, conflict.typestring
 
342
        elif getattr(conflict, "conflict_path", None) is not None:
 
343
            return conflict.conflict_path, conflict.typestring
 
344
        else:
 
345
            return None, conflict.typestring
 
346
 
295
347
 
296
348
class PathConflict(Conflict):
297
349
    """A conflict was encountered merging file paths"""
364
416
                 conflict_file_id=None):
365
417
        HandledConflict.__init__(self, action, path, file_id)
366
418
        self.conflict_path = conflict_path 
367
 
        self.conflict_file_id = conflict_file_id
 
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)
368
423
        
369
424
    def _cmp_list(self):
370
425
        return HandledConflict._cmp_list(self) + [self.conflict_path, 
374
429
        s = HandledConflict.as_stanza(self)
375
430
        s.add('conflict_path', self.conflict_path)
376
431
        if self.conflict_file_id is not None:
377
 
            s.add('conflict_file_id', self.conflict_file_id)
 
432
            s.add('conflict_file_id', self.conflict_file_id.decode('utf8'))
378
433
            
379
434
        return s
380
435
 
419
474
 
420
475
    typestring = 'unversioned parent'
421
476
 
422
 
    format = 'Conflict adding versioned files to %(path)s.  %(action)s.'
 
477
    format = 'Conflict because %(path)s is not versioned, but has versioned'\
 
478
             ' children.  %(action)s.'
423
479
 
424
480
 
425
481
class MissingParent(HandledConflict):
426
482
    """An attempt to add files to a directory that is not present.
427
 
    Typically, the result of a merge where one tree deleted the directory and
428
 
    the other added a file to it.
 
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)
429
486
    """
430
487
 
431
488
    typestring = 'missing parent'
433
490
    format = 'Conflict adding files to %(path)s.  %(action)s.'
434
491
 
435
492
 
 
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
 
436
504
 
437
505
ctype = {}
438
506
 
445
513
 
446
514
 
447
515
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
448
 
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,)
 
516
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
 
517
               DeletingParent,)