57
57
# when revisions are pushed, the left-most accessible parents must
58
58
# become the revision-history.
59
59
mine = self.make_from_branch_and_tree('mine')
60
mine.commit('1st post', rev_id='P1', allow_pointless=True)
61
other = self.sprout_to(mine.controldir, 'other').open_workingtree()
62
other.commit('my change', rev_id='M1', allow_pointless=True)
63
mine.merge_from_branch(other.branch)
64
mine.commit('merge my change', rev_id='P2')
60
mine.commit('1st post', allow_pointless=True)
62
other = self.sprout_to(mine.controldir, 'other').open_workingtree()
63
except errors.NoRoundtrippingSupport:
64
raise tests.TestNotApplicable(
65
'lossless push between %r and %r not supported' %
66
(self.branch_format_from, self.branch_format_to))
67
m1 = other.commit('my change', allow_pointless=True)
69
mine.merge_from_branch(other.branch)
70
except errors.NoRoundtrippingSupport:
71
raise tests.TestNotApplicable(
72
'lossless push between %r and %r not supported' %
73
(self.branch_format_from, self.branch_format_to))
74
p2 = mine.commit('merge my change')
65
75
result = mine.branch.push(other.branch)
66
self.assertEqual('P2', other.branch.last_revision())
76
self.assertEqual(p2, other.branch.last_revision())
67
77
# result object contains some structured data
68
self.assertEqual(result.old_revid, 'M1')
69
self.assertEqual(result.new_revid, 'P2')
78
self.assertEqual(result.old_revid, m1)
79
self.assertEqual(result.new_revid, p2)
71
81
def test_push_merged_indirect(self):
72
82
# it should be possible to do a push from one branch into another
74
84
# via a third branch - so its buried in the ancestry and is not
75
85
# directly accessible.
76
86
mine = self.make_from_branch_and_tree('mine')
77
mine.commit('1st post', rev_id='P1', allow_pointless=True)
78
target = self.sprout_to(mine.controldir, 'target').open_workingtree()
79
target.commit('my change', rev_id='M1', allow_pointless=True)
87
p1 = mine.commit('1st post', allow_pointless=True)
89
target = self.sprout_to(mine.controldir, 'target').open_workingtree()
90
except errors.NoRoundtrippingSupport:
91
raise tests.TestNotApplicable(
92
'lossless push between %r and %r not supported' %
93
(self.branch_format_from, self.branch_format_to))
94
m1 = target.commit('my change', allow_pointless=True)
80
95
other = self.sprout_to(mine.controldir, 'other').open_workingtree()
81
96
other.merge_from_branch(target.branch)
82
other.commit('merge my change', rev_id='O2')
83
mine.merge_from_branch(other.branch)
84
mine.commit('merge other', rev_id='P2')
97
o2 = other.commit('merge my change')
99
mine.merge_from_branch(other.branch)
100
except errors.NoRoundtrippingSupport:
101
raise tests.TestNotApplicable(
102
'lossless push between %r and %r not supported' %
103
(self.branch_format_from, self.branch_format_to))
104
p2 = mine.commit('merge other')
85
105
mine.branch.push(target.branch)
86
self.assertEqual('P2', target.branch.last_revision())
106
self.assertEqual(p2, target.branch.last_revision())
88
108
def test_push_to_checkout_updates_master(self):
89
109
"""Pushing into a checkout updates the checkout and the master branch"""
97
117
rev1 = checkout.commit('master')
99
other_bzrdir = self.sprout_from(master_tree.branch.controldir, 'other')
120
other_bzrdir = self.sprout_from(master_tree.branch.controldir, 'other')
121
except errors.NoRoundtrippingSupport:
122
raise tests.TestNotApplicable(
123
'lossless push between %r and %r not supported' %
124
(self.branch_format_from, self.branch_format_to))
100
125
other = other_bzrdir.open_workingtree()
101
126
rev2 = other.commit('other commit')
102
127
# now push, which should update both checkout and master.
130
155
source.add(['a'])
131
156
source.commit('a')
133
source.branch.lock_read()
159
with source.branch.lock_read(), target.lock_write():
137
160
source.branch.push(target, stop_revision=source.last_revision())
141
source.branch.unlock()
161
except errors.NoRoundtrippingSupport:
162
raise tests.TestNotApplicable(
163
'lossless push between %r and %r not supported' %
164
(self.branch_format_from, self.branch_format_to))
166
def test_push_uses_read_lock_lossy(self):
167
"""Push should only need a read lock on the source side."""
168
source = self.make_from_branch_and_tree('source')
169
target = self.make_to_branch('target')
171
self.build_tree(['source/a'])
176
with source.branch.lock_read(), target.lock_write():
177
source.branch.push(target, stop_revision=source.last_revision(), lossy=True)
178
except errors.LossyPushToSameVCS:
179
raise tests.TestNotApplicable(
180
'push between branches of same format')
143
182
def test_push_within_repository(self):
144
183
"""Push from one branch to another inside the same repository."""
174
213
to_branch = self.make_to_branch('repo/branch')
175
tree.branch.push(to_branch)
177
self.assertEqual(tree.branch.last_revision(),
178
to_branch.last_revision())
215
tree.branch.push(to_branch)
216
except errors.NoRoundtrippingSupport:
217
tree.branch.push(to_branch, lossy=True)
219
self.assertEqual(tree.branch.last_revision(),
220
to_branch.last_revision())
180
222
def test_push_overwrite_of_non_tip_with_stop_revision(self):
181
223
"""Combining the stop_revision and overwrite options works.
186
228
target = self.make_to_branch('target')
188
230
source.commit('1st commit')
189
source.branch.push(target)
190
source.commit('2nd commit', rev_id='rev-2')
232
source.branch.push(target)
233
except errors.NoRoundtrippingSupport:
234
raise tests.TestNotApplicable(
235
'lossless push between %r and %r not supported' %
236
(self.branch_format_from, self.branch_format_to))
237
rev2 = source.commit('2nd commit')
191
238
source.commit('3rd commit')
193
source.branch.push(target, stop_revision='rev-2', overwrite=True)
194
self.assertEqual('rev-2', target.last_revision())
240
source.branch.push(target, stop_revision=rev2, overwrite=True)
241
self.assertEqual(rev2, target.last_revision())
196
243
def test_push_with_default_stacking_does_not_create_broken_branch(self):
197
244
"""Pushing a new standalone branch works even when there's a default
212
259
# - rev-2, no changes
213
260
# - rev-3, modifies the file.
214
261
repo = self.make_repository('repo', shared=True, format='1.6')
215
builder = self.make_from_branch_builder('repo/local')
263
builder = self.make_from_branch_builder('repo/local')
264
except errors.UninitializableFormat:
265
raise tests.TestNotApplicable(
266
'BranchBuilder can not initialize some formats')
216
267
builder.start_series()
217
builder.build_snapshot('rev-1', None, [
218
('add', ('', 'root-id', 'directory', '')),
219
('add', ('filename', 'f-id', 'file', 'content\n'))])
220
builder.build_snapshot('rev-2', ['rev-1'], [])
221
builder.build_snapshot('rev-3', ['rev-2'],
222
[('modify', ('f-id', 'new-content\n'))])
268
revid1 = builder.build_snapshot(None, [
269
('add', ('', None, 'directory', '')),
270
('add', ('filename', None, 'file', 'content\n'))])
271
revid2 = builder.build_snapshot([revid1], [])
272
revid3 = builder.build_snapshot([revid2],
273
[('modify', ('filename', 'new-content\n'))])
223
274
builder.finish_series()
224
275
trunk = builder.get_branch()
225
276
# Sprout rev-1 to "trunk", so that we can stack on it.
226
trunk.controldir.sprout(self.get_url('trunk'), revision_id='rev-1')
277
trunk.controldir.sprout(self.get_url('trunk'), revision_id=revid1)
227
278
# Set a default stacking policy so that new branches will automatically
228
279
# stack on trunk.
229
280
self.make_controldir('.').get_config().set_default_stack_on('trunk')
230
281
# Push rev-2 to a new branch "remote". It will be stacked on "trunk".
231
282
output = BytesIO()
232
push._show_push_branch(trunk, 'rev-2', self.get_url('remote'), output)
283
push._show_push_branch(trunk, revid2, self.get_url('remote'), output)
233
284
# Push rev-3 onto "remote". If "remote" not stacked and is missing the
234
285
# fulltext record for f-id @ rev-1, then this will fail.
235
286
remote_branch = Branch.open(self.get_url('remote'))
252
303
except (errors.TransportNotPossible, errors.UninitializableFormat):
253
304
raise tests.TestNotApplicable('format not directly constructable')
254
305
builder.start_series()
255
builder.build_snapshot('first', None, [
256
('add', ('', 'root-id', 'directory', ''))])
257
builder.build_snapshot('second', ['first'], [])
258
builder.build_snapshot('third', ['second'], [])
259
builder.build_snapshot('fourth', ['third'], [])
306
first = builder.build_snapshot(None, [
307
('add', ('', None, 'directory', ''))])
308
second = builder.build_snapshot([first], [])
309
third = builder.build_snapshot([second], [])
310
fourth = builder.build_snapshot([third], [])
260
311
builder.finish_series()
261
312
local = branch.Branch.open(self.get_vfs_only_url('local'))
262
313
# Initial push of three revisions
263
314
remote_bzrdir = local.controldir.sprout(
264
self.get_url('remote'), revision_id='third')
315
self.get_url('remote'), revision_id=third)
265
316
remote = remote_bzrdir.open_branch()
317
if not remote.repository._format.supports_full_versioned_files:
318
raise tests.TestNotApplicable(
319
'remote is not a VersionedFile repository')
266
320
# Push fourth revision
267
321
self.reset_smart_call_log()
268
322
self.disableOptimisticGetParentMap()
269
323
self.assertFalse(local.is_locked())
270
324
local.push(remote)
271
325
hpss_call_names = [item.call.method for item in self.hpss_calls]
272
self.assertTrue('Repository.insert_stream_1.19' in hpss_call_names)
326
self.assertIn('Repository.insert_stream_1.19', hpss_call_names)
273
327
insert_stream_idx = hpss_call_names.index(
274
328
'Repository.insert_stream_1.19')
275
329
calls_after_insert_stream = hpss_call_names[insert_stream_idx:]