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
22
from bzrlib import (
29
from bzrlib.branch import Branch
30
from bzrlib.bzrdir import BzrDir
31
from bzrlib.memorytree import MemoryTree
32
from bzrlib.revision import NULL_REVISION
33
from bzrlib.smart import client, server
34
from bzrlib.tests.branch_implementations.test_branch import TestCaseWithBranch
35
from bzrlib.transport import get_transport
36
from bzrlib.transport.local import LocalURLServer
39
class TestPush(TestCaseWithBranch):
36
from bzrlib.smart import (
39
repository as _mod_smart_repo,
41
from bzrlib.tests import (
47
class TestPush(per_branch.TestCaseWithBranch):
41
49
def test_push_convergence_simple(self):
42
# when revisions are pushed, the left-most accessible parents must
50
# when revisions are pushed, the left-most accessible parents must
43
51
# become the revision-history.
44
52
mine = self.make_branch_and_tree('mine')
45
53
mine.commit('1st post', rev_id='P1', allow_pointless=True)
177
186
source.branch.push(target, stop_revision='rev-2', overwrite=True)
178
187
self.assertEqual('rev-2', target.last_revision())
181
class TestPushHook(TestCaseWithBranch):
189
def test_push_with_default_stacking_does_not_create_broken_branch(self):
190
"""Pushing a new standalone branch works even when there's a default
191
stacking policy at the destination.
193
The new branch will preserve the repo format (even if it isn't the
194
default for the branch), and will be stacked when the repo format
195
allows (which means that the branch format isn't necessarly preserved).
197
if isinstance(self.branch_format, branch.BzrBranchFormat4):
198
raise tests.TestNotApplicable('Not a metadir format.')
199
if isinstance(self.branch_format, branch.BranchReferenceFormat):
200
# This test could in principle apply to BranchReferenceFormat, but
201
# make_branch_builder doesn't support it.
202
raise tests.TestSkipped(
203
"BranchBuilder can't make reference branches.")
204
# Make a branch called "local" in a stackable repository
205
# The branch has 3 revisions:
206
# - rev-1, adds a file
207
# - rev-2, no changes
208
# - rev-3, modifies the file.
209
repo = self.make_repository('repo', shared=True, format='1.6')
210
builder = self.make_branch_builder('repo/local')
211
builder.start_series()
212
builder.build_snapshot('rev-1', None, [
213
('add', ('', 'root-id', 'directory', '')),
214
('add', ('filename', 'f-id', 'file', 'content\n'))])
215
builder.build_snapshot('rev-2', ['rev-1'], [])
216
builder.build_snapshot('rev-3', ['rev-2'],
217
[('modify', ('f-id', 'new-content\n'))])
218
builder.finish_series()
219
trunk = builder.get_branch()
220
# Sprout rev-1 to "trunk", so that we can stack on it.
221
trunk.bzrdir.sprout(self.get_url('trunk'), revision_id='rev-1')
222
# Set a default stacking policy so that new branches will automatically
224
self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
225
# Push rev-2 to a new branch "remote". It will be stacked on "trunk".
227
push._show_push_branch(trunk, 'rev-2', self.get_url('remote'), output)
228
# Push rev-3 onto "remote". If "remote" not stacked and is missing the
229
# fulltext record for f-id @ rev-1, then this will fail.
230
remote_branch = branch.Branch.open(self.get_url('remote'))
231
trunk.push(remote_branch)
232
check.check_dwim(remote_branch.base, False, True, True)
234
def test_no_get_parent_map_after_insert_stream(self):
235
# Effort test for bug 331823
236
self.setup_smart_server_with_call_log()
237
# Make a local branch with four revisions. Four revisions because:
238
# one to push, one there for _walk_to_common_revisions to find, one we
239
# don't want to access, one for luck :)
240
if isinstance(self.branch_format, branch.BranchReferenceFormat):
241
# This test could in principle apply to BranchReferenceFormat, but
242
# make_branch_builder doesn't support it.
243
raise tests.TestSkipped(
244
"BranchBuilder can't make reference branches.")
246
builder = self.make_branch_builder('local')
247
except (errors.TransportNotPossible, errors.UninitializableFormat):
248
raise tests.TestNotApplicable('format not directly constructable')
249
builder.start_series()
250
builder.build_snapshot('first', None, [
251
('add', ('', 'root-id', 'directory', ''))])
252
builder.build_snapshot('second', ['first'], [])
253
builder.build_snapshot('third', ['second'], [])
254
builder.build_snapshot('fourth', ['third'], [])
255
builder.finish_series()
256
local = builder.get_branch()
257
local = branch.Branch.open(self.get_vfs_only_url('local'))
258
# Initial push of three revisions
259
remote_bzrdir = local.bzrdir.sprout(
260
self.get_url('remote'), revision_id='third')
261
remote = remote_bzrdir.open_branch()
262
# Push fourth revision
263
self.reset_smart_call_log()
264
self.disableOptimisticGetParentMap()
265
self.assertFalse(local.is_locked())
267
hpss_call_names = [item.call.method for item in self.hpss_calls]
268
self.assertTrue('Repository.insert_stream_1.19' in hpss_call_names)
269
insert_stream_idx = hpss_call_names.index(
270
'Repository.insert_stream_1.19')
271
calls_after_insert_stream = hpss_call_names[insert_stream_idx:]
272
# After inserting the stream the client has no reason to query the
273
# remote graph any further.
275
['Repository.insert_stream_1.19', 'Repository.insert_stream_1.19',
276
'get', 'Branch.set_last_revision_info', 'Branch.unlock'],
277
calls_after_insert_stream)
279
def disableOptimisticGetParentMap(self):
280
# Tweak some class variables to stop remote get_parent_map calls asking
281
# for or receiving more data than the caller asked for.
282
self.overrideAttr(repository.InterRepository,
283
'_walk_to_common_revisions_batch_size', 1)
284
self.overrideAttr(_mod_smart_repo.SmartServerRepositoryGetParentMap,
285
'no_extra_results', True)
288
class TestPushHook(per_branch.TestCaseWithBranch):
184
291
self.hook_calls = []
185
TestCaseWithBranch.setUp(self)
292
super(TestPushHook, self).setUp()
187
294
def capture_post_push_hook(self, result):
188
295
"""Capture post push hook calls to self.hook_calls.
190
297
The call is logged, as is some state of the two branches.
192
299
if result.local_branch:
206
313
def test_post_push_empty_history(self):
207
314
target = self.make_branch('target')
208
315
source = self.make_branch('source')
209
Branch.hooks.install_named_hook('post_push',
210
self.capture_post_push_hook, None)
316
branch.Branch.hooks.install_named_hook(
317
'post_push', self.capture_post_push_hook, None)
211
318
source.push(target)
212
319
# with nothing there we should still get a notification, and
213
320
# have both branches locked at the notification time.
214
321
self.assertEqual([
215
('post_push', source, None, target.base, 0, NULL_REVISION,
216
0, NULL_REVISION, True, None, True)
322
('post_push', source, None, target.base, 0, revision.NULL_REVISION,
323
0, revision.NULL_REVISION, True, None, True)
220
327
def test_post_push_bound_branch(self):
221
328
# pushing to a bound branch should pass in the master branch to the
222
329
# hook, allowing the correct number of emails to be sent, while still
223
# allowing hooks that want to modify the target to do so to both
330
# allowing hooks that want to modify the target to do so to both
225
332
target = self.make_branch('target')
226
333
local = self.make_branch('local')
232
339
# remotebranches can't be bound. Let's instead make a new local
233
340
# branch of the default type, which does allow binding.
234
341
# See https://bugs.launchpad.net/bzr/+bug/112020
235
local = BzrDir.create_branch_convenience('local2')
342
local = bzrdir.BzrDir.create_branch_convenience('local2')
236
343
local.bind(target)
237
344
source = self.make_branch('source')
238
Branch.hooks.install_named_hook('post_push',
239
self.capture_post_push_hook, None)
345
branch.Branch.hooks.install_named_hook(
346
'post_push', self.capture_post_push_hook, None)
240
347
source.push(local)
241
348
# with nothing there we should still get a notification, and
242
349
# have both branches locked at the notification time.
243
350
self.assertEqual([
244
('post_push', source, local.base, target.base, 0, NULL_REVISION,
245
0, NULL_REVISION, True, True, True)
351
('post_push', source, local.base, target.base, 0,
352
revision.NULL_REVISION, 0, revision.NULL_REVISION,