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