~bzr-pqm/bzr/bzr.dev

3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
1
# Copyright (C) 2005, 2007 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1185.14.3 by Aaron Bentley
Copied conflict lister in
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1185.14.3 by Aaron Bentley
Copied conflict lister in
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1185.14.3 by Aaron Bentley
Copied conflict lister in
13
# You should have received a copy of the GNU General Public License
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
16
1185.16.11 by Martin Pool
todo
17
# TODO: Move this into builtins
18
19
# TODO: 'bzr resolve' should accept a directory name and work from that 
20
# point down
21
1185.16.33 by Martin Pool
- move 'conflict' and 'resolved' from shipped plugin to regular builtins
22
import os
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
23
24
from bzrlib.lazy_import import lazy_import
25
lazy_import(globals(), """
1185.16.33 by Martin Pool
- move 'conflict' and 'resolved' from shipped plugin to regular builtins
26
import errno
27
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
28
from bzrlib import (
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
29
    builtins,
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
30
    commands,
31
    errors,
32
    osutils,
33
    rio,
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
34
    trace,
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
35
    )
36
""")
1551.2.18 by Aaron Bentley
Updated docs to clarify conflict handling
37
from bzrlib.option import Option
1534.10.6 by Aaron Bentley
Conflict serialization working for WorkingTree3
38
39
40
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
41
1185.14.3 by Aaron Bentley
Copied conflict lister in
42
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
43
class cmd_conflicts(commands.Command):
1185.14.3 by Aaron Bentley
Copied conflict lister in
44
    """List files with conflicts.
1551.2.18 by Aaron Bentley
Updated docs to clarify conflict handling
45
46
    Merge will do its best to combine the changes in two branches, but there
47
    are some kinds of problems only a human can fix.  When it encounters those,
48
    it will mark a conflict.  A conflict means that you need to fix something,
49
    before you should commit.
50
1551.9.8 by Aaron Bentley
Add --text parameter to conflicts
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
1551.2.18 by Aaron Bentley
Updated docs to clarify conflict handling
55
    Use bzr resolve when you have fixed a problem.
56
57
    See also bzr resolve.
1185.14.3 by Aaron Bentley
Copied conflict lister in
58
    """
2598.1.1 by Martin Pool
Add test for and documentation of option style, fix up existing options to comply
59
    takes_options = [
60
            Option('text',
61
                   help='List paths of files with text conflicts.'),
62
        ]
1551.9.8 by Aaron Bentley
Add --text parameter to conflicts
63
64
    def run(self, text=False):
1534.10.6 by Aaron Bentley
Conflict serialization working for WorkingTree3
65
        from bzrlib.workingtree import WorkingTree
1534.10.9 by Aaron Bentley
Switched display functions to conflict_lines
66
        wt = WorkingTree.open_containing(u'.')[0]
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
67
        for conflict in wt.conflicts():
1551.9.8 by Aaron Bentley
Add --text parameter to 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')
1185.14.3 by Aaron Bentley
Copied conflict lister in
74
1652.1.1 by Martin Pool
Fix 'bzr resolve' run from subdirectory
75
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
76
class cmd_resolve(commands.Command):
1185.14.3 by Aaron Bentley
Copied conflict lister in
77
    """Mark a conflict as resolved.
1551.2.18 by Aaron Bentley
Updated docs to clarify conflict handling
78
79
    Merge will do its best to combine the changes in two branches, but there
80
    are some kinds of problems only a human can fix.  When it encounters those,
81
    it will mark a conflict.  A conflict means that you need to fix something,
82
    before you should commit.
83
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as 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.
1551.2.18 by Aaron Bentley
Updated docs to clarify conflict handling
87
88
    See also bzr conflicts.
1185.14.3 by Aaron Bentley
Copied conflict lister in
89
    """
1185.33.24 by Martin Pool
Add alias 'resolved'
90
    aliases = ['resolved']
1185.14.3 by Aaron Bentley
Copied conflict lister in
91
    takes_args = ['file*']
2598.1.2 by Martin Pool
Also check that option help ends in a period, and fix those that don't
92
    takes_options = [
93
            Option('all', help='Resolve all conflicts in this tree.'),
94
            ]
1185.14.3 by Aaron Bentley
Copied conflict lister in
95
    def run(self, file_list=None, all=False):
1534.10.6 by Aaron Bentley
Conflict serialization working for WorkingTree3
96
        from bzrlib.workingtree import WorkingTree
1652.1.3 by Martin Pool
Improved bzr resolve command line handling
97
        if all:
98
            if file_list:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
99
                raise errors.BzrCommandError("If --all is specified,"
100
                                             " no FILE may be provided")
1652.1.3 by Martin Pool
Improved bzr resolve command line handling
101
            tree = WorkingTree.open_containing('.')[0]
102
            resolve(tree)
1185.14.3 by Aaron Bentley
Copied conflict lister in
103
        else:
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
104
            tree, file_list = builtins.tree_files(file_list)
1652.1.3 by Martin Pool
Improved bzr resolve command line handling
105
            if file_list is None:
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
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:
2120.7.4 by Aaron Bentley
Fix normal resolve
117
                resolve(tree, file_list)
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
118
119
3017.2.1 by Aaron Bentley
Revert now resolves conflicts recursively (#102739)
120
def resolve(tree, paths=None, ignore_misses=False, recursive=False):
3017.2.2 by Aaron Bentley
Add docstring to resolve
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
    """
1997.1.3 by Robert Collins
All WorkingTree methods which write to the tree, but not to the branch
133
    tree.lock_tree_write()
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
134
    try:
1534.10.23 by Aaron Bentley
Removed conflicts_to_stanzas and stanzas_to_conflicts
135
        tree_conflicts = tree.conflicts()
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
136
        if paths is None:
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
137
            new_conflicts = ConflictList()
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
138
            selected_conflicts = tree_conflicts
139
        else:
140
            new_conflicts, selected_conflicts = \
3017.2.1 by Aaron Bentley
Revert now resolves conflicts recursively (#102739)
141
                tree_conflicts.select_conflicts(tree, paths, ignore_misses,
142
                    recursive)
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
143
        try:
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
144
            tree.set_conflicts(new_conflicts)
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
145
        except errors.UnsupportedOperation:
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
146
            pass
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
147
        selected_conflicts.remove_files(tree)
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
148
    finally:
149
        tree.unlock()
150
151
1185.35.1 by Aaron Bentley
Implemented conflicts.restore
152
def restore(filename):
153
    """\
154
    Restore a conflicted file to the state it was in before merging.
155
    Only text restoration supported at present.
156
    """
157
    conflicted = False
158
    try:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
159
        osutils.rename(filename + ".THIS", filename)
1185.35.1 by Aaron Bentley
Implemented conflicts.restore
160
        conflicted = True
161
    except OSError, e:
162
        if e.errno != errno.ENOENT:
163
            raise
164
    try:
165
        os.unlink(filename + ".BASE")
166
        conflicted = True
167
    except OSError, e:
168
        if e.errno != errno.ENOENT:
169
            raise
170
    try:
171
        os.unlink(filename + ".OTHER")
172
        conflicted = True
173
    except OSError, e:
174
        if e.errno != errno.ENOENT:
175
            raise
176
    if not conflicted:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
177
        raise errors.NotConflicted(filename)
1534.10.4 by Aaron Bentley
Implemented conflict serialization
178
179
1534.10.22 by Aaron Bentley
Got ConflictList implemented
180
class ConflictList(object):
1652.1.1 by Martin Pool
Fix 'bzr resolve' run from subdirectory
181
    """List of conflicts.
182
183
    Typically obtained from WorkingTree.conflicts()
184
1534.10.22 by Aaron Bentley
Got ConflictList implemented
185
    Can be instantiated from stanzas or from Conflict subclasses.
186
    """
187
188
    def __init__(self, conflicts=None):
189
        object.__init__(self)
190
        if conflicts is None:
191
            self.__list = []
192
        else:
193
            self.__list = conflicts
194
1652.1.1 by Martin Pool
Fix 'bzr resolve' run from subdirectory
195
    def is_empty(self):
196
        return len(self.__list) == 0
197
1534.10.22 by Aaron Bentley
Got ConflictList implemented
198
    def __len__(self):
199
        return len(self.__list)
200
201
    def __iter__(self):
202
        return iter(self.__list)
203
204
    def __getitem__(self, key):
205
        return self.__list[key]
206
207
    def append(self, conflict):
208
        return self.__list.append(conflict)
209
210
    def __eq__(self, other_list):
211
        return list(self) == list(other_list)
212
213
    def __ne__(self, other_list):
214
        return not (self == other_list)
215
216
    def __repr__(self):
217
        return "ConflictList(%r)" % self.__list
218
219
    @staticmethod
220
    def from_stanzas(stanzas):
221
        """Produce a new ConflictList from an iterable of stanzas"""
222
        conflicts = ConflictList()
223
        for stanza in stanzas:
224
            conflicts.append(Conflict.factory(**stanza.as_dict()))
225
        return conflicts
226
227
    def to_stanzas(self):
228
        """Generator of stanzas"""
229
        for conflict in self:
230
            yield conflict.as_stanza()
231
            
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
232
    def to_strings(self):
233
        """Generate strings for the provided conflicts"""
234
        for conflict in self:
235
            yield str(conflict)
236
237
    def remove_files(self, tree):
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
238
        """Remove the THIS, BASE and OTHER files for listed conflicts"""
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
239
        for conflict in self:
240
            if not conflict.has_files:
241
                continue
242
            for suffix in CONFLICT_SUFFIXES:
243
                try:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
244
                    osutils.delete_any(tree.abspath(conflict.path+suffix))
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
245
                except OSError, e:
246
                    if e.errno != errno.ENOENT:
247
                        raise
1534.10.21 by Aaron Bentley
Moved and renamed conflict functions
248
1551.15.58 by Aaron Bentley
Status honours selected paths for conflicts (#127606)
249
    def select_conflicts(self, tree, paths, ignore_misses=False,
250
                         recurse=False):
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
251
        """Select the conflicts associated with paths in a tree.
252
        
253
        File-ids are also used for this.
1551.7.10 by Aaron Bentley
Remerge doesn't clear unrelated conflicts
254
        :return: a pair of ConflictLists: (not_selected, selected)
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
255
        """
256
        path_set = set(paths)
257
        ids = {}
258
        selected_paths = set()
259
        new_conflicts = ConflictList()
260
        selected_conflicts = ConflictList()
261
        for path in paths:
262
            file_id = tree.path2id(path)
263
            if file_id is not None:
264
                ids[file_id] = path
265
266
        for conflict in self:
267
            selected = False
268
            for key in ('path', 'conflict_path'):
269
                cpath = getattr(conflict, key, None)
270
                if cpath is None:
271
                    continue
272
                if cpath in path_set:
273
                    selected = True
274
                    selected_paths.add(cpath)
1551.15.58 by Aaron Bentley
Status honours selected paths for conflicts (#127606)
275
                if recurse:
276
                    if osutils.is_inside_any(path_set, cpath):
277
                        selected = True
278
                        selected_paths.add(cpath)
279
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
280
            for key in ('file_id', 'conflict_file_id'):
281
                cfile_id = getattr(conflict, key, None)
282
                if cfile_id is None:
283
                    continue
284
                try:
285
                    cpath = ids[cfile_id]
286
                except KeyError:
287
                    continue
288
                selected = True
289
                selected_paths.add(cpath)
290
            if selected:
291
                selected_conflicts.append(conflict)
292
            else:
293
                new_conflicts.append(conflict)
294
        if ignore_misses is not True:
295
            for path in [p for p in paths if p not in selected_paths]:
296
                if not os.path.exists(tree.abspath(path)):
297
                    print "%s does not exist" % path
298
                else:
299
                    print "%s is not conflicted" % path
300
        return new_conflicts, selected_conflicts
301
302
 
1534.10.18 by Aaron Bentley
Defined all new Conflict types
303
class Conflict(object):
304
    """Base class for all types of conflict"""
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
305
306
    has_files = False
307
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
308
    def __init__(self, path, file_id=None):
1534.10.18 by Aaron Bentley
Defined all new Conflict types
309
        self.path = path
2309.4.13 by John Arbash Meinel
Conflicts go through Stanza so the need to be aware of utf8 versus unicode file ids.
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)
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
313
314
    def as_stanza(self):
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
315
        s = rio.Stanza(type=self.typestring, path=self.path)
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
316
        if self.file_id is not None:
2309.4.13 by John Arbash Meinel
Conflicts go through Stanza so the need to be aware of utf8 versus unicode file ids.
317
            # Stanza requires Unicode apis
318
            s.add('file_id', self.file_id.decode('utf8'))
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
319
        return s
320
1534.10.22 by Aaron Bentley
Got ConflictList implemented
321
    def _cmp_list(self):
322
        return [type(self), self.path, self.file_id]
323
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
324
    def __cmp__(self, other):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
325
        if getattr(other, "_cmp_list", None) is None:
326
            return -1
327
        return cmp(self._cmp_list(), other._cmp_list())
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
328
1551.7.11 by Aaron Bentley
Add WorkingTree.add_conflicts
329
    def __hash__(self):
330
        return hash((type(self), self.path, self.file_id))
331
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
332
    def __eq__(self, other):
333
        return self.__cmp__(other) == 0
334
335
    def __ne__(self, other):
336
        return not self.__eq__(other)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
337
1534.10.20 by Aaron Bentley
Got all tests passing
338
    def __str__(self):
339
        return self.format % self.__dict__
340
1534.10.22 by Aaron Bentley
Got ConflictList implemented
341
    def __repr__(self):
342
        rdict = dict(self.__dict__)
343
        rdict['class'] = self.__class__.__name__
344
        return self.rformat % rdict
345
1534.10.18 by Aaron Bentley
Defined all new Conflict types
346
    @staticmethod
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
347
    def factory(type, **kwargs):
1534.10.18 by Aaron Bentley
Defined all new Conflict types
348
        global ctype
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
349
        return ctype[type](**kwargs)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
350
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
351
    @staticmethod
352
    def sort_key(conflict):
353
        if conflict.path is not None:
354
            return conflict.path, conflict.typestring
355
        elif getattr(conflict, "conflict_path", None) is not None:
356
            return conflict.conflict_path, conflict.typestring
357
        else:
358
            return None, conflict.typestring
359
1534.10.18 by Aaron Bentley
Defined all new Conflict types
360
361
class PathConflict(Conflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
362
    """A conflict was encountered merging file paths"""
363
1534.10.18 by Aaron Bentley
Defined all new Conflict types
364
    typestring = 'path conflict'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
365
1534.10.20 by Aaron Bentley
Got all tests passing
366
    format = 'Path conflict: %(path)s / %(conflict_path)s'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
367
368
    rformat = '%(class)s(%(path)r, %(conflict_path)r, %(file_id)r)'
1534.10.20 by Aaron Bentley
Got all tests passing
369
    def __init__(self, path, conflict_path=None, file_id=None):
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
370
        Conflict.__init__(self, path, file_id)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
371
        self.conflict_path = conflict_path
372
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
373
    def as_stanza(self):
374
        s = Conflict.as_stanza(self)
1534.10.20 by Aaron Bentley
Got all tests passing
375
        if self.conflict_path is not None:
376
            s.add('conflict_path', self.conflict_path)
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
377
        return s
378
1534.10.20 by Aaron Bentley
Got all tests passing
379
380
class ContentsConflict(PathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
381
    """The files are of different types, or not present"""
382
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
383
    has_files = True
384
1534.10.20 by Aaron Bentley
Got all tests passing
385
    typestring = 'contents conflict'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
386
1534.10.20 by Aaron Bentley
Got all tests passing
387
    format = 'Contents conflict in %(path)s'
388
389
390
class TextConflict(PathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
391
    """The merge algorithm could not resolve all differences encountered."""
392
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
393
    has_files = True
394
1534.10.20 by Aaron Bentley
Got all tests passing
395
    typestring = 'text conflict'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
396
1534.10.20 by Aaron Bentley
Got all tests passing
397
    format = 'Text conflict in %(path)s'
398
399
1534.10.18 by Aaron Bentley
Defined all new Conflict types
400
class HandledConflict(Conflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
401
    """A path problem that has been provisionally resolved.
402
    This is intended to be a base class.
403
    """
404
405
    rformat = "%(class)s(%(action)r, %(path)r, %(file_id)r)"
406
    
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
407
    def __init__(self, action, path, file_id=None):
408
        Conflict.__init__(self, path, file_id)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
409
        self.action = action
410
1534.10.22 by Aaron Bentley
Got ConflictList implemented
411
    def _cmp_list(self):
412
        return Conflict._cmp_list(self) + [self.action]
413
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
414
    def as_stanza(self):
415
        s = Conflict.as_stanza(self)
416
        s.add('action', self.action)
417
        return s
418
1534.10.20 by Aaron Bentley
Got all tests passing
419
1534.10.18 by Aaron Bentley
Defined all new Conflict types
420
class HandledPathConflict(HandledConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
421
    """A provisionally-resolved path problem involving two paths.
422
    This is intended to be a base class.
423
    """
424
425
    rformat = "%(class)s(%(action)r, %(path)r, %(conflict_path)r,"\
426
        " %(file_id)r, %(conflict_file_id)r)"
427
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
428
    def __init__(self, action, path, conflict_path, file_id=None,
1534.10.18 by Aaron Bentley
Defined all new Conflict types
429
                 conflict_file_id=None):
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
430
        HandledConflict.__init__(self, action, path, file_id)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
431
        self.conflict_path = conflict_path 
2309.4.13 by John Arbash Meinel
Conflicts go through Stanza so the need to be aware of utf8 versus unicode file ids.
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)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
436
        
1534.10.22 by Aaron Bentley
Got ConflictList implemented
437
    def _cmp_list(self):
438
        return HandledConflict._cmp_list(self) + [self.conflict_path, 
439
                                                  self.conflict_file_id]
440
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
441
    def as_stanza(self):
442
        s = HandledConflict.as_stanza(self)
443
        s.add('conflict_path', self.conflict_path)
444
        if self.conflict_file_id is not None:
2309.4.13 by John Arbash Meinel
Conflicts go through Stanza so the need to be aware of utf8 versus unicode file ids.
445
            s.add('conflict_file_id', self.conflict_file_id.decode('utf8'))
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
446
            
447
        return s
1534.10.20 by Aaron Bentley
Got all tests passing
448
449
1534.10.18 by Aaron Bentley
Defined all new Conflict types
450
class DuplicateID(HandledPathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
451
    """Two files want the same file_id."""
452
1534.10.18 by Aaron Bentley
Defined all new Conflict types
453
    typestring = 'duplicate id'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
454
1534.10.20 by Aaron Bentley
Got all tests passing
455
    format = 'Conflict adding id to %(conflict_path)s.  %(action)s %(path)s.'
456
1534.10.18 by Aaron Bentley
Defined all new Conflict types
457
458
class DuplicateEntry(HandledPathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
459
    """Two directory entries want to have the same name."""
460
1534.10.18 by Aaron Bentley
Defined all new Conflict types
461
    typestring = 'duplicate'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
462
1534.10.20 by Aaron Bentley
Got all tests passing
463
    format = 'Conflict adding file %(conflict_path)s.  %(action)s %(path)s.'
464
1534.10.18 by Aaron Bentley
Defined all new Conflict types
465
466
class ParentLoop(HandledPathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
467
    """An attempt to create an infinitely-looping directory structure.
468
    This is rare, but can be produced like so:
469
470
    tree A:
471
      mv foo/bar
472
    tree B:
473
      mv bar/foo
474
    merge A and B
475
    """
476
1534.10.18 by Aaron Bentley
Defined all new Conflict types
477
    typestring = 'parent loop'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
478
1534.10.20 by Aaron Bentley
Got all tests passing
479
    format = 'Conflict moving %(conflict_path)s into %(path)s.  %(action)s.'
480
1534.10.18 by Aaron Bentley
Defined all new Conflict types
481
482
class UnversionedParent(HandledConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
483
    """An attempt to version an file whose parent directory is not versioned.
484
    Typically, the result of a merge where one tree unversioned the directory
485
    and the other added a versioned file to it.
486
    """
487
1534.10.18 by Aaron Bentley
Defined all new Conflict types
488
    typestring = 'unversioned parent'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
489
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
490
    format = 'Conflict because %(path)s is not versioned, but has versioned'\
491
             ' children.  %(action)s.'
1534.10.20 by Aaron Bentley
Got all tests passing
492
1534.10.18 by Aaron Bentley
Defined all new Conflict types
493
494
class MissingParent(HandledConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
495
    """An attempt to add files to a directory that is not present.
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
496
    Typically, the result of a merge where THIS deleted the directory and
497
    the OTHER added a file to it.
498
    See also: DeletingParent (same situation, reversed THIS and OTHER)
1534.10.22 by Aaron Bentley
Got ConflictList implemented
499
    """
500
1534.10.18 by Aaron Bentley
Defined all new Conflict types
501
    typestring = 'missing parent'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
502
1534.10.20 by Aaron Bentley
Got all tests passing
503
    format = 'Conflict adding files to %(path)s.  %(action)s.'
504
505
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
506
class DeletingParent(HandledConflict):
507
    """An attempt to add files to a directory that is not present.
508
    Typically, the result of a merge where one OTHER deleted the directory and
509
    the THIS added a file to it.
510
    """
511
512
    typestring = 'deleting parent'
513
1551.8.23 by Aaron Bentley
Tweaked conflict message to be more understandable
514
    format = "Conflict: can't delete %(path)s because it is not empty.  "\
515
             "%(action)s."
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
516
1534.10.18 by Aaron Bentley
Defined all new Conflict types
517
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
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
1534.10.18 by Aaron Bentley
Defined all new Conflict types
528
ctype = {}
1534.10.20 by Aaron Bentley
Got all tests passing
529
530
1534.10.18 by Aaron Bentley
Defined all new Conflict types
531
def register_types(*conflict_types):
532
    """Register a Conflict subclass for serialization purposes"""
533
    global ctype
534
    for conflict_type in conflict_types:
535
        ctype[conflict_type.typestring] = conflict_type
536
1534.10.20 by Aaron Bentley
Got all tests passing
537
1534.10.18 by Aaron Bentley
Defined all new Conflict types
538
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
539
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
540
               DeletingParent, NonDirectoryParent)