~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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1185.14.3 by Aaron Bentley
Copied conflict lister in
16
1185.16.11 by Martin Pool
todo
17
# TODO: Move this into builtins
18
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
19
# TODO: 'bzr resolve' should accept a directory name and work from that
1185.16.11 by Martin Pool
todo
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.
1185.14.3 by Aaron Bentley
Copied conflict lister in
56
    """
2598.1.1 by Martin Pool
Add test for and documentation of option style, fix up existing options to comply
57
    takes_options = [
58
            Option('text',
59
                   help='List paths of files with text conflicts.'),
60
        ]
4798.7.1 by Neil Martinsen-Burrell
fix some formatting and see also usage
61
    _see_also = ['resolve']
1551.9.8 by Aaron Bentley
Add --text parameter to conflicts
62
63
    def run(self, text=False):
1534.10.6 by Aaron Bentley
Conflict serialization working for WorkingTree3
64
        from bzrlib.workingtree import WorkingTree
1534.10.9 by Aaron Bentley
Switched display functions to conflict_lines
65
        wt = WorkingTree.open_containing(u'.')[0]
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
66
        for conflict in wt.conflicts():
1551.9.8 by Aaron Bentley
Add --text parameter to conflicts
67
            if text:
68
                if conflict.typestring != 'text conflict':
69
                    continue
70
                self.outf.write(conflict.path + '\n')
71
            else:
72
                self.outf.write(str(conflict) + '\n')
1185.14.3 by Aaron Bentley
Copied conflict lister in
73
1652.1.1 by Martin Pool
Fix 'bzr resolve' run from subdirectory
74
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
75
class cmd_resolve(commands.Command):
1185.14.3 by Aaron Bentley
Copied conflict lister in
76
    """Mark a conflict as resolved.
1551.2.18 by Aaron Bentley
Updated docs to clarify conflict handling
77
78
    Merge will do its best to combine the changes in two branches, but there
79
    are some kinds of problems only a human can fix.  When it encounters those,
80
    it will mark a conflict.  A conflict means that you need to fix something,
81
    before you should commit.
82
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
83
    Once you have fixed a problem, use "bzr resolve" to automatically mark
4798.7.1 by Neil Martinsen-Burrell
fix some formatting and see also usage
84
    text conflicts as fixed, "bzr resolve FILE" to mark a specific conflict as
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
85
    resolved, or "bzr resolve --all" to mark all conflicts as resolved.
1185.14.3 by Aaron Bentley
Copied conflict lister in
86
    """
1185.33.24 by Martin Pool
Add alias 'resolved'
87
    aliases = ['resolved']
1185.14.3 by Aaron Bentley
Copied conflict lister in
88
    takes_args = ['file*']
2598.1.2 by Martin Pool
Also check that option help ends in a period, and fix those that don't
89
    takes_options = [
90
            Option('all', help='Resolve all conflicts in this tree.'),
91
            ]
4798.7.1 by Neil Martinsen-Burrell
fix some formatting and see also usage
92
    _see_also = ['conflicts']
1185.14.3 by Aaron Bentley
Copied conflict lister in
93
    def run(self, file_list=None, all=False):
1534.10.6 by Aaron Bentley
Conflict serialization working for WorkingTree3
94
        from bzrlib.workingtree import WorkingTree
1652.1.3 by Martin Pool
Improved bzr resolve command line handling
95
        if all:
96
            if file_list:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
97
                raise errors.BzrCommandError("If --all is specified,"
98
                                             " no FILE may be provided")
1652.1.3 by Martin Pool
Improved bzr resolve command line handling
99
            tree = WorkingTree.open_containing('.')[0]
100
            resolve(tree)
1185.14.3 by Aaron Bentley
Copied conflict lister in
101
        else:
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
102
            tree, file_list = builtins.tree_files(file_list)
1652.1.3 by Martin Pool
Improved bzr resolve command line handling
103
            if file_list is None:
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
104
                un_resolved, resolved = tree.auto_resolve()
105
                if len(un_resolved) > 0:
106
                    trace.note('%d conflict(s) auto-resolved.', len(resolved))
107
                    trace.note('Remaining conflicts:')
108
                    for conflict in un_resolved:
109
                        trace.note(conflict)
110
                    return 1
111
                else:
112
                    trace.note('All conflicts resolved.')
113
                    return 0
114
            else:
2120.7.4 by Aaron Bentley
Fix normal resolve
115
                resolve(tree, file_list)
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
116
117
3017.2.1 by Aaron Bentley
Revert now resolves conflicts recursively (#102739)
118
def resolve(tree, paths=None, ignore_misses=False, recursive=False):
3017.2.2 by Aaron Bentley
Add docstring to resolve
119
    """Resolve some or all of the conflicts in a working tree.
120
121
    :param paths: If None, resolve all conflicts.  Otherwise, select only
122
        specified conflicts.
123
    :param recursive: If True, then elements of paths which are directories
124
        have all their children resolved, etc.  When invoked as part of
125
        recursive commands like revert, this should be True.  For commands
126
        or applications wishing finer-grained control, like the resolve
127
        command, this should be False.
128
    :ignore_misses: If False, warnings will be printed if the supplied paths
129
        do not have conflicts.
130
    """
1997.1.3 by Robert Collins
All WorkingTree methods which write to the tree, but not to the branch
131
    tree.lock_tree_write()
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
132
    try:
1534.10.23 by Aaron Bentley
Removed conflicts_to_stanzas and stanzas_to_conflicts
133
        tree_conflicts = tree.conflicts()
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
134
        if paths is None:
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
135
            new_conflicts = ConflictList()
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
136
            selected_conflicts = tree_conflicts
137
        else:
138
            new_conflicts, selected_conflicts = \
3017.2.1 by Aaron Bentley
Revert now resolves conflicts recursively (#102739)
139
                tree_conflicts.select_conflicts(tree, paths, ignore_misses,
140
                    recursive)
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
141
        try:
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
142
            tree.set_conflicts(new_conflicts)
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
143
        except errors.UnsupportedOperation:
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
144
            pass
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
145
        selected_conflicts.remove_files(tree)
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
146
    finally:
147
        tree.unlock()
148
149
1185.35.1 by Aaron Bentley
Implemented conflicts.restore
150
def restore(filename):
151
    """\
152
    Restore a conflicted file to the state it was in before merging.
153
    Only text restoration supported at present.
154
    """
155
    conflicted = False
156
    try:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
157
        osutils.rename(filename + ".THIS", filename)
1185.35.1 by Aaron Bentley
Implemented conflicts.restore
158
        conflicted = True
159
    except OSError, e:
160
        if e.errno != errno.ENOENT:
161
            raise
162
    try:
163
        os.unlink(filename + ".BASE")
164
        conflicted = True
165
    except OSError, e:
166
        if e.errno != errno.ENOENT:
167
            raise
168
    try:
169
        os.unlink(filename + ".OTHER")
170
        conflicted = True
171
    except OSError, e:
172
        if e.errno != errno.ENOENT:
173
            raise
174
    if not conflicted:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
175
        raise errors.NotConflicted(filename)
1534.10.4 by Aaron Bentley
Implemented conflict serialization
176
177
1534.10.22 by Aaron Bentley
Got ConflictList implemented
178
class ConflictList(object):
1652.1.1 by Martin Pool
Fix 'bzr resolve' run from subdirectory
179
    """List of conflicts.
180
181
    Typically obtained from WorkingTree.conflicts()
182
1534.10.22 by Aaron Bentley
Got ConflictList implemented
183
    Can be instantiated from stanzas or from Conflict subclasses.
184
    """
185
186
    def __init__(self, conflicts=None):
187
        object.__init__(self)
188
        if conflicts is None:
189
            self.__list = []
190
        else:
191
            self.__list = conflicts
192
1652.1.1 by Martin Pool
Fix 'bzr resolve' run from subdirectory
193
    def is_empty(self):
194
        return len(self.__list) == 0
195
1534.10.22 by Aaron Bentley
Got ConflictList implemented
196
    def __len__(self):
197
        return len(self.__list)
198
199
    def __iter__(self):
200
        return iter(self.__list)
201
202
    def __getitem__(self, key):
203
        return self.__list[key]
204
205
    def append(self, conflict):
206
        return self.__list.append(conflict)
207
208
    def __eq__(self, other_list):
209
        return list(self) == list(other_list)
210
211
    def __ne__(self, other_list):
212
        return not (self == other_list)
213
214
    def __repr__(self):
215
        return "ConflictList(%r)" % self.__list
216
217
    @staticmethod
218
    def from_stanzas(stanzas):
219
        """Produce a new ConflictList from an iterable of stanzas"""
220
        conflicts = ConflictList()
221
        for stanza in stanzas:
222
            conflicts.append(Conflict.factory(**stanza.as_dict()))
223
        return conflicts
224
225
    def to_stanzas(self):
226
        """Generator of stanzas"""
227
        for conflict in self:
228
            yield conflict.as_stanza()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
229
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
230
    def to_strings(self):
231
        """Generate strings for the provided conflicts"""
232
        for conflict in self:
233
            yield str(conflict)
234
235
    def remove_files(self, tree):
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
236
        """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
237
        for conflict in self:
238
            if not conflict.has_files:
239
                continue
240
            for suffix in CONFLICT_SUFFIXES:
241
                try:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
242
                    osutils.delete_any(tree.abspath(conflict.path+suffix))
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
243
                except OSError, e:
244
                    if e.errno != errno.ENOENT:
245
                        raise
1534.10.21 by Aaron Bentley
Moved and renamed conflict functions
246
1551.15.58 by Aaron Bentley
Status honours selected paths for conflicts (#127606)
247
    def select_conflicts(self, tree, paths, ignore_misses=False,
248
                         recurse=False):
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
249
        """Select the conflicts associated with paths in a tree.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
250
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
251
        File-ids are also used for this.
1551.7.10 by Aaron Bentley
Remerge doesn't clear unrelated conflicts
252
        :return: a pair of ConflictLists: (not_selected, selected)
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
253
        """
254
        path_set = set(paths)
255
        ids = {}
256
        selected_paths = set()
257
        new_conflicts = ConflictList()
258
        selected_conflicts = ConflictList()
259
        for path in paths:
260
            file_id = tree.path2id(path)
261
            if file_id is not None:
262
                ids[file_id] = path
263
264
        for conflict in self:
265
            selected = False
266
            for key in ('path', 'conflict_path'):
267
                cpath = getattr(conflict, key, None)
268
                if cpath is None:
269
                    continue
270
                if cpath in path_set:
271
                    selected = True
272
                    selected_paths.add(cpath)
1551.15.58 by Aaron Bentley
Status honours selected paths for conflicts (#127606)
273
                if recurse:
274
                    if osutils.is_inside_any(path_set, cpath):
275
                        selected = True
276
                        selected_paths.add(cpath)
277
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
278
            for key in ('file_id', 'conflict_file_id'):
279
                cfile_id = getattr(conflict, key, None)
280
                if cfile_id is None:
281
                    continue
282
                try:
283
                    cpath = ids[cfile_id]
284
                except KeyError:
285
                    continue
286
                selected = True
287
                selected_paths.add(cpath)
288
            if selected:
289
                selected_conflicts.append(conflict)
290
            else:
291
                new_conflicts.append(conflict)
292
        if ignore_misses is not True:
293
            for path in [p for p in paths if p not in selected_paths]:
294
                if not os.path.exists(tree.abspath(path)):
295
                    print "%s does not exist" % path
296
                else:
297
                    print "%s is not conflicted" % path
298
        return new_conflicts, selected_conflicts
299
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
300
1534.10.18 by Aaron Bentley
Defined all new Conflict types
301
class Conflict(object):
302
    """Base class for all types of conflict"""
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
303
304
    has_files = False
305
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
306
    def __init__(self, path, file_id=None):
1534.10.18 by Aaron Bentley
Defined all new Conflict types
307
        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.
308
        # warn turned off, because the factory blindly transfers the Stanza
309
        # values to __init__ and Stanza is purely a Unicode api.
310
        self.file_id = osutils.safe_file_id(file_id, warn=False)
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
311
312
    def as_stanza(self):
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
313
        s = rio.Stanza(type=self.typestring, path=self.path)
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
314
        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.
315
            # Stanza requires Unicode apis
316
            s.add('file_id', self.file_id.decode('utf8'))
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
317
        return s
318
1534.10.22 by Aaron Bentley
Got ConflictList implemented
319
    def _cmp_list(self):
320
        return [type(self), self.path, self.file_id]
321
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
322
    def __cmp__(self, other):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
323
        if getattr(other, "_cmp_list", None) is None:
324
            return -1
325
        return cmp(self._cmp_list(), other._cmp_list())
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
326
1551.7.11 by Aaron Bentley
Add WorkingTree.add_conflicts
327
    def __hash__(self):
328
        return hash((type(self), self.path, self.file_id))
329
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
330
    def __eq__(self, other):
331
        return self.__cmp__(other) == 0
332
333
    def __ne__(self, other):
334
        return not self.__eq__(other)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
335
1534.10.20 by Aaron Bentley
Got all tests passing
336
    def __str__(self):
337
        return self.format % self.__dict__
338
1534.10.22 by Aaron Bentley
Got ConflictList implemented
339
    def __repr__(self):
340
        rdict = dict(self.__dict__)
341
        rdict['class'] = self.__class__.__name__
342
        return self.rformat % rdict
343
1534.10.18 by Aaron Bentley
Defined all new Conflict types
344
    @staticmethod
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
345
    def factory(type, **kwargs):
1534.10.18 by Aaron Bentley
Defined all new Conflict types
346
        global ctype
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
347
        return ctype[type](**kwargs)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
348
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
349
    @staticmethod
350
    def sort_key(conflict):
351
        if conflict.path is not None:
352
            return conflict.path, conflict.typestring
353
        elif getattr(conflict, "conflict_path", None) is not None:
354
            return conflict.conflict_path, conflict.typestring
355
        else:
356
            return None, conflict.typestring
357
1534.10.18 by Aaron Bentley
Defined all new Conflict types
358
359
class PathConflict(Conflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
360
    """A conflict was encountered merging file paths"""
361
1534.10.18 by Aaron Bentley
Defined all new Conflict types
362
    typestring = 'path conflict'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
363
1534.10.20 by Aaron Bentley
Got all tests passing
364
    format = 'Path conflict: %(path)s / %(conflict_path)s'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
365
366
    rformat = '%(class)s(%(path)r, %(conflict_path)r, %(file_id)r)'
1534.10.20 by Aaron Bentley
Got all tests passing
367
    def __init__(self, path, conflict_path=None, file_id=None):
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
368
        Conflict.__init__(self, path, file_id)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
369
        self.conflict_path = conflict_path
370
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
371
    def as_stanza(self):
372
        s = Conflict.as_stanza(self)
1534.10.20 by Aaron Bentley
Got all tests passing
373
        if self.conflict_path is not None:
374
            s.add('conflict_path', self.conflict_path)
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
375
        return s
376
1534.10.20 by Aaron Bentley
Got all tests passing
377
378
class ContentsConflict(PathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
379
    """The files are of different types, or not present"""
380
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
381
    has_files = True
382
1534.10.20 by Aaron Bentley
Got all tests passing
383
    typestring = 'contents conflict'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
384
1534.10.20 by Aaron Bentley
Got all tests passing
385
    format = 'Contents conflict in %(path)s'
386
387
388
class TextConflict(PathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
389
    """The merge algorithm could not resolve all differences encountered."""
390
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
391
    has_files = True
392
1534.10.20 by Aaron Bentley
Got all tests passing
393
    typestring = 'text conflict'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
394
1534.10.20 by Aaron Bentley
Got all tests passing
395
    format = 'Text conflict in %(path)s'
396
397
1534.10.18 by Aaron Bentley
Defined all new Conflict types
398
class HandledConflict(Conflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
399
    """A path problem that has been provisionally resolved.
400
    This is intended to be a base class.
401
    """
402
403
    rformat = "%(class)s(%(action)r, %(path)r, %(file_id)r)"
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
404
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
405
    def __init__(self, action, path, file_id=None):
406
        Conflict.__init__(self, path, file_id)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
407
        self.action = action
408
1534.10.22 by Aaron Bentley
Got ConflictList implemented
409
    def _cmp_list(self):
410
        return Conflict._cmp_list(self) + [self.action]
411
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
412
    def as_stanza(self):
413
        s = Conflict.as_stanza(self)
414
        s.add('action', self.action)
415
        return s
416
1534.10.20 by Aaron Bentley
Got all tests passing
417
1534.10.18 by Aaron Bentley
Defined all new Conflict types
418
class HandledPathConflict(HandledConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
419
    """A provisionally-resolved path problem involving two paths.
420
    This is intended to be a base class.
421
    """
422
423
    rformat = "%(class)s(%(action)r, %(path)r, %(conflict_path)r,"\
424
        " %(file_id)r, %(conflict_file_id)r)"
425
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
426
    def __init__(self, action, path, conflict_path, file_id=None,
1534.10.18 by Aaron Bentley
Defined all new Conflict types
427
                 conflict_file_id=None):
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
428
        HandledConflict.__init__(self, action, path, file_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
429
        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.
430
        # warn turned off, because the factory blindly transfers the Stanza
431
        # values to __init__.
432
        self.conflict_file_id = osutils.safe_file_id(conflict_file_id,
433
                                                     warn=False)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
434
1534.10.22 by Aaron Bentley
Got ConflictList implemented
435
    def _cmp_list(self):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
436
        return HandledConflict._cmp_list(self) + [self.conflict_path,
1534.10.22 by Aaron Bentley
Got ConflictList implemented
437
                                                  self.conflict_file_id]
438
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
439
    def as_stanza(self):
440
        s = HandledConflict.as_stanza(self)
441
        s.add('conflict_path', self.conflict_path)
442
        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.
443
            s.add('conflict_file_id', self.conflict_file_id.decode('utf8'))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
444
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
445
        return s
1534.10.20 by Aaron Bentley
Got all tests passing
446
447
1534.10.18 by Aaron Bentley
Defined all new Conflict types
448
class DuplicateID(HandledPathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
449
    """Two files want the same file_id."""
450
1534.10.18 by Aaron Bentley
Defined all new Conflict types
451
    typestring = 'duplicate id'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
452
1534.10.20 by Aaron Bentley
Got all tests passing
453
    format = 'Conflict adding id to %(conflict_path)s.  %(action)s %(path)s.'
454
1534.10.18 by Aaron Bentley
Defined all new Conflict types
455
456
class DuplicateEntry(HandledPathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
457
    """Two directory entries want to have the same name."""
458
1534.10.18 by Aaron Bentley
Defined all new Conflict types
459
    typestring = 'duplicate'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
460
1534.10.20 by Aaron Bentley
Got all tests passing
461
    format = 'Conflict adding file %(conflict_path)s.  %(action)s %(path)s.'
462
1534.10.18 by Aaron Bentley
Defined all new Conflict types
463
464
class ParentLoop(HandledPathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
465
    """An attempt to create an infinitely-looping directory structure.
466
    This is rare, but can be produced like so:
467
468
    tree A:
469
      mv foo/bar
470
    tree B:
471
      mv bar/foo
472
    merge A and B
473
    """
474
1534.10.18 by Aaron Bentley
Defined all new Conflict types
475
    typestring = 'parent loop'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
476
1534.10.20 by Aaron Bentley
Got all tests passing
477
    format = 'Conflict moving %(conflict_path)s into %(path)s.  %(action)s.'
478
1534.10.18 by Aaron Bentley
Defined all new Conflict types
479
480
class UnversionedParent(HandledConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
481
    """An attempt to version an file whose parent directory is not versioned.
482
    Typically, the result of a merge where one tree unversioned the directory
483
    and the other added a versioned file to it.
484
    """
485
1534.10.18 by Aaron Bentley
Defined all new Conflict types
486
    typestring = 'unversioned parent'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
487
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
488
    format = 'Conflict because %(path)s is not versioned, but has versioned'\
489
             ' children.  %(action)s.'
1534.10.20 by Aaron Bentley
Got all tests passing
490
1534.10.18 by Aaron Bentley
Defined all new Conflict types
491
492
class MissingParent(HandledConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
493
    """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
494
    Typically, the result of a merge where THIS deleted the directory and
495
    the OTHER added a file to it.
496
    See also: DeletingParent (same situation, reversed THIS and OTHER)
1534.10.22 by Aaron Bentley
Got ConflictList implemented
497
    """
498
1534.10.18 by Aaron Bentley
Defined all new Conflict types
499
    typestring = 'missing parent'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
500
1534.10.20 by Aaron Bentley
Got all tests passing
501
    format = 'Conflict adding files to %(path)s.  %(action)s.'
502
503
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
504
class DeletingParent(HandledConflict):
505
    """An attempt to add files to a directory that is not present.
506
    Typically, the result of a merge where one OTHER deleted the directory and
507
    the THIS added a file to it.
508
    """
509
510
    typestring = 'deleting parent'
511
1551.8.23 by Aaron Bentley
Tweaked conflict message to be more understandable
512
    format = "Conflict: can't delete %(path)s because it is not empty.  "\
513
             "%(action)s."
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
514
1534.10.18 by Aaron Bentley
Defined all new Conflict types
515
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
516
class NonDirectoryParent(HandledConflict):
517
    """An attempt to add files to a directory that is not a director or
518
    an attempt to change the kind of a directory with files.
519
    """
520
521
    typestring = 'non-directory parent'
522
523
    format = "Conflict: %(path)s is not a directory, but has files in it."\
524
             "  %(action)s."
525
1534.10.18 by Aaron Bentley
Defined all new Conflict types
526
ctype = {}
1534.10.20 by Aaron Bentley
Got all tests passing
527
528
1534.10.18 by Aaron Bentley
Defined all new Conflict types
529
def register_types(*conflict_types):
530
    """Register a Conflict subclass for serialization purposes"""
531
    global ctype
532
    for conflict_type in conflict_types:
533
        ctype[conflict_type.typestring] = conflict_type
534
1534.10.20 by Aaron Bentley
Got all tests passing
535
1534.10.18 by Aaron Bentley
Defined all new Conflict types
536
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
537
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
538
               DeletingParent, NonDirectoryParent)