1
# Copyright (C) 2005, 2007 Canonical Ltd
1
# Copyright (C) 2005 by Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
from bzrlib.selftest.testrevision import make_branches
18
from bzrlib.trace import mutter
19
from bzrlib.branch import Branch
30
from bzrlib.branch import Branch
31
from bzrlib.bzrdir import BzrDir
32
from bzrlib.repofmt import knitrepo
33
from bzrlib.tests import TestCaseWithTransport
34
from bzrlib.tests.http_utils import TestCaseWithWebserver
35
from bzrlib.tests.test_revision import make_branches
36
from bzrlib.trace import mutter
37
from bzrlib.upgrade import Convert
38
from bzrlib.workingtree import WorkingTree
40
# These tests are a bit old; please instead add new tests into
41
# interrepository_implementations/ so they'll run on all relevant
45
def has_revision(branch, revision_id):
46
return branch.repository.has_revision(revision_id)
48
def fetch_steps(self, br_a, br_b, writable_a):
49
"""A foreign test method for testing fetch locally and remotely."""
51
# TODO RBC 20060201 make this a repository test.
52
repo_b = br_b.repository
53
self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
54
self.assertTrue(repo_b.has_revision(br_a.revision_history()[2]))
55
self.assertEquals(len(br_b.revision_history()), 7)
56
self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[2])[0], 0)
57
# branch.fetch is not supposed to alter the revision history
58
self.assertEquals(len(br_b.revision_history()), 7)
59
self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
61
# fetching the next revision up in sample data copies one revision
62
self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[3])[0], 1)
63
self.assertTrue(repo_b.has_revision(br_a.revision_history()[3]))
64
self.assertFalse(has_revision(br_a, br_b.revision_history()[6]))
65
self.assertTrue(br_a.repository.has_revision(br_b.revision_history()[5]))
67
# When a non-branch ancestor is missing, it should be unlisted...
68
# as its not reference from the inventory weave.
69
br_b4 = self.make_branch('br_4')
70
count, failures = br_b4.fetch(br_b)
71
self.assertEqual(count, 7)
72
self.assertEqual(failures, [])
74
self.assertEqual(writable_a.fetch(br_b)[0], 1)
75
self.assertTrue(has_revision(br_a, br_b.revision_history()[3]))
76
self.assertTrue(has_revision(br_a, br_b.revision_history()[4]))
78
br_b2 = self.make_branch('br_b2')
79
self.assertEquals(br_b2.fetch(br_b)[0], 7)
80
self.assertTrue(has_revision(br_b2, br_b.revision_history()[4]))
81
self.assertTrue(has_revision(br_b2, br_a.revision_history()[2]))
82
self.assertFalse(has_revision(br_b2, br_a.revision_history()[3]))
84
br_a2 = self.make_branch('br_a2')
85
self.assertEquals(br_a2.fetch(br_a)[0], 9)
86
self.assertTrue(has_revision(br_a2, br_b.revision_history()[4]))
87
self.assertTrue(has_revision(br_a2, br_a.revision_history()[3]))
88
self.assertTrue(has_revision(br_a2, br_a.revision_history()[2]))
90
br_a3 = self.make_branch('br_a3')
91
# pulling a branch with no revisions grabs nothing, regardless of
92
# whats in the inventory.
93
self.assertEquals(br_a3.fetch(br_a2)[0], 0)
94
for revno in range(4):
96
br_a3.repository.has_revision(br_a.revision_history()[revno]))
97
self.assertEqual(br_a3.fetch(br_a2, br_a.revision_history()[2])[0], 3)
98
# pull the 3 revisions introduced by a@u-0-3
99
fetched = br_a3.fetch(br_a2, br_a.revision_history()[3])[0]
100
self.assertEquals(fetched, 3, "fetched %d instead of 3" % fetched)
101
# InstallFailed should be raised if the branch is missing the revision
102
# that was requested.
103
self.assertRaises(errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
105
# TODO: Test trying to fetch from a branch that points to a revision not
106
# actually present in its repository. Not every branch format allows you
107
# to directly point to such revisions, so it's a bit complicated to
108
# construct. One way would be to uncommit and gc the revision, but not
109
# every branch supports that. -- mbp 20070814
111
#TODO: test that fetch correctly does reweaving when needed. RBC 20051008
112
# Note that this means - updating the weave when ghosts are filled in to
113
# add the right parents.
116
class TestFetch(TestCaseWithTransport):
118
def test_fetch(self):
119
#highest indices a: 5, b: 7
120
br_a, br_b = make_branches(self, format='dirstate-tags')
121
fetch_steps(self, br_a, br_b, br_a)
123
def test_fetch_self(self):
124
wt = self.make_branch_and_tree('br')
125
self.assertEqual(wt.branch.fetch(wt.branch), (0, []))
127
def test_fetch_root_knit(self):
128
"""Ensure that knit2.fetch() updates the root knit
130
This tests the case where the root has a new revision, but there are no
131
corresponding filename, parent, contents or other changes.
133
knit1_format = bzrdir.BzrDirMetaFormat1()
134
knit1_format.repository_format = knitrepo.RepositoryFormatKnit1()
135
knit2_format = bzrdir.BzrDirMetaFormat1()
136
knit2_format.repository_format = knitrepo.RepositoryFormatKnit3()
137
# we start with a knit1 repository because that causes the
138
# root revision to change for each commit, even though the content,
139
# parent, name, and other attributes are unchanged.
140
tree = self.make_branch_and_tree('tree', knit1_format)
141
tree.set_root_id('tree-root')
142
tree.commit('rev1', rev_id='rev1')
143
tree.commit('rev2', rev_id='rev2')
145
# Now we convert it to a knit2 repository so that it has a root knit
146
Convert(tree.basedir, knit2_format)
147
tree = WorkingTree.open(tree.basedir)
148
branch = self.make_branch('branch', format=knit2_format)
149
branch.pull(tree.branch, stop_revision='rev1')
150
repo = branch.repository
153
# Make sure fetch retrieved only what we requested
154
self.assertEqual({('tree-root', 'rev1'):()},
155
repo.texts.get_parent_map(
156
[('tree-root', 'rev1'), ('tree-root', 'rev2')]))
159
branch.pull(tree.branch)
160
# Make sure that the next revision in the root knit was retrieved,
161
# even though the text, name, parent_id, etc., were unchanged.
164
# Make sure fetch retrieved only what we requested
165
self.assertEqual({('tree-root', 'rev2'):(('tree-root', 'rev1'),)},
166
repo.texts.get_parent_map([('tree-root', 'rev2')]))
170
def test_fetch_incompatible(self):
171
knit_tree = self.make_branch_and_tree('knit', format='knit')
172
knit3_tree = self.make_branch_and_tree('knit3',
173
format='dirstate-with-subtree')
174
knit3_tree.commit('blah')
175
e = self.assertRaises(errors.IncompatibleRepositories,
176
knit_tree.branch.fetch, knit3_tree.branch)
177
self.assertContainsRe(str(e),
178
r"(?m).*/knit.*\nis not compatible with\n.*/knit3/.*\n"
179
r"different rich-root support")
182
class TestMergeFetch(TestCaseWithTransport):
184
def test_merge_fetches_unrelated(self):
185
"""Merge brings across history from unrelated source"""
186
wt1 = self.make_branch_and_tree('br1')
188
wt1.commit(message='rev 1-1', rev_id='1-1')
189
wt1.commit(message='rev 1-2', rev_id='1-2')
190
wt2 = self.make_branch_and_tree('br2')
192
wt2.commit(message='rev 2-1', rev_id='2-1')
193
wt2.merge_from_branch(br1, from_revision='null:')
194
self._check_revs_present(br2)
196
def test_merge_fetches(self):
197
"""Merge brings across history from source"""
198
wt1 = self.make_branch_and_tree('br1')
200
wt1.commit(message='rev 1-1', rev_id='1-1')
201
dir_2 = br1.bzrdir.sprout('br2')
202
br2 = dir_2.open_branch()
203
wt1.commit(message='rev 1-2', rev_id='1-2')
204
wt2 = dir_2.open_workingtree()
205
wt2.commit(message='rev 2-1', rev_id='2-1')
206
wt2.merge_from_branch(br1)
207
self._check_revs_present(br2)
209
def _check_revs_present(self, br2):
210
for rev_id in '1-1', '1-2', '2-1':
211
self.assertTrue(br2.repository.has_revision(rev_id))
212
rev = br2.repository.get_revision(rev_id)
213
self.assertEqual(rev.revision_id, rev_id)
214
self.assertTrue(br2.repository.get_inventory(rev_id))
217
class TestMergeFileHistory(TestCaseWithTransport):
220
super(TestMergeFileHistory, self).setUp()
221
wt1 = self.make_branch_and_tree('br1')
223
self.build_tree_contents([('br1/file', 'original contents\n')])
224
wt1.add('file', 'this-file-id')
225
wt1.commit(message='rev 1-1', rev_id='1-1')
226
dir_2 = br1.bzrdir.sprout('br2')
227
br2 = dir_2.open_branch()
228
wt2 = dir_2.open_workingtree()
229
self.build_tree_contents([('br1/file', 'original from 1\n')])
230
wt1.commit(message='rev 1-2', rev_id='1-2')
231
self.build_tree_contents([('br1/file', 'agreement\n')])
232
wt1.commit(message='rev 1-3', rev_id='1-3')
233
self.build_tree_contents([('br2/file', 'contents in 2\n')])
234
wt2.commit(message='rev 2-1', rev_id='2-1')
235
self.build_tree_contents([('br2/file', 'agreement\n')])
236
wt2.commit(message='rev 2-2', rev_id='2-2')
238
def test_merge_fetches_file_history(self):
239
"""Merge brings across file histories"""
240
br2 = Branch.open('br2')
241
br1 = Branch.open('br1')
242
wt2 = WorkingTree.open('br2').merge_from_branch(br1)
244
self.addCleanup(br2.unlock)
245
for rev_id, text in [('1-2', 'original from 1\n'),
246
('1-3', 'agreement\n'),
247
('2-1', 'contents in 2\n'),
248
('2-2', 'agreement\n')]:
249
self.assertEqualDiff(
250
br2.repository.revision_tree(
251
rev_id).get_file_text('this-file-id'), text)
254
class TestHttpFetch(TestCaseWithWebserver):
255
# FIXME RBC 20060124 this really isn't web specific, perhaps an
256
# instrumented readonly transport? Can we do an instrumented
257
# adapter and use self.get_readonly_url ?
259
def test_fetch(self):
260
#highest indices a: 5, b: 7
261
br_a, br_b = make_branches(self)
262
br_rem_a = Branch.open(self.get_readonly_url('branch1'))
263
fetch_steps(self, br_rem_a, br_b, br_a)
265
def _count_log_matches(self, target, logs):
266
"""Count the number of times the target file pattern was fetched in an http log"""
267
get_succeeds_re = re.compile(
268
'.*"GET .*%s HTTP/1.1" 20[06] - "-" "bzr/%s' %
269
( target, bzrlib.__version__))
272
if get_succeeds_re.match(line):
276
def test_weaves_are_retrieved_once(self):
277
self.build_tree(("source/", "source/file", "target/"))
278
# This test depends on knit dasta storage.
279
wt = self.make_branch_and_tree('source', format='dirstate-tags')
281
wt.add(["file"], ["id"])
282
wt.commit("added file")
283
open("source/file", 'w').write("blah\n")
284
wt.commit("changed file")
285
target = BzrDir.create_branch_and_repo("target/")
286
source = Branch.open(self.get_readonly_url("source/"))
287
self.assertEqual(target.fetch(source), (2, []))
288
# this is the path to the literal file. As format changes
289
# occur it needs to be updated. FIXME: ask the store for the
291
self.log("web server logs are:")
292
http_logs = self.get_readonly_server().logs
293
self.log('\n'.join(http_logs))
294
# unfortunately this log entry is branch format specific. We could
295
# factor out the 'what files does this format use' to a method on the
296
# repository, which would let us to this generically. RBC 20060419
297
# RBC 20080408: Or perhaps we can assert that no files are fully read
299
self.assertEqual(1, self._count_log_matches('/ce/id.kndx', http_logs))
300
self.assertEqual(1, self._count_log_matches('/ce/id.knit', http_logs))
301
self.assertEqual(1, self._count_log_matches('inventory.kndx', http_logs))
302
# this r-h check test will prevent regressions, but it currently already
303
# passes, before the patch to cache-rh is applied :[
304
self.assertTrue(1 >= self._count_log_matches('revision-history',
306
self.assertTrue(1 >= self._count_log_matches('last-revision',
308
# FIXME naughty poking in there.
309
self.get_readonly_server().logs = []
310
# check there is nothing more to fetch. We take care to re-use the
311
# existing transport so that the request logs we're about to examine
312
# aren't cluttered with redundant probes for a smart server.
313
# XXX: Perhaps this further parameterisation: test http with smart
314
# server, and test http without smart server?
315
source = Branch.open(
316
self.get_readonly_url("source/"),
317
possible_transports=[source.bzrdir.root_transport])
318
self.assertEqual(target.fetch(source), (0, []))
319
# should make just two requests
320
http_logs = self.get_readonly_server().logs
321
self.log("web server logs are:")
322
self.log('\n'.join(http_logs))
323
self.assertEqual(1, self._count_log_matches('branch-format', http_logs))
324
self.assertEqual(1, self._count_log_matches('branch/format', http_logs))
325
self.assertEqual(1, self._count_log_matches('repository/format',
327
self.assertTrue(1 >= self._count_log_matches('revision-history',
329
self.assertTrue(1 >= self._count_log_matches('last-revision',
331
self.assertEqual(4, len(http_logs))
334
class TestKnitToPackFetch(TestCaseWithTransport):
336
def find_get_record_stream(self, calls):
337
"""In a list of calls, find 'get_record_stream' calls.
339
This also ensures that there is only one get_record_stream call.
341
get_record_call = None
343
if call[0] == 'get_record_stream':
344
self.assertIs(None, get_record_call,
345
"there should only be one call to"
346
" get_record_stream")
347
get_record_call = call
348
self.assertIsNot(None, get_record_call,
349
"there should be exactly one call to "
350
" get_record_stream")
351
return get_record_call
353
def test_fetch_with_deltas_no_delta_closure(self):
354
tree = self.make_branch_and_tree('source', format='dirstate')
355
target = self.make_repository('target', format='pack-0.92')
356
self.build_tree(['source/file'])
357
tree.set_root_id('root-id')
358
tree.add('file', 'file-id')
359
tree.commit('one', rev_id='rev-one')
360
source = tree.branch.repository
361
source.texts = versionedfile.RecordingVersionedFilesDecorator(
363
source.signatures = versionedfile.RecordingVersionedFilesDecorator(
365
source.revisions = versionedfile.RecordingVersionedFilesDecorator(
367
source.inventories = versionedfile.RecordingVersionedFilesDecorator(
370
self.assertTrue(target._fetch_uses_deltas)
371
target.fetch(source, revision_id='rev-one')
372
self.assertEqual(('get_record_stream', [('file-id', 'rev-one')],
373
target._fetch_order, False),
374
self.find_get_record_stream(source.texts.calls))
375
self.assertEqual(('get_record_stream', [('rev-one',)],
376
target._fetch_order, False),
377
self.find_get_record_stream(source.inventories.calls))
378
self.assertEqual(('get_record_stream', [('rev-one',)],
379
target._fetch_order, False),
380
self.find_get_record_stream(source.revisions.calls))
381
# XXX: Signatures is special, and slightly broken. The
382
# standard item_keys_introduced_by actually does a lookup for every
383
# signature to see if it exists, rather than waiting to do them all at
384
# once at the end. The fetch code then does an all-at-once and just
385
# allows for some of them to be missing.
386
# So we know there will be extra calls, but the *last* one is the one
388
signature_calls = source.signatures.calls[-1:]
389
self.assertEqual(('get_record_stream', [('rev-one',)],
390
target._fetch_order, False),
391
self.find_get_record_stream(signature_calls))
393
def test_fetch_no_deltas_with_delta_closure(self):
394
tree = self.make_branch_and_tree('source', format='dirstate')
395
target = self.make_repository('target', format='pack-0.92')
396
self.build_tree(['source/file'])
397
tree.set_root_id('root-id')
398
tree.add('file', 'file-id')
399
tree.commit('one', rev_id='rev-one')
400
source = tree.branch.repository
401
source.texts = versionedfile.RecordingVersionedFilesDecorator(
403
source.signatures = versionedfile.RecordingVersionedFilesDecorator(
405
source.revisions = versionedfile.RecordingVersionedFilesDecorator(
407
source.inventories = versionedfile.RecordingVersionedFilesDecorator(
409
target._fetch_uses_deltas = False
410
target.fetch(source, revision_id='rev-one')
411
self.assertEqual(('get_record_stream', [('file-id', 'rev-one')],
412
target._fetch_order, True),
413
self.find_get_record_stream(source.texts.calls))
414
self.assertEqual(('get_record_stream', [('rev-one',)],
415
target._fetch_order, True),
416
self.find_get_record_stream(source.inventories.calls))
417
self.assertEqual(('get_record_stream', [('rev-one',)],
418
target._fetch_order, True),
419
self.find_get_record_stream(source.revisions.calls))
420
# XXX: Signatures is special, and slightly broken. The
421
# standard item_keys_introduced_by actually does a lookup for every
422
# signature to see if it exists, rather than waiting to do them all at
423
# once at the end. The fetch code then does an all-at-once and just
424
# allows for some of them to be missing.
425
# So we know there will be extra calls, but the *last* one is the one
427
signature_calls = source.signatures.calls[-1:]
428
self.assertEqual(('get_record_stream', [('rev-one',)],
429
target._fetch_order, True),
430
self.find_get_record_stream(signature_calls))
432
def test_fetch_revisions_with_deltas_into_pack(self):
433
# See BUG #261339, dev versions of bzr could accidentally create deltas
434
# in revision texts in knit branches (when fetching from packs). So we
435
# ensure that *if* a knit repository has a delta in revisions, that it
436
# gets properly expanded back into a fulltext when stored in the pack
438
tree = self.make_branch_and_tree('source', format='dirstate')
439
target = self.make_repository('target', format='pack-0.92')
440
self.build_tree(['source/file'])
441
tree.set_root_id('root-id')
442
tree.add('file', 'file-id')
443
tree.commit('one', rev_id='rev-one')
444
# Hack the KVF for revisions so that it "accidentally" allows a delta
445
tree.branch.repository.revisions._max_delta_chain = 200
446
tree.commit('two', rev_id='rev-two')
447
source = tree.branch.repository
448
# Ensure that we stored a delta
450
self.addCleanup(source.unlock)
451
record = source.revisions.get_record_stream([('rev-two',)],
452
'unordered', False).next()
453
self.assertEqual('knit-delta-gz', record.storage_kind)
454
target.fetch(tree.branch.repository, revision_id='rev-two')
455
# The record should get expanded back to a fulltext
457
self.addCleanup(target.unlock)
458
record = target.revisions.get_record_stream([('rev-two',)],
459
'unordered', False).next()
460
self.assertEqual('knit-ft-gz', record.storage_kind)
462
def test_fetch_with_fallback_and_merge(self):
463
builder = self.make_branch_builder('source', format='pack-0.92')
464
builder.start_series()
475
# A & B are present in the base (stacked-on) repository, A-E are
476
# present in the source.
477
# This reproduces bug #304841
478
# We need a large enough inventory that total size of compressed deltas
479
# is shorter than the size of a compressed fulltext. We have to use
480
# random ids because otherwise the inventory fulltext compresses too
481
# well and the deltas get bigger.
483
('add', ('', 'TREE_ROOT', 'directory', None))]
485
fname = 'file%03d' % (i,)
486
fileid = '%s-%s' % (fname, osutils.rand_chars(64))
487
to_add.append(('add', (fname, fileid, 'file', 'content\n')))
488
builder.build_snapshot('A', None, to_add)
489
builder.build_snapshot('B', ['A'], [])
490
builder.build_snapshot('C', ['A'], [])
491
builder.build_snapshot('D', ['C'], [])
492
builder.build_snapshot('E', ['D'], [])
493
builder.build_snapshot('F', ['E', 'B'], [])
494
builder.finish_series()
495
source_branch = builder.get_branch()
496
source_branch.bzrdir.sprout('base', revision_id='B')
497
target_branch = self.make_branch('target', format='1.6')
498
target_branch.set_stacked_on_url('../base')
499
source = source_branch.repository
501
self.addCleanup(source.unlock)
502
source.inventories = versionedfile.OrderingVersionedFilesDecorator(
504
key_priority={('E',): 1, ('D',): 2, ('C',): 4,
506
# Ensure that the content is yielded in the proper order, and given as
508
records = [(record.key, record.storage_kind)
509
for record in source.inventories.get_record_stream(
510
[('D',), ('C',), ('E',), ('F',)], 'unordered', False)]
511
self.assertEqual([(('E',), 'knit-delta-gz'), (('D',), 'knit-delta-gz'),
512
(('F',), 'knit-delta-gz'), (('C',), 'knit-delta-gz')],
515
target_branch.lock_write()
516
self.addCleanup(target_branch.unlock)
517
target = target_branch.repository
518
target.fetch(source, revision_id='F')
519
# 'C' should be expanded to a fulltext, but D and E should still be
521
stream = target.inventories.get_record_stream(
522
[('C',), ('D',), ('E',), ('F',)],
524
kinds = dict((record.key, record.storage_kind) for record in stream)
525
self.assertEqual({('C',): 'knit-ft-gz', ('D',): 'knit-delta-gz',
526
('E',): 'knit-delta-gz', ('F',): 'knit-delta-gz'},
530
class Test1To2Fetch(TestCaseWithTransport):
531
"""Tests for Model1To2 failure modes"""
533
def make_tree_and_repo(self):
534
self.tree = self.make_branch_and_tree('tree', format='pack-0.92')
535
self.repo = self.make_repository('rich-repo', format='rich-root-pack')
536
self.repo.lock_write()
537
self.addCleanup(self.repo.unlock)
539
def do_fetch_order_test(self, first, second):
540
"""Test that fetch works no matter what the set order of revision is.
542
This test depends on the order of items in a set, which is
543
implementation-dependant, so we test A, B and then B, A.
545
self.make_tree_and_repo()
546
self.tree.commit('Commit 1', rev_id=first)
547
self.tree.commit('Commit 2', rev_id=second)
548
self.repo.fetch(self.tree.branch.repository, second)
550
def test_fetch_order_AB(self):
551
"""See do_fetch_order_test"""
552
self.do_fetch_order_test('A', 'B')
554
def test_fetch_order_BA(self):
555
"""See do_fetch_order_test"""
556
self.do_fetch_order_test('B', 'A')
558
def get_parents(self, file_id, revision_id):
559
self.repo.lock_read()
561
parent_map = self.repo.texts.get_parent_map([(file_id, revision_id)])
562
return parent_map[(file_id, revision_id)]
566
def test_fetch_ghosts(self):
567
self.make_tree_and_repo()
568
self.tree.commit('first commit', rev_id='left-parent')
569
self.tree.add_parent_tree_id('ghost-parent')
570
fork = self.tree.bzrdir.sprout('fork', 'null:').open_workingtree()
571
fork.commit('not a ghost', rev_id='not-ghost-parent')
572
self.tree.branch.repository.fetch(fork.branch.repository,
574
self.tree.add_parent_tree_id('not-ghost-parent')
575
self.tree.commit('second commit', rev_id='second-id')
576
self.repo.fetch(self.tree.branch.repository, 'second-id')
577
root_id = self.tree.get_root_id()
579
((root_id, 'left-parent'), (root_id, 'ghost-parent'),
580
(root_id, 'not-ghost-parent')),
581
self.get_parents(root_id, 'second-id'))
583
def make_two_commits(self, change_root, fetch_twice):
584
self.make_tree_and_repo()
585
self.tree.commit('first commit', rev_id='first-id')
587
self.tree.set_root_id('unique-id')
588
self.tree.commit('second commit', rev_id='second-id')
590
self.repo.fetch(self.tree.branch.repository, 'first-id')
591
self.repo.fetch(self.tree.branch.repository, 'second-id')
593
def test_fetch_changed_root(self):
594
self.make_two_commits(change_root=True, fetch_twice=False)
595
self.assertEqual((), self.get_parents('unique-id', 'second-id'))
597
def test_two_fetch_changed_root(self):
598
self.make_two_commits(change_root=True, fetch_twice=True)
599
self.assertEqual((), self.get_parents('unique-id', 'second-id'))
601
def test_two_fetches(self):
602
self.make_two_commits(change_root=False, fetch_twice=True)
603
self.assertEqual((('TREE_ROOT', 'first-id'),),
604
self.get_parents('TREE_ROOT', 'second-id'))
23
from bzrlib.selftest import TestCaseInTempDir
26
class TestFetch(TestCaseInTempDir):
28
from bzrlib.fetch import greedy_fetch, has_revision
32
return Branch.initialize(name)
34
#highest indices a: 5, b: 7
35
br_a, br_b = make_branches()
36
assert not has_revision(br_b, br_a.revision_history()[3])
37
assert has_revision(br_b, br_a.revision_history()[2])
38
assert len(br_b.revision_history()) == 7
39
assert greedy_fetch(br_b, br_a, br_a.revision_history()[2])[0] == 0
41
# greedy_fetch is not supposed to alter the revision history
42
assert len(br_b.revision_history()) == 7
43
assert not has_revision(br_b, br_a.revision_history()[3])
45
assert len(br_b.revision_history()) == 7
46
assert greedy_fetch(br_b, br_a, br_a.revision_history()[3])[0] == 1
47
assert has_revision(br_b, br_a.revision_history()[3])
48
assert not has_revision(br_a, br_b.revision_history()[3])
49
assert not has_revision(br_a, br_b.revision_history()[4])
51
# When a non-branch ancestor is missing, it should be a failure, not
53
br_a4 = new_branch('br_a4')
54
count, failures = greedy_fetch(br_a4, br_a)
56
assert failures == set((br_b.revision_history()[4],
57
br_b.revision_history()[5]))
59
assert greedy_fetch(br_a, br_b)[0] == 4
60
assert has_revision(br_a, br_b.revision_history()[3])
61
assert has_revision(br_a, br_b.revision_history()[4])
63
br_b2 = new_branch('br_b2')
64
assert greedy_fetch(br_b2, br_b)[0] == 7
65
assert has_revision(br_b2, br_b.revision_history()[4])
66
assert has_revision(br_b2, br_a.revision_history()[2])
67
assert not has_revision(br_b2, br_a.revision_history()[3])
69
br_a2 = new_branch('br_a2')
70
assert greedy_fetch(br_a2, br_a)[0] == 9
71
assert has_revision(br_a2, br_b.revision_history()[4])
72
assert has_revision(br_a2, br_a.revision_history()[3])
74
br_a3 = new_branch('br_a3')
75
assert greedy_fetch(br_a3, br_a2)[0] == 0
76
for revno in range(4):
77
assert not has_revision(br_a3, br_a.revision_history()[revno])
78
assert greedy_fetch(br_a3, br_a2, br_a.revision_history()[2])[0] == 3
79
fetched = greedy_fetch(br_a3, br_a2, br_a.revision_history()[3])[0]
80
assert fetched == 3, "fetched %d instead of 3" % fetched
81
# InstallFailed should be raised if the branch is missing the revision
83
self.assertRaises(bzrlib.errors.InstallFailed, greedy_fetch, br_a3,
85
# InstallFailed should be raised if the branch is missing a revision
86
# from its own revision history
87
br_a2.append_revision('a-b-c')
88
self.assertRaises(bzrlib.errors.InstallFailed, greedy_fetch, br_a3,
93
if __name__ == '__main__':
95
sys.exit(unittest.run_suite(unittest.makeSuite()))