~bzr-pqm/bzr/bzr.dev

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