1
# Copyright (C) 2010, 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24
from bzrlib.tests.per_repository import TestCaseWithRepository
27
class TestCaseWithStackedTarget(TestCaseWithRepository):
32
def make_stacked_target(self):
33
base_tree = self.make_branch_and_tree('base')
34
self.build_tree(['base/f1.txt'])
35
base_tree.add(['f1.txt'], ['f1.txt-id'])
36
base_tree.commit('initial', rev_id=self.r1_key[0])
37
self.build_tree(['base/f2.txt'])
38
base_tree.add(['f2.txt'], ['f2.txt-id'])
39
base_tree.commit('base adds f2', rev_id=self.r2_key[0])
40
stacked_url = urlutils.join(base_tree.branch.base, '../stacked')
41
stacked_bzrdir = base_tree.bzrdir.sprout(stacked_url,
43
if isinstance(stacked_bzrdir, remote.RemoteBzrDir):
44
stacked_branch = stacked_bzrdir.open_branch()
45
stacked_tree = stacked_branch.create_checkout('stacked',
48
stacked_tree = stacked_bzrdir.open_workingtree()
49
return base_tree, stacked_tree
52
class TestCommitWithStacking(TestCaseWithStackedTarget):
55
super(TestCommitWithStacking, self).setUp()
56
format = self.repository_format
57
if (not (isinstance(format, remote.RemoteRepositoryFormat)
58
or format.supports_chks)):
59
raise tests.TestNotApplicable('stacked commit only supported'
60
' for chk repositories')
62
def get_only_repo(self, tree):
63
"""Open just the repository used by this tree.
65
This returns a read locked Repository object without any stacking
68
repo = tree.branch.repository.bzrdir.open_repository()
70
self.addCleanup(repo.unlock)
73
def assertPresent(self, expected, vf, keys):
74
"""Check which of the supplied keys are present."""
75
parent_map = vf.get_parent_map(keys)
76
self.assertEqual(sorted(expected), sorted(parent_map))
78
def test_simple_commit(self):
79
base_tree, stacked_tree = self.make_stacked_target()
81
len(stacked_tree.branch.repository._fallback_repositories))
82
self.build_tree_contents([('stacked/f1.txt', 'new content\n')])
83
stacked_tree.commit('new content', rev_id='new-rev-id')
84
# We open the repository without fallbacks to ensure the data is
86
stacked_only_repo = self.get_only_repo(stacked_tree)
87
# We should have the immediate parent inventory available, but not the
89
self.assertPresent([self.r2_key],
90
stacked_only_repo.inventories, [self.r1_key, self.r2_key])
91
# And we should be able to pull this revision into another stacked
93
stacked2_branch = base_tree.bzrdir.sprout('stacked2',
94
stacked=True).open_branch()
95
stacked2_branch.repository.fetch(stacked_only_repo,
96
revision_id='new-rev-id')
98
def test_merge_commit(self):
99
base_tree, stacked_tree = self.make_stacked_target()
100
self.build_tree_contents([('base/f1.txt', 'new content\n')])
101
r3_key = ('rev3-id',)
102
base_tree.commit('second base', rev_id=r3_key[0])
103
to_be_merged_tree = base_tree.bzrdir.sprout('merged'
105
self.build_tree(['merged/f2.txt'])
106
to_be_merged_tree.add(['f2.txt'], ['f2.txt-id'])
107
to_merge_key = ('to-merge-rev-id',)
108
to_be_merged_tree.commit('new-to-be-merged', rev_id=to_merge_key[0])
109
stacked_tree.merge_from_branch(to_be_merged_tree.branch)
110
merged_key = ('merged-rev-id',)
111
stacked_tree.commit('merge', rev_id=merged_key[0])
112
# to-merge isn't in base, so it should be in stacked.
113
# rev3-id is a parent of a revision we have, so we should have the
114
# inventory, but not the revision.
115
# merged has a parent of r2, so we should also have r2's
116
# inventory-but-not-revision.
117
# Nothing has r1 directly, so we shouldn't have anything present for it
118
stacked_only_repo = self.get_only_repo(stacked_tree)
119
all_keys = [self.r1_key, self.r2_key, r3_key, to_merge_key, merged_key]
120
self.assertPresent([to_merge_key, merged_key],
121
stacked_only_repo.revisions, all_keys)
122
self.assertPresent([self.r2_key, r3_key, to_merge_key, merged_key],
123
stacked_only_repo.inventories, all_keys)
125
def test_merge_from_master(self):
126
base_tree, stacked_tree = self.make_stacked_target()
127
self.build_tree_contents([('base/f1.txt', 'new content\n')])
128
r3_key = ('rev3-id',)
129
base_tree.commit('second base', rev_id=r3_key[0])
130
stacked_tree.merge_from_branch(base_tree.branch)
131
merged_key = ('merged-rev-id',)
132
stacked_tree.commit('merge', rev_id=merged_key[0])
133
all_keys = [self.r1_key, self.r2_key, r3_key, merged_key]
134
# We shouldn't have any of the base revisions in the local repo, but we
135
# should have both base inventories.
136
stacked_only_repo = self.get_only_repo(stacked_tree)
137
self.assertPresent([merged_key],
138
stacked_only_repo.revisions, all_keys)
139
self.assertPresent([self.r2_key, r3_key, merged_key],
140
stacked_only_repo.inventories, all_keys)
142
def test_multi_stack(self):
143
"""base + stacked + stacked-on-stacked"""
144
base_tree, stacked_tree = self.make_stacked_target()
145
self.build_tree(['stacked/f3.txt'])
146
stacked_tree.add(['f3.txt'], ['f3.txt-id'])
147
stacked_key = ('stacked-rev-id',)
148
stacked_tree.commit('add f3', rev_id=stacked_key[0])
149
stacked_only_repo = self.get_only_repo(stacked_tree)
150
self.assertPresent([self.r2_key], stacked_only_repo.inventories,
151
[self.r1_key, self.r2_key])
152
# This ensures we get a Remote URL, rather than a local one.
153
stacked2_url = urlutils.join(base_tree.branch.base, '../stacked2')
154
stacked2_bzrdir = stacked_tree.bzrdir.sprout(stacked2_url,
155
revision_id=self.r1_key[0],
157
if isinstance(stacked2_bzrdir, remote.RemoteBzrDir):
158
stacked2_branch = stacked2_bzrdir.open_branch()
159
stacked2_tree = stacked2_branch.create_checkout('stacked2',
162
stacked2_tree = stacked2_bzrdir.open_workingtree()
163
# stacked2 is stacked on stacked, but its content is rev1, so
164
# it needs to pull the basis information from a fallback-of-fallback.
165
self.build_tree(['stacked2/f3.txt'])
166
stacked2_only_repo = self.get_only_repo(stacked2_tree)
167
self.assertPresent([], stacked2_only_repo.inventories,
168
[self.r1_key, self.r2_key])
169
stacked2_tree.add(['f3.txt'], ['f3.txt-id'])
170
stacked2_tree.commit('add f3', rev_id='stacked2-rev-id')
171
# We added data to this read-locked repo, so refresh it
172
stacked2_only_repo.refresh_data()
173
self.assertPresent([self.r1_key], stacked2_only_repo.inventories,
174
[self.r1_key, self.r2_key])
176
def test_commit_with_ghosts_fails(self):
177
base_tree, stacked_tree = self.make_stacked_target()
178
stacked_tree.set_parent_ids([stacked_tree.last_revision(),
180
self.assertRaises(errors.BzrError,
181
stacked_tree.commit, 'failed_commit')
183
def test_commit_with_ghost_in_ancestry(self):
184
base_tree, stacked_tree = self.make_stacked_target()
185
self.build_tree_contents([('base/f1.txt', 'new content\n')])
186
r3_key = ('rev3-id',)
187
base_tree.commit('second base', rev_id=r3_key[0])
188
to_be_merged_tree = base_tree.bzrdir.sprout('merged'
190
self.build_tree(['merged/f2.txt'])
191
to_be_merged_tree.add(['f2.txt'], ['f2.txt-id'])
192
ghost_key = ('ghost-rev-id',)
193
to_be_merged_tree.set_parent_ids([r3_key[0], ghost_key[0]])
194
to_merge_key = ('to-merge-rev-id',)
195
to_be_merged_tree.commit('new-to-be-merged', rev_id=to_merge_key[0])
196
stacked_tree.merge_from_branch(to_be_merged_tree.branch)
197
merged_key = ('merged-rev-id',)
198
stacked_tree.commit('merge', rev_id=merged_key[0])
199
# vs test_merge_commit, the fetch for 'merge_from_branch' should
200
# already have handled that 'ghost-rev-id' is a ghost, and commit
201
# should not try to fill it in at this point.
202
stacked_only_repo = self.get_only_repo(stacked_tree)
203
all_keys = [self.r1_key, self.r2_key, r3_key, to_merge_key, merged_key,
205
self.assertPresent([to_merge_key, merged_key],
206
stacked_only_repo.revisions, all_keys)
207
self.assertPresent([self.r2_key, r3_key, to_merge_key, merged_key],
208
stacked_only_repo.inventories, all_keys)
211
class TestCommitStackedFailsAppropriately(TestCaseWithStackedTarget):
213
def test_stacked_commit_fails_on_old_formats(self):
214
base_tree, stacked_tree = self.make_stacked_target()
215
format = stacked_tree.branch.repository._format
216
if format.supports_chks:
217
stacked_tree.commit('should succeed')
219
self.assertRaises(errors.BzrError,
220
stacked_tree.commit, 'unsupported format')