1
# Copyright (C) 2008 Canonical Ltd
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.
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.
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
17
"""Tests for Branch.get_stacked_on_url and set_stacked_on_url."""
23
from bzrlib.revision import NULL_REVISION
24
from bzrlib.tests import TestNotApplicable, KnownFailure
25
from bzrlib.tests.branch_implementations import TestCaseWithBranch
28
class TestStacking(TestCaseWithBranch):
30
def test_get_set_stacked_on_url(self):
31
# branches must either:
32
# raise UnstackableBranchFormat or
33
# raise UnstackableRepositoryFormat or
34
# permit stacking to be done and then return the stacked location.
35
branch = self.make_branch('branch')
36
target = self.make_branch('target')
38
errors.UnstackableBranchFormat,
39
errors.UnstackableRepositoryFormat,
42
branch.set_stacked_on_url(target.base)
43
except old_format_errors:
44
# if the set failed, so must the get
45
self.assertRaises(old_format_errors, branch.get_stacked_on_url)
47
# now we have a stacked branch:
48
self.assertEqual(target.base, branch.get_stacked_on_url())
49
branch.set_stacked_on_url(None)
50
self.assertRaises(errors.NotStacked, branch.get_stacked_on_url)
52
def test_get_set_stacked_on_relative(self):
53
# Branches can be stacked on other branches using relative paths.
54
branch = self.make_branch('branch')
55
target = self.make_branch('target')
57
errors.UnstackableBranchFormat,
58
errors.UnstackableRepositoryFormat,
61
branch.set_stacked_on_url('../target')
62
except old_format_errors:
63
# if the set failed, so must the get
64
self.assertRaises(old_format_errors, branch.get_stacked_on_url)
66
self.assertEqual('../target', branch.get_stacked_on_url())
68
def assertRevisionInRepository(self, repo_path, revid):
69
"""Check that a revision is in a repository, disregarding stacking."""
70
repo = bzrdir.BzrDir.open(repo_path).open_repository()
71
self.assertTrue(repo.has_revision(revid))
73
def assertRevisionNotInRepository(self, repo_path, revid):
74
"""Check that a revision is not in a repository, disregarding stacking."""
75
repo = bzrdir.BzrDir.open(repo_path).open_repository()
76
self.assertFalse(repo.has_revision(revid))
78
def test_get_graph_stacked(self):
79
"""A stacked repository shows the graph of its parent."""
80
trunk_tree = self.make_branch_and_tree('mainline')
81
trunk_revid = trunk_tree.commit('mainline')
82
# make a new branch, and stack on the existing one. we don't use
83
# sprout(stacked=True) here because if that is buggy and copies data
84
# it would cause a false pass of this test.
85
new_branch = self.make_branch('new_branch')
87
new_branch.set_stacked_on_url(trunk_tree.branch.base)
88
except (errors.UnstackableBranchFormat,
89
errors.UnstackableRepositoryFormat), e:
90
raise TestNotApplicable(e)
91
# reading the graph from the stacked branch's repository should see
92
# data from the stacked-on branch
93
new_repo = new_branch.repository
96
self.assertEqual(new_repo.get_parent_map([trunk_revid]),
97
{trunk_revid: (NULL_REVISION, )})
101
def test_sprout_stacked(self):
103
trunk_tree = self.make_branch_and_tree('mainline')
104
trunk_revid = trunk_tree.commit('mainline')
105
# and make branch from it which is stacked
107
new_dir = trunk_tree.bzrdir.sprout('newbranch', stacked=True)
108
except (errors.UnstackableBranchFormat,
109
errors.UnstackableRepositoryFormat), e:
110
raise TestNotApplicable(e)
112
self.assertRevisionNotInRepository('newbranch', trunk_revid)
113
new_tree = new_dir.open_workingtree()
114
new_branch_revid = new_tree.commit('something local')
115
self.assertRevisionNotInRepository('mainline', new_branch_revid)
116
self.assertRevisionInRepository('newbranch', new_branch_revid)
118
def test_unstack_fetches(self):
119
"""Removing the stacked-on branch pulls across all data"""
121
trunk_tree = self.make_branch_and_tree('mainline')
122
trunk_revid = trunk_tree.commit('revision on mainline')
123
# and make branch from it which is stacked
125
new_dir = trunk_tree.bzrdir.sprout('newbranch', stacked=True)
126
except (errors.UnstackableBranchFormat,
127
errors.UnstackableRepositoryFormat), e:
128
raise TestNotApplicable(e)
130
self.assertRevisionNotInRepository('newbranch', trunk_revid)
131
# now when we unstack that should implicitly fetch, to make sure that
132
# the branch will still work
133
new_branch = new_dir.open_branch()
134
new_branch.set_stacked_on_url(None)
135
self.assertRevisionInRepository('newbranch', trunk_revid)
136
# of course it's still in the mainline
137
self.assertRevisionInRepository('mainline', trunk_revid)
138
# and now we're no longer stacked
139
self.assertRaises(errors.NotStacked,
140
new_branch.get_stacked_on_url)
142
def make_stacked_bzrdir(self, in_directory=None):
143
"""Create a stacked branch and return its bzrdir.
145
:param in_directory: If not None, create a directory of this
146
name and create the stacking and stacked-on bzrdirs in
149
if in_directory is not None:
150
self.get_transport().mkdir(in_directory)
151
prefix = in_directory + '/'
154
tree = self.make_branch_and_tree(prefix + 'stacked-on')
155
tree.commit('Added foo')
156
stacked_bzrdir = tree.branch.bzrdir.sprout(
157
prefix + 'stacked', tree.branch.last_revision(), stacked=True)
158
return stacked_bzrdir
160
def test_clone_from_stacked_branch_preserve_stacking(self):
161
# We can clone from the bzrdir of a stacked branch. If
162
# preserve_stacking is True, the cloned branch is stacked on the
163
# same branch as the original.
165
stacked_bzrdir = self.make_stacked_bzrdir()
166
except (errors.UnstackableBranchFormat,
167
errors.UnstackableRepositoryFormat), e:
168
# not a testable combination.
169
raise TestNotApplicable(e)
170
cloned_bzrdir = stacked_bzrdir.clone('cloned', preserve_stacking=True)
173
stacked_bzrdir.open_branch().get_stacked_on_url(),
174
cloned_bzrdir.open_branch().get_stacked_on_url())
175
except (errors.UnstackableBranchFormat,
176
errors.UnstackableRepositoryFormat):
179
def test_clone_from_branch_stacked_on_relative_url_preserve_stacking(self):
180
# If a branch's stacked-on url is relative, we can still clone
181
# from it with preserve_stacking True and get a branch stacked
182
# on an appropriately adjusted relative url.
184
stacked_bzrdir = self.make_stacked_bzrdir(in_directory='dir')
185
except (errors.UnstackableBranchFormat,
186
errors.UnstackableRepositoryFormat), e:
187
# not a testable combination.
188
raise TestNotApplicable(e)
189
stacked_bzrdir.open_branch().set_stacked_on_url('../stacked-on')
190
cloned_bzrdir = stacked_bzrdir.clone('cloned', preserve_stacking=True)
193
cloned_bzrdir.open_branch().get_stacked_on_url())
195
def test_clone_from_stacked_branch_no_preserve_stacking(self):
197
stacked_bzrdir = self.make_stacked_bzrdir()
198
except (errors.UnstackableBranchFormat,
199
errors.UnstackableRepositoryFormat), e:
200
# not a testable combination.
201
raise TestNotApplicable(e)
202
cloned_unstacked_bzrdir = stacked_bzrdir.clone('cloned-unstacked',
203
preserve_stacking=False)
204
unstacked_branch = cloned_unstacked_bzrdir.open_branch()
205
self.assertRaises((errors.NotStacked, errors.UnstackableBranchFormat),
206
unstacked_branch.get_stacked_on_url)
208
def test_no_op_preserve_stacking(self):
209
"""With no stacking, preserve_stacking should be a no-op."""
210
branch = self.make_branch('source')
211
cloned_bzrdir = branch.bzrdir.clone('cloned', preserve_stacking=True)
212
self.assertRaises((errors.NotStacked, errors.UnstackableBranchFormat),
213
cloned_bzrdir.open_branch().get_stacked_on_url)
215
def test_sprout_stacking_policy_handling(self):
216
"""Obey policy where possible, ignore otherwise."""
217
stack_on = self.make_branch('stack-on')
218
parent_bzrdir = self.make_bzrdir('.', format='default')
219
parent_bzrdir.get_config().set_default_stack_on('stack-on')
220
source = self.make_branch('source')
221
target = source.bzrdir.sprout('target').open_branch()
223
self.assertEqual('../stack-on', target.get_stacked_on_url())
224
except errors.UnstackableBranchFormat:
227
def test_clone_stacking_policy_handling(self):
228
"""Obey policy where possible, ignore otherwise."""
229
stack_on = self.make_branch('stack-on')
230
parent_bzrdir = self.make_bzrdir('.', format='default')
231
parent_bzrdir.get_config().set_default_stack_on('stack-on')
232
source = self.make_branch('source')
233
target = source.bzrdir.clone('target').open_branch()
235
self.assertEqual('../stack-on', target.get_stacked_on_url())
236
except errors.UnstackableBranchFormat:
239
def prepare_stacked_on_fetch(self):
240
stack_on = self.make_branch_and_tree('stack-on')
241
stack_on.commit('first commit', rev_id='rev1')
243
stacked_dir = stack_on.bzrdir.sprout('stacked', stacked=True)
244
except (errors.UnstackableRepositoryFormat,
245
errors.UnstackableBranchFormat):
246
raise TestNotApplicable('Format does not support stacking.')
247
unstacked = self.make_repository('unstacked')
248
return stacked_dir.open_workingtree(), unstacked
250
def test_fetch_copies_from_stacked_on(self):
251
stacked, unstacked = self.prepare_stacked_on_fetch()
252
unstacked.fetch(stacked.branch.repository, 'rev1')
253
unstacked.get_revision('rev1')
255
def test_fetch_copies_from_stacked_on_and_stacked(self):
256
stacked, unstacked = self.prepare_stacked_on_fetch()
257
stacked.commit('second commit', rev_id='rev2')
258
unstacked.fetch(stacked.branch.repository, 'rev2')
259
unstacked.get_revision('rev1')
260
unstacked.get_revision('rev2')
262
def test_autopack_when_stacked(self):
263
# in bzr.dev as of 20080730, autopack was reported to fail in stacked
264
# repositories because of problems with text deltas spanning physical
265
# repository boundaries. however, i didn't actually get this test to
266
# fail on that code. -- mbp
267
# see https://bugs.launchpad.net/bzr/+bug/252821
268
if not self.branch_format.supports_stacking():
269
raise TestNotApplicable("%r does not support stacking"
270
% self.branch_format)
271
stack_on = self.make_branch_and_tree('stack-on')
272
text_lines = ['line %d blah blah blah\n' % i for i in range(20)]
273
self.build_tree_contents([('stack-on/a', ''.join(text_lines))])
275
stack_on.commit('base commit')
276
stacked_dir = stack_on.bzrdir.sprout('stacked', stacked=True)
277
stacked_tree = stacked_dir.open_workingtree()
279
text_lines[0] = 'changed in %d\n' % i
280
self.build_tree_contents([('stacked/a', ''.join(text_lines))])
281
stacked_tree.commit('commit %d' % i)
282
stacked_tree.branch.repository.pack()
283
stacked_tree.branch.check()
285
def test_pull_delta_when_stacked(self):
286
if not self.branch_format.supports_stacking():
287
raise TestNotApplicable("%r does not support stacking"
288
% self.branch_format)
289
stack_on = self.make_branch_and_tree('stack-on')
290
text_lines = ['line %d blah blah blah\n' % i for i in range(20)]
291
self.build_tree_contents([('stack-on/a', ''.join(text_lines))])
293
stack_on.commit('base commit')
294
# make a stacked branch from the mainline
295
stacked_dir = stack_on.bzrdir.sprout('stacked', stacked=True)
296
stacked_tree = stacked_dir.open_workingtree()
297
# make a second non-stacked branch from the mainline
298
other_dir = stack_on.bzrdir.sprout('other')
299
other_tree = other_dir.open_workingtree()
300
text_lines[9] = 'changed in other\n'
301
self.build_tree_contents([('other/a', ''.join(text_lines))])
302
other_tree.commit('commit in other')
303
# this should have generated a delta; try to pull that across
304
# bug 252821 caused a RevisionNotPresent here...
305
stacked_tree.pull(other_tree.branch)
306
stacked_tree.branch.repository.pack()
307
stacked_tree.branch.check()
309
def test_fetch_revisions_with_file_changes(self):
310
# Fetching revisions including file changes into a stacked branch
311
# works without error.
312
# Make the source tree.
313
src_tree = self.make_branch_and_tree('src')
314
self.build_tree_contents([('src/a', 'content')])
316
src_tree.commit('first commit')
318
# Make the stacked-on branch.
319
src_tree.bzrdir.sprout('stacked-on')
321
# Make a branch stacked on it.
322
target = self.make_branch('target')
324
target.set_stacked_on_url('../stacked-on')
325
except (errors.UnstackableRepositoryFormat,
326
errors.UnstackableBranchFormat):
327
raise TestNotApplicable('Format does not support stacking.')
329
# Change the source branch.
330
self.build_tree_contents([('src/a', 'new content')])
331
src_tree.commit('second commit', rev_id='rev2')
333
# Fetch changes to the target.
334
target.fetch(src_tree.branch)
335
rtree = target.repository.revision_tree('rev2')
337
self.addCleanup(rtree.unlock)
338
self.assertEqual('new content', rtree.get_file_by_path('a').read())