/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4597.3.48 by Vincent Ladeuil
Start cleaning BRANCH.TODO by pushing changes to either the python modules or the documentation.
1
# Copyright (C) 2005, 2007, 2009 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
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
17
# TODO: 'bzr resolve' should accept a directory name and work from that
1185.16.11 by Martin Pool
todo
18
# point down
19
1185.16.33 by Martin Pool
- move 'conflict' and 'resolved' from shipped plugin to regular builtins
20
import os
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
21
import re
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
22
23
from bzrlib.lazy_import import lazy_import
24
lazy_import(globals(), """
1185.16.33 by Martin Pool
- move 'conflict' and 'resolved' from shipped plugin to regular builtins
25
import errno
26
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
27
from bzrlib import (
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
28
    builtins,
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
29
    commands,
30
    errors,
31
    osutils,
32
    rio,
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
33
    trace,
4597.3.24 by Vincent Ladeuil
Fix imports in bzrlib/conflicts.py.
34
    workingtree,
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
35
    )
36
""")
4597.3.51 by Vincent Ladeuil
Implement conflicts.ResolveActionOption.
37
from bzrlib import (
38
    option,
39
    registry,
40
    )
1534.10.6 by Aaron Bentley
Conflict serialization working for WorkingTree3
41
42
43
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
44
1185.14.3 by Aaron Bentley
Copied conflict lister in
45
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
46
class cmd_conflicts(commands.Command):
1185.14.3 by Aaron Bentley
Copied conflict lister in
47
    """List files with conflicts.
1551.2.18 by Aaron Bentley
Updated docs to clarify conflict handling
48
49
    Merge will do its best to combine the changes in two branches, but there
50
    are some kinds of problems only a human can fix.  When it encounters those,
51
    it will mark a conflict.  A conflict means that you need to fix something,
52
    before you should commit.
53
1551.9.8 by Aaron Bentley
Add --text parameter to conflicts
54
    Conflicts normally are listed as short, human-readable messages.  If --text
55
    is supplied, the pathnames of files with text conflicts are listed,
56
    instead.  (This is useful for editing all files with text conflicts.)
57
1551.2.18 by Aaron Bentley
Updated docs to clarify conflict handling
58
    Use bzr resolve when you have fixed a problem.
1185.14.3 by Aaron Bentley
Copied conflict lister in
59
    """
2598.1.1 by Martin Pool
Add test for and documentation of option style, fix up existing options to comply
60
    takes_options = [
4597.3.51 by Vincent Ladeuil
Implement conflicts.ResolveActionOption.
61
            option.Option('text',
62
                          help='List paths of files with text conflicts.'),
2598.1.1 by Martin Pool
Add test for and documentation of option style, fix up existing options to comply
63
        ]
4798.7.1 by Neil Martinsen-Burrell
fix some formatting and see also usage
64
    _see_also = ['resolve']
1551.9.8 by Aaron Bentley
Add --text parameter to conflicts
65
66
    def run(self, text=False):
4597.3.24 by Vincent Ladeuil
Fix imports in bzrlib/conflicts.py.
67
        wt = workingtree.WorkingTree.open_containing(u'.')[0]
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
68
        for conflict in wt.conflicts():
1551.9.8 by Aaron Bentley
Add --text parameter to conflicts
69
            if text:
70
                if conflict.typestring != 'text conflict':
71
                    continue
72
                self.outf.write(conflict.path + '\n')
73
            else:
74
                self.outf.write(str(conflict) + '\n')
1185.14.3 by Aaron Bentley
Copied conflict lister in
75
1652.1.1 by Martin Pool
Fix 'bzr resolve' run from subdirectory
76
4597.3.51 by Vincent Ladeuil
Implement conflicts.ResolveActionOption.
77
resolve_action_registry = registry.Registry()
78
79
80
resolve_action_registry.register(
81
    'done', 'done', 'Marks the conflict as resolved' )
82
resolve_action_registry.register(
83
    'keep-mine', 'keep_mine',
84
    'Resolve the conflict preserving the version in the working tree' )
85
resolve_action_registry.register(
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
86
    'take-their', 'take_their',
4597.3.51 by Vincent Ladeuil
Implement conflicts.ResolveActionOption.
87
    'Resolve the conflict taking the merged version into account' )
88
resolve_action_registry.default_key = 'done'
89
90
class ResolveActionOption(option.RegistryOption):
91
92
    def __init__(self):
93
        super(ResolveActionOption, self).__init__(
94
            'action', 'How to resolve the conflict.',
95
            value_switches=True,
96
            registry=resolve_action_registry)
97
98
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
99
class cmd_resolve(commands.Command):
1185.14.3 by Aaron Bentley
Copied conflict lister in
100
    """Mark a conflict as resolved.
1551.2.18 by Aaron Bentley
Updated docs to clarify conflict handling
101
102
    Merge will do its best to combine the changes in two branches, but there
103
    are some kinds of problems only a human can fix.  When it encounters those,
104
    it will mark a conflict.  A conflict means that you need to fix something,
105
    before you should commit.
106
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
107
    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
108
    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
109
    resolved, or "bzr resolve --all" to mark all conflicts as resolved.
1185.14.3 by Aaron Bentley
Copied conflict lister in
110
    """
1185.33.24 by Martin Pool
Add alias 'resolved'
111
    aliases = ['resolved']
1185.14.3 by Aaron Bentley
Copied conflict lister in
112
    takes_args = ['file*']
2598.1.2 by Martin Pool
Also check that option help ends in a period, and fix those that don't
113
    takes_options = [
4597.3.51 by Vincent Ladeuil
Implement conflicts.ResolveActionOption.
114
            option.Option('all', help='Resolve all conflicts in this tree.'),
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
115
            ResolveActionOption(),
2598.1.2 by Martin Pool
Also check that option help ends in a period, and fix those that don't
116
            ]
4798.7.1 by Neil Martinsen-Burrell
fix some formatting and see also usage
117
    _see_also = ['conflicts']
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
118
    def run(self, file_list=None, all=False, action=None):
1652.1.3 by Martin Pool
Improved bzr resolve command line handling
119
        if all:
120
            if file_list:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
121
                raise errors.BzrCommandError("If --all is specified,"
122
                                             " no FILE may be provided")
4597.3.24 by Vincent Ladeuil
Fix imports in bzrlib/conflicts.py.
123
            tree = workingtree.WorkingTree.open_containing('.')[0]
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
124
            if action is None:
125
                action = 'done'
1185.14.3 by Aaron Bentley
Copied conflict lister in
126
        else:
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
127
            tree, file_list = builtins.tree_files(file_list)
1652.1.3 by Martin Pool
Improved bzr resolve command line handling
128
            if file_list is None:
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
129
                if action is None:
130
                    action = 'auto'
131
            else:
132
                if action is None:
133
                    action = 'done'
134
        if action == 'auto':
135
            if file_list is None:
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
136
                un_resolved, resolved = tree.auto_resolve()
137
                if len(un_resolved) > 0:
138
                    trace.note('%d conflict(s) auto-resolved.', len(resolved))
139
                    trace.note('Remaining conflicts:')
140
                    for conflict in un_resolved:
141
                        trace.note(conflict)
142
                    return 1
143
                else:
144
                    trace.note('All conflicts resolved.')
145
                    return 0
146
            else:
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
147
                # FIXME: This can never occur but the block above needs some
148
                # refactoring so transfer tree.auto_resolve() to
149
                # conflict.auto(tree) --vila 091242
150
                pass
151
        else:
152
            resolve(tree, file_list, action=action)
153
154
155
def resolve(tree, paths=None, ignore_misses=False, recursive=False,
156
            action='done'):
3017.2.2 by Aaron Bentley
Add docstring to resolve
157
    """Resolve some or all of the conflicts in a working tree.
158
159
    :param paths: If None, resolve all conflicts.  Otherwise, select only
160
        specified conflicts.
161
    :param recursive: If True, then elements of paths which are directories
162
        have all their children resolved, etc.  When invoked as part of
163
        recursive commands like revert, this should be True.  For commands
164
        or applications wishing finer-grained control, like the resolve
165
        command, this should be False.
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
166
    :param ignore_misses: If False, warnings will be printed if the supplied
167
        paths do not have conflicts.
168
    :param action: How the conflict should be resolved,
3017.2.2 by Aaron Bentley
Add docstring to resolve
169
    """
1997.1.3 by Robert Collins
All WorkingTree methods which write to the tree, but not to the branch
170
    tree.lock_tree_write()
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
171
    try:
1534.10.23 by Aaron Bentley
Removed conflicts_to_stanzas and stanzas_to_conflicts
172
        tree_conflicts = tree.conflicts()
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
173
        if paths is None:
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
174
            new_conflicts = ConflictList()
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
175
            to_process = tree_conflicts
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
176
        else:
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
177
            new_conflicts, to_process = tree_conflicts.select_conflicts(
178
                tree, paths, ignore_misses, recursive)
179
        for conflict in to_process:
180
            try:
181
                conflict._do(action, tree)
182
                conflict.cleanup(tree)
183
            except NotImplementedError:
184
                new_conflicts.append(conflict)
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
185
        try:
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
186
            tree.set_conflicts(new_conflicts)
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
187
        except errors.UnsupportedOperation:
1534.10.10 by Aaron Bentley
Resolve uses the new stuff.
188
            pass
4597.3.26 by Vincent Ladeuil
Tests passing for a minimal --interactive implementation.
189
    finally:
190
        tree.unlock()
191
192
1185.35.1 by Aaron Bentley
Implemented conflicts.restore
193
def restore(filename):
4597.2.6 by Vincent Ladeuil
Cleanup doc string.
194
    """Restore a conflicted file to the state it was in before merging.
195
196
    Only text restoration is supported at present.
1185.35.1 by Aaron Bentley
Implemented conflicts.restore
197
    """
198
    conflicted = False
199
    try:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
200
        osutils.rename(filename + ".THIS", filename)
1185.35.1 by Aaron Bentley
Implemented conflicts.restore
201
        conflicted = True
202
    except OSError, e:
203
        if e.errno != errno.ENOENT:
204
            raise
205
    try:
206
        os.unlink(filename + ".BASE")
207
        conflicted = True
208
    except OSError, e:
209
        if e.errno != errno.ENOENT:
210
            raise
211
    try:
212
        os.unlink(filename + ".OTHER")
213
        conflicted = True
214
    except OSError, e:
215
        if e.errno != errno.ENOENT:
216
            raise
217
    if not conflicted:
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
218
        raise errors.NotConflicted(filename)
1534.10.4 by Aaron Bentley
Implemented conflict serialization
219
220
1534.10.22 by Aaron Bentley
Got ConflictList implemented
221
class ConflictList(object):
1652.1.1 by Martin Pool
Fix 'bzr resolve' run from subdirectory
222
    """List of conflicts.
223
224
    Typically obtained from WorkingTree.conflicts()
225
1534.10.22 by Aaron Bentley
Got ConflictList implemented
226
    Can be instantiated from stanzas or from Conflict subclasses.
227
    """
228
229
    def __init__(self, conflicts=None):
230
        object.__init__(self)
231
        if conflicts is None:
232
            self.__list = []
233
        else:
234
            self.__list = conflicts
235
1652.1.1 by Martin Pool
Fix 'bzr resolve' run from subdirectory
236
    def is_empty(self):
237
        return len(self.__list) == 0
238
1534.10.22 by Aaron Bentley
Got ConflictList implemented
239
    def __len__(self):
240
        return len(self.__list)
241
242
    def __iter__(self):
243
        return iter(self.__list)
244
245
    def __getitem__(self, key):
246
        return self.__list[key]
247
248
    def append(self, conflict):
249
        return self.__list.append(conflict)
250
251
    def __eq__(self, other_list):
252
        return list(self) == list(other_list)
253
254
    def __ne__(self, other_list):
255
        return not (self == other_list)
256
257
    def __repr__(self):
258
        return "ConflictList(%r)" % self.__list
259
260
    @staticmethod
261
    def from_stanzas(stanzas):
262
        """Produce a new ConflictList from an iterable of stanzas"""
263
        conflicts = ConflictList()
264
        for stanza in stanzas:
265
            conflicts.append(Conflict.factory(**stanza.as_dict()))
266
        return conflicts
267
268
    def to_stanzas(self):
269
        """Generator of stanzas"""
270
        for conflict in self:
271
            yield conflict.as_stanza()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
272
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
273
    def to_strings(self):
274
        """Generate strings for the provided conflicts"""
275
        for conflict in self:
276
            yield str(conflict)
277
278
    def remove_files(self, tree):
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
279
        """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
280
        for conflict in self:
281
            if not conflict.has_files:
282
                continue
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
283
            conflict.cleanup(tree)
1534.10.21 by Aaron Bentley
Moved and renamed conflict functions
284
1551.15.58 by Aaron Bentley
Status honours selected paths for conflicts (#127606)
285
    def select_conflicts(self, tree, paths, ignore_misses=False,
286
                         recurse=False):
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
287
        """Select the conflicts associated with paths in a tree.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
288
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
289
        File-ids are also used for this.
1551.7.10 by Aaron Bentley
Remerge doesn't clear unrelated conflicts
290
        :return: a pair of ConflictLists: (not_selected, selected)
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
291
        """
292
        path_set = set(paths)
293
        ids = {}
294
        selected_paths = set()
295
        new_conflicts = ConflictList()
296
        selected_conflicts = ConflictList()
297
        for path in paths:
298
            file_id = tree.path2id(path)
299
            if file_id is not None:
300
                ids[file_id] = path
301
302
        for conflict in self:
303
            selected = False
304
            for key in ('path', 'conflict_path'):
305
                cpath = getattr(conflict, key, None)
306
                if cpath is None:
307
                    continue
308
                if cpath in path_set:
309
                    selected = True
310
                    selected_paths.add(cpath)
1551.15.58 by Aaron Bentley
Status honours selected paths for conflicts (#127606)
311
                if recurse:
312
                    if osutils.is_inside_any(path_set, cpath):
313
                        selected = True
314
                        selected_paths.add(cpath)
315
1534.10.25 by Aaron Bentley
Move select_conflicts into ConflictList
316
            for key in ('file_id', 'conflict_file_id'):
317
                cfile_id = getattr(conflict, key, None)
318
                if cfile_id is None:
319
                    continue
320
                try:
321
                    cpath = ids[cfile_id]
322
                except KeyError:
323
                    continue
324
                selected = True
325
                selected_paths.add(cpath)
326
            if selected:
327
                selected_conflicts.append(conflict)
328
            else:
329
                new_conflicts.append(conflict)
330
        if ignore_misses is not True:
331
            for path in [p for p in paths if p not in selected_paths]:
332
                if not os.path.exists(tree.abspath(path)):
333
                    print "%s does not exist" % path
334
                else:
335
                    print "%s is not conflicted" % path
336
        return new_conflicts, selected_conflicts
337
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
338
1534.10.18 by Aaron Bentley
Defined all new Conflict types
339
class Conflict(object):
340
    """Base class for all types of conflict"""
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
341
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
342
    # FIXME: cleanup should take care of that
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
343
    has_files = False
344
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
345
    def __init__(self, path, file_id=None):
1534.10.18 by Aaron Bentley
Defined all new Conflict types
346
        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.
347
        # warn turned off, because the factory blindly transfers the Stanza
348
        # values to __init__ and Stanza is purely a Unicode api.
349
        self.file_id = osutils.safe_file_id(file_id, warn=False)
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
350
351
    def as_stanza(self):
1996.3.33 by John Arbash Meinel
make bzrlib/conflicts.py lazy
352
        s = rio.Stanza(type=self.typestring, path=self.path)
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
353
        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.
354
            # Stanza requires Unicode apis
355
            s.add('file_id', self.file_id.decode('utf8'))
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
356
        return s
357
1534.10.22 by Aaron Bentley
Got ConflictList implemented
358
    def _cmp_list(self):
359
        return [type(self), self.path, self.file_id]
360
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
361
    def __cmp__(self, other):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
362
        if getattr(other, "_cmp_list", None) is None:
363
            return -1
364
        return cmp(self._cmp_list(), other._cmp_list())
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
365
1551.7.11 by Aaron Bentley
Add WorkingTree.add_conflicts
366
    def __hash__(self):
367
        return hash((type(self), self.path, self.file_id))
368
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
369
    def __eq__(self, other):
370
        return self.__cmp__(other) == 0
371
372
    def __ne__(self, other):
373
        return not self.__eq__(other)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
374
1534.10.20 by Aaron Bentley
Got all tests passing
375
    def __str__(self):
376
        return self.format % self.__dict__
377
1534.10.22 by Aaron Bentley
Got ConflictList implemented
378
    def __repr__(self):
379
        rdict = dict(self.__dict__)
380
        rdict['class'] = self.__class__.__name__
381
        return self.rformat % rdict
382
1534.10.18 by Aaron Bentley
Defined all new Conflict types
383
    @staticmethod
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
384
    def factory(type, **kwargs):
1534.10.18 by Aaron Bentley
Defined all new Conflict types
385
        global ctype
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
386
        return ctype[type](**kwargs)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
387
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
388
    @staticmethod
389
    def sort_key(conflict):
390
        if conflict.path is not None:
391
            return conflict.path, conflict.typestring
392
        elif getattr(conflict, "conflict_path", None) is not None:
393
            return conflict.conflict_path, conflict.typestring
394
        else:
395
            return None, conflict.typestring
396
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
397
    def _do(self, action, tree):
398
        """Apply the specified action to the conflict.
399
400
        :param action: The method name to call.
401
402
        :param tree: The tree passed as a parameter to the method.
403
        """
404
        meth = getattr(self, action, None)
405
        if meth is None:
406
            raise NotImplementedError(self.__class__.__name__ + '.' + action)
407
        meth(tree)
408
409
    def cleanup(self, tree):
410
        raise NotImplementedError(self.cleanup)
411
412
    def done(self, tree):
4597.3.54 by Vincent Ladeuil
There is no point to define Conflict.done() multiple times.
413
        """Mark the conflict as solved once it has been handled."""
414
        # This method does nothing but simplifies the design of upper levels.
415
        pass
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
416
417
    def keep_mine(self, tree):
418
        raise NotImplementedError(self.keep_mine)
419
420
    def take_their(self, tree):
421
        raise NotImplementedError(self.take_their)
422
1534.10.18 by Aaron Bentley
Defined all new Conflict types
423
424
class PathConflict(Conflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
425
    """A conflict was encountered merging file paths"""
426
1534.10.18 by Aaron Bentley
Defined all new Conflict types
427
    typestring = 'path conflict'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
428
1534.10.20 by Aaron Bentley
Got all tests passing
429
    format = 'Path conflict: %(path)s / %(conflict_path)s'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
430
431
    rformat = '%(class)s(%(path)r, %(conflict_path)r, %(file_id)r)'
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
432
1534.10.20 by Aaron Bentley
Got all tests passing
433
    def __init__(self, path, conflict_path=None, file_id=None):
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
434
        Conflict.__init__(self, path, file_id)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
435
        self.conflict_path = conflict_path
436
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
437
    def as_stanza(self):
438
        s = Conflict.as_stanza(self)
1534.10.20 by Aaron Bentley
Got all tests passing
439
        if self.conflict_path is not None:
440
            s.add('conflict_path', self.conflict_path)
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
441
        return s
442
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
443
    def cleanup(self, tree):
4597.3.55 by Vincent Ladeuil
More cleanup.
444
        # No additional files have been generated here
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
445
        pass
446
4597.3.33 by Vincent Ladeuil
Implement --interactive for PathConflict.
447
    def keep_mine(self, tree):
448
        tree.rename_one(self.conflict_path, self.path)
449
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
450
    def take_their(self, tree):
4597.3.33 by Vincent Ladeuil
Implement --interactive for PathConflict.
451
        # just acccept bzr proposal
452
        pass
453
1534.10.20 by Aaron Bentley
Got all tests passing
454
455
class ContentsConflict(PathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
456
    """The files are of different types, or not present"""
457
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
458
    has_files = True
459
1534.10.20 by Aaron Bentley
Got all tests passing
460
    typestring = 'contents conflict'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
461
1534.10.20 by Aaron Bentley
Got all tests passing
462
    format = 'Contents conflict in %(path)s'
463
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
464
    def cleanup(self, tree):
465
        for suffix in ('.BASE', '.OTHER'):
466
            try:
467
                osutils.delete_any(tree.abspath(self.path + suffix))
468
            except OSError, e:
469
                if e.errno != errno.ENOENT:
470
                    raise
471
472
    # FIXME: I smell something weird here and it seems we should be able to be
473
    # more coherent with some other conflict ? bzr *did* a choice there but
474
    # neither keep_mine nor take_their reflect that... -- vila 091224
4597.3.28 by Vincent Ladeuil
Implement --interactive for ContentsConflict.
475
    def keep_mine(self, tree):
476
        tree.remove([self.path + '.OTHER'], force=True, keep_files=False)
477
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
478
    def take_their(self, tree):
4597.3.28 by Vincent Ladeuil
Implement --interactive for ContentsConflict.
479
        tree.remove([self.path], force=True, keep_files=False)
480
4597.3.30 by Vincent Ladeuil
Light changes learned while starting to understand multiple conflicts on
481
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
482
4597.3.29 by Vincent Ladeuil
Fix bogus tests.
483
# FIXME: TextConflict is about a single file-id, there never is a conflict_path
484
# attribute so we shouldn't inherit from PathConflict but simply from Conflict
4597.3.48 by Vincent Ladeuil
Start cleaning BRANCH.TODO by pushing changes to either the python modules or the documentation.
485
486
# TODO: There should be a base revid attribute to better inform the user about
487
# how the conflicts were generated.
1534.10.20 by Aaron Bentley
Got all tests passing
488
class TextConflict(PathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
489
    """The merge algorithm could not resolve all differences encountered."""
490
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
491
    has_files = True
492
1534.10.20 by Aaron Bentley
Got all tests passing
493
    typestring = 'text conflict'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
494
1534.10.20 by Aaron Bentley
Got all tests passing
495
    format = 'Text conflict in %(path)s'
496
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
497
    def cleanup(self, tree):
498
        for suffix in CONFLICT_SUFFIXES:
499
            try:
500
                osutils.delete_any(tree.abspath(self.path+suffix))
501
            except OSError, e:
502
                if e.errno != errno.ENOENT:
503
                    raise
4597.3.33 by Vincent Ladeuil
Implement --interactive for PathConflict.
504
1534.10.20 by Aaron Bentley
Got all tests passing
505
1534.10.18 by Aaron Bentley
Defined all new Conflict types
506
class HandledConflict(Conflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
507
    """A path problem that has been provisionally resolved.
508
    This is intended to be a base class.
509
    """
510
511
    rformat = "%(class)s(%(action)r, %(path)r, %(file_id)r)"
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
512
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
513
    def __init__(self, action, path, file_id=None):
514
        Conflict.__init__(self, path, file_id)
1534.10.18 by Aaron Bentley
Defined all new Conflict types
515
        self.action = action
516
1534.10.22 by Aaron Bentley
Got ConflictList implemented
517
    def _cmp_list(self):
518
        return Conflict._cmp_list(self) + [self.action]
519
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
520
    def as_stanza(self):
521
        s = Conflict.as_stanza(self)
522
        s.add('action', self.action)
523
        return s
524
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
525
    def cleanup(self, tree):
526
        """Nothing to cleanup."""
527
        pass
528
1534.10.20 by Aaron Bentley
Got all tests passing
529
1534.10.18 by Aaron Bentley
Defined all new Conflict types
530
class HandledPathConflict(HandledConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
531
    """A provisionally-resolved path problem involving two paths.
532
    This is intended to be a base class.
533
    """
534
535
    rformat = "%(class)s(%(action)r, %(path)r, %(conflict_path)r,"\
536
        " %(file_id)r, %(conflict_file_id)r)"
537
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
538
    def __init__(self, action, path, conflict_path, file_id=None,
1534.10.18 by Aaron Bentley
Defined all new Conflict types
539
                 conflict_file_id=None):
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
540
        HandledConflict.__init__(self, action, path, file_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
541
        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.
542
        # warn turned off, because the factory blindly transfers the Stanza
543
        # values to __init__.
544
        self.conflict_file_id = osutils.safe_file_id(conflict_file_id,
545
                                                     warn=False)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
546
1534.10.22 by Aaron Bentley
Got ConflictList implemented
547
    def _cmp_list(self):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
548
        return HandledConflict._cmp_list(self) + [self.conflict_path,
1534.10.22 by Aaron Bentley
Got ConflictList implemented
549
                                                  self.conflict_file_id]
550
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
551
    def as_stanza(self):
552
        s = HandledConflict.as_stanza(self)
553
        s.add('conflict_path', self.conflict_path)
554
        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.
555
            s.add('conflict_file_id', self.conflict_file_id.decode('utf8'))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
556
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
557
        return s
1534.10.20 by Aaron Bentley
Got all tests passing
558
559
1534.10.18 by Aaron Bentley
Defined all new Conflict types
560
class DuplicateID(HandledPathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
561
    """Two files want the same file_id."""
562
1534.10.18 by Aaron Bentley
Defined all new Conflict types
563
    typestring = 'duplicate id'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
564
1534.10.20 by Aaron Bentley
Got all tests passing
565
    format = 'Conflict adding id to %(conflict_path)s.  %(action)s %(path)s.'
566
1534.10.18 by Aaron Bentley
Defined all new Conflict types
567
568
class DuplicateEntry(HandledPathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
569
    """Two directory entries want to have the same name."""
570
1534.10.18 by Aaron Bentley
Defined all new Conflict types
571
    typestring = 'duplicate'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
572
1534.10.20 by Aaron Bentley
Got all tests passing
573
    format = 'Conflict adding file %(conflict_path)s.  %(action)s %(path)s.'
574
4597.3.27 by Vincent Ladeuil
(keep_mine, take_theirs) sounds clearer than (keep_this, keep_other).
575
    def keep_mine(self, tree):
4597.3.26 by Vincent Ladeuil
Tests passing for a minimal --interactive implementation.
576
        tree.remove([self.conflict_path], force=True, keep_files=False)
577
        tree.rename_one(self.path, self.conflict_path)
578
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
579
    def take_their(self, tree):
4597.3.26 by Vincent Ladeuil
Tests passing for a minimal --interactive implementation.
580
        tree.remove([self.path], force=True, keep_files=False)
581
1534.10.18 by Aaron Bentley
Defined all new Conflict types
582
583
class ParentLoop(HandledPathConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
584
    """An attempt to create an infinitely-looping directory structure.
585
    This is rare, but can be produced like so:
586
587
    tree A:
4597.3.50 by Vincent Ladeuil
Integrate most of BRANCH.TOTO into bzrlib/help_topics/en/conflicts.txt, fix a typo in a test.
588
      mv foo bar
1534.10.22 by Aaron Bentley
Got ConflictList implemented
589
    tree B:
4597.3.50 by Vincent Ladeuil
Integrate most of BRANCH.TOTO into bzrlib/help_topics/en/conflicts.txt, fix a typo in a test.
590
      mv bar foo
1534.10.22 by Aaron Bentley
Got ConflictList implemented
591
    merge A and B
592
    """
593
1534.10.18 by Aaron Bentley
Defined all new Conflict types
594
    typestring = 'parent loop'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
595
1534.10.20 by Aaron Bentley
Got all tests passing
596
    format = 'Conflict moving %(conflict_path)s into %(path)s.  %(action)s.'
597
4597.3.34 by Vincent Ladeuil
Implement --interactive for ParentLoop.
598
    def keep_mine(self, tree):
599
        # just acccept bzr proposal
600
        pass
601
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
602
    def take_their(self, tree):
4597.3.50 by Vincent Ladeuil
Integrate most of BRANCH.TOTO into bzrlib/help_topics/en/conflicts.txt, fix a typo in a test.
603
        # FIXME: We shouldn't have to manipulate so many paths here (and there
604
        # is probably a bug or two...)
4597.3.34 by Vincent Ladeuil
Implement --interactive for ParentLoop.
605
        conflict_base_path = osutils.basename(self.conflict_path)
606
        base_path = osutils.basename(self.path)
607
        tree.rename_one(self.conflict_path, conflict_base_path)
608
        tree.rename_one(self.path,
609
                        osutils.joinpath([conflict_base_path, base_path]))
610
1534.10.18 by Aaron Bentley
Defined all new Conflict types
611
612
class UnversionedParent(HandledConflict):
4597.2.1 by Vincent Ladeuil
Fix some typos.
613
    """An attempt to version a file whose parent directory is not versioned.
1534.10.22 by Aaron Bentley
Got ConflictList implemented
614
    Typically, the result of a merge where one tree unversioned the directory
615
    and the other added a versioned file to it.
616
    """
617
1534.10.18 by Aaron Bentley
Defined all new Conflict types
618
    typestring = 'unversioned parent'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
619
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
620
    format = 'Conflict because %(path)s is not versioned, but has versioned'\
621
             ' children.  %(action)s.'
1534.10.20 by Aaron Bentley
Got all tests passing
622
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
623
    # FIXME: We silently do nothing to make tests pass, but most probably the
624
    # conflict shouldn't exist (the long story is that the conflict is
625
    # generated with another one that can be resolved properly) -- vila 091224
626
    def keep_mine(self, tree):
627
        pass
628
629
    def take_their(self, tree):
630
        pass
631
1534.10.18 by Aaron Bentley
Defined all new Conflict types
632
633
class MissingParent(HandledConflict):
1534.10.22 by Aaron Bentley
Got ConflictList implemented
634
    """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
635
    Typically, the result of a merge where THIS deleted the directory and
636
    the OTHER added a file to it.
4597.3.50 by Vincent Ladeuil
Integrate most of BRANCH.TOTO into bzrlib/help_topics/en/conflicts.txt, fix a typo in a test.
637
    See also: DeletingParent (same situation, THIS and OTHER reversed)
1534.10.22 by Aaron Bentley
Got ConflictList implemented
638
    """
639
1534.10.18 by Aaron Bentley
Defined all new Conflict types
640
    typestring = 'missing parent'
1534.10.22 by Aaron Bentley
Got ConflictList implemented
641
1534.10.20 by Aaron Bentley
Got all tests passing
642
    format = 'Conflict adding files to %(path)s.  %(action)s.'
643
4597.3.31 by Vincent Ladeuil
Implement --interactive for MissingParent.
644
    def keep_mine(self, tree):
645
        tree.remove([self.path], force=True, keep_files=False)
646
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
647
    def take_their(self, tree):
4597.3.31 by Vincent Ladeuil
Implement --interactive for MissingParent.
648
        # just acccept bzr proposal
649
        pass
650
1534.10.20 by Aaron Bentley
Got all tests passing
651
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
652
class DeletingParent(HandledConflict):
653
    """An attempt to add files to a directory that is not present.
654
    Typically, the result of a merge where one OTHER deleted the directory and
655
    the THIS added a file to it.
656
    """
657
658
    typestring = 'deleting parent'
659
1551.8.23 by Aaron Bentley
Tweaked conflict message to be more understandable
660
    format = "Conflict: can't delete %(path)s because it is not empty.  "\
661
             "%(action)s."
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
662
4597.3.32 by Vincent Ladeuil
Implement --interactive for DeletingParent noting the inconsistency.
663
    # FIXME: It's a bit strange that the default action is not coherent with
664
    # MissingParent from the *user* pov.
665
666
    def keep_mine(self, tree):
667
        # just acccept bzr proposal
668
        pass
669
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
670
    def take_their(self, tree):
4597.3.32 by Vincent Ladeuil
Implement --interactive for DeletingParent noting the inconsistency.
671
        tree.remove([self.path], force=True, keep_files=False)
672
1534.10.18 by Aaron Bentley
Defined all new Conflict types
673
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
674
class NonDirectoryParent(HandledConflict):
4597.3.35 by Vincent Ladeuil
Implement --interactive for NonDirectoryParent, sort of.
675
    """An attempt to add files to a directory that is not a directory or
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
676
    an attempt to change the kind of a directory with files.
677
    """
678
679
    typestring = 'non-directory parent'
680
681
    format = "Conflict: %(path)s is not a directory, but has files in it."\
682
             "  %(action)s."
683
4597.3.35 by Vincent Ladeuil
Implement --interactive for NonDirectoryParent, sort of.
684
    def keep_mine(self, tree):
4597.3.55 by Vincent Ladeuil
More cleanup.
685
        # FIXME: we should preserve that path when the conflict is generated !
4597.3.35 by Vincent Ladeuil
Implement --interactive for NonDirectoryParent, sort of.
686
        if self.path.endswith('.new'):
687
            conflict_path = self.path[:-(len('.new'))]
688
            tree.remove([self.path], force=True, keep_files=False)
689
            tree.add(conflict_path)
690
        else:
691
            raise NotImplementedError(self.keep_mine)
692
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
693
    def take_their(self, tree):
4597.3.55 by Vincent Ladeuil
More cleanup.
694
        # FIXME: we should preserve that path when the conflict is generated !
4597.3.35 by Vincent Ladeuil
Implement --interactive for NonDirectoryParent, sort of.
695
        if self.path.endswith('.new'):
696
            conflict_path = self.path[:-(len('.new'))]
697
            tree.remove([conflict_path], force=True, keep_files=False)
698
            tree.rename_one(self.path, conflict_path)
699
        else:
4597.3.52 by Vincent Ladeuil
Replace --interactive by --action.
700
            raise NotImplementedError(self.take_their)
4597.3.35 by Vincent Ladeuil
Implement --interactive for NonDirectoryParent, sort of.
701
702
1534.10.18 by Aaron Bentley
Defined all new Conflict types
703
ctype = {}
1534.10.20 by Aaron Bentley
Got all tests passing
704
705
1534.10.18 by Aaron Bentley
Defined all new Conflict types
706
def register_types(*conflict_types):
707
    """Register a Conflict subclass for serialization purposes"""
708
    global ctype
709
    for conflict_type in conflict_types:
710
        ctype[conflict_type.typestring] = conflict_type
711
712
register_types(ContentsConflict, TextConflict, PathConflict, DuplicateID,
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
713
               DuplicateEntry, ParentLoop, UnversionedParent, MissingParent,
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
714
               DeletingParent, NonDirectoryParent)