20
from bzrlib.branch import Branch
21
from bzrlib.bzrdir import BzrDir
22
from bzrlib.builtins import merge
20
23
import bzrlib.errors
24
from bzrlib.tests import TestCaseWithTransport
25
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
21
26
from bzrlib.tests.test_revision import make_branches
22
27
from bzrlib.trace import mutter
23
from bzrlib.branch import Branch
24
from bzrlib.fetch import greedy_fetch
25
from bzrlib.merge import merge
26
from bzrlib.clone import copy_branch
28
from bzrlib.tests import TestCaseInTempDir
29
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
28
from bzrlib.workingtree import WorkingTree
32
31
def has_revision(branch, revision_id):
34
branch.get_revision_xml(revision_id)
36
except bzrlib.errors.NoSuchRevision:
32
return branch.repository.has_revision(revision_id)
39
34
def fetch_steps(self, br_a, br_b, writable_a):
40
35
"""A foreign test method for testing fetch locally and remotely."""
43
return Branch.initialize(name)
45
self.assertFalse(has_revision(br_b, br_a.revision_history()[3]))
46
self.assert_(has_revision(br_b, br_a.revision_history()[2]))
47
self.assertEquals(len(br_b.revision_history()), 7)
48
self.assertEquals(greedy_fetch(br_b, br_a, br_a.revision_history()[2])[0], 0)
50
# greedy_fetch is not supposed to alter the revision history
51
self.assertEquals(len(br_b.revision_history()), 7)
52
self.assertFalse(has_revision(br_b, br_a.revision_history()[3]))
54
self.assertEquals(len(br_b.revision_history()), 7)
55
self.assertEquals(greedy_fetch(br_b, br_a, br_a.revision_history()[3])[0], 1)
56
self.assert_(has_revision(br_b, br_a.revision_history()[3]))
37
# TODO RBC 20060201 make this a repository test.
38
repo_b = br_b.repository
39
self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
40
self.assertTrue(repo_b.has_revision(br_a.revision_history()[2]))
41
self.assertEquals(len(br_b.revision_history()), 7)
42
self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[2])[0], 0)
43
# branch.fetch is not supposed to alter the revision history
44
self.assertEquals(len(br_b.revision_history()), 7)
45
self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
47
# fetching the next revision up in sample data copies one revision
48
self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[3])[0], 1)
49
self.assertTrue(repo_b.has_revision(br_a.revision_history()[3]))
57
50
self.assertFalse(has_revision(br_a, br_b.revision_history()[6]))
58
self.assert_(has_revision(br_a, br_b.revision_history()[5]))
51
self.assertTrue(br_a.repository.has_revision(br_b.revision_history()[5]))
60
53
# When a non-branch ancestor is missing, it should be unlisted...
61
54
# as its not reference from the inventory weave.
62
br_b4 = new_branch('br_4')
63
count, failures = greedy_fetch(br_b4, br_b)
55
br_b4 = self.make_branch('br_4')
56
count, failures = br_b4.fetch(br_b)
64
57
self.assertEqual(count, 7)
65
58
self.assertEqual(failures, [])
67
self.assertEqual(greedy_fetch(writable_a, br_b)[0], 1)
68
self.assert_(has_revision(br_a, br_b.revision_history()[3]))
69
self.assert_(has_revision(br_a, br_b.revision_history()[4]))
60
self.assertEqual(writable_a.fetch(br_b)[0], 1)
61
self.assertTrue(has_revision(br_a, br_b.revision_history()[3]))
62
self.assertTrue(has_revision(br_a, br_b.revision_history()[4]))
71
br_b2 = new_branch('br_b2')
72
self.assertEquals(greedy_fetch(br_b2, br_b)[0], 7)
73
self.assert_(has_revision(br_b2, br_b.revision_history()[4]))
74
self.assert_(has_revision(br_b2, br_a.revision_history()[2]))
64
br_b2 = self.make_branch('br_b2')
65
self.assertEquals(br_b2.fetch(br_b)[0], 7)
66
self.assertTrue(has_revision(br_b2, br_b.revision_history()[4]))
67
self.assertTrue(has_revision(br_b2, br_a.revision_history()[2]))
75
68
self.assertFalse(has_revision(br_b2, br_a.revision_history()[3]))
77
br_a2 = new_branch('br_a2')
78
self.assertEquals(greedy_fetch(br_a2, br_a)[0], 9)
79
self.assert_(has_revision(br_a2, br_b.revision_history()[4]))
80
self.assert_(has_revision(br_a2, br_a.revision_history()[3]))
81
self.assert_(has_revision(br_a2, br_a.revision_history()[2]))
70
br_a2 = self.make_branch('br_a2')
71
self.assertEquals(br_a2.fetch(br_a)[0], 9)
72
self.assertTrue(has_revision(br_a2, br_b.revision_history()[4]))
73
self.assertTrue(has_revision(br_a2, br_a.revision_history()[3]))
74
self.assertTrue(has_revision(br_a2, br_a.revision_history()[2]))
83
br_a3 = new_branch('br_a3')
84
self.assertEquals(greedy_fetch(br_a3, br_a2)[0], 0)
76
br_a3 = self.make_branch('br_a3')
77
# pulling a branch with no revisions grabs nothing, regardless of
78
# whats in the inventory.
79
self.assertEquals(br_a3.fetch(br_a2)[0], 0)
85
80
for revno in range(4):
86
self.assertFalse(has_revision(br_a3, br_a.revision_history()[revno]))
87
self.assertEqual(greedy_fetch(br_a3, br_a2, br_a.revision_history()[2])[0], 3)
88
fetched = greedy_fetch(br_a3, br_a2, br_a.revision_history()[3])[0]
82
br_a3.repository.has_revision(br_a.revision_history()[revno]))
83
self.assertEqual(br_a3.fetch(br_a2, br_a.revision_history()[2])[0], 3)
84
# pull the 3 revisions introduced by a@u-0-3
85
fetched = br_a3.fetch(br_a2, br_a.revision_history()[3])[0]
89
86
self.assertEquals(fetched, 3, "fetched %d instead of 3" % fetched)
90
87
# InstallFailed should be raised if the branch is missing the revision
91
88
# that was requested.
92
self.assertRaises(bzrlib.errors.InstallFailed, greedy_fetch, br_a3,
89
self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
94
90
# InstallFailed should be raised if the branch is missing a revision
95
91
# from its own revision history
96
92
br_a2.append_revision('a-b-c')
97
self.assertRaises(bzrlib.errors.InstallFailed, greedy_fetch, br_a3,
93
self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2)
101
94
#TODO: test that fetch correctly does reweaving when needed. RBC 20051008
103
class TestFetch(TestCaseInTempDir):
95
# Note that this means - updating the weave when ghosts are filled in to
96
# add the right parents.
99
class TestFetch(TestCaseWithTransport):
105
101
def test_fetch(self):
106
102
#highest indices a: 5, b: 7
108
104
fetch_steps(self, br_a, br_b, br_a)
111
class TestMergeFetch(TestCaseInTempDir):
107
class TestMergeFetch(TestCaseWithTransport):
113
109
def test_merge_fetches_unrelated(self):
114
110
"""Merge brings across history from unrelated source"""
116
br1 = Branch.initialize('br1')
117
br1.working_tree().commit(message='rev 1-1', rev_id='1-1')
118
br1.working_tree().commit(message='rev 1-2', rev_id='1-2')
120
br2 = Branch.initialize('br2')
121
br2.working_tree().commit(message='rev 2-1', rev_id='2-1')
111
wt1 = self.make_branch_and_tree('br1')
113
wt1.commit(message='rev 1-1', rev_id='1-1')
114
wt1.commit(message='rev 1-2', rev_id='1-2')
115
wt2 = self.make_branch_and_tree('br2')
117
wt2.commit(message='rev 2-1', rev_id='2-1')
122
118
merge(other_revision=['br1', -1], base_revision=['br1', 0],
124
120
self._check_revs_present(br2)
126
122
def test_merge_fetches(self):
127
123
"""Merge brings across history from source"""
129
br1 = Branch.initialize('br1')
130
br1.working_tree().commit(message='rev 1-1', rev_id='1-1')
131
copy_branch(br1, 'br2')
132
br2 = Branch.open('br2')
133
br1.working_tree().commit(message='rev 1-2', rev_id='1-2')
134
br2.working_tree().commit(message='rev 2-1', rev_id='2-1')
124
wt1 = self.make_branch_and_tree('br1')
126
wt1.commit(message='rev 1-1', rev_id='1-1')
127
dir_2 = br1.bzrdir.sprout('br2')
128
br2 = dir_2.open_branch()
129
wt1.commit(message='rev 1-2', rev_id='1-2')
130
dir_2.open_workingtree().commit(message='rev 2-1', rev_id='2-1')
135
131
merge(other_revision=['br1', -1], base_revision=[None, None],
137
133
self._check_revs_present(br2)
139
135
def _check_revs_present(self, br2):
140
136
for rev_id in '1-1', '1-2', '2-1':
141
self.assertTrue(br2.has_revision(rev_id))
142
rev = br2.get_revision(rev_id)
137
self.assertTrue(br2.repository.has_revision(rev_id))
138
rev = br2.repository.get_revision(rev_id)
143
139
self.assertEqual(rev.revision_id, rev_id)
144
self.assertTrue(br2.get_inventory(rev_id))
148
class TestMergeFileHistory(TestCaseInTempDir):
140
self.assertTrue(br2.repository.get_inventory(rev_id))
143
class TestMergeFileHistory(TestCaseWithTransport):
150
TestCaseInTempDir.setUp(self)
152
br1 = Branch.initialize('br1')
146
super(TestMergeFileHistory, self).setUp()
147
wt1 = self.make_branch_and_tree('br1')
153
149
self.build_tree_contents([('br1/file', 'original contents\n')])
154
br1.working_tree().add(['file'], ['this-file-id'])
155
br1.working_tree().commit(message='rev 1-1', rev_id='1-1')
156
copy_branch(br1, 'br2')
157
br2 = Branch.open('br2')
150
wt1.add('file', 'this-file-id')
151
wt1.commit(message='rev 1-1', rev_id='1-1')
152
dir_2 = br1.bzrdir.sprout('br2')
153
br2 = dir_2.open_branch()
154
wt2 = dir_2.open_workingtree()
158
155
self.build_tree_contents([('br1/file', 'original from 1\n')])
159
br1.working_tree().commit(message='rev 1-2', rev_id='1-2')
156
wt1.commit(message='rev 1-2', rev_id='1-2')
160
157
self.build_tree_contents([('br1/file', 'agreement\n')])
161
br1.working_tree().commit(message='rev 1-3', rev_id='1-3')
158
wt1.commit(message='rev 1-3', rev_id='1-3')
162
159
self.build_tree_contents([('br2/file', 'contents in 2\n')])
163
br2.working_tree().commit(message='rev 2-1', rev_id='2-1')
160
wt2.commit(message='rev 2-1', rev_id='2-1')
164
161
self.build_tree_contents([('br2/file', 'agreement\n')])
165
br2.working_tree().commit(message='rev 2-2', rev_id='2-2')
162
wt2.commit(message='rev 2-2', rev_id='2-2')
167
164
def test_merge_fetches_file_history(self):
168
165
"""Merge brings across file histories"""
173
170
('1-3', 'agreement\n'),
174
171
('2-1', 'contents in 2\n'),
175
172
('2-2', 'agreement\n')]:
176
self.assertEqualDiff(br2.revision_tree(rev_id).get_file_text('this-file-id'),
173
self.assertEqualDiff(
174
br2.repository.revision_tree(
175
rev_id).get_file_text('this-file-id'), text)
182
178
class TestHttpFetch(TestCaseWithWebserver):
185
super(TestHttpFetch, self).setUp()
179
# FIXME RBC 20060124 this really isn't web specific, perhaps an
180
# instrumented readonly transport? Can we do an instrumented
181
# adapter and use self.get_readonly_url ?
188
183
def test_fetch(self):
189
184
#highest indices a: 5, b: 7
190
185
br_a, br_b = make_branches(self)
191
br_rem_a = Branch.open(self.get_remote_url(br_a._transport.base))
186
br_rem_a = Branch.open(self.get_readonly_url('branch1'))
192
187
fetch_steps(self, br_rem_a, br_b, br_a)
194
def log(self, *args):
195
"""Capture web server log messages for introspection."""
196
super(TestHttpFetch, self).log(*args)
197
# if this call indicates a url being fetched, save it specially
198
if args[0].startswith("webserver"):
199
self.weblogs.append(args[3])
201
189
def test_weaves_are_retrieved_once(self):
202
190
self.build_tree(("source/", "source/file", "target/"))
203
branch = Branch.initialize("source")
204
branch.working_tree().add(["file"], ["id"])
205
branch.working_tree().commit("added file")
191
wt = self.make_branch_and_tree('source')
193
wt.add(["file"], ["id"])
194
wt.commit("added file")
206
195
print >>open("source/file", 'w'), "blah"
207
branch.working_tree().commit("changed file")
208
target = Branch.initialize("target/")
209
source = Branch.open(self.get_remote_url("source/"))
210
self.assertEqual(greedy_fetch(target, source), (2, []))
196
wt.commit("changed file")
197
target = BzrDir.create_branch_and_repo("target/")
198
source = Branch.open(self.get_readonly_url("source/"))
199
self.assertEqual(target.fetch(source), (2, []))
200
log_pattern = '%%s HTTP/1.1" 200 - "-" "bzr/%s"' % bzrlib.__version__
211
201
# this is the path to the literal file. As format changes
212
202
# occur it needs to be updated. FIXME: ask the store for the
214
weave_suffix = 'weaves/ce/id.weave HTTP/1.1" 200 -'
216
len([log for log in self.weblogs if log.endswith(weave_suffix)]))
217
inventory_weave_suffix = 'inventory.weave HTTP/1.1" 200 -'
219
len([log for log in self.weblogs if log.endswith(
204
weave_suffix = log_pattern % 'weaves/ce/id.weave'
206
len([log for log in self.get_readonly_server().logs if log.endswith(weave_suffix)]))
207
inventory_weave_suffix = log_pattern % 'inventory.weave'
209
len([log for log in self.get_readonly_server().logs if log.endswith(
220
210
inventory_weave_suffix)]))
221
211
# this r-h check test will prevent regressions, but it currently already
222
212
# passes, before the patch to cache-rh is applied :[
223
revision_history_suffix = 'revision-history HTTP/1.1" 200 -'
213
revision_history_suffix = log_pattern % 'revision-history'
224
214
self.assertEqual(1,
225
len([log for log in self.weblogs if log.endswith(
215
len([log for log in self.get_readonly_server().logs if log.endswith(
226
216
revision_history_suffix)]))
217
# FIXME naughty poking in there.
218
self.get_readonly_server().logs = []
228
219
# check there is nothing more to fetch
229
source = Branch.open(self.get_remote_url("source/"))
230
self.assertEqual(greedy_fetch(target, source), (0, []))
231
self.failUnless(self.weblogs[0].endswith('branch-format HTTP/1.1" 200 -'))
232
self.failUnless(self.weblogs[1].endswith('revision-history HTTP/1.1" 200 -'))
233
self.assertEqual(2, len(self.weblogs))
220
source = Branch.open(self.get_readonly_url("source/"))
221
self.assertEqual(target.fetch(source), (0, []))
222
self.failUnless(self.get_readonly_server().logs[0].endswith(log_pattern % 'branch-format'))
223
self.failUnless(self.get_readonly_server().logs[1].endswith(log_pattern % 'revision-history'))
224
self.assertEqual(2, len(self.get_readonly_server().logs))