/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5387.2.7 by John Arbash Meinel
Merge bzr.dev 5444 to resolve some small text conflicts.
1
# Copyright (C) 2008, 2009, 2010 Canonical Ltd
0.12.12 by Aaron Bentley
Implement shelf creator
2
#
0.12.80 by Aaron Bentley
Re-format GPL notifications
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.
7
#
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.
12
#
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
0.12.12 by Aaron Bentley
Implement shelf creator
16
6379.6.3 by Jelmer Vernooij
Use absolute_import.
17
from __future__ import absolute_import
0.12.12 by Aaron Bentley
Implement shelf creator
18
0.12.50 by Aaron Bentley
Improve error handling for non-existant shelf-ids
19
import errno
0.12.77 by Aaron Bentley
Use names of the form shelf-5 for shelves
20
import re
0.12.13 by Aaron Bentley
Implement shelving content
21
6734.1.1 by Jelmer Vernooij
Fix more imports.
22
from . import errors
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
23
from .lazy_import import lazy_import
5418.4.8 by Parth Malwankar
fixed import tariff
24
lazy_import(globals(), """
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
25
from breezy import (
2694.5.4 by Jelmer Vernooij
Move bzrlib.util.bencode to bzrlib._bencode_py.
26
    bencode,
0.15.15 by Aaron Bentley
Merge prepare-shelf into unshelve
27
    merge,
0.14.19 by Aaron Bentley
Convert bzrlib import to split-line
28
    merge3,
29
    transform,
30
)
6670.4.1 by Jelmer Vernooij
Update imports.
31
from breezy.bzr import (
32
    pack,
33
    )
5418.4.8 by Parth Malwankar
fixed import tariff
34
""")
0.12.19 by Aaron Bentley
Add support for writing shelves
35
0.12.12 by Aaron Bentley
Implement shelf creator
36
6734.1.1 by Jelmer Vernooij
Fix more imports.
37
class ShelfCorrupt(errors.BzrError):
38
39
    _fmt = "Shelf corrupt."
40
41
42
class NoSuchShelfId(errors.BzrError):
43
44
    _fmt = 'No changes are shelved with id "%(shelf_id)d".'
45
46
    def __init__(self, shelf_id):
47
        errors.BzrError.__init__(self, shelf_id=shelf_id)
48
49
50
class InvalidShelfId(errors.BzrError):
51
52
    _fmt = '"%(invalid_id)s" is not a valid shelf id, try a number instead.'
53
54
    def __init__(self, invalid_id):
55
        errors.BzrError.__init__(self, invalid_id=invalid_id)
56
57
0.12.12 by Aaron Bentley
Implement shelf creator
58
class ShelfCreator(object):
0.14.27 by Aaron Bentley
Update docs
59
    """Create a transform to shelve objects and its inverse."""
0.12.12 by Aaron Bentley
Implement shelf creator
60
0.14.21 by Aaron Bentley
Update to accept a list of files.
61
    def __init__(self, work_tree, target_tree, file_list=None):
0.14.27 by Aaron Bentley
Update docs
62
        """Constructor.
63
4595.13.2 by Alexander Belchenko
[cherrypick revno 4650 from bzr.dev] Fix shelve on windows. (Robert Collins, #305006)
64
        :param work_tree: The working tree to apply changes to. This is not
65
            required to be locked - a tree_write lock will be taken out.
0.14.27 by Aaron Bentley
Update docs
66
        :param target_tree: The tree to make the working tree more similar to.
4595.13.2 by Alexander Belchenko
[cherrypick revno 4650 from bzr.dev] Fix shelve on windows. (Robert Collins, #305006)
67
            This is not required to be locked - a read_lock will be taken out.
0.14.27 by Aaron Bentley
Update docs
68
        :param file_list: The files to make more similar to the target.
69
        """
0.12.12 by Aaron Bentley
Implement shelf creator
70
        self.work_tree = work_tree
7350.3.1 by Jelmer Vernooij
Add Tree.get_transform.
71
        self.work_transform = work_tree.get_transform()
3873.2.1 by Benoît Pierre
Clean-up when an error occurs during the creation of a ShelfCreator: fix
72
        try:
4123.1.3 by Aaron Bentley
Properly handle exceptions during __init__
73
            self.target_tree = target_tree
74
            self.shelf_transform = transform.TransformPreview(self.target_tree)
75
            try:
76
                self.renames = {}
77
                self.creation = {}
78
                self.deletion = {}
79
                self.iter_changes = work_tree.iter_changes(
80
                    self.target_tree, specific_files=file_list)
81
            except:
82
                self.shelf_transform.finalize()
83
                raise
84
        except:
85
            self.work_transform.finalize()
86
            raise
0.12.12 by Aaron Bentley
Implement shelf creator
87
0.14.32 by Aaron Bentley
Replace ShelfCreator.__iter__ with ShelfCreator.iter_shelvable
88
    def iter_shelvable(self):
0.14.27 by Aaron Bentley
Update docs
89
        """Iterable of tuples describing shelvable changes.
90
91
        As well as generating the tuples, this updates several members.
5891.1.3 by Andrew Bennetts
Move docstring formatting fixes.
92
        Tuples may be::
93
0.14.27 by Aaron Bentley
Update docs
94
           ('add file', file_id, work_kind, work_path)
95
           ('delete file', file_id, target_kind, target_path)
96
           ('rename', file_id, target_path, work_path)
97
           ('change kind', file_id, target_kind, work_kind, target_path)
98
           ('modify text', file_id)
4119.5.1 by James Westby
Shelve can now shelve changes to a symlink's target.
99
           ('modify target', file_id, target_target, work_target)
0.14.27 by Aaron Bentley
Update docs
100
        """
7322.1.6 by Jelmer Vernooij
Use the new attributes on TreeChange.
101
        for change in self.iter_changes:
4595.9.1 by Aaron Bentley
Fix shelve in uncommitted trees.
102
            # don't shelve add of tree root.  Working tree should never
103
            # lack roots, and bzr misbehaves when they do.
104
            # FIXME ADHB (2009-08-09): should still shelve adds of tree roots
105
            # when a tree root was deleted / renamed.
7322.1.6 by Jelmer Vernooij
Use the new attributes on TreeChange.
106
            if change.kind[0] is None and change.name[1] == '':
4595.9.1 by Aaron Bentley
Fix shelve in uncommitted trees.
107
                continue
6011.1.2 by Aaron Bentley
Fix shelve to always ignore roots.
108
            # Also don't shelve deletion of tree root.
7322.1.6 by Jelmer Vernooij
Use the new attributes on TreeChange.
109
            if change.kind[1] is None and change.name[0] == '':
6011.1.2 by Aaron Bentley
Fix shelve to always ignore roots.
110
                continue
7322.1.6 by Jelmer Vernooij
Use the new attributes on TreeChange.
111
            if change.kind[0] is None or change.versioned[0] is False:
112
                self.creation[change.file_id] = (
113
                    change.kind[1], change.name[1], change.parent_id[1], change.versioned)
114
                yield ('add file', change.file_id, change.kind[1], change.path[1])
115
            elif change.kind[1] is None or change.versioned[0] is False:
116
                self.deletion[change.file_id] = (
117
                    change.kind[0], change.name[0], change.parent_id[0], change.versioned)
118
                yield ('delete file', change.file_id, change.kind[0], change.path[0])
0.12.14 by Aaron Bentley
Add shelving of created files
119
            else:
7322.1.6 by Jelmer Vernooij
Use the new attributes on TreeChange.
120
                if change.name[0] != change.name[1] or change.parent_id[0] != change.parent_id[1]:
121
                    self.renames[change.file_id] = (change.name, change.parent_id)
122
                    yield ('rename', change.file_id) + change.path
0.14.23 by Aaron Bentley
Allow shelving kind change
123
7322.1.6 by Jelmer Vernooij
Use the new attributes on TreeChange.
124
                if change.kind[0] != change.kind[1]:
125
                    yield ('change kind', change.file_id, change.kind[0], change.kind[1], change.path[0])
126
                elif change.kind[0] == 'symlink':
127
                    t_target = self.target_tree.get_symlink_target(change.path[0])
128
                    w_target = self.work_tree.get_symlink_target(change.path[1])
129
                    yield ('modify target', change.file_id, change.path[0], t_target,
7143.15.2 by Jelmer Vernooij
Run autopep8.
130
                           w_target)
7322.1.6 by Jelmer Vernooij
Use the new attributes on TreeChange.
131
                elif change.changed_content:
132
                    yield ('modify text', change.file_id)
0.12.12 by Aaron Bentley
Implement shelf creator
133
4526.6.1 by Aaron Bentley
Reverse the way changes are described by Shelver.
134
    def shelve_change(self, change):
4526.7.2 by Aaron Bentley
Update docs.
135
        """Shelve a change in the iter_shelvable format."""
4526.6.1 by Aaron Bentley
Reverse the way changes are described by Shelver.
136
        if change[0] == 'rename':
137
            self.shelve_rename(change[1])
138
        elif change[0] == 'delete file':
139
            self.shelve_deletion(change[1])
140
        elif change[0] == 'add file':
141
            self.shelve_creation(change[1])
4595.8.1 by Aaron Bentley
shelve_change handles text modification.
142
        elif change[0] in ('change kind', 'modify text'):
4526.6.1 by Aaron Bentley
Reverse the way changes are described by Shelver.
143
            self.shelve_content_change(change[1])
144
        elif change[0] == 'modify target':
6809.4.14 by Jelmer Vernooij
Fix shelf failures.
145
            self.shelve_modify_target(change[1])
4526.7.3 by Aaron Bentley
Test shelve_change.
146
        else:
147
            raise ValueError('Unknown change kind: "%s"' % change[0])
4526.6.1 by Aaron Bentley
Reverse the way changes are described by Shelver.
148
4595.8.2 by Aaron Bentley
Implement shelve_all
149
    def shelve_all(self):
6538.1.7 by Aaron Bentley
Move to shelve_all and improve it.
150
        """Shelve all changes.
151
7183.3.1 by Martin
Fix E71* lint errors
152
        :return: ``True`` if changes were shelved, otherwise ``False``.
6538.1.7 by Aaron Bentley
Move to shelve_all and improve it.
153
        """
154
        change = None
4595.8.2 by Aaron Bentley
Implement shelve_all
155
        for change in self.iter_shelvable():
156
            self.shelve_change(change)
6538.1.7 by Aaron Bentley
Move to shelve_all and improve it.
157
        return change is not None
4595.8.2 by Aaron Bentley
Implement shelve_all
158
0.12.12 by Aaron Bentley
Implement shelf creator
159
    def shelve_rename(self, file_id):
0.14.27 by Aaron Bentley
Update docs
160
        """Shelve a file rename.
161
162
        :param file_id: The file id of the file to shelve the renaming of.
163
        """
0.12.12 by Aaron Bentley
Implement shelf creator
164
        names, parents = self.renames[file_id]
165
        w_trans_id = self.work_transform.trans_id_file_id(file_id)
166
        work_parent = self.work_transform.trans_id_file_id(parents[0])
167
        self.work_transform.adjust_path(names[0], work_parent, w_trans_id)
168
169
        s_trans_id = self.shelf_transform.trans_id_file_id(file_id)
170
        shelf_parent = self.shelf_transform.trans_id_file_id(parents[1])
171
        self.shelf_transform.adjust_path(names[1], shelf_parent, s_trans_id)
172
6809.4.14 by Jelmer Vernooij
Fix shelf failures.
173
    def shelve_modify_target(self, file_id):
4119.5.1 by James Westby
Shelve can now shelve changes to a symlink's target.
174
        """Shelve a change of symlink target.
175
176
        :param file_id: The file id of the symlink which changed target.
6809.4.14 by Jelmer Vernooij
Fix shelf failures.
177
        :param new_target: The target that the symlink should have due
178
            to shelving.
4119.5.1 by James Westby
Shelve can now shelve changes to a symlink's target.
179
        """
6809.4.14 by Jelmer Vernooij
Fix shelf failures.
180
        new_path = self.target_tree.id2path(file_id)
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
181
        new_target = self.target_tree.get_symlink_target(new_path)
4119.5.1 by James Westby
Shelve can now shelve changes to a symlink's target.
182
        w_trans_id = self.work_transform.trans_id_file_id(file_id)
183
        self.work_transform.delete_contents(w_trans_id)
6809.4.14 by Jelmer Vernooij
Fix shelf failures.
184
        self.work_transform.create_symlink(new_target, w_trans_id)
4119.5.1 by James Westby
Shelve can now shelve changes to a symlink's target.
185
6809.4.14 by Jelmer Vernooij
Fix shelf failures.
186
        old_path = self.work_tree.id2path(file_id)
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
187
        old_target = self.work_tree.get_symlink_target(old_path)
4119.5.1 by James Westby
Shelve can now shelve changes to a symlink's target.
188
        s_trans_id = self.shelf_transform.trans_id_file_id(file_id)
189
        self.shelf_transform.delete_contents(s_trans_id)
6809.4.14 by Jelmer Vernooij
Fix shelf failures.
190
        self.shelf_transform.create_symlink(old_target, s_trans_id)
4119.5.1 by James Westby
Shelve can now shelve changes to a symlink's target.
191
0.14.14 by Aaron Bentley
Change shelf_text to shelve_lines
192
    def shelve_lines(self, file_id, new_lines):
0.14.27 by Aaron Bentley
Update docs
193
        """Shelve text changes to a file, using provided lines.
194
195
        :param file_id: The file id of the file to shelve the text of.
196
        :param new_lines: The lines that the file should have due to shelving.
197
        """
0.12.13 by Aaron Bentley
Implement shelving content
198
        w_trans_id = self.work_transform.trans_id_file_id(file_id)
199
        self.work_transform.delete_contents(w_trans_id)
200
        self.work_transform.create_file(new_lines, w_trans_id)
201
202
        s_trans_id = self.shelf_transform.trans_id_file_id(file_id)
203
        self.shelf_transform.delete_contents(s_trans_id)
204
        inverse_lines = self._inverse_lines(new_lines, file_id)
205
        self.shelf_transform.create_file(inverse_lines, s_trans_id)
206
0.14.23 by Aaron Bentley
Allow shelving kind change
207
    @staticmethod
208
    def _content_from_tree(tt, tree, file_id):
209
        trans_id = tt.trans_id_file_id(file_id)
210
        tt.delete_contents(trans_id)
7350.5.1 by Jelmer Vernooij
Remove file_id from transform's create_from_tree, get filter tree paths by path.
211
        transform.create_from_tree(tt, trans_id, tree, tree.id2path(file_id))
0.14.23 by Aaron Bentley
Allow shelving kind change
212
213
    def shelve_content_change(self, file_id):
0.14.27 by Aaron Bentley
Update docs
214
        """Shelve a kind change or binary file content change.
215
216
        :param file_id: The file id of the file to shelve the content change
217
            of.
218
        """
0.14.23 by Aaron Bentley
Allow shelving kind change
219
        self._content_from_tree(self.work_transform, self.target_tree, file_id)
220
        self._content_from_tree(self.shelf_transform, self.work_tree, file_id)
221
0.14.2 by Aaron Bentley
Somewhat clean up shelving
222
    def shelve_creation(self, file_id):
0.14.27 by Aaron Bentley
Update docs
223
        """Shelve creation of a file.
224
225
        This handles content and inventory id.
226
        :param file_id: The file_id of the file to shelve creation of.
227
        """
0.14.10 by Aaron Bentley
Fix behavior with deletions, unversioning, ...
228
        kind, name, parent, versioned = self.creation[file_id]
229
        version = not versioned[0]
0.14.4 by Aaron Bentley
Implement shelving deletion
230
        self._shelve_creation(self.work_tree, file_id, self.work_transform,
0.14.10 by Aaron Bentley
Fix behavior with deletions, unversioning, ...
231
                              self.shelf_transform, kind, name, parent,
232
                              version)
0.14.4 by Aaron Bentley
Implement shelving deletion
233
234
    def shelve_deletion(self, file_id):
0.14.27 by Aaron Bentley
Update docs
235
        """Shelve deletion of a file.
236
237
        This handles content and inventory id.
238
        :param file_id: The file_id of the file to shelve deletion of.
239
        """
0.14.10 by Aaron Bentley
Fix behavior with deletions, unversioning, ...
240
        kind, name, parent, versioned = self.deletion[file_id]
241
        existing_path = self.target_tree.id2path(file_id)
242
        if not self.work_tree.has_filename(existing_path):
243
            existing_path = None
244
        version = not versioned[1]
0.14.4 by Aaron Bentley
Implement shelving deletion
245
        self._shelve_creation(self.target_tree, file_id, self.shelf_transform,
0.14.10 by Aaron Bentley
Fix behavior with deletions, unversioning, ...
246
                              self.work_transform, kind, name, parent,
247
                              version, existing_path=existing_path)
0.14.4 by Aaron Bentley
Implement shelving deletion
248
249
    def _shelve_creation(self, tree, file_id, from_transform, to_transform,
0.14.10 by Aaron Bentley
Fix behavior with deletions, unversioning, ...
250
                         kind, name, parent, version, existing_path=None):
0.14.4 by Aaron Bentley
Implement shelving deletion
251
        w_trans_id = from_transform.trans_id_file_id(file_id)
0.14.12 by Aaron Bentley
Handle new dangling ids
252
        if parent is not None and kind is not None:
0.14.4 by Aaron Bentley
Implement shelving deletion
253
            from_transform.delete_contents(w_trans_id)
254
        from_transform.unversion_file(w_trans_id)
255
0.14.10 by Aaron Bentley
Fix behavior with deletions, unversioning, ...
256
        if existing_path is not None:
257
            s_trans_id = to_transform.trans_id_tree_path(existing_path)
258
        else:
259
            s_trans_id = to_transform.trans_id_file_id(file_id)
0.14.4 by Aaron Bentley
Implement shelving deletion
260
        if parent is not None:
261
            s_parent_id = to_transform.trans_id_file_id(parent)
0.14.9 by Aaron Bentley
Shelve deleted files properly
262
            to_transform.adjust_path(name, s_parent_id, s_trans_id)
0.14.10 by Aaron Bentley
Fix behavior with deletions, unversioning, ...
263
            if existing_path is None:
0.14.18 by Aaron Bentley
Simplify creating files
264
                if kind is None:
6973.11.7 by Jelmer Vernooij
Fix more tests.
265
                    to_transform.create_file([b''], s_trans_id)
0.14.18 by Aaron Bentley
Simplify creating files
266
                else:
6809.4.21 by Jelmer Vernooij
Fix long lines.
267
                    transform.create_from_tree(
7143.15.2 by Jelmer Vernooij
Run autopep8.
268
                        to_transform, s_trans_id, tree,
7350.5.1 by Jelmer Vernooij
Remove file_id from transform's create_from_tree, get filter tree paths by path.
269
                        tree.id2path(file_id))
0.14.10 by Aaron Bentley
Fix behavior with deletions, unversioning, ...
270
        if version:
271
            to_transform.version_file(file_id, s_trans_id)
0.12.14 by Aaron Bentley
Add shelving of created files
272
273
    def _inverse_lines(self, new_lines, file_id):
274
        """Produce a version with only those changes removed from new_lines."""
6809.4.5 by Jelmer Vernooij
Swap arguments for get_file_*.
275
        target_path = self.target_tree.id2path(file_id)
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
276
        target_lines = self.target_tree.get_file_lines(target_path)
6809.4.14 by Jelmer Vernooij
Fix shelf failures.
277
        work_path = self.work_tree.id2path(file_id)
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
278
        work_lines = self.work_tree.get_file_lines(work_path)
0.14.1 by Aaron Bentley
Use explicit target in ShelfCreator
279
        return merge3.Merge3(new_lines, target_lines, work_lines).merge_lines()
0.12.13 by Aaron Bentley
Implement shelving content
280
0.12.12 by Aaron Bentley
Implement shelf creator
281
    def finalize(self):
0.14.27 by Aaron Bentley
Update docs
282
        """Release all resources used by this ShelfCreator."""
0.12.12 by Aaron Bentley
Implement shelf creator
283
        self.work_transform.finalize()
284
        self.shelf_transform.finalize()
0.12.13 by Aaron Bentley
Implement shelving content
285
286
    def transform(self):
0.14.27 by Aaron Bentley
Update docs
287
        """Shelve changes from working tree."""
0.12.13 by Aaron Bentley
Implement shelving content
288
        self.work_transform.apply()
0.12.19 by Aaron Bentley
Add support for writing shelves
289
6538.1.8 by Aaron Bentley
Implement branch.get_uncommitted_data.
290
    @staticmethod
291
    def metadata_record(serializer, revision_id, message=None):
6855.4.5 by Jelmer Vernooij
Fix more bees, use with rather than try/finally for some files.
292
        metadata = {b'revision_id': revision_id}
6538.1.8 by Aaron Bentley
Implement branch.get_uncommitted_data.
293
        if message is not None:
6855.4.5 by Jelmer Vernooij
Fix more bees, use with rather than try/finally for some files.
294
            metadata[b'message'] = message.encode('utf-8')
6538.1.8 by Aaron Bentley
Implement branch.get_uncommitted_data.
295
        return serializer.bytes_record(
6973.6.2 by Jelmer Vernooij
Fix more tests.
296
            bencode.bencode(metadata), ((b'metadata',),))
6538.1.8 by Aaron Bentley
Implement branch.get_uncommitted_data.
297
0.12.51 by Aaron Bentley
Merge unshelve into shelf-manager
298
    def write_shelf(self, shelf_file, message=None):
0.14.27 by Aaron Bentley
Update docs
299
        """Serialize the shelved changes to a file.
300
0.12.66 by Aaron Bentley
Merge with unshelve
301
        :param shelf_file: A file-like object to write the shelf to.
0.15.29 by Aaron Bentley
Merge with prepare-shelf
302
        :param message: An optional message describing the shelved changes.
0.14.27 by Aaron Bentley
Update docs
303
        :return: the filename of the written file.
304
        """
0.12.24 by Aaron Bentley
Get unshelve using merge codepath, not applying transform directly
305
        transform.resolve_conflicts(self.shelf_transform)
6538.1.10 by Aaron Bentley
Implement WorkingTree.get_uncommitted_data
306
        revision_id = self.target_tree.get_revision_id()
307
        return self._write_shelf(shelf_file, self.shelf_transform, revision_id,
308
                                 message)
309
310
    @classmethod
311
    def _write_shelf(cls, shelf_file, transform, revision_id, message=None):
0.12.28 by Aaron Bentley
Update for shelf manager
312
        serializer = pack.ContainerSerialiser()
313
        shelf_file.write(serializer.begin())
6538.1.10 by Aaron Bentley
Implement WorkingTree.get_uncommitted_data
314
        metadata = cls.metadata_record(serializer, revision_id, message)
6538.1.8 by Aaron Bentley
Implement branch.get_uncommitted_data.
315
        shelf_file.write(metadata)
6538.1.10 by Aaron Bentley
Implement WorkingTree.get_uncommitted_data
316
        for bytes in transform.serialize(serializer):
0.12.28 by Aaron Bentley
Update for shelf manager
317
            shelf_file.write(bytes)
318
        shelf_file.write(serializer.end())
0.12.21 by Aaron Bentley
Add failing test of unshelver
319
320
321
class Unshelver(object):
0.15.30 by Aaron Bentley
Update docs.
322
    """Unshelve shelved changes."""
0.12.21 by Aaron Bentley
Add failing test of unshelver
323
0.15.19 by Aaron Bentley
Generalize first record as metadata.
324
    def __init__(self, tree, base_tree, transform, message):
0.15.30 by Aaron Bentley
Update docs.
325
        """Constructor.
326
327
        :param tree: The tree to apply the changes to.
328
        :param base_tree: The basis to apply the tranform to.
329
        :param message: A message from the shelved transform.
330
        """
0.12.21 by Aaron Bentley
Add failing test of unshelver
331
        self.tree = tree
0.12.24 by Aaron Bentley
Get unshelve using merge codepath, not applying transform directly
332
        self.base_tree = base_tree
0.12.21 by Aaron Bentley
Add failing test of unshelver
333
        self.transform = transform
0.15.19 by Aaron Bentley
Generalize first record as metadata.
334
        self.message = message
0.12.21 by Aaron Bentley
Add failing test of unshelver
335
0.16.110 by Aaron Bentley
Implement ls-shelf command
336
    @staticmethod
337
    def iter_records(shelf_file):
0.12.21 by Aaron Bentley
Add failing test of unshelver
338
        parser = pack.ContainerPushParser()
0.12.28 by Aaron Bentley
Update for shelf manager
339
        parser.accept_bytes(shelf_file.read())
0.16.110 by Aaron Bentley
Implement ls-shelf command
340
        return iter(parser.read_pending_records())
341
342
    @staticmethod
343
    def parse_metadata(records):
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
344
        names, metadata_bytes = next(records)
7045.3.1 by Jelmer Vernooij
Fix another ~500 tests.
345
        if names[0] != (b'metadata',):
6734.1.1 by Jelmer Vernooij
Fix more imports.
346
            raise ShelfCorrupt
0.15.19 by Aaron Bentley
Generalize first record as metadata.
347
        metadata = bencode.bdecode(metadata_bytes)
7045.3.1 by Jelmer Vernooij
Fix another ~500 tests.
348
        message = metadata.get(b'message')
0.15.19 by Aaron Bentley
Generalize first record as metadata.
349
        if message is not None:
7045.3.1 by Jelmer Vernooij
Fix another ~500 tests.
350
            metadata[b'message'] = message.decode('utf-8')
0.16.111 by Aaron Bentley
Return metadata as a dict.
351
        return metadata
0.16.110 by Aaron Bentley
Implement ls-shelf command
352
353
    @classmethod
354
    def from_tree_and_shelf(klass, tree, shelf_file):
355
        """Create an Unshelver from a tree and a shelf file.
356
357
        :param tree: The tree to apply shelved changes to.
358
        :param shelf_file: A file-like object containing shelved changes.
359
        :return: The Unshelver.
360
        """
361
        records = klass.iter_records(shelf_file)
0.16.111 by Aaron Bentley
Return metadata as a dict.
362
        metadata = klass.parse_metadata(records)
7045.3.1 by Jelmer Vernooij
Fix another ~500 tests.
363
        base_revision_id = metadata[b'revision_id']
0.12.26 by Aaron Bentley
Use correct base for shelving
364
        try:
365
            base_tree = tree.revision_tree(base_revision_id)
366
        except errors.NoSuchRevisionInTree:
367
            base_tree = tree.branch.repository.revision_tree(base_revision_id)
0.15.23 by Aaron Bentley
Use correct tree for desrializing transform
368
        tt = transform.TransformPreview(base_tree)
0.15.26 by Aaron Bentley
Merge with prepare-shelf
369
        tt.deserialize(records)
7045.3.1 by Jelmer Vernooij
Fix another ~500 tests.
370
        return klass(tree, base_tree, tt, metadata.get(b'message'))
0.12.21 by Aaron Bentley
Add failing test of unshelver
371
6719.1.2 by Jelmer Vernooij
Fix some tests.
372
    def make_merger(self):
0.15.30 by Aaron Bentley
Update docs.
373
        """Return a merger that can unshelve the changes."""
1551.21.7 by Aaron Bentley
Fix progress warning
374
        target_tree = self.transform.get_preview_tree()
375
        merger = merge.Merger.from_uncommitted(self.tree, target_tree,
7143.15.2 by Jelmer Vernooij
Run autopep8.
376
                                               self.base_tree)
1551.21.7 by Aaron Bentley
Fix progress warning
377
        merger.merge_type = merge.Merge3Merger
378
        return merger
0.12.25 by Aaron Bentley
Update to use new from_uncommitted API
379
380
    def finalize(self):
0.15.30 by Aaron Bentley
Update docs.
381
        """Release all resources held by this Unshelver."""
0.12.25 by Aaron Bentley
Update to use new from_uncommitted API
382
        self.transform.finalize()
0.12.27 by Aaron Bentley
Implement shelf manager
383
384
385
class ShelfManager(object):
0.12.68 by Aaron Bentley
Update docs, move items to proper files.
386
    """Maintain a list of shelved changes."""
0.12.27 by Aaron Bentley
Implement shelf manager
387
0.12.42 by Aaron Bentley
Get shelf from tree
388
    def __init__(self, tree, transport):
389
        self.tree = tree
0.12.41 by Aaron Bentley
Change shelf to use WT control dir for shelves
390
        self.transport = transport.clone('shelf')
0.12.27 by Aaron Bentley
Implement shelf manager
391
        self.transport.ensure_base()
392
0.12.77 by Aaron Bentley
Use names of the form shelf-5 for shelves
393
    def get_shelf_filename(self, shelf_id):
394
        return 'shelf-%d' % shelf_id
395
396
    def get_shelf_ids(self, filenames):
397
        matcher = re.compile('shelf-([1-9][0-9]*)')
398
        shelf_ids = []
399
        for filename in filenames:
400
            match = matcher.match(filename)
401
            if match is not None:
402
                shelf_ids.append(int(match.group(1)))
403
        return shelf_ids
404
0.12.27 by Aaron Bentley
Implement shelf manager
405
    def new_shelf(self):
0.12.68 by Aaron Bentley
Update docs, move items to proper files.
406
        """Return a file object and id for a new set of shelved changes."""
0.12.27 by Aaron Bentley
Implement shelf manager
407
        last_shelf = self.last_shelf()
408
        if last_shelf is None:
409
            next_shelf = 1
410
        else:
411
            next_shelf = last_shelf + 1
0.12.77 by Aaron Bentley
Use names of the form shelf-5 for shelves
412
        filename = self.get_shelf_filename(next_shelf)
413
        shelf_file = open(self.transport.local_abspath(filename), 'wb')
0.12.27 by Aaron Bentley
Implement shelf manager
414
        return next_shelf, shelf_file
415
0.12.53 by Aaron Bentley
Allow adding message to shelf
416
    def shelve_changes(self, creator, message=None):
0.12.68 by Aaron Bentley
Update docs, move items to proper files.
417
        """Store the changes in a ShelfCreator on a shelf."""
0.12.43 by Aaron Bentley
Make ShelfManager consume ShelfCreator and produce Unshelver
418
        next_shelf, shelf_file = self.new_shelf()
419
        try:
0.12.53 by Aaron Bentley
Allow adding message to shelf
420
            creator.write_shelf(shelf_file, message)
0.12.43 by Aaron Bentley
Make ShelfManager consume ShelfCreator and produce Unshelver
421
        finally:
422
            shelf_file.close()
0.12.44 by Aaron Bentley
Give manager responsibility for applying transform
423
        creator.transform()
0.12.43 by Aaron Bentley
Make ShelfManager consume ShelfCreator and produce Unshelver
424
        return next_shelf
425
0.12.27 by Aaron Bentley
Implement shelf manager
426
    def read_shelf(self, shelf_id):
0.12.68 by Aaron Bentley
Update docs, move items to proper files.
427
        """Return the file associated with a shelf_id for reading.
428
429
        :param shelf_id: The id of the shelf to retrive the file for.
430
        """
0.12.77 by Aaron Bentley
Use names of the form shelf-5 for shelves
431
        filename = self.get_shelf_filename(shelf_id)
0.12.50 by Aaron Bentley
Improve error handling for non-existant shelf-ids
432
        try:
0.12.77 by Aaron Bentley
Use names of the form shelf-5 for shelves
433
            return open(self.transport.local_abspath(filename), 'rb')
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
434
        except IOError as e:
0.12.50 by Aaron Bentley
Improve error handling for non-existant shelf-ids
435
            if e.errno != errno.ENOENT:
436
                raise
6734.1.1 by Jelmer Vernooij
Fix more imports.
437
            raise NoSuchShelfId(shelf_id)
0.12.27 by Aaron Bentley
Implement shelf manager
438
0.12.43 by Aaron Bentley
Make ShelfManager consume ShelfCreator and produce Unshelver
439
    def get_unshelver(self, shelf_id):
0.12.68 by Aaron Bentley
Update docs, move items to proper files.
440
        """Return an unshelver for a given shelf_id.
441
442
        :param shelf_id: The shelf id to return the unshelver for.
443
        """
0.12.43 by Aaron Bentley
Make ShelfManager consume ShelfCreator and produce Unshelver
444
        shelf_file = self.read_shelf(shelf_id)
445
        try:
446
            return Unshelver.from_tree_and_shelf(self.tree, shelf_file)
447
        finally:
448
            shelf_file.close()
449
0.16.112 by Aaron Bentley
Add tests
450
    def get_metadata(self, shelf_id):
451
        """Return the metadata associated with a given shelf_id."""
0.16.110 by Aaron Bentley
Implement ls-shelf command
452
        shelf_file = self.read_shelf(shelf_id)
453
        try:
454
            records = Unshelver.iter_records(shelf_file)
455
        finally:
456
            shelf_file.close()
457
        return Unshelver.parse_metadata(records)
458
0.12.27 by Aaron Bentley
Implement shelf manager
459
    def delete_shelf(self, shelf_id):
0.12.68 by Aaron Bentley
Update docs, move items to proper files.
460
        """Delete the shelved changes for a given id.
461
462
        :param shelf_id: id of the shelved changes to delete.
463
        """
0.12.77 by Aaron Bentley
Use names of the form shelf-5 for shelves
464
        filename = self.get_shelf_filename(shelf_id)
465
        self.transport.delete(filename)
0.12.27 by Aaron Bentley
Implement shelf manager
466
467
    def active_shelves(self):
0.12.68 by Aaron Bentley
Update docs, move items to proper files.
468
        """Return a list of shelved changes."""
6619.3.18 by Jelmer Vernooij
Run 2to3 idioms fixer.
469
        active = sorted(self.get_shelf_ids(self.transport.list_dir('.')))
0.12.77 by Aaron Bentley
Use names of the form shelf-5 for shelves
470
        return active
0.12.27 by Aaron Bentley
Implement shelf manager
471
472
    def last_shelf(self):
0.12.68 by Aaron Bentley
Update docs, move items to proper files.
473
        """Return the id of the last-created shelved change."""
0.12.27 by Aaron Bentley
Implement shelf manager
474
        active = self.active_shelves()
475
        if len(active) > 0:
0.12.77 by Aaron Bentley
Use names of the form shelf-5 for shelves
476
            return active[-1]
0.12.27 by Aaron Bentley
Implement shelf manager
477
        else:
478
            return None