Merge lp:~bialix/bzr/bzr-1.18-oslocks-win32 into lp:bzr/1.18

Proposed by Alexander Belchenko
Status: Merged
Merged at revision: not available
Proposed branch: lp:~bialix/bzr/bzr-1.18-oslocks-win32
Merge into: lp:bzr/1.18
Diff against target: 755 lines
To merge this branch: bzr merge lp:~bialix/bzr/bzr-1.18-oslocks-win32
Reviewer Review Type Date Requested Status
Vincent Ladeuil (community) Approve
Robert Collins Pending
Review via email: mp+10793@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Alexander Belchenko (bialix) wrote :

I've cherrypicked 2 revisions from bzr.dev (4635 and 4650) which contains fixes for OS locks problems on Windows (local push and shelve).

Will be nice if Robert will review result of cherrypick and then I'd like to propose merge it back to 1.18 branch and prepare 1.18.1 release soon. Even if this will be win32-only release.

I think this will help to get more testing of these changes and provides more windows users benefits of fixes.

Revision history for this message
Robert Collins (lifeless) wrote :

review +1

Revision history for this message
Vincent Ladeuil (vila) wrote :

Robert forgets the leading space so his votes get ignored.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'NEWS'
--- NEWS 2009-08-25 05:32:07 +0000
+++ NEWS 2009-08-27 08:35:10 +0000
@@ -17,9 +17,22 @@
17 conversion will commit too many copies a file.17 conversion will commit too many copies a file.
18 (Martin Pool, #415508)18 (Martin Pool, #415508)
1919
20Improvements
21************
22
23* ``bzr push`` locally on windows will no longer give a locking error with
24 dirstate based formats. (Robert Collins)
25
26* ``bzr shelve`` and ``bzr unshelve`` now work on windows.
27 (Robert Collins, #305006)
28
20API Changes29API Changes
21***********30***********
2231
32* ``bzrlib.shelf_ui`` has had the ``from_args`` convenience methods of its
33 classes changed to manage lock lifetime of the trees they open in a way
34 consistent with reader-exclusive locks. (Robert Collins, #305006)
35
23* ``Tree.path_content_summary`` may return a size of None, when called on36* ``Tree.path_content_summary`` may return a size of None, when called on
24 a tree with content filtering where the size of the canonical form37 a tree with content filtering where the size of the canonical form
25 cannot be cheaply determined. (Martin Pool)38 cannot be cheaply determined. (Martin Pool)
2639
=== modified file 'bzrlib/builtins.py'
--- bzrlib/builtins.py 2009-07-27 06:22:57 +0000
+++ bzrlib/builtins.py 2009-08-27 08:35:10 +0000
@@ -120,6 +120,15 @@
120120
121121
122def _get_one_revision_tree(command_name, revisions, branch=None, tree=None):122def _get_one_revision_tree(command_name, revisions, branch=None, tree=None):
123 """Get a revision tree. Not suitable for commands that change the tree.
124
125 Specifically, the basis tree in dirstate trees is coupled to the dirstate
126 and doing a commit/uncommit/pull will at best fail due to changing the
127 basis revision data.
128
129 If tree is passed in, it should be already locked, for lifetime management
130 of the trees internal cached state.
131 """
123 if branch is None:132 if branch is None:
124 branch = tree.branch133 branch = tree.branch
125 if revisions is None:134 if revisions is None:
@@ -5627,8 +5636,12 @@
5627 if writer is None:5636 if writer is None:
5628 writer = bzrlib.option.diff_writer_registry.get()5637 writer = bzrlib.option.diff_writer_registry.get()
5629 try:5638 try:
5630 Shelver.from_args(writer(sys.stdout), revision, all, file_list,5639 shelver = Shelver.from_args(writer(sys.stdout), revision, all,
5631 message, destroy=destroy).run()5640 file_list, message, destroy=destroy)
5641 try:
5642 shelver.run()
5643 finally:
5644 shelver.work_tree.unlock()
5632 except errors.UserAbort:5645 except errors.UserAbort:
5633 return 05646 return 0
56345647
@@ -5673,7 +5686,11 @@
56735686
5674 def run(self, shelf_id=None, action='apply'):5687 def run(self, shelf_id=None, action='apply'):
5675 from bzrlib.shelf_ui import Unshelver5688 from bzrlib.shelf_ui import Unshelver
5676 Unshelver.from_args(shelf_id, action).run()5689 unshelver = Unshelver.from_args(shelf_id, action)
5690 try:
5691 unshelver.run()
5692 finally:
5693 unshelver.tree.unlock()
56775694
56785695
5679class cmd_clean_tree(Command):5696class cmd_clean_tree(Command):
56805697
=== modified file 'bzrlib/merge.py'
--- bzrlib/merge.py 2009-07-02 13:07:14 +0000
+++ bzrlib/merge.py 2009-08-27 08:35:10 +0000
@@ -64,8 +64,12 @@
6464
6565
66def transform_tree(from_tree, to_tree, interesting_ids=None):66def transform_tree(from_tree, to_tree, interesting_ids=None):
67 merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,67 from_tree.lock_tree_write()
68 interesting_ids=interesting_ids, this_tree=from_tree)68 try:
69 merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
70 interesting_ids=interesting_ids, this_tree=from_tree)
71 finally:
72 from_tree.unlock()
6973
7074
71class Merger(object):75class Merger(object):
@@ -102,6 +106,17 @@
102 self._is_criss_cross = None106 self._is_criss_cross = None
103 self._lca_trees = None107 self._lca_trees = None
104108
109 def cache_trees_with_revision_ids(self, trees):
110 """Cache any tree in trees if it has a revision_id."""
111 for maybe_tree in trees:
112 if maybe_tree is None:
113 continue
114 try:
115 rev_id = maybe_tree.get_revision_id()
116 except AttributeError:
117 continue
118 self._cached_trees[rev_id] = maybe_tree
119
105 @property120 @property
106 def revision_graph(self):121 def revision_graph(self):
107 if self._revision_graph is None:122 if self._revision_graph is None:
@@ -1516,6 +1531,7 @@
1516 get_revision_id = getattr(base_tree, 'get_revision_id', None)1531 get_revision_id = getattr(base_tree, 'get_revision_id', None)
1517 if get_revision_id is None:1532 if get_revision_id is None:
1518 get_revision_id = base_tree.last_revision1533 get_revision_id = base_tree.last_revision
1534 merger.cache_trees_with_revision_ids([other_tree, base_tree, this_tree])
1519 merger.set_base_revision(get_revision_id(), this_branch)1535 merger.set_base_revision(get_revision_id(), this_branch)
1520 return merger.do_merge()1536 return merger.do_merge()
15211537
15221538
=== modified file 'bzrlib/shelf.py'
--- bzrlib/shelf.py 2009-07-13 17:35:09 +0000
+++ bzrlib/shelf.py 2009-08-27 08:35:10 +0000
@@ -37,8 +37,10 @@
37 def __init__(self, work_tree, target_tree, file_list=None):37 def __init__(self, work_tree, target_tree, file_list=None):
38 """Constructor.38 """Constructor.
3939
40 :param work_tree: The working tree to apply changes to40 :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.
41 :param target_tree: The tree to make the working tree more similar to.42 :param target_tree: The tree to make the working tree more similar to.
43 This is not required to be locked - a read_lock will be taken out.
42 :param file_list: The files to make more similar to the target.44 :param file_list: The files to make more similar to the target.
43 """45 """
44 self.work_tree = work_tree46 self.work_tree = work_tree
4547
=== modified file 'bzrlib/shelf_ui.py'
--- bzrlib/shelf_ui.py 2009-07-13 13:00:27 +0000
+++ bzrlib/shelf_ui.py 2009-08-27 08:35:10 +0000
@@ -149,6 +149,9 @@
149 message=None, directory='.', destroy=False):149 message=None, directory='.', destroy=False):
150 """Create a shelver from commandline arguments.150 """Create a shelver from commandline arguments.
151151
152 The returned shelver wil have a work_tree that is locked and should
153 be unlocked.
154
152 :param revision: RevisionSpec of the revision to compare to.155 :param revision: RevisionSpec of the revision to compare to.
153 :param all: If True, shelve all changes without prompting.156 :param all: If True, shelve all changes without prompting.
154 :param file_list: If supplied, only files in this list may be shelved.157 :param file_list: If supplied, only files in this list may be shelved.
@@ -158,9 +161,16 @@
158 changes.161 changes.
159 """162 """
160 tree, path = workingtree.WorkingTree.open_containing(directory)163 tree, path = workingtree.WorkingTree.open_containing(directory)
161 target_tree = builtins._get_one_revision_tree('shelf2', revision,164 # Ensure that tree is locked for the lifetime of target_tree, as
162 tree.branch, tree)165 # target tree may be reading from the same dirstate.
163 files = builtins.safe_relpath_files(tree, file_list)166 tree.lock_tree_write()
167 try:
168 target_tree = builtins._get_one_revision_tree('shelf2', revision,
169 tree.branch, tree)
170 files = builtins.safe_relpath_files(tree, file_list)
171 except:
172 tree.unlock()
173 raise
164 return klass(tree, target_tree, diff_writer, all, all, files, message,174 return klass(tree, target_tree, diff_writer, all, all, files, message,
165 destroy)175 destroy)
166176
@@ -313,32 +323,40 @@
313 def from_args(klass, shelf_id=None, action='apply', directory='.'):323 def from_args(klass, shelf_id=None, action='apply', directory='.'):
314 """Create an unshelver from commandline arguments.324 """Create an unshelver from commandline arguments.
315325
326 The returned shelver wil have a tree that is locked and should
327 be unlocked.
328
316 :param shelf_id: Integer id of the shelf, as a string.329 :param shelf_id: Integer id of the shelf, as a string.
317 :param action: action to perform. May be 'apply', 'dry-run',330 :param action: action to perform. May be 'apply', 'dry-run',
318 'delete'.331 'delete'.
319 :param directory: The directory to unshelve changes into.332 :param directory: The directory to unshelve changes into.
320 """333 """
321 tree, path = workingtree.WorkingTree.open_containing(directory)334 tree, path = workingtree.WorkingTree.open_containing(directory)
322 manager = tree.get_shelf_manager()335 tree.lock_tree_write()
323 if shelf_id is not None:336 try:
324 try:337 manager = tree.get_shelf_manager()
325 shelf_id = int(shelf_id)338 if shelf_id is not None:
326 except ValueError:339 try:
327 raise errors.InvalidShelfId(shelf_id)340 shelf_id = int(shelf_id)
328 else:341 except ValueError:
329 shelf_id = manager.last_shelf()342 raise errors.InvalidShelfId(shelf_id)
330 if shelf_id is None:343 else:
331 raise errors.BzrCommandError('No changes are shelved.')344 shelf_id = manager.last_shelf()
332 trace.note('Unshelving changes with id "%d".' % shelf_id)345 if shelf_id is None:
333 apply_changes = True346 raise errors.BzrCommandError('No changes are shelved.')
334 delete_shelf = True347 trace.note('Unshelving changes with id "%d".' % shelf_id)
335 read_shelf = True348 apply_changes = True
336 if action == 'dry-run':349 delete_shelf = True
337 apply_changes = False350 read_shelf = True
338 delete_shelf = False351 if action == 'dry-run':
339 if action == 'delete-only':352 apply_changes = False
340 apply_changes = False353 delete_shelf = False
341 read_shelf = False354 if action == 'delete-only':
355 apply_changes = False
356 read_shelf = False
357 except:
358 tree.unlock()
359 raise
342 return klass(tree, manager, shelf_id, apply_changes, delete_shelf,360 return klass(tree, manager, shelf_id, apply_changes, delete_shelf,
343 read_shelf)361 read_shelf)
344362
@@ -364,7 +382,7 @@
364382
365 def run(self):383 def run(self):
366 """Perform the unshelving operation."""384 """Perform the unshelving operation."""
367 self.tree.lock_write()385 self.tree.lock_tree_write()
368 cleanups = [self.tree.unlock]386 cleanups = [self.tree.unlock]
369 try:387 try:
370 if self.read_shelf:388 if self.read_shelf:
371389
=== modified file 'bzrlib/tests/per_workingtree/test_executable.py'
--- bzrlib/tests/per_workingtree/test_executable.py 2009-07-10 07:14:02 +0000
+++ bzrlib/tests/per_workingtree/test_executable.py 2009-08-27 08:35:10 +0000
@@ -30,7 +30,6 @@
3030
31 def setUp(self):31 def setUp(self):
32 super(TestExecutable, self).setUp()32 super(TestExecutable, self).setUp()
33
34 self.a_id = "a-20051208024829-849e76f7968d7a86"33 self.a_id = "a-20051208024829-849e76f7968d7a86"
35 self.b_id = "b-20051208024829-849e76f7968d7a86"34 self.b_id = "b-20051208024829-849e76f7968d7a86"
36 wt = self.make_branch_and_tree('b1')35 wt = self.make_branch_and_tree('b1')
3736
=== modified file 'bzrlib/tests/per_workingtree/test_set_root_id.py'
--- bzrlib/tests/per_workingtree/test_set_root_id.py 2009-07-10 07:14:02 +0000
+++ bzrlib/tests/per_workingtree/test_set_root_id.py 2009-08-27 08:35:10 +0000
@@ -23,6 +23,8 @@
23class TestSetRootId(TestCaseWithWorkingTree):23class TestSetRootId(TestCaseWithWorkingTree):
2424
25 def test_set_and_read_unicode(self):25 def test_set_and_read_unicode(self):
26 # This test tests that setting the root doesn't flush, so it
27 # deliberately tests concurrent access that isn't possible on windows.
26 tree = self.make_branch_and_tree('a-tree')28 tree = self.make_branch_and_tree('a-tree')
27 # setting the root id allows it to be read via get_root_id.29 # setting the root id allows it to be read via get_root_id.
28 root_id = u'\xe5n-id'.encode('utf8')30 root_id = u'\xe5n-id'.encode('utf8')
2931
=== modified file 'bzrlib/tests/test_merge.py'
--- bzrlib/tests/test_merge.py 2009-04-29 17:02:36 +0000
+++ bzrlib/tests/test_merge.py 2009-08-27 08:35:10 +0000
@@ -218,13 +218,15 @@
218 tree_a.add('file')218 tree_a.add('file')
219 tree_a.commit('commit base')219 tree_a.commit('commit base')
220 # basis_tree() is only guaranteed to be valid as long as it is actually220 # basis_tree() is only guaranteed to be valid as long as it is actually
221 # the basis tree. This mutates the tree after grabbing basis, so go to221 # the basis tree. This test commits to the tree after grabbing basis,
222 # the repository.222 # so we go to the repository.
223 base_tree = tree_a.branch.repository.revision_tree(tree_a.last_revision())223 base_tree = tree_a.branch.repository.revision_tree(tree_a.last_revision())
224 tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()224 tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
225 self.build_tree_contents([('tree_a/file', 'content_2')])225 self.build_tree_contents([('tree_a/file', 'content_2')])
226 tree_a.commit('commit other')226 tree_a.commit('commit other')
227 other_tree = tree_a.basis_tree()227 other_tree = tree_a.basis_tree()
228 # 'file' is now missing but isn't altered in any commit in b so no
229 # change should be applied.
228 os.unlink('tree_b/file')230 os.unlink('tree_b/file')
229 merge_inner(tree_b.branch, other_tree, base_tree, this_tree=tree_b)231 merge_inner(tree_b.branch, other_tree, base_tree, this_tree=tree_b)
230232
@@ -1232,6 +1234,27 @@
12321234
1233class TestMergerInMemory(TestMergerBase):1235class TestMergerInMemory(TestMergerBase):
12341236
1237 def test_cache_trees_with_revision_ids_None(self):
1238 merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1239 original_cache = dict(merger._cached_trees)
1240 merger.cache_trees_with_revision_ids([None])
1241 self.assertEqual(original_cache, merger._cached_trees)
1242
1243 def test_cache_trees_with_revision_ids_no_revision_id(self):
1244 merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1245 original_cache = dict(merger._cached_trees)
1246 tree = self.make_branch_and_memory_tree('tree')
1247 merger.cache_trees_with_revision_ids([tree])
1248 self.assertEqual(original_cache, merger._cached_trees)
1249
1250 def test_cache_trees_with_revision_ids_having_revision_id(self):
1251 merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1252 original_cache = dict(merger._cached_trees)
1253 tree = merger.this_branch.repository.revision_tree('B-id')
1254 original_cache['B-id'] = tree
1255 merger.cache_trees_with_revision_ids([tree])
1256 self.assertEqual(original_cache, merger._cached_trees)
1257
1235 def test_find_base(self):1258 def test_find_base(self):
1236 merger = self.make_Merger(self.setup_simple_graph(), 'C-id')1259 merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1237 self.assertEqual('A-id', merger.base_rev_id)1260 self.assertEqual('A-id', merger.base_rev_id)
12381261
=== modified file 'bzrlib/tests/test_shelf.py'
--- bzrlib/tests/test_shelf.py 2009-07-13 17:35:09 +0000
+++ bzrlib/tests/test_shelf.py 2009-08-27 08:35:10 +0000
@@ -46,6 +46,8 @@
46 tree.add(['foo'], ['foo-id'])46 tree.add(['foo'], ['foo-id'])
47 tree.commit('foo')47 tree.commit('foo')
48 tree.rename_one('foo', 'bar')48 tree.rename_one('foo', 'bar')
49 tree.lock_tree_write()
50 self.addCleanup(tree.unlock)
49 creator = shelf.ShelfCreator(tree, tree.basis_tree())51 creator = shelf.ShelfCreator(tree, tree.basis_tree())
50 self.addCleanup(creator.finalize)52 self.addCleanup(creator.finalize)
51 self.assertEqual([('rename', 'foo-id', 'foo', 'bar')],53 self.assertEqual([('rename', 'foo-id', 'foo', 'bar')],
@@ -76,6 +78,8 @@
76 tree.add(['foo', 'bar', 'foo/baz'], ['foo-id', 'bar-id', 'baz-id'])78 tree.add(['foo', 'bar', 'foo/baz'], ['foo-id', 'bar-id', 'baz-id'])
77 tree.commit('foo')79 tree.commit('foo')
78 tree.rename_one('foo/baz', 'bar/baz')80 tree.rename_one('foo/baz', 'bar/baz')
81 tree.lock_tree_write()
82 self.addCleanup(tree.unlock)
79 creator = shelf.ShelfCreator(tree, tree.basis_tree())83 creator = shelf.ShelfCreator(tree, tree.basis_tree())
80 self.addCleanup(creator.finalize)84 self.addCleanup(creator.finalize)
81 self.assertEqual([('rename', 'baz-id', 'foo/baz', 'bar/baz')],85 self.assertEqual([('rename', 'baz-id', 'foo/baz', 'bar/baz')],
@@ -310,6 +314,8 @@
310 tree.add('foo', 'foo-id')314 tree.add('foo', 'foo-id')
311 tree.commit('Added file and directory')315 tree.commit('Added file and directory')
312 os.unlink('tree/foo')316 os.unlink('tree/foo')
317 tree.lock_tree_write()
318 self.addCleanup(tree.unlock)
313 creator = shelf.ShelfCreator(tree, tree.basis_tree())319 creator = shelf.ShelfCreator(tree, tree.basis_tree())
314 self.addCleanup(creator.finalize)320 self.addCleanup(creator.finalize)
315 self.assertEqual([('delete file', 'foo-id', 'file', 'foo')],321 self.assertEqual([('delete file', 'foo-id', 'file', 'foo')],
@@ -325,6 +331,8 @@
325 tree.commit('Added file and directory')331 tree.commit('Added file and directory')
326 os.unlink('tree/foo')332 os.unlink('tree/foo')
327 os.mkdir('tree/foo')333 os.mkdir('tree/foo')
334 tree.lock_tree_write()
335 self.addCleanup(tree.unlock)
328 creator = shelf.ShelfCreator(tree, tree.basis_tree())336 creator = shelf.ShelfCreator(tree, tree.basis_tree())
329 self.addCleanup(creator.finalize)337 self.addCleanup(creator.finalize)
330 self.assertEqual([('change kind', 'foo-id', 'file', 'directory',338 self.assertEqual([('change kind', 'foo-id', 'file', 'directory',
@@ -352,6 +360,8 @@
352360
353 def test_shelve_change_unknown_change(self):361 def test_shelve_change_unknown_change(self):
354 tree = self.make_branch_and_tree('tree')362 tree = self.make_branch_and_tree('tree')
363 tree.lock_tree_write()
364 self.addCleanup(tree.unlock)
355 creator = shelf.ShelfCreator(tree, tree.basis_tree())365 creator = shelf.ShelfCreator(tree, tree.basis_tree())
356 self.addCleanup(creator.finalize)366 self.addCleanup(creator.finalize)
357 e = self.assertRaises(ValueError, creator.shelve_change, ('unknown',))367 e = self.assertRaises(ValueError, creator.shelve_change, ('unknown',))
@@ -363,6 +373,8 @@
363 tree.add('foo', 'foo-id')373 tree.add('foo', 'foo-id')
364 tree.commit('Added file and directory')374 tree.commit('Added file and directory')
365 tree.unversion(['foo-id'])375 tree.unversion(['foo-id'])
376 tree.lock_tree_write()
377 self.addCleanup(tree.unlock)
366 creator = shelf.ShelfCreator(tree, tree.basis_tree())378 creator = shelf.ShelfCreator(tree, tree.basis_tree())
367 self.addCleanup(creator.finalize)379 self.addCleanup(creator.finalize)
368 self.assertEqual([('delete file', 'foo-id', 'file', 'foo')],380 self.assertEqual([('delete file', 'foo-id', 'file', 'foo')],
@@ -373,6 +385,8 @@
373385
374 def test_shelve_serialization(self):386 def test_shelve_serialization(self):
375 tree = self.make_branch_and_tree('.')387 tree = self.make_branch_and_tree('.')
388 tree.lock_tree_write()
389 self.addCleanup(tree.unlock)
376 creator = shelf.ShelfCreator(tree, tree.basis_tree())390 creator = shelf.ShelfCreator(tree, tree.basis_tree())
377 self.addCleanup(creator.finalize)391 self.addCleanup(creator.finalize)
378 shelf_file = open('shelf', 'wb')392 shelf_file = open('shelf', 'wb')
@@ -387,6 +401,8 @@
387 tree = self.make_branch_and_tree('tree')401 tree = self.make_branch_and_tree('tree')
388 self.build_tree(['tree/foo'])402 self.build_tree(['tree/foo'])
389 tree.add('foo', 'foo-id')403 tree.add('foo', 'foo-id')
404 tree.lock_tree_write()
405 self.addCleanup(tree.unlock)
390 creator = shelf.ShelfCreator(tree, tree.basis_tree())406 creator = shelf.ShelfCreator(tree, tree.basis_tree())
391 self.addCleanup(creator.finalize)407 self.addCleanup(creator.finalize)
392 list(creator.iter_shelvable())408 list(creator.iter_shelvable())
@@ -411,8 +427,12 @@
411427
412 def test_shelve_unversioned(self):428 def test_shelve_unversioned(self):
413 tree = self.make_branch_and_tree('tree')429 tree = self.make_branch_and_tree('tree')
414 self.assertRaises(errors.PathsNotVersionedError,430 tree.lock_tree_write()
415 shelf.ShelfCreator, tree, tree.basis_tree(), ['foo'])431 try:
432 self.assertRaises(errors.PathsNotVersionedError,
433 shelf.ShelfCreator, tree, tree.basis_tree(), ['foo'])
434 finally:
435 tree.unlock()
416 # We should be able to lock/unlock the tree if ShelfCreator cleaned436 # We should be able to lock/unlock the tree if ShelfCreator cleaned
417 # after itself.437 # after itself.
418 wt = workingtree.WorkingTree.open('tree')438 wt = workingtree.WorkingTree.open('tree')
@@ -420,8 +440,15 @@
420 wt.unlock()440 wt.unlock()
421 # And a second tentative should raise the same error (no441 # And a second tentative should raise the same error (no
422 # limbo/pending_deletion leftovers).442 # limbo/pending_deletion leftovers).
423 self.assertRaises(errors.PathsNotVersionedError,443 tree.lock_tree_write()
424 shelf.ShelfCreator, tree, tree.basis_tree(), ['foo'])444 try:
445 self.assertRaises(errors.PathsNotVersionedError,
446 shelf.ShelfCreator, tree, tree.basis_tree(), ['foo'])
447 finally:
448 tree.unlock()
449
450 tree.lock_tree_write()
451 self.addCleanup(tree.unlock)
425452
426453
427class TestUnshelver(tests.TestCaseWithTransport):454class TestUnshelver(tests.TestCaseWithTransport):
@@ -525,6 +552,7 @@
525 shelf_file = open('shelf', 'rb')552 shelf_file = open('shelf', 'rb')
526 self.addCleanup(shelf_file.close)553 self.addCleanup(shelf_file.close)
527 unshelver = shelf.Unshelver.from_tree_and_shelf(tree, shelf_file)554 unshelver = shelf.Unshelver.from_tree_and_shelf(tree, shelf_file)
555 unshelver.finalize()
528556
529 def test_corrupt_shelf(self):557 def test_corrupt_shelf(self):
530 tree = self.make_branch_and_tree('.')558 tree = self.make_branch_and_tree('.')
@@ -644,7 +672,10 @@
644672
645 def test_get_metadata(self):673 def test_get_metadata(self):
646 tree = self.make_branch_and_tree('.')674 tree = self.make_branch_and_tree('.')
675 tree.lock_tree_write()
676 self.addCleanup(tree.unlock)
647 creator = shelf.ShelfCreator(tree, tree.basis_tree())677 creator = shelf.ShelfCreator(tree, tree.basis_tree())
678 self.addCleanup(creator.finalize)
648 shelf_manager = tree.get_shelf_manager()679 shelf_manager = tree.get_shelf_manager()
649 shelf_id = shelf_manager.shelve_changes(creator, 'foo')680 shelf_id = shelf_manager.shelve_changes(creator, 'foo')
650 metadata = shelf_manager.get_metadata(shelf_id)681 metadata = shelf_manager.get_metadata(shelf_id)
651682
=== modified file 'bzrlib/tests/test_shelf_ui.py'
--- bzrlib/tests/test_shelf_ui.py 2009-07-14 13:19:52 +0000
+++ bzrlib/tests/test_shelf_ui.py 2009-08-27 08:35:10 +0000
@@ -68,12 +68,16 @@
6868
69 def test_unexpected_prompt_failure(self):69 def test_unexpected_prompt_failure(self):
70 tree = self.create_shelvable_tree()70 tree = self.create_shelvable_tree()
71 tree.lock_tree_write()
72 self.addCleanup(tree.unlock)
71 shelver = ExpectShelver(tree, tree.basis_tree())73 shelver = ExpectShelver(tree, tree.basis_tree())
72 e = self.assertRaises(AssertionError, shelver.run)74 e = self.assertRaises(AssertionError, shelver.run)
73 self.assertEqual('Unexpected prompt: Shelve? [yNfq?]', str(e))75 self.assertEqual('Unexpected prompt: Shelve? [yNfq?]', str(e))
7476
75 def test_wrong_prompt_failure(self):77 def test_wrong_prompt_failure(self):
76 tree = self.create_shelvable_tree()78 tree = self.create_shelvable_tree()
79 tree.lock_tree_write()
80 self.addCleanup(tree.unlock)
77 shelver = ExpectShelver(tree, tree.basis_tree())81 shelver = ExpectShelver(tree, tree.basis_tree())
78 shelver.expect('foo', 'y')82 shelver.expect('foo', 'y')
79 e = self.assertRaises(AssertionError, shelver.run)83 e = self.assertRaises(AssertionError, shelver.run)
@@ -81,6 +85,8 @@
8185
82 def test_shelve_not_diff(self):86 def test_shelve_not_diff(self):
83 tree = self.create_shelvable_tree()87 tree = self.create_shelvable_tree()
88 tree.lock_tree_write()
89 self.addCleanup(tree.unlock)
84 shelver = ExpectShelver(tree, tree.basis_tree())90 shelver = ExpectShelver(tree, tree.basis_tree())
85 shelver.expect('Shelve? [yNfq?]', 'n')91 shelver.expect('Shelve? [yNfq?]', 'n')
86 shelver.expect('Shelve? [yNfq?]', 'n')92 shelver.expect('Shelve? [yNfq?]', 'n')
@@ -90,6 +96,8 @@
9096
91 def test_shelve_diff_no(self):97 def test_shelve_diff_no(self):
92 tree = self.create_shelvable_tree()98 tree = self.create_shelvable_tree()
99 tree.lock_tree_write()
100 self.addCleanup(tree.unlock)
93 shelver = ExpectShelver(tree, tree.basis_tree())101 shelver = ExpectShelver(tree, tree.basis_tree())
94 shelver.expect('Shelve? [yNfq?]', 'y')102 shelver.expect('Shelve? [yNfq?]', 'y')
95 shelver.expect('Shelve? [yNfq?]', 'y')103 shelver.expect('Shelve? [yNfq?]', 'y')
@@ -99,6 +107,8 @@
99107
100 def test_shelve_diff(self):108 def test_shelve_diff(self):
101 tree = self.create_shelvable_tree()109 tree = self.create_shelvable_tree()
110 tree.lock_tree_write()
111 self.addCleanup(tree.unlock)
102 shelver = ExpectShelver(tree, tree.basis_tree())112 shelver = ExpectShelver(tree, tree.basis_tree())
103 shelver.expect('Shelve? [yNfq?]', 'y')113 shelver.expect('Shelve? [yNfq?]', 'y')
104 shelver.expect('Shelve? [yNfq?]', 'y')114 shelver.expect('Shelve? [yNfq?]', 'y')
@@ -108,6 +118,8 @@
108118
109 def test_shelve_one_diff(self):119 def test_shelve_one_diff(self):
110 tree = self.create_shelvable_tree()120 tree = self.create_shelvable_tree()
121 tree.lock_tree_write()
122 self.addCleanup(tree.unlock)
111 shelver = ExpectShelver(tree, tree.basis_tree())123 shelver = ExpectShelver(tree, tree.basis_tree())
112 shelver.expect('Shelve? [yNfq?]', 'y')124 shelver.expect('Shelve? [yNfq?]', 'y')
113 shelver.expect('Shelve? [yNfq?]', 'n')125 shelver.expect('Shelve? [yNfq?]', 'n')
@@ -118,6 +130,8 @@
118 def test_shelve_binary_change(self):130 def test_shelve_binary_change(self):
119 tree = self.create_shelvable_tree()131 tree = self.create_shelvable_tree()
120 self.build_tree_contents([('tree/foo', '\x00')])132 self.build_tree_contents([('tree/foo', '\x00')])
133 tree.lock_tree_write()
134 self.addCleanup(tree.unlock)
121 shelver = ExpectShelver(tree, tree.basis_tree())135 shelver = ExpectShelver(tree, tree.basis_tree())
122 shelver.expect('Shelve binary changes? [yNfq?]', 'y')136 shelver.expect('Shelve binary changes? [yNfq?]', 'y')
123 shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')137 shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
@@ -127,6 +141,8 @@
127 def test_shelve_rename(self):141 def test_shelve_rename(self):
128 tree = self.create_shelvable_tree()142 tree = self.create_shelvable_tree()
129 tree.rename_one('foo', 'bar')143 tree.rename_one('foo', 'bar')
144 tree.lock_tree_write()
145 self.addCleanup(tree.unlock)
130 shelver = ExpectShelver(tree, tree.basis_tree())146 shelver = ExpectShelver(tree, tree.basis_tree())
131 shelver.expect('Shelve renaming "foo" => "bar"? [yNfq?]', 'y')147 shelver.expect('Shelve renaming "foo" => "bar"? [yNfq?]', 'y')
132 shelver.expect('Shelve? [yNfq?]', 'y')148 shelver.expect('Shelve? [yNfq?]', 'y')
@@ -138,6 +154,8 @@
138 def test_shelve_deletion(self):154 def test_shelve_deletion(self):
139 tree = self.create_shelvable_tree()155 tree = self.create_shelvable_tree()
140 os.unlink('tree/foo')156 os.unlink('tree/foo')
157 tree.lock_tree_write()
158 self.addCleanup(tree.unlock)
141 shelver = ExpectShelver(tree, tree.basis_tree())159 shelver = ExpectShelver(tree, tree.basis_tree())
142 shelver.expect('Shelve removing file "foo"? [yNfq?]', 'y')160 shelver.expect('Shelve removing file "foo"? [yNfq?]', 'y')
143 shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')161 shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
@@ -149,6 +167,8 @@
149 tree.commit('add tree root')167 tree.commit('add tree root')
150 self.build_tree(['tree/foo'])168 self.build_tree(['tree/foo'])
151 tree.add('foo')169 tree.add('foo')
170 tree.lock_tree_write()
171 self.addCleanup(tree.unlock)
152 shelver = ExpectShelver(tree, tree.basis_tree())172 shelver = ExpectShelver(tree, tree.basis_tree())
153 shelver.expect('Shelve adding file "foo"? [yNfq?]', 'y')173 shelver.expect('Shelve adding file "foo"? [yNfq?]', 'y')
154 shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')174 shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
@@ -159,6 +179,8 @@
159 tree = self.create_shelvable_tree()179 tree = self.create_shelvable_tree()
160 os.unlink('tree/foo')180 os.unlink('tree/foo')
161 os.mkdir('tree/foo')181 os.mkdir('tree/foo')
182 tree.lock_tree_write()
183 self.addCleanup(tree.unlock)
162 shelver = ExpectShelver(tree, tree.basis_tree())184 shelver = ExpectShelver(tree, tree.basis_tree())
163 shelver.expect('Shelve changing "foo" from file to directory? [yNfq?]',185 shelver.expect('Shelve changing "foo" from file to directory? [yNfq?]',
164 'y')186 'y')
@@ -172,6 +194,8 @@
172 tree.commit("Add symlink")194 tree.commit("Add symlink")
173 os.unlink('tree/baz')195 os.unlink('tree/baz')
174 os.symlink('vax', 'tree/baz')196 os.symlink('vax', 'tree/baz')
197 tree.lock_tree_write()
198 self.addCleanup(tree.unlock)
175 shelver = ExpectShelver(tree, tree.basis_tree())199 shelver = ExpectShelver(tree, tree.basis_tree())
176 shelver.expect('Shelve changing target of "baz" from "bar" to '200 shelver.expect('Shelve changing target of "baz" from "bar" to '
177 '"vax"? [yNfq?]', 'y')201 '"vax"? [yNfq?]', 'y')
@@ -181,6 +205,8 @@
181205
182 def test_shelve_finish(self):206 def test_shelve_finish(self):
183 tree = self.create_shelvable_tree()207 tree = self.create_shelvable_tree()
208 tree.lock_tree_write()
209 self.addCleanup(tree.unlock)
184 shelver = ExpectShelver(tree, tree.basis_tree())210 shelver = ExpectShelver(tree, tree.basis_tree())
185 shelver.expect('Shelve? [yNfq?]', 'f')211 shelver.expect('Shelve? [yNfq?]', 'f')
186 shelver.expect('Shelve 2 change(s)? [yNfq?]', 'y')212 shelver.expect('Shelve 2 change(s)? [yNfq?]', 'y')
@@ -189,6 +215,8 @@
189215
190 def test_shelve_quit(self):216 def test_shelve_quit(self):
191 tree = self.create_shelvable_tree()217 tree = self.create_shelvable_tree()
218 tree.lock_tree_write()
219 self.addCleanup(tree.unlock)
192 shelver = ExpectShelver(tree, tree.basis_tree())220 shelver = ExpectShelver(tree, tree.basis_tree())
193 shelver.expect('Shelve? [yNfq?]', 'q')221 shelver.expect('Shelve? [yNfq?]', 'q')
194 self.assertRaises(errors.UserAbort, shelver.run)222 self.assertRaises(errors.UserAbort, shelver.run)
@@ -196,13 +224,20 @@
196224
197 def test_shelve_all(self):225 def test_shelve_all(self):
198 tree = self.create_shelvable_tree()226 tree = self.create_shelvable_tree()
199 ExpectShelver.from_args(sys.stdout, all=True, directory='tree').run()227 shelver = ExpectShelver.from_args(sys.stdout, all=True,
228 directory='tree')
229 try:
230 shelver.run()
231 finally:
232 shelver.work_tree.unlock()
200 self.assertFileEqual(LINES_AJ, 'tree/foo')233 self.assertFileEqual(LINES_AJ, 'tree/foo')
201234
202 def test_shelve_filename(self):235 def test_shelve_filename(self):
203 tree = self.create_shelvable_tree()236 tree = self.create_shelvable_tree()
204 self.build_tree(['tree/bar'])237 self.build_tree(['tree/bar'])
205 tree.add('bar')238 tree.add('bar')
239 tree.lock_tree_write()
240 self.addCleanup(tree.unlock)
206 shelver = ExpectShelver(tree, tree.basis_tree(), file_list=['bar'])241 shelver = ExpectShelver(tree, tree.basis_tree(), file_list=['bar'])
207 shelver.expect('Shelve adding file "bar"? [yNfq?]', 'y')242 shelver.expect('Shelve adding file "bar"? [yNfq?]', 'y')
208 shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')243 shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
@@ -210,6 +245,8 @@
210245
211 def test_shelve_help(self):246 def test_shelve_help(self):
212 tree = self.create_shelvable_tree()247 tree = self.create_shelvable_tree()
248 tree.lock_tree_write()
249 self.addCleanup(tree.unlock)
213 shelver = ExpectShelver(tree, tree.basis_tree())250 shelver = ExpectShelver(tree, tree.basis_tree())
214 shelver.expect('Shelve? [yNfq?]', '?')251 shelver.expect('Shelve? [yNfq?]', '?')
215 shelver.expect('Shelve? [(y)es, (N)o, (f)inish, or (q)uit]', 'f')252 shelver.expect('Shelve? [(y)es, (N)o, (f)inish, or (q)uit]', 'f')
@@ -220,7 +257,10 @@
220 tree = self.create_shelvable_tree()257 tree = self.create_shelvable_tree()
221 shelver = shelf_ui.Shelver.from_args(sys.stdout, all=True,258 shelver = shelf_ui.Shelver.from_args(sys.stdout, all=True,
222 directory='tree', destroy=True)259 directory='tree', destroy=True)
223 shelver.run()260 try:
261 shelver.run()
262 finally:
263 shelver.work_tree.unlock()
224 self.assertIs(None, tree.get_shelf_manager().last_shelf())264 self.assertIs(None, tree.get_shelf_manager().last_shelf())
225 self.assertFileEqual(LINES_AJ, 'tree/foo')265 self.assertFileEqual(LINES_AJ, 'tree/foo')
226266
@@ -229,6 +269,8 @@
229269
230 def test_shelve_not_diff(self):270 def test_shelve_not_diff(self):
231 tree = self.create_shelvable_tree()271 tree = self.create_shelvable_tree()
272 tree.lock_tree_write()
273 self.addCleanup(tree.unlock)
232 shelver = ExpectShelver(tree, tree.basis_tree(),274 shelver = ExpectShelver(tree, tree.basis_tree(),
233 reporter=shelf_ui.ApplyReporter())275 reporter=shelf_ui.ApplyReporter())
234 shelver.expect('Apply change? [yNfq?]', 'n')276 shelver.expect('Apply change? [yNfq?]', 'n')
@@ -239,6 +281,8 @@
239281
240 def test_shelve_diff_no(self):282 def test_shelve_diff_no(self):
241 tree = self.create_shelvable_tree()283 tree = self.create_shelvable_tree()
284 tree.lock_tree_write()
285 self.addCleanup(tree.unlock)
242 shelver = ExpectShelver(tree, tree.basis_tree(),286 shelver = ExpectShelver(tree, tree.basis_tree(),
243 reporter=shelf_ui.ApplyReporter())287 reporter=shelf_ui.ApplyReporter())
244 shelver.expect('Apply change? [yNfq?]', 'y')288 shelver.expect('Apply change? [yNfq?]', 'y')
@@ -249,6 +293,8 @@
249293
250 def test_shelve_diff(self):294 def test_shelve_diff(self):
251 tree = self.create_shelvable_tree()295 tree = self.create_shelvable_tree()
296 tree.lock_tree_write()
297 self.addCleanup(tree.unlock)
252 shelver = ExpectShelver(tree, tree.basis_tree(),298 shelver = ExpectShelver(tree, tree.basis_tree(),
253 reporter=shelf_ui.ApplyReporter())299 reporter=shelf_ui.ApplyReporter())
254 shelver.expect('Apply change? [yNfq?]', 'y')300 shelver.expect('Apply change? [yNfq?]', 'y')
@@ -260,6 +306,8 @@
260 def test_shelve_binary_change(self):306 def test_shelve_binary_change(self):
261 tree = self.create_shelvable_tree()307 tree = self.create_shelvable_tree()
262 self.build_tree_contents([('tree/foo', '\x00')])308 self.build_tree_contents([('tree/foo', '\x00')])
309 tree.lock_tree_write()
310 self.addCleanup(tree.unlock)
263 shelver = ExpectShelver(tree, tree.basis_tree(),311 shelver = ExpectShelver(tree, tree.basis_tree(),
264 reporter=shelf_ui.ApplyReporter())312 reporter=shelf_ui.ApplyReporter())
265 shelver.expect('Apply binary changes? [yNfq?]', 'y')313 shelver.expect('Apply binary changes? [yNfq?]', 'y')
@@ -270,6 +318,8 @@
270 def test_shelve_rename(self):318 def test_shelve_rename(self):
271 tree = self.create_shelvable_tree()319 tree = self.create_shelvable_tree()
272 tree.rename_one('foo', 'bar')320 tree.rename_one('foo', 'bar')
321 tree.lock_tree_write()
322 self.addCleanup(tree.unlock)
273 shelver = ExpectShelver(tree, tree.basis_tree(),323 shelver = ExpectShelver(tree, tree.basis_tree(),
274 reporter=shelf_ui.ApplyReporter())324 reporter=shelf_ui.ApplyReporter())
275 shelver.expect('Rename "bar" => "foo"? [yNfq?]', 'y')325 shelver.expect('Rename "bar" => "foo"? [yNfq?]', 'y')
@@ -282,6 +332,8 @@
282 def test_shelve_deletion(self):332 def test_shelve_deletion(self):
283 tree = self.create_shelvable_tree()333 tree = self.create_shelvable_tree()
284 os.unlink('tree/foo')334 os.unlink('tree/foo')
335 tree.lock_tree_write()
336 self.addCleanup(tree.unlock)
285 shelver = ExpectShelver(tree, tree.basis_tree(),337 shelver = ExpectShelver(tree, tree.basis_tree(),
286 reporter=shelf_ui.ApplyReporter())338 reporter=shelf_ui.ApplyReporter())
287 shelver.expect('Add file "foo"? [yNfq?]', 'y')339 shelver.expect('Add file "foo"? [yNfq?]', 'y')
@@ -294,6 +346,8 @@
294 tree.commit('add tree root')346 tree.commit('add tree root')
295 self.build_tree(['tree/foo'])347 self.build_tree(['tree/foo'])
296 tree.add('foo')348 tree.add('foo')
349 tree.lock_tree_write()
350 self.addCleanup(tree.unlock)
297 shelver = ExpectShelver(tree, tree.basis_tree(),351 shelver = ExpectShelver(tree, tree.basis_tree(),
298 reporter=shelf_ui.ApplyReporter())352 reporter=shelf_ui.ApplyReporter())
299 shelver.expect('Delete file "foo"? [yNfq?]', 'y')353 shelver.expect('Delete file "foo"? [yNfq?]', 'y')
@@ -305,6 +359,8 @@
305 tree = self.create_shelvable_tree()359 tree = self.create_shelvable_tree()
306 os.unlink('tree/foo')360 os.unlink('tree/foo')
307 os.mkdir('tree/foo')361 os.mkdir('tree/foo')
362 tree.lock_tree_write()
363 self.addCleanup(tree.unlock)
308 shelver = ExpectShelver(tree, tree.basis_tree(),364 shelver = ExpectShelver(tree, tree.basis_tree(),
309 reporter=shelf_ui.ApplyReporter())365 reporter=shelf_ui.ApplyReporter())
310 shelver.expect('Change "foo" from directory to a file? [yNfq?]', 'y')366 shelver.expect('Change "foo" from directory to a file? [yNfq?]', 'y')
@@ -318,6 +374,8 @@
318 tree.commit("Add symlink")374 tree.commit("Add symlink")
319 os.unlink('tree/baz')375 os.unlink('tree/baz')
320 os.symlink('vax', 'tree/baz')376 os.symlink('vax', 'tree/baz')
377 tree.lock_tree_write()
378 self.addCleanup(tree.unlock)
321 shelver = ExpectShelver(tree, tree.basis_tree(),379 shelver = ExpectShelver(tree, tree.basis_tree(),
322 reporter=shelf_ui.ApplyReporter())380 reporter=shelf_ui.ApplyReporter())
323 shelver.expect('Change target of "baz" from "vax" to "bar"? [yNfq?]',381 shelver.expect('Change target of "baz" from "vax" to "bar"? [yNfq?]',
@@ -331,12 +389,16 @@
331389
332 def create_tree_with_shelf(self):390 def create_tree_with_shelf(self):
333 tree = self.make_branch_and_tree('tree')391 tree = self.make_branch_and_tree('tree')
334 self.build_tree_contents([('tree/foo', LINES_AJ)])392 tree.lock_write()
335 tree.add('foo', 'foo-id')393 try:
336 tree.commit('added foo')394 self.build_tree_contents([('tree/foo', LINES_AJ)])
337 self.build_tree_contents([('tree/foo', LINES_ZY)])395 tree.add('foo', 'foo-id')
338 shelf_ui.Shelver(tree, tree.basis_tree(), auto_apply=True,396 tree.commit('added foo')
339 auto=True).run()397 self.build_tree_contents([('tree/foo', LINES_ZY)])
398 shelf_ui.Shelver(tree, tree.basis_tree(), auto_apply=True,
399 auto=True).run()
400 finally:
401 tree.unlock()
340 return tree402 return tree
341403
342 def test_unshelve(self):404 def test_unshelve(self):
@@ -349,13 +411,22 @@
349411
350 def test_unshelve_args(self):412 def test_unshelve_args(self):
351 tree = self.create_tree_with_shelf()413 tree = self.create_tree_with_shelf()
352 shelf_ui.Unshelver.from_args(directory='tree').run()414 unshelver = shelf_ui.Unshelver.from_args(directory='tree')
415 try:
416 unshelver.run()
417 finally:
418 unshelver.tree.unlock()
353 self.assertFileEqual(LINES_ZY, 'tree/foo')419 self.assertFileEqual(LINES_ZY, 'tree/foo')
354 self.assertIs(None, tree.get_shelf_manager().last_shelf())420 self.assertIs(None, tree.get_shelf_manager().last_shelf())
355421
356 def test_unshelve_args_dry_run(self):422 def test_unshelve_args_dry_run(self):
357 tree = self.create_tree_with_shelf()423 tree = self.create_tree_with_shelf()
358 shelf_ui.Unshelver.from_args(directory='tree', action='dry-run').run()424 unshelver = shelf_ui.Unshelver.from_args(directory='tree',
425 action='dry-run')
426 try:
427 unshelver.run()
428 finally:
429 unshelver.tree.unlock()
359 self.assertFileEqual(LINES_AJ, 'tree/foo')430 self.assertFileEqual(LINES_AJ, 'tree/foo')
360 self.assertEqual(1, tree.get_shelf_manager().last_shelf())431 self.assertEqual(1, tree.get_shelf_manager().last_shelf())
361432
@@ -369,7 +440,10 @@
369 shelf_file.close()440 shelf_file.close()
370 unshelver = shelf_ui.Unshelver.from_args(directory='tree',441 unshelver = shelf_ui.Unshelver.from_args(directory='tree',
371 action='delete-only')442 action='delete-only')
372 unshelver.run()443 try:
444 unshelver.run()
445 finally:
446 unshelver.tree.unlock()
373 self.assertIs(None, manager.last_shelf())447 self.assertIs(None, manager.last_shelf())
374448
375 def test_unshelve_args_invalid_shelf_id(self):449 def test_unshelve_args_invalid_shelf_id(self):

Subscribers

People subscribed via source and target branches