~bzr/ubuntu/lucid/bzr/beta-ppa

« back to all changes in this revision

Viewing changes to bzrlib/tests/per_branch/test_stacking.py

  • Committer: Martin Pool
  • Date: 2010-07-02 07:29:40 UTC
  • mfrom: (129.1.7 packaging-karmic)
  • Revision ID: mbp@sourcefrog.net-20100702072940-hpzq5elg8wjve8rh
* PPA rebuild.
* PPA rebuild for Karmic.
* PPA rebuild for Jaunty.
* PPA rebuild for Hardy.
* From postinst, actually remove the example bash completion scripts.
  (LP: #249452)
* New upstream release.
* New upstream release.
* New upstream release.
* Revert change to Build-depends: Dapper does not have python-central.
  Should be python-support..
* Target ppa..
* Target ppa..
* Target ppa..
* Target ppa..
* New upstream release.
* Switch to dpkg-source 3.0 (quilt) format.
* Bump standards version to 3.8.4.
* Remove embedded copy of python-configobj. Closes: #555336
* Remove embedded copy of python-elementtree. Closes: #555343
* Change section from 'Devel' to 'Vcs'..
* Change section from 'Devel' to 'Vcs'..
* Change section from 'Devel' to 'Vcs'..
* Change section from 'Devel' to 'Vcs'..
* Change section from 'Devel' to 'Vcs'..
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* debian/control: Fix obsolete-relation-form-in-source
  lintian warning. 
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Split out docs into bzr-doc package.
* New upstream release.
* Added John Francesco Ferlito to Uploaders.
* Fix install path to quick-reference guide
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Fix FTBFS due to path changes, again.
* Fix FTBFS due to doc paths changing
* New upstream release.
* Fix FTBFS due to path changes, again.
* Fix FTBFS due to doc paths changing
* New upstream release.
* Fix FTBFS due to path changes, again.
* Fix FTBFS due to doc paths changing
* New upstream release.
* Fix FTBFS due to path changes, again, again.
* Fix FTBFS due to path changes, again.
* Fix FTBFS due to path changes.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Bump standards version to 3.8.3.
* Remove unused patch system.
* New upstream release.
* New upstream release.
* New upstream release.
* Fix copy and paste tab error in .install file
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
 + Fixes compatibility with Python 2.4. Closes: #537708
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream version.
* Bump standards version to 3.8.2.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Add python-pyrex to build-deps to ensure C extensions are always build.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Split documentation into bzr-doc package. ((LP: #385074)
* Multiple packaging changes to make us more linitan clean.
* New upstream release.
* Split documentation into bzr-doc package. ((LP: #385074)
* Multiple packaging changes to make us more linitan clean.
* New upstream release.
* Split documentation into bzr-doc package. ((LP: #385074)
* Multiple packaging changes to make us more linitan clean.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Fix API compatibility version. (Closes: #526233)
* New upstream release.
  + Fixes default format for upgrade command. (Closes: #464688)
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Add missing dependency on zlib development library. (Closes:
  #523595)
* Add zlib build-depends.
* Add zlib build-depends.
* Add zlib build-depends.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Move to section vcs.
* Bump standards version to 3.8.1.
* New upstream release.
* Remove temporary patch for missing .c files from distribution
* New upstream release.
* Remove temporary patch for missing .c files from distribution
* New upstream release.
* Remove temporary patch for missing .c files from distribution
* Add temporary patch for missing .c files from distribution
* Add temporary patch for missing .c files from distribution
* Add temporary patch for missing .c files from distribution
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Recommend ca-certificates. (Closes: #452024)
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Update watch file. bazaar now uses launchpad to host its sources.
* Remove patch for inventory root revision copy, applied upstream.
* New upstream release.
* New upstream release.
* New upstream release
* Force removal of files installed in error to /etc/bash_completion.d/
  (LP: #249452)
* New upstream release.
* New upstream release
* New upstream release.
* Bump standards version.
* Include patch for inventory root revision copy, required for bzr-svn.
* New upstream release.
* Remove unused lintian overrides.
* Correct the package version not to be native.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Final 1.5 release.
* New upstream release.
* New upstream release.
* New upstream release.
* Add myself as a co-maintainer.
* Add a Dm-Upload-Allowed: yes header.
* New upstream bugfix release.
* New upstream release.
* Final 1.3 release.
* New upstream release.
* First release candidate of the upcoming 1.3 release.
* Rebuild to fix the problem caused by a build with a broken python-central.
* New upstream release.
* Rebuild for dapper PPA.
* Apply Lamont's patches to fix build-dependencies on dapper.
  (See: https://bugs.launchpad.net/bzr/+bug/189915)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2008 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Tests for Branch.get_stacked_on_url and set_stacked_on_url."""
 
18
 
 
19
from bzrlib import (
 
20
    branch,
 
21
    bzrdir,
 
22
    check,
 
23
    errors,
 
24
    )
 
25
from bzrlib.revision import NULL_REVISION
 
26
from bzrlib.smart import server
 
27
from bzrlib.tests import TestNotApplicable, KnownFailure, transport_util
 
28
from bzrlib.tests.per_branch import TestCaseWithBranch
 
29
from bzrlib.transport import get_transport
 
30
 
 
31
 
 
32
unstackable_format_errors = (
 
33
    errors.UnstackableBranchFormat,
 
34
    errors.UnstackableRepositoryFormat,
 
35
    )
 
36
 
 
37
 
 
38
class TestStacking(TestCaseWithBranch):
 
39
 
 
40
    def check_lines_added_or_present(self, stacked_branch, revid):
 
41
        # similar to a failure seen in bug 288751 by mbp 20081120
 
42
        stacked_repo = stacked_branch.repository
 
43
        stacked_repo.lock_read()
 
44
        try:
 
45
            list(stacked_repo.inventories.iter_lines_added_or_present_in_keys(
 
46
                    [(revid,)]))
 
47
        finally:
 
48
            stacked_repo.unlock()
 
49
 
 
50
    def test_get_set_stacked_on_url(self):
 
51
        # branches must either:
 
52
        # raise UnstackableBranchFormat or
 
53
        # raise UnstackableRepositoryFormat or
 
54
        # permit stacking to be done and then return the stacked location.
 
55
        branch = self.make_branch('branch')
 
56
        target = self.make_branch('target')
 
57
        try:
 
58
            branch.set_stacked_on_url(target.base)
 
59
        except unstackable_format_errors:
 
60
            # if the set failed, so must the get
 
61
            self.assertRaises(unstackable_format_errors, branch.get_stacked_on_url)
 
62
            self.assertFalse(branch._format.supports_stacking())
 
63
            return
 
64
        self.assertTrue(branch._format.supports_stacking())
 
65
        # now we have a stacked branch:
 
66
        self.assertEqual(target.base, branch.get_stacked_on_url())
 
67
        branch.set_stacked_on_url(None)
 
68
        self.assertRaises(errors.NotStacked, branch.get_stacked_on_url)
 
69
 
 
70
    def test_get_set_stacked_on_relative(self):
 
71
        # Branches can be stacked on other branches using relative paths.
 
72
        branch = self.make_branch('branch')
 
73
        target = self.make_branch('target')
 
74
        try:
 
75
            branch.set_stacked_on_url('../target')
 
76
        except unstackable_format_errors:
 
77
            # if the set failed, so must the get
 
78
            self.assertRaises(unstackable_format_errors, branch.get_stacked_on_url)
 
79
            return
 
80
        self.assertEqual('../target', branch.get_stacked_on_url())
 
81
 
 
82
    def test_set_stacked_on_same_branch_raises(self):
 
83
        # Stacking on the same branch silently raises and doesn't execute the
 
84
        # change. Reported in bug 376243.
 
85
        branch = self.make_branch('branch')
 
86
        try:
 
87
            self.assertRaises(errors.UnstackableLocationError,
 
88
                branch.set_stacked_on_url, '../branch')
 
89
        except unstackable_format_errors:
 
90
            # if the set failed, so must the get
 
91
            self.assertRaises(unstackable_format_errors, branch.get_stacked_on_url)
 
92
            return
 
93
        self.assertRaises(errors.NotStacked, branch.get_stacked_on_url)
 
94
 
 
95
    def test_set_stacked_on_same_branch_after_being_stacked_raises(self):
 
96
        # Stacking on the same branch silently raises and doesn't execute the
 
97
        # change.
 
98
        branch = self.make_branch('branch')
 
99
        target = self.make_branch('target')
 
100
        try:
 
101
            branch.set_stacked_on_url('../target')
 
102
        except unstackable_format_errors:
 
103
            # if the set failed, so must the get
 
104
            self.assertRaises(unstackable_format_errors, branch.get_stacked_on_url)
 
105
            return
 
106
        self.assertRaises(errors.UnstackableLocationError,
 
107
            branch.set_stacked_on_url, '../branch')
 
108
        self.assertEqual('../target', branch.get_stacked_on_url())
 
109
 
 
110
    def assertRevisionInRepository(self, repo_path, revid):
 
111
        """Check that a revision is in a repository, disregarding stacking."""
 
112
        repo = bzrdir.BzrDir.open(repo_path).open_repository()
 
113
        self.assertTrue(repo.has_revision(revid))
 
114
 
 
115
    def assertRevisionNotInRepository(self, repo_path, revid):
 
116
        """Check that a revision is not in a repository, disregarding stacking."""
 
117
        repo = bzrdir.BzrDir.open(repo_path).open_repository()
 
118
        self.assertFalse(repo.has_revision(revid))
 
119
 
 
120
    def test_get_graph_stacked(self):
 
121
        """A stacked repository shows the graph of its parent."""
 
122
        trunk_tree = self.make_branch_and_tree('mainline')
 
123
        trunk_revid = trunk_tree.commit('mainline')
 
124
        # make a new branch, and stack on the existing one.  we don't use
 
125
        # sprout(stacked=True) here because if that is buggy and copies data
 
126
        # it would cause a false pass of this test.
 
127
        new_branch = self.make_branch('new_branch')
 
128
        try:
 
129
            new_branch.set_stacked_on_url(trunk_tree.branch.base)
 
130
        except unstackable_format_errors, e:
 
131
            raise TestNotApplicable(e)
 
132
        # reading the graph from the stacked branch's repository should see
 
133
        # data from the stacked-on branch
 
134
        new_repo = new_branch.repository
 
135
        new_repo.lock_read()
 
136
        try:
 
137
            self.assertEqual(new_repo.get_parent_map([trunk_revid]),
 
138
                {trunk_revid: (NULL_REVISION, )})
 
139
        finally:
 
140
            new_repo.unlock()
 
141
 
 
142
    def test_sprout_stacked(self):
 
143
        # We have a mainline
 
144
        trunk_tree = self.make_branch_and_tree('mainline')
 
145
        trunk_revid = trunk_tree.commit('mainline')
 
146
        # and make branch from it which is stacked
 
147
        try:
 
148
            new_dir = trunk_tree.bzrdir.sprout('newbranch', stacked=True)
 
149
        except unstackable_format_errors, e:
 
150
            raise TestNotApplicable(e)
 
151
        # stacked repository
 
152
        self.assertRevisionNotInRepository('newbranch', trunk_revid)
 
153
        tree = new_dir.open_branch().create_checkout('local')
 
154
        new_branch_revid = tree.commit('something local')
 
155
        self.assertRevisionNotInRepository('mainline', new_branch_revid)
 
156
        self.assertRevisionInRepository('newbranch', new_branch_revid)
 
157
 
 
158
    def test_sprout_stacked_from_smart_server(self):
 
159
        if isinstance(self.branch_format, branch.BzrBranchFormat4):
 
160
            raise TestNotApplicable('Branch format 4 is not usable via HPSS.')
 
161
        # We have a mainline
 
162
        trunk_tree = self.make_branch_and_tree('mainline')
 
163
        trunk_revid = trunk_tree.commit('mainline')
 
164
        # Make sure that we can make a stacked branch from it
 
165
        try:
 
166
            trunk_tree.bzrdir.sprout('testbranch', stacked=True)
 
167
        except unstackable_format_errors, e:
 
168
            raise TestNotApplicable(e)
 
169
        # Now serve the original mainline from a smart server
 
170
        remote_transport = self.make_smart_server('mainline')
 
171
        remote_bzrdir = bzrdir.BzrDir.open_from_transport(remote_transport)
 
172
        # and make branch from the smart server which is stacked
 
173
        new_dir = remote_bzrdir.sprout('newbranch', stacked=True)
 
174
        # stacked repository
 
175
        self.assertRevisionNotInRepository('newbranch', trunk_revid)
 
176
        tree = new_dir.open_branch().create_checkout('local')
 
177
        new_branch_revid = tree.commit('something local')
 
178
        self.assertRevisionNotInRepository('mainline', new_branch_revid)
 
179
        self.assertRevisionInRepository('newbranch', new_branch_revid)
 
180
 
 
181
    def test_unstack_fetches(self):
 
182
        """Removing the stacked-on branch pulls across all data"""
 
183
        # We have a mainline
 
184
        trunk_tree = self.make_branch_and_tree('mainline')
 
185
        trunk_revid = trunk_tree.commit('revision on mainline')
 
186
        # and make branch from it which is stacked
 
187
        try:
 
188
            new_dir = trunk_tree.bzrdir.sprout(self.get_url('newbranch'),
 
189
                stacked=True)
 
190
        except unstackable_format_errors, e:
 
191
            raise TestNotApplicable(e)
 
192
        # stacked repository
 
193
        self.assertRevisionNotInRepository('newbranch', trunk_revid)
 
194
        # TODO: we'd like to commit in the stacked repository; that requires
 
195
        # some care (maybe a BranchBuilder) if it's remote and has no
 
196
        # workingtree
 
197
        ##newbranch_revid = new_dir.open_workingtree().commit('revision in '
 
198
            ##'newbranch')
 
199
        # now when we unstack that should implicitly fetch, to make sure that
 
200
        # the branch will still work
 
201
        new_branch = new_dir.open_branch()
 
202
        new_branch.set_stacked_on_url(None)
 
203
        self.assertRevisionInRepository('newbranch', trunk_revid)
 
204
        # of course it's still in the mainline
 
205
        self.assertRevisionInRepository('mainline', trunk_revid)
 
206
        # and now we're no longer stacked
 
207
        self.assertRaises(errors.NotStacked,
 
208
            new_branch.get_stacked_on_url)
 
209
 
 
210
    def make_stacked_bzrdir(self, in_directory=None):
 
211
        """Create a stacked branch and return its bzrdir.
 
212
 
 
213
        :param in_directory: If not None, create a directory of this
 
214
            name and create the stacking and stacked-on bzrdirs in
 
215
            this directory.
 
216
        """
 
217
        if in_directory is not None:
 
218
            self.get_transport().mkdir(in_directory)
 
219
            prefix = in_directory + '/'
 
220
        else:
 
221
            prefix = ''
 
222
        tree = self.make_branch_and_tree(prefix + 'stacked-on')
 
223
        tree.commit('Added foo')
 
224
        stacked_bzrdir = tree.branch.bzrdir.sprout(
 
225
            prefix + 'stacked', tree.branch.last_revision(), stacked=True)
 
226
        return stacked_bzrdir
 
227
 
 
228
    def test_clone_from_stacked_branch_preserve_stacking(self):
 
229
        # We can clone from the bzrdir of a stacked branch. If
 
230
        # preserve_stacking is True, the cloned branch is stacked on the
 
231
        # same branch as the original.
 
232
        try:
 
233
            stacked_bzrdir = self.make_stacked_bzrdir()
 
234
        except unstackable_format_errors, e:
 
235
            raise TestNotApplicable(e)
 
236
        cloned_bzrdir = stacked_bzrdir.clone('cloned', preserve_stacking=True)
 
237
        try:
 
238
            self.assertEqual(
 
239
                stacked_bzrdir.open_branch().get_stacked_on_url(),
 
240
                cloned_bzrdir.open_branch().get_stacked_on_url())
 
241
        except unstackable_format_errors, e:
 
242
            pass
 
243
 
 
244
    def test_clone_from_branch_stacked_on_relative_url_preserve_stacking(self):
 
245
        # If a branch's stacked-on url is relative, we can still clone
 
246
        # from it with preserve_stacking True and get a branch stacked
 
247
        # on an appropriately adjusted relative url.
 
248
        try:
 
249
            stacked_bzrdir = self.make_stacked_bzrdir(in_directory='dir')
 
250
        except unstackable_format_errors, e:
 
251
            raise TestNotApplicable(e)
 
252
        stacked_bzrdir.open_branch().set_stacked_on_url('../stacked-on')
 
253
        cloned_bzrdir = stacked_bzrdir.clone('cloned', preserve_stacking=True)
 
254
        self.assertEqual(
 
255
            '../dir/stacked-on',
 
256
            cloned_bzrdir.open_branch().get_stacked_on_url())
 
257
 
 
258
    def test_clone_from_stacked_branch_no_preserve_stacking(self):
 
259
        try:
 
260
            stacked_bzrdir = self.make_stacked_bzrdir()
 
261
        except unstackable_format_errors, e:
 
262
            # not a testable combination.
 
263
            raise TestNotApplicable(e)
 
264
        cloned_unstacked_bzrdir = stacked_bzrdir.clone('cloned-unstacked',
 
265
            preserve_stacking=False)
 
266
        unstacked_branch = cloned_unstacked_bzrdir.open_branch()
 
267
        self.assertRaises((errors.NotStacked, errors.UnstackableBranchFormat),
 
268
                          unstacked_branch.get_stacked_on_url)
 
269
 
 
270
    def test_no_op_preserve_stacking(self):
 
271
        """With no stacking, preserve_stacking should be a no-op."""
 
272
        branch = self.make_branch('source')
 
273
        cloned_bzrdir = branch.bzrdir.clone('cloned', preserve_stacking=True)
 
274
        self.assertRaises((errors.NotStacked, errors.UnstackableBranchFormat),
 
275
                          cloned_bzrdir.open_branch().get_stacked_on_url)
 
276
 
 
277
    def make_stacked_on_matching(self, source):
 
278
        if source.repository.supports_rich_root():
 
279
            if source.repository._format.supports_chks:
 
280
                format = "2a"
 
281
            else:
 
282
                format = "1.9-rich-root"
 
283
        else:
 
284
            format = "1.9"
 
285
        return self.make_branch('stack-on', format)
 
286
 
 
287
    def test_sprout_stacking_policy_handling(self):
 
288
        """Obey policy where possible, ignore otherwise."""
 
289
        if isinstance(self.branch_format, branch.BzrBranchFormat4):
 
290
            raise TestNotApplicable('Branch format 4 does not autoupgrade.')
 
291
        source = self.make_branch('source')
 
292
        stack_on = self.make_stacked_on_matching(source)
 
293
        parent_bzrdir = self.make_bzrdir('.', format='default')
 
294
        parent_bzrdir.get_config().set_default_stack_on('stack-on')
 
295
        target = source.bzrdir.sprout('target').open_branch()
 
296
        # When we sprout we upgrade the branch when there is a default stack_on
 
297
        # set by a config *and* the targeted branch supports stacking.
 
298
        if stack_on._format.supports_stacking():
 
299
            self.assertEqual('../stack-on', target.get_stacked_on_url())
 
300
        else:
 
301
            self.assertRaises(
 
302
                errors.UnstackableBranchFormat, target.get_stacked_on_url)
 
303
 
 
304
    def test_clone_stacking_policy_handling(self):
 
305
        """Obey policy where possible, ignore otherwise."""
 
306
        if isinstance(self.branch_format, branch.BzrBranchFormat4):
 
307
            raise TestNotApplicable('Branch format 4 does not autoupgrade.')
 
308
        source = self.make_branch('source')
 
309
        stack_on = self.make_stacked_on_matching(source)
 
310
        parent_bzrdir = self.make_bzrdir('.', format='default')
 
311
        parent_bzrdir.get_config().set_default_stack_on('stack-on')
 
312
        target = source.bzrdir.clone('target').open_branch()
 
313
        # When we clone we upgrade the branch when there is a default stack_on
 
314
        # set by a config *and* the targeted branch supports stacking.
 
315
        if stack_on._format.supports_stacking():
 
316
            self.assertEqual('../stack-on', target.get_stacked_on_url())
 
317
        else:
 
318
            self.assertRaises(
 
319
                errors.UnstackableBranchFormat, target.get_stacked_on_url)
 
320
 
 
321
    def test_sprout_to_smart_server_stacking_policy_handling(self):
 
322
        """Obey policy where possible, ignore otherwise."""
 
323
        if isinstance(self.branch_format, branch.BzrBranchFormat4):
 
324
            raise TestNotApplicable('Branch format 4 is not usable via HPSS.')
 
325
        source = self.make_branch('source')
 
326
        stack_on = self.make_stacked_on_matching(source)
 
327
        parent_bzrdir = self.make_bzrdir('.', format='default')
 
328
        parent_bzrdir.get_config().set_default_stack_on('stack-on')
 
329
        url = self.make_smart_server('target').base
 
330
        target = source.bzrdir.sprout(url).open_branch()
 
331
        # When we sprout we upgrade the branch when there is a default stack_on
 
332
        # set by a config *and* the targeted branch supports stacking.
 
333
        if stack_on._format.supports_stacking():
 
334
            self.assertEqual('../stack-on', target.get_stacked_on_url())
 
335
        else:
 
336
            self.assertRaises(
 
337
                errors.UnstackableBranchFormat, target.get_stacked_on_url)
 
338
 
 
339
    def prepare_stacked_on_fetch(self):
 
340
        stack_on = self.make_branch_and_tree('stack-on')
 
341
        stack_on.commit('first commit', rev_id='rev1')
 
342
        try:
 
343
            stacked_dir = stack_on.bzrdir.sprout('stacked', stacked=True)
 
344
        except unstackable_format_errors, e:
 
345
            raise TestNotApplicable('Format does not support stacking.')
 
346
        unstacked = self.make_repository('unstacked')
 
347
        return stacked_dir.open_workingtree(), unstacked
 
348
 
 
349
    def test_fetch_copies_from_stacked_on(self):
 
350
        stacked, unstacked = self.prepare_stacked_on_fetch()
 
351
        unstacked.fetch(stacked.branch.repository, 'rev1')
 
352
        unstacked.get_revision('rev1')
 
353
 
 
354
    def test_fetch_copies_from_stacked_on_and_stacked(self):
 
355
        stacked, unstacked = self.prepare_stacked_on_fetch()
 
356
        tree = stacked.branch.create_checkout('local')
 
357
        tree.commit('second commit', rev_id='rev2')
 
358
        unstacked.fetch(stacked.branch.repository, 'rev2')
 
359
        unstacked.get_revision('rev1')
 
360
        unstacked.get_revision('rev2')
 
361
        self.check_lines_added_or_present(stacked.branch, 'rev1')
 
362
        self.check_lines_added_or_present(stacked.branch, 'rev2')
 
363
 
 
364
    def test_autopack_when_stacked(self):
 
365
        # in bzr.dev as of 20080730, autopack was reported to fail in stacked
 
366
        # repositories because of problems with text deltas spanning physical
 
367
        # repository boundaries.  however, i didn't actually get this test to
 
368
        # fail on that code. -- mbp
 
369
        # see https://bugs.launchpad.net/bzr/+bug/252821
 
370
        stack_on = self.make_branch_and_tree('stack-on')
 
371
        if not stack_on.branch._format.supports_stacking():
 
372
            raise TestNotApplicable("%r does not support stacking"
 
373
                % self.branch_format)
 
374
        text_lines = ['line %d blah blah blah\n' % i for i in range(20)]
 
375
        self.build_tree_contents([('stack-on/a', ''.join(text_lines))])
 
376
        stack_on.add('a')
 
377
        stack_on.commit('base commit')
 
378
        stacked_dir = stack_on.bzrdir.sprout('stacked', stacked=True)
 
379
        stacked_branch = stacked_dir.open_branch()
 
380
        local_tree = stack_on.bzrdir.sprout('local').open_workingtree()
 
381
        for i in range(20):
 
382
            text_lines[0] = 'changed in %d\n' % i
 
383
            self.build_tree_contents([('local/a', ''.join(text_lines))])
 
384
            local_tree.commit('commit %d' % i)
 
385
            local_tree.branch.push(stacked_branch)
 
386
        stacked_branch.repository.pack()
 
387
        check.check_dwim(stacked_branch.base, False, True, True)
 
388
 
 
389
    def test_pull_delta_when_stacked(self):
 
390
        if not self.branch_format.supports_stacking():
 
391
            raise TestNotApplicable("%r does not support stacking"
 
392
                % self.branch_format)
 
393
        stack_on = self.make_branch_and_tree('stack-on')
 
394
        text_lines = ['line %d blah blah blah\n' % i for i in range(20)]
 
395
        self.build_tree_contents([('stack-on/a', ''.join(text_lines))])
 
396
        stack_on.add('a')
 
397
        stack_on.commit('base commit')
 
398
        # make a stacked branch from the mainline
 
399
        stacked_dir = stack_on.bzrdir.sprout('stacked', stacked=True)
 
400
        stacked_tree = stacked_dir.open_workingtree()
 
401
        # make a second non-stacked branch from the mainline
 
402
        other_dir = stack_on.bzrdir.sprout('other')
 
403
        other_tree = other_dir.open_workingtree()
 
404
        text_lines[9] = 'changed in other\n'
 
405
        self.build_tree_contents([('other/a', ''.join(text_lines))])
 
406
        stacked_revid = other_tree.commit('commit in other')
 
407
        # this should have generated a delta; try to pull that across
 
408
        # bug 252821 caused a RevisionNotPresent here...
 
409
        stacked_tree.pull(other_tree.branch)
 
410
        stacked_tree.branch.repository.pack()
 
411
        check.check_dwim(stacked_tree.branch.base, False, True, True)
 
412
        self.check_lines_added_or_present(stacked_tree.branch, stacked_revid)
 
413
 
 
414
    def test_fetch_revisions_with_file_changes(self):
 
415
        # Fetching revisions including file changes into a stacked branch
 
416
        # works without error.
 
417
        # Make the source tree.
 
418
        src_tree = self.make_branch_and_tree('src')
 
419
        self.build_tree_contents([('src/a', 'content')])
 
420
        src_tree.add('a')
 
421
        src_tree.commit('first commit')
 
422
 
 
423
        # Make the stacked-on branch.
 
424
        src_tree.bzrdir.sprout('stacked-on')
 
425
 
 
426
        # Make a branch stacked on it.
 
427
        target = self.make_branch('target')
 
428
        try:
 
429
            target.set_stacked_on_url('../stacked-on')
 
430
        except unstackable_format_errors, e:
 
431
            raise TestNotApplicable('Format does not support stacking.')
 
432
 
 
433
        # Change the source branch.
 
434
        self.build_tree_contents([('src/a', 'new content')])
 
435
        src_tree.commit('second commit', rev_id='rev2')
 
436
 
 
437
        # Fetch changes to the target.
 
438
        target.fetch(src_tree.branch)
 
439
        rtree = target.repository.revision_tree('rev2')
 
440
        rtree.lock_read()
 
441
        self.addCleanup(rtree.unlock)
 
442
        self.assertEqual('new content', rtree.get_file_by_path('a').read())
 
443
        self.check_lines_added_or_present(target, 'rev2')
 
444
 
 
445
    def test_transform_fallback_location_hook(self):
 
446
        # The 'transform_fallback_location' branch hook allows us to inspect
 
447
        # and transform the URL of the fallback location for the branch.
 
448
        stack_on = self.make_branch('stack-on')
 
449
        stacked = self.make_branch('stacked')
 
450
        try:
 
451
            stacked.set_stacked_on_url('../stack-on')
 
452
        except unstackable_format_errors, e:
 
453
            raise TestNotApplicable('Format does not support stacking.')
 
454
        self.get_transport().rename('stack-on', 'new-stack-on')
 
455
        hook_calls = []
 
456
        def hook(stacked_branch, url):
 
457
            hook_calls.append(url)
 
458
            return '../new-stack-on'
 
459
        branch.Branch.hooks.install_named_hook(
 
460
            'transform_fallback_location', hook, None)
 
461
        branch.Branch.open('stacked')
 
462
        self.assertEqual(['../stack-on'], hook_calls)
 
463
 
 
464
    def test_stack_on_repository_branch(self):
 
465
        # Stacking should work when the repo isn't co-located with the
 
466
        # stack-on branch.
 
467
        try:
 
468
            repo = self.make_repository('repo', shared=True)
 
469
        except errors.IncompatibleFormat:
 
470
            raise TestNotApplicable()
 
471
        # Avoid make_branch, which produces standalone branches.
 
472
        bzrdir = self.make_bzrdir('repo/stack-on')
 
473
        try:
 
474
            b = bzrdir.create_branch()
 
475
        except errors.UninitializableFormat:
 
476
            raise TestNotApplicable()
 
477
        transport = self.get_transport('stacked')
 
478
        b.bzrdir.clone_on_transport(transport, stacked_on=b.base)
 
479
        # Ensure that opening the branch doesn't raise.
 
480
        branch.Branch.open(transport.base)
 
481
 
 
482
    def test_revision_history_of_stacked(self):
 
483
        # See <https://launchpad.net/bugs/380314>.
 
484
        stack_on = self.make_branch_and_tree('stack-on')
 
485
        stack_on.commit('first commit', rev_id='rev1')
 
486
        try:
 
487
            stacked_dir = stack_on.bzrdir.sprout(
 
488
                self.get_url('stacked'), stacked=True)
 
489
        except unstackable_format_errors, e:
 
490
            raise TestNotApplicable('Format does not support stacking.')
 
491
        try:
 
492
            stacked = stacked_dir.open_workingtree()
 
493
        except errors.NoWorkingTree:
 
494
            stacked = stacked_dir.open_branch().create_checkout(
 
495
                'stacked-checkout', lightweight=True)
 
496
        tree = stacked.branch.create_checkout('local')
 
497
        tree.commit('second commit', rev_id='rev2')
 
498
        # Sanity check: stacked's repo should not contain rev1, otherwise this
 
499
        # test isn't testing what it's supposed to.
 
500
        repo = stacked.branch.repository.bzrdir.open_repository()
 
501
        repo.lock_read()
 
502
        self.addCleanup(repo.unlock)
 
503
        self.assertEqual({}, repo.get_parent_map(['rev1']))
 
504
        # revision_history should work, even though the history is spread over
 
505
        # multiple repositories.
 
506
        self.assertLength(2, stacked.branch.revision_history())
 
507
 
 
508
 
 
509
class TestStackingConnections(
 
510
    transport_util.TestCaseWithConnectionHookedTransport):
 
511
 
 
512
    def setUp(self):
 
513
        super(TestStackingConnections, self).setUp()
 
514
        try:
 
515
            base_tree = self.make_branch_and_tree('base',
 
516
                                                  format=self.bzrdir_format)
 
517
        except errors.UninitializableFormat, e:
 
518
            raise TestNotApplicable(e)
 
519
        stacked = self.make_branch('stacked', format=self.bzrdir_format)
 
520
        try:
 
521
            stacked.set_stacked_on_url(base_tree.branch.base)
 
522
        except unstackable_format_errors, e:
 
523
            raise TestNotApplicable(e)
 
524
        base_tree.commit('first', rev_id='rev-base')
 
525
        stacked.set_last_revision_info(1, 'rev-base')
 
526
        stacked_relative = self.make_branch('stacked_relative',
 
527
                                            format=self.bzrdir_format)
 
528
        stacked_relative.set_stacked_on_url('../base')
 
529
        stacked.set_last_revision_info(1, 'rev-base')
 
530
        self.start_logging_connections()
 
531
 
 
532
    def test_open_stacked(self):
 
533
        b = branch.Branch.open(self.get_url('stacked'))
 
534
        rev = b.repository.get_revision('rev-base')
 
535
        self.assertEqual(1, len(self.connections))
 
536
 
 
537
    def test_open_stacked_relative(self):
 
538
        b = branch.Branch.open(self.get_url('stacked_relative'))
 
539
        rev = b.repository.get_revision('rev-base')
 
540
        self.assertEqual(1, len(self.connections))