13
13
# You should have received a copy of the GNU General Public License
14
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
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Tests for branch.push behaviour."""
19
from cStringIO import StringIO
21
from bzrlib import bzrdir, errors
22
32
from bzrlib.branch import Branch
23
33
from bzrlib.bzrdir import BzrDir
24
34
from bzrlib.memorytree import MemoryTree
25
from bzrlib.remote import RemoteBranch
26
35
from bzrlib.revision import NULL_REVISION
27
from bzrlib.tests import TestSkipped
36
from bzrlib.smart import client, server
37
from bzrlib.smart.repository import SmartServerRepositoryGetParentMap
28
38
from bzrlib.tests.branch_implementations.test_branch import TestCaseWithBranch
39
from bzrlib.transport import get_transport
29
40
from bzrlib.transport.local import LocalURLServer
32
43
class TestPush(TestCaseWithBranch):
34
45
def test_push_convergence_simple(self):
35
# when revisions are pushed, the left-most accessible parents must
46
# when revisions are pushed, the left-most accessible parents must
36
47
# become the revision-history.
37
48
mine = self.make_branch_and_tree('mine')
38
49
mine.commit('1st post', rev_id='P1', allow_pointless=True)
154
165
self.assertEqual(tree.branch.last_revision(),
155
166
to_branch.last_revision())
168
def test_push_overwrite_of_non_tip_with_stop_revision(self):
169
"""Combining the stop_revision and overwrite options works.
171
This was <https://bugs.launchpad.net/bzr/+bug/234229>.
173
source = self.make_branch_and_tree('source')
174
target = self.make_branch('target')
176
source.commit('1st commit')
177
source.branch.push(target)
178
source.commit('2nd commit', rev_id='rev-2')
179
source.commit('3rd commit')
181
source.branch.push(target, stop_revision='rev-2', overwrite=True)
182
self.assertEqual('rev-2', target.last_revision())
184
def test_push_with_default_stacking_does_not_create_broken_branch(self):
185
"""Pushing a new standalone branch works even when there's a default
186
stacking policy at the destination.
188
The new branch will preserve the repo format (even if it isn't the
189
default for the branch), and will be stacked when the repo format
190
allows (which means that the branch format isn't necessarly preserved).
192
if isinstance(self.branch_format, branch.BzrBranchFormat4):
193
raise tests.TestNotApplicable('Not a metadir format.')
194
if isinstance(self.branch_format, branch.BranchReferenceFormat):
195
# This test could in principle apply to BranchReferenceFormat, but
196
# make_branch_builder doesn't support it.
197
raise tests.TestSkipped(
198
"BranchBuilder can't make reference branches.")
199
# Make a branch called "local" in a stackable repository
200
# The branch has 3 revisions:
201
# - rev-1, adds a file
202
# - rev-2, no changes
203
# - rev-3, modifies the file.
204
repo = self.make_repository('repo', shared=True, format='1.6')
205
builder = self.make_branch_builder('repo/local')
206
builder.start_series()
207
builder.build_snapshot('rev-1', None, [
208
('add', ('', 'root-id', 'directory', '')),
209
('add', ('filename', 'f-id', 'file', 'content\n'))])
210
builder.build_snapshot('rev-2', ['rev-1'], [])
211
builder.build_snapshot('rev-3', ['rev-2'],
212
[('modify', ('f-id', 'new-content\n'))])
213
builder.finish_series()
214
trunk = builder.get_branch()
215
# Sprout rev-1 to "trunk", so that we can stack on it.
216
trunk.bzrdir.sprout(self.get_url('trunk'), revision_id='rev-1')
217
# Set a default stacking policy so that new branches will automatically
219
self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
220
# Push rev-2 to a new branch "remote". It will be stacked on "trunk".
222
push._show_push_branch(trunk, 'rev-2', self.get_url('remote'), output)
223
# Push rev-3 onto "remote". If "remote" not stacked and is missing the
224
# fulltext record for f-id @ rev-1, then this will fail.
225
remote_branch = Branch.open(self.get_url('remote'))
226
trunk.push(remote_branch)
227
remote_branch.check()
229
def test_no_get_parent_map_after_insert_stream(self):
230
# Effort test for bug 331823
231
self.setup_smart_server_with_call_log()
232
# Make a local branch with four revisions. Four revisions because:
233
# one to push, one there for _walk_to_common_revisions to find, one we
234
# don't want to access, one for luck :)
235
if isinstance(self.branch_format, branch.BranchReferenceFormat):
236
# This test could in principle apply to BranchReferenceFormat, but
237
# make_branch_builder doesn't support it.
238
raise tests.TestSkipped(
239
"BranchBuilder can't make reference branches.")
241
builder = self.make_branch_builder('local')
242
except (errors.TransportNotPossible, errors.UninitializableFormat):
243
raise tests.TestNotApplicable('format not directly constructable')
244
builder.start_series()
245
builder.build_snapshot('first', None, [
246
('add', ('', 'root-id', 'directory', ''))])
247
builder.build_snapshot('second', ['first'], [])
248
builder.build_snapshot('third', ['second'], [])
249
builder.build_snapshot('fourth', ['third'], [])
250
builder.finish_series()
251
local = builder.get_branch()
252
local = branch.Branch.open(self.get_vfs_only_url('local'))
253
# Initial push of three revisions
254
remote_bzrdir = local.bzrdir.sprout(
255
self.get_url('remote'), revision_id='third')
256
remote = remote_bzrdir.open_branch()
257
# Push fourth revision
258
self.reset_smart_call_log()
259
self.disableOptimisticGetParentMap()
260
self.assertFalse(local.is_locked())
262
hpss_call_names = [item.call.method for item in self.hpss_calls]
263
self.assertTrue('Repository.insert_stream' in hpss_call_names)
264
insert_stream_idx = hpss_call_names.index('Repository.insert_stream')
265
calls_after_insert_stream = hpss_call_names[insert_stream_idx:]
266
# After inserting the stream the client has no reason to query the
267
# remote graph any further.
269
['Repository.insert_stream', 'Repository.insert_stream', 'get',
270
'Branch.set_last_revision_info', 'Branch.unlock'],
271
calls_after_insert_stream)
273
def disableOptimisticGetParentMap(self):
274
# Tweak some class variables to stop remote get_parent_map calls asking
275
# for or receiving more data than the caller asked for.
276
old_flag = SmartServerRepositoryGetParentMap.no_extra_results
277
inter_class = repository.InterRepository
278
old_batch_size = inter_class._walk_to_common_revisions_batch_size
279
inter_class._walk_to_common_revisions_batch_size = 1
280
SmartServerRepositoryGetParentMap.no_extra_results = True
282
SmartServerRepositoryGetParentMap.no_extra_results = old_flag
283
inter_class._walk_to_common_revisions_batch_size = old_batch_size
284
self.addCleanup(reset_values)
158
287
class TestPushHook(TestCaseWithBranch):
239
371
2, rev2, True, None, True)
376
class EmptyPushSmartEffortTests(TestCaseWithBranch):
377
"""Tests that a push of 0 revisions should make a limited number of smart
382
# Skip some scenarios that don't apply to these tests.
383
if (self.transport_server is not None and
384
issubclass(self.transport_server, server.SmartTCPServer)):
385
raise tests.TestNotApplicable(
386
'Does not apply when remote backing branch is also '
388
if isinstance(self.branch_format, branch.BzrBranchFormat4):
389
raise tests.TestNotApplicable(
390
'Branch format 4 is not usable via HPSS.')
391
super(EmptyPushSmartEffortTests, self).setUp()
392
# Create a smart server that publishes whatever the backing VFS server
394
self.smart_server = server.SmartTCPServer_for_testing()
395
self.smart_server.setUp(self.get_server())
396
self.addCleanup(self.smart_server.tearDown)
397
# Make two empty branches, 'empty' and 'target'.
398
self.empty_branch = self.make_branch('empty')
399
self.make_branch('target')
400
# Log all HPSS calls into self.hpss_calls.
401
client._SmartClient.hooks.install_named_hook(
402
'call', self.capture_hpss_call, None)
405
def capture_hpss_call(self, params):
406
self.hpss_calls.append(params.method)
408
def test_empty_branch_api(self):
409
"""The branch_obj.push API should make a limited number of HPSS calls.
411
transport = get_transport(self.smart_server.get_url()).clone('target')
412
target = Branch.open_from_transport(transport)
413
self.empty_branch.push(target)
416
'BzrDir.open_branchV2',
417
'BzrDir.find_repositoryV3',
418
'Branch.get_stacked_on_url',
420
'Branch.last_revision_info',
424
def test_empty_branch_command(self):
425
"""The 'bzr push' command should make a limited number of HPSS calls.
427
cmd = builtins.cmd_push()
428
cmd.outf = tests.StringIOWrapper()
430
directory=self.get_url() + 'empty',
431
location=self.smart_server.get_url() + 'target')
432
# HPSS calls as of 2008/09/22:
433
# [BzrDir.open, BzrDir.open_branch, BzrDir.find_repositoryV2,
434
# Branch.get_stacked_on_url, get, get, Branch.lock_write,
435
# Branch.last_revision_info, Branch.unlock]
436
self.assertTrue(len(self.hpss_calls) <= 9, self.hpss_calls)
439
class TestLossyPush(TestCaseWithBranch):
443
TestCaseWithBranch.setUp(self)
445
def test_lossy_push_raises_same_vcs(self):
446
target = self.make_branch('target')
447
source = self.make_branch('source')
448
self.assertRaises(errors.LossyPushToSameVCS, source.lossy_push, target)