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