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