~bzr-pqm/bzr/bzr.dev

2052.3.3 by John Arbash Meinel
Add (c) Canonical to files that Aaron has approved
1
# Copyright (C) 2005 Aaron Bentley, 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
1534.10.15 by Aaron Bentley
Revert does resolve
120
def resolve(tree, paths=None, ignore_misses=False):
1997.1.3 by Robert Collins
All WorkingTree methods which write to the tree, but not to the branch
121
    tree.lock_tree_write()
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
122
    try:
1534.10.23 by Aaron Bentley
Removed conflicts_to_stanzas and stanzas_to_conflicts
123
        tree_conflicts = tree.conflicts()
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
124
        if paths is None:
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
125
            new_conflicts = ConflictList()
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
126
            selected_conflicts = tree_conflicts
127
        else:
128
            new_conflicts, selected_conflicts = \
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
129
                tree_conflicts.select_conflicts(tree, paths, ignore_misses)
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
130
        try:
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
131
            tree.set_conflicts(new_conflicts)
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
132
        except errors.UnsupportedOperation:
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
133
            pass
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
134
        selected_conflicts.remove_files(tree)
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
135
    finally:
136
        tree.unlock()
137
138
1185.35.1 by Aaron Bentley
Implemented conflicts.restore
139
def restore(filename):
140
    """\
141
    Restore a conflicted file to the state it was in before merging.
142
    Only text restoration supported at present.
143
    """
144
    conflicted = False
145
    try:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
146
        osutils.rename(filename + ".THIS", filename)
1185.35.1 by Aaron Bentley
Implemented conflicts.restore
147
        conflicted = True
148
    except OSError, e:
149
        if e.errno != errno.ENOENT:
150
            raise
151
    try:
152
        os.unlink(filename + ".BASE")
153
        conflicted = True
154
    except OSError, e:
155
        if e.errno != errno.ENOENT:
156
            raise
157
    try:
158
        os.unlink(filename + ".OTHER")
159
        conflicted = True
160
    except OSError, e:
161
        if e.errno != errno.ENOENT:
162
            raise
163
    if not conflicted:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
164
        raise errors.NotConflicted(filename)
1534.10.4 by Aaron Bentley
Implemented conflict serialization
165
166
1534.10.22 by Aaron Bentley
Got ConflictList implemented
167
class ConflictList(object):
1652.1.1 by Martin Pool
Fix 'bzr resolve' run from subdirectory
168
    """List of conflicts.
169
170
    Typically obtained from WorkingTree.conflicts()
171
1534.10.22 by Aaron Bentley
Got ConflictList implemented
172
    Can be instantiated from stanzas or from Conflict subclasses.
173
    """
174
175
    def __init__(self, conflicts=None):
176
        object.__init__(self)
177
        if conflicts is None:
178
            self.__list = []
179
        else:
180
            self.__list = conflicts
181
1652.1.1 by Martin Pool
Fix 'bzr resolve' run from subdirectory
182
    def is_empty(self):
183
        return len(self.__list) == 0
184
1534.10.22 by Aaron Bentley
Got ConflictList implemented
185
    def __len__(self):
186
        return len(self.__list)
187
188
    def __iter__(self):
189
        return iter(self.__list)
190
191
    def __getitem__(self, key):
192
        return self.__list[key]
193
194
    def append(self, conflict):
195
        return self.__list.append(conflict)
196
197
    def __eq__(self, other_list):
198
        return list(self) == list(other_list)
199
200
    def __ne__(self, other_list):
201
        return not (self == other_list)
202
203
    def __repr__(self):
204
        return "ConflictList(%r)" % self.__list
205
206
    @staticmethod
207
    def from_stanzas(stanzas):
208
        """Produce a new ConflictList from an iterable of stanzas"""
209
        conflicts = ConflictList()
210
        for stanza in stanzas:
211
            conflicts.append(Conflict.factory(**stanza.as_dict()))
212
        return conflicts
213
214
    def to_stanzas(self):
215
        """Generator of stanzas"""
216
        for conflict in self:
217
            yield conflict.as_stanza()
218
            
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
219
    def to_strings(self):
220
        """Generate strings for the provided conflicts"""
221
        for conflict in self:
222
            yield str(conflict)
223
224
    def remove_files(self, tree):
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
225
        """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
226
        for conflict in self:
227
            if not conflict.has_files:
228
                continue
229
            for suffix in CONFLICT_SUFFIXES:
230
                try:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
231
                    osutils.delete_any(tree.abspath(conflict.path+suffix))
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
232
                except OSError, e:
233
                    if e.errno != errno.ENOENT:
234
                        raise
1534.10.21 by Aaron Bentley
Moved and renamed conflict functions
235
1551.15.58 by Aaron Bentley
Status honours selected paths for conflicts (#127606)
236
    def select_conflicts(self, tree, paths, ignore_misses=False,
237
                         recurse=False):
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
238
        """Select the conflicts associated with paths in a tree.
239
        
240
        File-ids are also used for this.
1551.7.10 by Aaron Bentley
Remerge doesn't clear unrelated conflicts
241
        :return: a pair of ConflictLists: (not_selected, selected)
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
242
        """
243
        path_set = set(paths)
244
        ids = {}
245
        selected_paths = set()
246
        new_conflicts = ConflictList()
247
        selected_conflicts = ConflictList()
248
        for path in paths:
249
            file_id = tree.path2id(path)
250
            if file_id is not None:
251
                ids[file_id] = path
252
253
        for conflict in self:
254
            selected = False
255
            for key in ('path', 'conflict_path'):
256
                cpath = getattr(conflict, key, None)
257
                if cpath is None:
258
                    continue
259
                if cpath in path_set:
260
                    selected = True
261
                    selected_paths.add(cpath)
1551.15.58 by Aaron Bentley
Status honours selected paths for conflicts (#127606)
262
                if recurse:
263
                    if osutils.is_inside_any(path_set, cpath):
264
                        selected = True
265
                        selected_paths.add(cpath)
266
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
267
            for key in ('file_id', 'conflict_file_id'):
268
                cfile_id = getattr(conflict, key, None)
269
                if cfile_id is None:
270
                    continue
271
                try:
272
                    cpath = ids[cfile_id]
273
                except KeyError:
274
                    continue
275
                selected = True
276
                selected_paths.add(cpath)
277
            if selected:
278
                selected_conflicts.append(conflict)
279
            else:
280
                new_conflicts.append(conflict)
281
        if ignore_misses is not True:
282
            for path in [p for p in paths if p not in selected_paths]:
283
                if not os.path.exists(tree.abspath(path)):
284
                    print "%s does not exist" % path
285
                else:
286
                    print "%s is not conflicted" % path
287
        return new_conflicts, selected_conflicts
288
289
 
1534.10.18 by Aaron Bentley
Defined all new Conflict types
290
class Conflict(object):
291
    """Base class for all types of conflict"""
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
292
293
    has_files = False
294
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
295
    def __init__(self, path, file_id=None):
1534.10.18 by Aaron Bentley
Defined all new Conflict types
296
        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.
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)
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
300
301
    def as_stanza(self):
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
302
        s = rio.Stanza(type=self.typestring, path=self.path)
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
303
        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.
304
            # Stanza requires Unicode apis
305
            s.add('file_id', self.file_id.decode('utf8'))
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
306
        return s
307
1534.10.22 by Aaron Bentley
Got ConflictList implemented
308
    def _cmp_list(self):
309
        return [type(self), self.path, self.file_id]
310
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
311
    def __cmp__(self, other):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
312
        if getattr(other, "_cmp_list", None) is None:
313
            return -1
314
        return cmp(self._cmp_list(), other._cmp_list())
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
315
1551.7.11 by Aaron Bentley
Add WorkingTree.add_conflicts
316
    def __hash__(self):
317
        return hash((type(self), self.path, self.file_id))
318
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
319
    def __eq__(self, other):
320
        return self.__cmp__(other) == 0
321
322
    def __ne__(self, other):
323
        return not self.__eq__(other)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
324
1534.10.20 by Aaron Bentley
Got all tests passing
325
    def __str__(self):
326
        return self.format % self.__dict__
327
1534.10.22 by Aaron Bentley
Got ConflictList implemented
328
    def __repr__(self):
329
        rdict = dict(self.__dict__)
330
        rdict['class'] = self.__class__.__name__
331
        return self.rformat % rdict
332
1534.10.18 by Aaron Bentley
Defined all new Conflict types
333
    @staticmethod
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
334
    def factory(type, **kwargs):
1534.10.18 by Aaron Bentley
Defined all new Conflict types
335
        global ctype
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
336
        return ctype[type](**kwargs)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
337
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
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
1534.10.18 by Aaron Bentley
Defined all new Conflict types
347
348
class PathConflict(Conflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
349
    """A conflict was encountered merging file paths"""
350
1534.10.18 by Aaron Bentley
Defined all new Conflict types
351
    typestring = 'path conflict'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
352
1534.10.20 by Aaron Bentley
Got all tests passing
353
    format = 'Path conflict: %(path)s / %(conflict_path)s'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
354
355
    rformat = '%(class)s(%(path)r, %(conflict_path)r, %(file_id)r)'
1534.10.20 by Aaron Bentley
Got all tests passing
356
    def __init__(self, path, conflict_path=None, file_id=None):
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
357
        Conflict.__init__(self, path, file_id)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
358
        self.conflict_path = conflict_path
359
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
360
    def as_stanza(self):
361
        s = Conflict.as_stanza(self)
1534.10.20 by Aaron Bentley
Got all tests passing
362
        if self.conflict_path is not None:
363
            s.add('conflict_path', self.conflict_path)
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
364
        return s
365
1534.10.20 by Aaron Bentley
Got all tests passing
366
367
class ContentsConflict(PathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
368
    """The files are of different types, or not present"""
369
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
370
    has_files = True
371
1534.10.20 by Aaron Bentley
Got all tests passing
372
    typestring = 'contents conflict'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
373
1534.10.20 by Aaron Bentley
Got all tests passing
374
    format = 'Contents conflict in %(path)s'
375
376
377
class TextConflict(PathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
378
    """The merge algorithm could not resolve all differences encountered."""
379
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
380
    has_files = True
381
1534.10.20 by Aaron Bentley
Got all tests passing
382
    typestring = 'text conflict'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
383
1534.10.20 by Aaron Bentley
Got all tests passing
384
    format = 'Text conflict in %(path)s'
385
386
1534.10.18 by Aaron Bentley
Defined all new Conflict types
387
class HandledConflict(Conflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
388
    """A path problem that has been provisionally resolved.
389
    This is intended to be a base class.
390
    """
391
392
    rformat = "%(class)s(%(action)r, %(path)r, %(file_id)r)"
393
    
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
394
    def __init__(self, action, path, file_id=None):
395
        Conflict.__init__(self, path, file_id)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
396
        self.action = action
397
1534.10.22 by Aaron Bentley
Got ConflictList implemented
398
    def _cmp_list(self):
399
        return Conflict._cmp_list(self) + [self.action]
400
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
401
    def as_stanza(self):
402
        s = Conflict.as_stanza(self)
403
        s.add('action', self.action)
404
        return s
405
1534.10.20 by Aaron Bentley
Got all tests passing
406
1534.10.18 by Aaron Bentley
Defined all new Conflict types
407
class HandledPathConflict(HandledConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
408
    """A provisionally-resolved path problem involving two paths.
409
    This is intended to be a base class.
410
    """
411
412
    rformat = "%(class)s(%(action)r, %(path)r, %(conflict_path)r,"\
413
        " %(file_id)r, %(conflict_file_id)r)"
414
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
415
    def __init__(self, action, path, conflict_path, file_id=None,
1534.10.18 by Aaron Bentley
Defined all new Conflict types
416
                 conflict_file_id=None):
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
417
        HandledConflict.__init__(self, action, path, file_id)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
418
        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.
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)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
423
        
1534.10.22 by Aaron Bentley
Got ConflictList implemented
424
    def _cmp_list(self):
425
        return HandledConflict._cmp_list(self) + [self.conflict_path, 
426
                                                  self.conflict_file_id]
427
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
428
    def as_stanza(self):
429
        s = HandledConflict.as_stanza(self)
430
        s.add('conflict_path', self.conflict_path)
431
        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.
432
            s.add('conflict_file_id', self.conflict_file_id.decode('utf8'))
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
433
            
434
        return s
1534.10.20 by Aaron Bentley
Got all tests passing
435
436
1534.10.18 by Aaron Bentley
Defined all new Conflict types
437
class DuplicateID(HandledPathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
438
    """Two files want the same file_id."""
439
1534.10.18 by Aaron Bentley
Defined all new Conflict types
440
    typestring = 'duplicate id'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
441
1534.10.20 by Aaron Bentley
Got all tests passing
442
    format = 'Conflict adding id to %(conflict_path)s.  %(action)s %(path)s.'
443
1534.10.18 by Aaron Bentley
Defined all new Conflict types
444
445
class DuplicateEntry(HandledPathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
446
    """Two directory entries want to have the same name."""
447
1534.10.18 by Aaron Bentley
Defined all new Conflict types
448
    typestring = 'duplicate'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
449
1534.10.20 by Aaron Bentley
Got all tests passing
450
    format = 'Conflict adding file %(conflict_path)s.  %(action)s %(path)s.'
451
1534.10.18 by Aaron Bentley
Defined all new Conflict types
452
453
class ParentLoop(HandledPathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
454
    """An attempt to create an infinitely-looping directory structure.
455
    This is rare, but can be produced like so:
456
457
    tree A:
458
      mv foo/bar
459
    tree B:
460
      mv bar/foo
461
    merge A and B
462
    """
463
1534.10.18 by Aaron Bentley
Defined all new Conflict types
464
    typestring = 'parent loop'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
465
1534.10.20 by Aaron Bentley
Got all tests passing
466
    format = 'Conflict moving %(conflict_path)s into %(path)s.  %(action)s.'
467
1534.10.18 by Aaron Bentley
Defined all new Conflict types
468
469
class UnversionedParent(HandledConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
470
    """An attempt to version an file whose parent directory is not versioned.
471
    Typically, the result of a merge where one tree unversioned the directory
472
    and the other added a versioned file to it.
473
    """
474
1534.10.18 by Aaron Bentley
Defined all new Conflict types
475
    typestring = 'unversioned parent'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
476
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
477
    format = 'Conflict because %(path)s is not versioned, but has versioned'\
478
             ' children.  %(action)s.'
1534.10.20 by Aaron Bentley
Got all tests passing
479
1534.10.18 by Aaron Bentley
Defined all new Conflict types
480
481
class MissingParent(HandledConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
482
    """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
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)
1534.10.22 by Aaron Bentley
Got ConflictList implemented
486
    """
487
1534.10.18 by Aaron Bentley
Defined all new Conflict types
488
    typestring = 'missing parent'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
489
1534.10.20 by Aaron Bentley
Got all tests passing
490
    format = 'Conflict adding files to %(path)s.  %(action)s.'
491
492
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
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
1551.8.23 by Aaron Bentley
Tweaked conflict message to be more understandable
501
    format = "Conflict: can't delete %(path)s because it is not empty.  "\
502
             "%(action)s."
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
503
1534.10.18 by Aaron Bentley
Defined all new Conflict types
504
505
ctype = {}
1534.10.20 by Aaron Bentley
Got all tests passing
506
507
1534.10.18 by Aaron Bentley
Defined all new Conflict types
508
def register_types(*conflict_types):
509
    """Register a Conflict subclass for serialization purposes"""
510
    global ctype
511
    for conflict_type in conflict_types:
512
        ctype[conflict_type.typestring] = conflict_type
513
1534.10.20 by Aaron Bentley
Got all tests passing
514
1534.10.18 by Aaron Bentley
Defined all new Conflict types
515
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
516
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
517
               DeletingParent,)