/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1711.8.5 by John Arbash Meinel
Move the new locking tests into their own files, and move the helper functions into a test helper.
1
# Copyright (C) 2006 Canonical Ltd
2
#
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Test locks across all branch implemenations"""
18
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
19
from bzrlib import errors
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
20
from bzrlib.branch import BzrBranchFormat4
2018.13.1 by Andrew Bennetts
Skip some branch_implementation tests that use branch.control_files if testing RemoteBranches.
21
from bzrlib.remote import RemoteBranchFormat
22
from bzrlib.tests import TestSkipped
1711.8.5 by John Arbash Meinel
Move the new locking tests into their own files, and move the helper functions into a test helper.
23
from bzrlib.tests.branch_implementations.test_branch import TestCaseWithBranch
1711.8.7 by John Arbash Meinel
Renaming LockHelpers.py to lock_helpers.py
24
from bzrlib.tests.lock_helpers import TestPreventLocking, LockWrapper
1711.8.5 by John Arbash Meinel
Move the new locking tests into their own files, and move the helper functions into a test helper.
25
26
27
class TestBranchLocking(TestCaseWithBranch):
28
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
29
    def setUp(self):
30
        TestCaseWithBranch.setUp(self)
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
31
        self.reduceLockdirTimeout()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
32
1711.8.5 by John Arbash Meinel
Move the new locking tests into their own files, and move the helper functions into a test helper.
33
    def get_instrumented_branch(self):
34
        """Get a Branch object which has been instrumented"""
35
        # TODO: jam 20060630 It may be that not all formats have a 
36
        # 'control_files' member. So we should fail gracefully if
37
        # not there. But assuming it has them lets us test the exact 
38
        # lock/unlock order.
2018.13.1 by Andrew Bennetts
Skip some branch_implementation tests that use branch.control_files if testing RemoteBranches.
39
        if isinstance(self.branch_format, RemoteBranchFormat):
40
            raise TestSkipped(
41
                "RemoteBranches don't have 'control_files'.")
1711.8.5 by John Arbash Meinel
Move the new locking tests into their own files, and move the helper functions into a test helper.
42
        self.locks = []
43
        b = LockWrapper(self.locks, self.get_branch(), 'b')
44
        b.repository = LockWrapper(self.locks, b.repository, 'r')
45
        bcf = b.control_files
46
        rcf = b.repository.control_files
47
48
        # Look out for branch types that reuse their control files
49
        self.combined_control = bcf is rcf
50
51
        b.control_files = LockWrapper(self.locks, b.control_files, 'bc')
52
        b.repository.control_files = \
53
            LockWrapper(self.locks, b.repository.control_files, 'rc')
54
        return b
55
56
    def test_01_lock_read(self):
57
        # Test that locking occurs in the correct order
58
        b = self.get_instrumented_branch()
59
60
        self.assertFalse(b.is_locked())
61
        self.assertFalse(b.repository.is_locked())
62
        b.lock_read()
63
        try:
64
            self.assertTrue(b.is_locked())
65
            self.assertTrue(b.repository.is_locked())
66
        finally:
67
            b.unlock()
68
        self.assertFalse(b.is_locked())
69
        self.assertFalse(b.repository.is_locked())
70
71
        self.assertEqual([('b', 'lr', True),
72
                          ('r', 'lr', True),
73
                          ('rc', 'lr', True),
74
                          ('bc', 'lr', True),
75
                          ('b', 'ul', True),
76
                          ('bc', 'ul', True),
77
                          ('r', 'ul', True),
78
                          ('rc', 'ul', True),
79
                         ], self.locks)
80
81
    def test_02_lock_write(self):
82
        # Test that locking occurs in the correct order
83
        b = self.get_instrumented_branch()
84
85
        self.assertFalse(b.is_locked())
86
        self.assertFalse(b.repository.is_locked())
87
        b.lock_write()
88
        try:
89
            self.assertTrue(b.is_locked())
90
            self.assertTrue(b.repository.is_locked())
91
        finally:
92
            b.unlock()
93
        self.assertFalse(b.is_locked())
94
        self.assertFalse(b.repository.is_locked())
95
96
        self.assertEqual([('b', 'lw', True),
97
                          ('r', 'lw', True),
98
                          ('rc', 'lw', True),
99
                          ('bc', 'lw', True),
100
                          ('b', 'ul', True),
101
                          ('bc', 'ul', True),
102
                          ('r', 'ul', True),
103
                          ('rc', 'ul', True),
104
                         ], self.locks)
105
106
    def test_03_lock_fail_unlock_repo(self):
107
        # Make sure branch.unlock() is called, even if there is a
108
        # failure while unlocking the repository.
109
        b = self.get_instrumented_branch()
110
        b.repository.disable_unlock()
111
112
        self.assertFalse(b.is_locked())
113
        self.assertFalse(b.repository.is_locked())
114
        b.lock_write()
115
        try:
116
            self.assertTrue(b.is_locked())
117
            self.assertTrue(b.repository.is_locked())
118
            self.assertRaises(TestPreventLocking, b.unlock)
119
            if self.combined_control:
120
                self.assertTrue(b.is_locked())
121
            else:
122
                self.assertFalse(b.is_locked())
123
            self.assertTrue(b.repository.is_locked())
124
125
            # We unlock the branch control files, even if 
126
            # we fail to unlock the repository
127
            self.assertEqual([('b', 'lw', True),
128
                              ('r', 'lw', True),
129
                              ('rc', 'lw', True),
130
                              ('bc', 'lw', True),
131
                              ('b', 'ul', True),
132
                              ('bc', 'ul', True),
133
                              ('r', 'ul', False), 
134
                             ], self.locks)
135
136
        finally:
137
            # For cleanup purposes, make sure we are unlocked
138
            b.repository._other.unlock()
139
140
    def test_04_lock_fail_unlock_control(self):
141
        # Make sure repository.unlock() is called, if we fail to unlock self
142
        b = self.get_instrumented_branch()
143
        b.control_files.disable_unlock()
144
145
        self.assertFalse(b.is_locked())
146
        self.assertFalse(b.repository.is_locked())
147
        b.lock_write()
148
        try:
149
            self.assertTrue(b.is_locked())
150
            self.assertTrue(b.repository.is_locked())
151
            self.assertRaises(TestPreventLocking, b.unlock)
152
            self.assertTrue(b.is_locked())
153
            if self.combined_control:
154
                self.assertTrue(b.repository.is_locked())
155
            else:
156
                self.assertFalse(b.repository.is_locked())
157
158
            # We unlock the repository even if 
159
            # we fail to unlock the control files
160
            self.assertEqual([('b', 'lw', True),
161
                              ('r', 'lw', True),
162
                              ('rc', 'lw', True),
163
                              ('bc', 'lw', True),
164
                              ('b', 'ul', True),
165
                              ('bc', 'ul', False),
166
                              ('r', 'ul', True), 
167
                              ('rc', 'ul', True), 
168
                             ], self.locks)
169
170
        finally:
171
            # For cleanup purposes, make sure we are unlocked
172
            b.control_files._other.unlock()
173
174
    def test_05_lock_read_fail_repo(self):
175
        # Test that the branch is not locked if it cannot lock the repository
176
        b = self.get_instrumented_branch()
177
        b.repository.disable_lock_read()
178
179
        self.assertRaises(TestPreventLocking, b.lock_read)
180
        self.assertFalse(b.is_locked())
181
        self.assertFalse(b.repository.is_locked())
182
183
        self.assertEqual([('b', 'lr', True),
184
                          ('r', 'lr', False), 
185
                         ], self.locks)
186
187
    def test_06_lock_write_fail_repo(self):
188
        # Test that the branch is not locked if it cannot lock the repository
189
        b = self.get_instrumented_branch()
190
        b.repository.disable_lock_write()
191
192
        self.assertRaises(TestPreventLocking, b.lock_write)
193
        self.assertFalse(b.is_locked())
194
        self.assertFalse(b.repository.is_locked())
195
196
        self.assertEqual([('b', 'lw', True),
197
                          ('r', 'lw', False), 
198
                         ], self.locks)
199
200
    def test_07_lock_read_fail_control(self):
201
        # Test the repository is unlocked if we can't lock self
202
        b = self.get_instrumented_branch()
203
        b.control_files.disable_lock_read()
204
205
        self.assertRaises(TestPreventLocking, b.lock_read)
206
        self.assertFalse(b.is_locked())
207
        self.assertFalse(b.repository.is_locked())
208
209
        self.assertEqual([('b', 'lr', True),
210
                          ('r', 'lr', True),
211
                          ('rc', 'lr', True),
212
                          ('bc', 'lr', False),
213
                          ('r', 'ul', True),
214
                          ('rc', 'ul', True),
215
                         ], self.locks)
216
217
    def test_08_lock_write_fail_control(self):
218
        # Test the repository is unlocked if we can't lock self
219
        b = self.get_instrumented_branch()
220
        b.control_files.disable_lock_write()
221
222
        self.assertRaises(TestPreventLocking, b.lock_write)
223
        self.assertFalse(b.is_locked())
224
        self.assertFalse(b.repository.is_locked())
225
226
        self.assertEqual([('b', 'lw', True),
227
                          ('r', 'lw', True),
228
                          ('rc', 'lw', True),
229
                          ('bc', 'lw', False),
230
                          ('r', 'ul', True),
231
                          ('rc', 'ul', True),
232
                         ], self.locks)
233
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
234
    def test_lock_write_returns_None_refuses_tokens(self):
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
235
        branch = self.make_branch('b')
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
236
        tokens = branch.lock_write()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
237
        try:
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
238
            if tokens is not None:
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
239
                # This test does not apply, because this lockable supports
240
                # tokens.
241
                return
242
            self.assertRaises(errors.TokenLockingNotSupported,
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
243
                              branch.lock_write, tokens=('token','token'))
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
244
        finally:
245
            branch.unlock()
246
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
247
    def test_reentering_lock_write_raises_on_token_mismatch(self):
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
248
        branch = self.make_branch('b')
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
249
        tokens = branch.lock_write()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
250
        try:
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
251
            if tokens is None:
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
252
                # This test does not apply, because this lockable refuses
253
                # tokens.
254
                return
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
255
            branch_token, repo_token = tokens
256
            different_branch_token = branch_token + 'xxx'
257
            different_repo_token = repo_token + 'xxx'
258
            # Re-using the same lockable instance with a different branch token
259
            # will raise TokenMismatch.
260
            self.assertRaises(errors.TokenMismatch,
261
                              branch.lock_write,
262
                              tokens=(different_branch_token, repo_token))
263
            # Similarly for a different repository token.
264
            self.assertRaises(errors.TokenMismatch,
265
                              branch.lock_write,
266
                              tokens=(branch_token, different_repo_token))
267
        finally:
268
            branch.unlock()
269
270
    def test_lock_write_with_nonmatching_token(self):
271
        branch = self.make_branch('b')
272
        tokens = branch.lock_write()
273
        try:
274
            if tokens is None:
275
                # This test does not apply, because this branch refuses
276
                # tokens.
277
                return
278
            branch_token, repo_token = tokens
279
            different_branch_token = branch_token + 'xxx'
280
            different_repo_token = repo_token + 'xxx'
281
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
282
            new_branch = branch.bzrdir.open_branch()
283
            # We only want to test the relocking abilities of branch, so use the
284
            # existing repository object which is already locked.
285
            new_branch.repository = branch.repository
286
            self.assertRaises(errors.TokenMismatch,
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
287
                              new_branch.lock_write,
288
                              tokens=(different_branch_token, repo_token))
289
            self.assertRaises(errors.TokenMismatch,
290
                              new_branch.lock_write,
291
                              tokens=(branch_token, different_repo_token))
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
292
        finally:
293
            branch.unlock()
294
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
295
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
296
    def test_lock_write_with_matching_token(self):
297
        """Test that a branch can be locked with a token, if it is already
298
        locked by that token."""
299
        branch = self.make_branch('b')
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
300
        tokens = branch.lock_write()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
301
        try:
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
302
            if tokens is None:
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
303
                # This test does not apply, because this branch refuses tokens.
304
                return
305
            # The same instance will accept a second lock_write if the specified
306
            # token matches.
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
307
            branch.lock_write(tokens=tokens)
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
308
            branch.unlock()
309
            # Calling lock_write on a new instance for the same lockable will
310
            # also succeed.
311
            new_branch = branch.bzrdir.open_branch()
312
            # We only want to test the relocking abilities of branch, so use the
313
            # existing repository object which is already locked.
314
            new_branch.repository = branch.repository
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
315
            new_branch.lock_write(tokens=tokens)
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
316
            new_branch.unlock()
317
        finally:
318
            branch.unlock()
319
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
320
    def test_unlock_after_lock_write_with_tokens(self):
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
321
        # If lock_write did not physically acquire the lock (because it was
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
322
        # passed some tokens), then unlock should not physically release it.
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
323
        branch = self.make_branch('b')
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
324
        tokens = branch.lock_write()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
325
        try:
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
326
            if tokens is None:
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
327
                # This test does not apply, because this lockable refuses
328
                # tokens.
329
                return
330
            new_branch = branch.bzrdir.open_branch()
331
            # We only want to test the relocking abilities of branch, so use the
332
            # existing repository object which is already locked.
333
            new_branch.repository = branch.repository
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
334
            new_branch.lock_write(tokens=tokens)
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
335
            new_branch.unlock()
336
            self.assertTrue(branch.get_physical_lock_status()) #XXX
337
        finally:
338
            branch.unlock()
339
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
340
    def test_lock_write_with_tokens_fails_when_unlocked(self):
341
        # Lock and unlock to get superficially valid tokens.  This mimics a
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
342
        # likely programming error, where a caller accidentally tries to lock
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
343
        # with tokens that are no longer valid (because the original lock was
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
344
        # released).
345
        branch = self.make_branch('b')
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
346
        tokens = branch.lock_write()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
347
        branch.unlock()
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
348
        if tokens is None:
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
349
            # This test does not apply, because this lockable refuses
350
            # tokens.
351
            return
352
353
        self.assertRaises(errors.TokenMismatch,
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
354
                          branch.lock_write, tokens=tokens)
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
355
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
356
    def test_lock_write_reenter_with_tokens(self):
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
357
        branch = self.make_branch('b')
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
358
        tokens = branch.lock_write()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
359
        try:
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
360
            if tokens is None:
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
361
                # This test does not apply, because this lockable refuses
362
                # tokens.
363
                return
364
            # Relock with a token.
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
365
            branch.lock_write(tokens=tokens)
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
366
            branch.unlock()
367
        finally:
368
            branch.unlock()
369
        # The lock should be unlocked on disk.  Verify that with a new lock
370
        # instance.
371
        new_branch = branch.bzrdir.open_branch()
372
        # Calling lock_write now should work, rather than raise LockContention.
373
        new_branch.lock_write()
374
        new_branch.unlock()
375
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
376
    def test_leave_lock_in_place(self):
377
        branch = self.make_branch('b')
378
        # Lock the branch, then use leave_lock_in_place so that when we
379
        # unlock the branch the lock is still held on disk.
380
        tokens = branch.lock_write()
381
        try:
382
            if tokens is None:
383
                # This test does not apply, because this repository refuses lock
384
                # tokens.
385
                self.assertRaises(NotImplementedError, branch.leave_lock_in_place)
386
                return
387
            branch.leave_lock_in_place()
388
        finally:
389
            branch.unlock()
390
        # We should be unable to relock the repo.
391
        self.assertRaises(errors.LockContention, branch.lock_write)
392
393
    def test_dont_leave_lock_in_place(self):
394
        branch = self.make_branch('b')
395
        # Create a lock on disk.
396
        tokens = branch.lock_write()
397
        try:
398
            if tokens is None:
399
                # This test does not apply, because this branch refuses lock
400
                # tokens.
401
                self.assertRaises(NotImplementedError,
402
                                  branch.dont_leave_lock_in_place)
403
                return
404
            try:
405
                branch.leave_lock_in_place()
406
            except NotImplementedError:
407
                # This branch doesn't support this API.
408
                return
409
            branch.repository.leave_lock_in_place()
410
        finally:
411
            branch.unlock()
412
        # Reacquire the lock (with a different branch object) by using the
413
        # tokens.
414
        new_branch = branch.bzrdir.open_branch()
415
        new_branch.lock_write(tokens=tokens)
416
        # Call dont_leave_lock_in_place, so that the lock will be released by
417
        # this instance, even though the lock wasn't originally acquired by it.
418
        new_branch.dont_leave_lock_in_place()
419
        new_branch.repository.dont_leave_lock_in_place()
420
        new_branch.unlock()
2018.15.1 by Andrew Bennetts
All branch_implementations/test_locking tests passing.
421
        # Now the branch (and repository) is unlocked.  Test this by locking it
422
        # without tokens.
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
423
        branch.lock_write()
424
        branch.unlock()
425
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
426
    def test_lock_read_then_unlock(self):
427
        # Calling lock_read then unlocking should work without errors.
428
        branch = self.make_branch('b')
429
        branch.lock_read()
430
        branch.unlock()
431
432
    def test_lock_write_locks_repo_too(self):
433
        if isinstance(self.branch_format, BzrBranchFormat4):
434
            # Branch format 4 is combined with the repository, so this test
435
            # doesn't apply.
436
            return
437
        branch = self.make_branch('b')
438
        branch = branch.bzrdir.open_branch()
439
        branch.lock_write()
440
        try:
441
            # Now the branch.repository is locked, so we can't lock it with a new
442
            # repository without a token.
443
            new_repo = branch.bzrdir.open_repository()
444
            self.assertRaises(errors.LockContention, new_repo.lock_write)
445
            # We can call lock_write on the original repository object though,
446
            # because it is already locked.
447
            branch.repository.lock_write()
448
            branch.repository.unlock()
449
        finally:
450
            branch.unlock()
451
452
    #def test_lock_read_locks_repo_too(self):
453
    #    branch = self.make_branch('b')
454
455
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
456