~bzr-pqm/bzr/bzr.dev

2697.2.2 by Martin Pool
deprecate Branch.append_revision
1
# Copyright (C) 2005, 2007 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1115 by Martin Pool
- split fetch tests into a separate file
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1115 by Martin Pool
- split fetch tests into a separate file
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1115 by Martin Pool
- split fetch tests into a separate file
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
1238 by Martin Pool
- remove a lot of dead code from fetch
16
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
17
import os
2004.1.22 by v.ladeuil+lp at free
Implements Range header handling for GET requests. Fix a test.
18
import re
1238 by Martin Pool
- remove a lot of dead code from fetch
19
import sys
20
2697.2.2 by Martin Pool
deprecate Branch.append_revision
21
import bzrlib
2323.8.2 by Aaron Bentley
Give a nicer error on fetch when repos are in incompatible formats
22
from bzrlib import (
23
    bzrdir,
24
    errors,
3871.4.2 by John Arbash Meinel
Finally a test case that reproduces bug #304841
25
    osutils,
1551.15.70 by Aaron Bentley
Avoid using builtins.merge
26
    merge,
2323.8.2 by Aaron Bentley
Give a nicer error on fetch when repos are in incompatible formats
27
    repository,
3606.7.7 by John Arbash Meinel
Add tests for the fetching behavior.
28
    versionedfile,
2323.8.2 by Aaron Bentley
Give a nicer error on fetch when repos are in incompatible formats
29
    )
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
30
from bzrlib.branch import Branch
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
31
from bzrlib.bzrdir import BzrDir
2241.1.5 by Martin Pool
Move KnitFormat2 into repofmt
32
from bzrlib.repofmt import knitrepo
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
33
from bzrlib.tests import TestCaseWithTransport
3102.1.1 by Vincent Ladeuil
Rename bzrlib/test/HTTPTestUtils.py to bzrlib/tests/http_utils.py and fix
34
from bzrlib.tests.http_utils import TestCaseWithWebserver
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
35
from bzrlib.tests.test_revision import make_branches
36
from bzrlib.trace import mutter
1551.8.42 by Aaron Bentley
Ensure that fetch properly updates inventory root knit
37
from bzrlib.upgrade import Convert
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
38
from bzrlib.workingtree import WorkingTree
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
39
2696.3.3 by Martin Pool
Start setting the default format to dirstate-tags
40
# These tests are a bit old; please instead add new tests into
41
# interrepository_implementations/ so they'll run on all relevant
42
# combinations.
43
1115 by Martin Pool
- split fetch tests into a separate file
44
1238 by Martin Pool
- remove a lot of dead code from fetch
45
def has_revision(branch, revision_id):
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
46
    return branch.repository.has_revision(revision_id)
1534.4.5 by Robert Collins
Turn branch format.open into a factory.
47
1393 by Robert Collins
reenable remotebranch tests
48
def fetch_steps(self, br_a, br_b, writable_a):
49
    """A foreign test method for testing fetch locally and remotely."""
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
50
     
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]))
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
55
    self.assertEquals(len(br_b.revision_history()), 7)
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
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
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
58
    self.assertEquals(len(br_b.revision_history()), 7)
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
59
    self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
1393 by Robert Collins
reenable remotebranch tests
60
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
61
    # fetching the next revision up in sample data copies one revision
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
62
    self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[3])[0], 1)
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
63
    self.assertTrue(repo_b.has_revision(br_a.revision_history()[3]))
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
64
    self.assertFalse(has_revision(br_a, br_b.revision_history()[6]))
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
65
    self.assertTrue(br_a.repository.has_revision(br_b.revision_history()[5]))
1393 by Robert Collins
reenable remotebranch tests
66
1092.2.28 by Robert Collins
reenable test of fetching a branch with ghosts
67
    # When a non-branch ancestor is missing, it should be unlisted...
1415 by Robert Collins
remove the ancestry weave file
68
    # as its not reference from the inventory weave.
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
69
    br_b4 = self.make_branch('br_4')
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
70
    count, failures = br_b4.fetch(br_b)
1092.2.28 by Robert Collins
reenable test of fetching a branch with ghosts
71
    self.assertEqual(count, 7)
72
    self.assertEqual(failures, [])
1393 by Robert Collins
reenable remotebranch tests
73
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
74
    self.assertEqual(writable_a.fetch(br_b)[0], 1)
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
75
    self.assertTrue(has_revision(br_a, br_b.revision_history()[3]))
76
    self.assertTrue(has_revision(br_a, br_b.revision_history()[4]))
1393 by Robert Collins
reenable remotebranch tests
77
        
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
78
    br_b2 = self.make_branch('br_b2')
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
79
    self.assertEquals(br_b2.fetch(br_b)[0], 7)
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
80
    self.assertTrue(has_revision(br_b2, br_b.revision_history()[4]))
81
    self.assertTrue(has_revision(br_b2, br_a.revision_history()[2]))
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
82
    self.assertFalse(has_revision(br_b2, br_a.revision_history()[3]))
1393 by Robert Collins
reenable remotebranch tests
83
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
84
    br_a2 = self.make_branch('br_a2')
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
85
    self.assertEquals(br_a2.fetch(br_a)[0], 9)
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
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]))
1393 by Robert Collins
reenable remotebranch tests
89
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
90
    br_a3 = self.make_branch('br_a3')
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
91
    # pulling a branch with no revisions grabs nothing, regardless of 
92
    # whats in the inventory.
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
93
    self.assertEquals(br_a3.fetch(br_a2)[0], 0)
1393 by Robert Collins
reenable remotebranch tests
94
    for revno in range(4):
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
95
        self.assertFalse(
96
            br_a3.repository.has_revision(br_a.revision_history()[revno]))
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
97
    self.assertEqual(br_a3.fetch(br_a2, br_a.revision_history()[2])[0], 3)
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
98
    # pull the 3 revisions introduced by a@u-0-3
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
99
    fetched = br_a3.fetch(br_a2, br_a.revision_history()[3])[0]
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
100
    self.assertEquals(fetched, 3, "fetched %d instead of 3" % fetched)
1393 by Robert Collins
reenable remotebranch tests
101
    # InstallFailed should be raised if the branch is missing the revision
102
    # that was requested.
2697.2.2 by Martin Pool
deprecate Branch.append_revision
103
    self.assertRaises(errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
104
2697.2.5 by Martin Pool
Kill off append_revision
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
1638.1.2 by Robert Collins
Change the basis-inventory file to not have the revision-id in the file name.
110
1185.13.4 by Robert Collins
make reweave visible as a weave method, and quickly integrate into fetch
111
    #TODO: test that fetch correctly does reweaving when needed. RBC 20051008
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
112
    # Note that this means - updating the weave when ghosts are filled in to 
113
    # add the right parents.
114
1185.13.4 by Robert Collins
make reweave visible as a weave method, and quickly integrate into fetch
115
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
116
class TestFetch(TestCaseWithTransport):
1392 by Robert Collins
reinstate testfetch test case
117
118
    def test_fetch(self):
1115 by Martin Pool
- split fetch tests into a separate file
119
        #highest indices a: 5, b: 7
2696.3.3 by Martin Pool
Start setting the default format to dirstate-tags
120
        br_a, br_b = make_branches(self, format='dirstate-tags')
1393 by Robert Collins
reenable remotebranch tests
121
        fetch_steps(self, br_a, br_b, br_a)
1404 by Robert Collins
only pull remote text weaves once per fetch operation
122
1558.4.11 by Aaron Bentley
Allow merge against self, make fetching self a noop
123
    def test_fetch_self(self):
124
        wt = self.make_branch_and_tree('br')
125
        self.assertEqual(wt.branch.fetch(wt.branch), (0, []))
126
1551.8.42 by Aaron Bentley
Ensure that fetch properly updates inventory root knit
127
    def test_fetch_root_knit(self):
1551.8.43 by Aaron Bentley
Update from reviews
128
        """Ensure that knit2.fetch() updates the root knit
1551.8.42 by Aaron Bentley
Ensure that fetch properly updates inventory root knit
129
        
1551.8.43 by Aaron Bentley
Update from reviews
130
        This tests the case where the root has a new revision, but there are no
131
        corresponding filename, parent, contents or other changes.
1551.8.42 by Aaron Bentley
Ensure that fetch properly updates inventory root knit
132
        """
1551.8.43 by Aaron Bentley
Update from reviews
133
        knit1_format = bzrdir.BzrDirMetaFormat1()
2241.1.6 by Martin Pool
Move Knit repositories into the submodule bzrlib.repofmt.knitrepo and
134
        knit1_format.repository_format = knitrepo.RepositoryFormatKnit1()
1551.8.43 by Aaron Bentley
Update from reviews
135
        knit2_format = bzrdir.BzrDirMetaFormat1()
2255.2.211 by Robert Collins
Remove knit2 repository format- it has never been supported.
136
        knit2_format.repository_format = knitrepo.RepositoryFormatKnit3()
1551.8.43 by Aaron Bentley
Update from reviews
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)
1551.8.42 by Aaron Bentley
Ensure that fetch properly updates inventory root knit
141
        tree.set_root_id('tree-root')
142
        tree.commit('rev1', rev_id='rev1')
143
        tree.commit('rev2', rev_id='rev2')
1551.8.43 by Aaron Bentley
Update from reviews
144
145
        # Now we convert it to a knit2 repository so that it has a root knit
146
        Convert(tree.basedir, knit2_format)
1551.8.42 by Aaron Bentley
Ensure that fetch properly updates inventory root knit
147
        tree = WorkingTree.open(tree.basedir)
1551.8.43 by Aaron Bentley
Update from reviews
148
        branch = self.make_branch('branch', format=knit2_format)
1551.8.42 by Aaron Bentley
Ensure that fetch properly updates inventory root knit
149
        branch.pull(tree.branch, stop_revision='rev1')
150
        repo = branch.repository
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
151
        repo.lock_read()
152
        try:
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')]))
157
        finally:
158
            repo.unlock()
1551.8.42 by Aaron Bentley
Ensure that fetch properly updates inventory root knit
159
        branch.pull(tree.branch)
1551.8.43 by Aaron Bentley
Update from reviews
160
        # Make sure that the next revision in the root knit was retrieved,
161
        # even though the text, name, parent_id, etc., were unchanged.
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
162
        repo.lock_read()
163
        try:
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')]))
167
        finally:
168
            repo.unlock()
1551.8.42 by Aaron Bentley
Ensure that fetch properly updates inventory root knit
169
2323.8.2 by Aaron Bentley
Give a nicer error on fetch when repos are in incompatible formats
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')
3582.1.2 by Martin Pool
Default InterRepository.fetch raises IncompatibleRepositories
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")
2323.8.2 by Aaron Bentley
Give a nicer error on fetch when repos are in incompatible formats
180
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
181
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
182
class TestMergeFetch(TestCaseWithTransport):
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
183
184
    def test_merge_fetches_unrelated(self):
185
        """Merge brings across history from unrelated source"""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
186
        wt1 = self.make_branch_and_tree('br1')
187
        br1 = wt1.branch
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')
191
        br2 = wt2.branch
192
        wt2.commit(message='rev 2-1', rev_id='2-1')
1551.15.70 by Aaron Bentley
Avoid using builtins.merge
193
        wt2.merge_from_branch(br1, from_revision='null:')
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
194
        self._check_revs_present(br2)
195
1185.16.94 by mbp at sourcefrog
New test that merge fetches revisions from source
196
    def test_merge_fetches(self):
197
        """Merge brings across history from source"""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
198
        wt1 = self.make_branch_and_tree('br1')
199
        br1 = wt1.branch
200
        wt1.commit(message='rev 1-1', rev_id='1-1')
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
201
        dir_2 = br1.bzrdir.sprout('br2')
202
        br2 = dir_2.open_branch()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
203
        wt1.commit(message='rev 1-2', rev_id='1-2')
1551.15.70 by Aaron Bentley
Avoid using builtins.merge
204
        wt2 = dir_2.open_workingtree()
205
        wt2.commit(message='rev 2-1', rev_id='2-1')
206
        wt2.merge_from_branch(br1)
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
207
        self._check_revs_present(br2)
208
209
    def _check_revs_present(self, br2):
1185.16.94 by mbp at sourcefrog
New test that merge fetches revisions from source
210
        for rev_id in '1-1', '1-2', '2-1':
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
211
            self.assertTrue(br2.repository.has_revision(rev_id))
212
            rev = br2.repository.get_revision(rev_id)
1185.16.94 by mbp at sourcefrog
New test that merge fetches revisions from source
213
            self.assertEqual(rev.revision_id, rev_id)
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
214
            self.assertTrue(br2.repository.get_inventory(rev_id))
1185.16.94 by mbp at sourcefrog
New test that merge fetches revisions from source
215
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
216
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
217
class TestMergeFileHistory(TestCaseWithTransport):
218
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
219
    def setUp(self):
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
220
        super(TestMergeFileHistory, self).setUp()
221
        wt1 = self.make_branch_and_tree('br1')
222
        br1 = wt1.branch
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
223
        self.build_tree_contents([('br1/file', 'original contents\n')])
1534.4.28 by Robert Collins
first cut at merge from integration.
224
        wt1.add('file', 'this-file-id')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
225
        wt1.commit(message='rev 1-1', rev_id='1-1')
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
226
        dir_2 = br1.bzrdir.sprout('br2')
227
        br2 = dir_2.open_branch()
228
        wt2 = dir_2.open_workingtree()
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
229
        self.build_tree_contents([('br1/file', 'original from 1\n')])
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
230
        wt1.commit(message='rev 1-2', rev_id='1-2')
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
231
        self.build_tree_contents([('br1/file', 'agreement\n')])
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
232
        wt1.commit(message='rev 1-3', rev_id='1-3')
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
233
        self.build_tree_contents([('br2/file', 'contents in 2\n')])
1534.4.28 by Robert Collins
first cut at merge from integration.
234
        wt2.commit(message='rev 2-1', rev_id='2-1')
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
235
        self.build_tree_contents([('br2/file', 'agreement\n')])
1534.4.28 by Robert Collins
first cut at merge from integration.
236
        wt2.commit(message='rev 2-2', rev_id='2-2')
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
237
238
    def test_merge_fetches_file_history(self):
239
        """Merge brings across file histories"""
240
        br2 = Branch.open('br2')
1551.15.70 by Aaron Bentley
Avoid using builtins.merge
241
        br1 = Branch.open('br1')
242
        wt2 = WorkingTree.open('br2').merge_from_branch(br1)
3010.1.4 by Robert Collins
Make the knit specific fetch tests knit specific, and lock the branch when looking at historical texts in test_fetch.
243
        br2.lock_read()
244
        self.addCleanup(br2.unlock)
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
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')]:
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
249
            self.assertEqualDiff(
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
250
                br2.repository.revision_tree(
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
251
                    rev_id).get_file_text('this-file-id'), text)
1185.16.96 by mbp at sourcefrog
More merge/fetch tests
252
253
1404 by Robert Collins
only pull remote text weaves once per fetch operation
254
class TestHttpFetch(TestCaseWithWebserver):
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
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 ?
1404 by Robert Collins
only pull remote text weaves once per fetch operation
258
259
    def test_fetch(self):
260
        #highest indices a: 5, b: 7
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
261
        br_a, br_b = make_branches(self)
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
262
        br_rem_a = Branch.open(self.get_readonly_url('branch1'))
1404 by Robert Collins
only pull remote text weaves once per fetch operation
263
        fetch_steps(self, br_rem_a, br_b, br_a)
264
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
265
    def _count_log_matches(self, target, logs):
266
        """Count the number of times the target file pattern was fetched in an http log"""
2004.1.22 by v.ladeuil+lp at free
Implements Range header handling for GET requests. Fix a test.
267
        get_succeeds_re = re.compile(
268
            '.*"GET .*%s HTTP/1.1" 20[06] - "-" "bzr/%s' %
269
            (     target,                    bzrlib.__version__))
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
270
        c = 0
271
        for line in logs:
2004.1.22 by v.ladeuil+lp at free
Implements Range header handling for GET requests. Fix a test.
272
            if get_succeeds_re.match(line):
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
273
                c += 1
274
        return c
275
1404 by Robert Collins
only pull remote text weaves once per fetch operation
276
    def test_weaves_are_retrieved_once(self):
277
        self.build_tree(("source/", "source/file", "target/"))
3010.1.4 by Robert Collins
Make the knit specific fetch tests knit specific, and lock the branch when looking at historical texts in test_fetch.
278
        # This test depends on knit dasta storage.
279
        wt = self.make_branch_and_tree('source', format='dirstate-tags')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
280
        branch = wt.branch
281
        wt.add(["file"], ["id"])
282
        wt.commit("added file")
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
283
        open("source/file", 'w').write("blah\n")
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
284
        wt.commit("changed file")
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
285
        target = BzrDir.create_branch_and_repo("target/")
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
286
        source = Branch.open(self.get_readonly_url("source/"))
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
287
        self.assertEqual(target.fetch(source), (2, []))
1430 by Robert Collins
touchup the prefixed-store patch
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
290
        # path.
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
291
        self.log("web server logs are:")
292
        http_logs = self.get_readonly_server().logs
293
        self.log('\n'.join(http_logs))
1666.1.6 by Robert Collins
Make knit the default format.
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
3422.1.1 by John Arbash Meinel
merge in bzr-1.5rc1, revert the transaction cache change
297
        # RBC 20080408: Or perhaps we can assert that no files are fully read
298
        # twice?
1666.1.6 by Robert Collins
Make knit the default format.
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))
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
301
        self.assertEqual(1, self._count_log_matches('inventory.kndx', http_logs))
1417.1.12 by Robert Collins
cache revision history during read transactions
302
        # this r-h check test will prevent regressions, but it currently already 
303
        # passes, before the patch to cache-rh is applied :[
2230.3.10 by Aaron Bentley
Genericised test_fetch to handle branch 6 retrieval patterns
304
        self.assertTrue(1 >= self._count_log_matches('revision-history',
305
                                                     http_logs))
306
        self.assertTrue(1 >= self._count_log_matches('last-revision',
307
                                                     http_logs))
1530.1.18 by Robert Collins
unbreak test_fetch
308
        # FIXME naughty poking in there.
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
309
        self.get_readonly_server().logs = []
3241.1.4 by Andrew Bennetts
Use get_smart_medium as suggested by Robert, and deal with the fallout.
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.
3241.1.5 by Andrew Bennetts
Add comment suggested by Robert.
313
        # XXX: Perhaps this further parameterisation: test http with smart
314
        # server, and test http without smart server?
3241.1.4 by Andrew Bennetts
Use get_smart_medium as suggested by Robert, and deal with the fallout.
315
        source = Branch.open(
316
            self.get_readonly_url("source/"),
317
            possible_transports=[source.bzrdir.root_transport])
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
318
        self.assertEqual(target.fetch(source), (0, []))
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
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))
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
323
        self.assertEqual(1, self._count_log_matches('branch-format', http_logs))
324
        self.assertEqual(1, self._count_log_matches('branch/format', http_logs))
3422.1.1 by John Arbash Meinel
merge in bzr-1.5rc1, revert the transaction cache change
325
        self.assertEqual(1, self._count_log_matches('repository/format',
326
            http_logs))
2230.3.11 by Aaron Bentley
Fix line endings
327
        self.assertTrue(1 >= self._count_log_matches('revision-history',
328
                                                     http_logs))
329
        self.assertTrue(1 >= self._count_log_matches('last-revision',
330
                                                     http_logs))
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
331
        self.assertEqual(4, len(http_logs))
3380.1.1 by Aaron Bentley
Fix inventory insertion to work in topological order
332
333
3606.7.7 by John Arbash Meinel
Add tests for the fetching behavior.
334
class TestKnitToPackFetch(TestCaseWithTransport):
335
336
    def find_get_record_stream(self, calls):
337
        """In a list of calls, find 'get_record_stream' calls.
338
339
        This also ensures that there is only one get_record_stream call.
340
        """
341
        get_record_call = None
342
        for call in calls:
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
352
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(
362
                        source.texts)
363
        source.signatures = versionedfile.RecordingVersionedFilesDecorator(
364
                        source.signatures)
365
        source.revisions = versionedfile.RecordingVersionedFilesDecorator(
366
                        source.revisions)
367
        source.inventories = versionedfile.RecordingVersionedFilesDecorator(
368
                        source.inventories)
369
        # precondition
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',)],
3849.3.1 by John Arbash Meinel
Part of bug #300289, stop requiring plain fulltexts for revisions.
379
                          target._fetch_order, False),
3606.7.7 by John Arbash Meinel
Add tests for the fetching behavior.
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
387
        # we care about.
388
        signature_calls = source.signatures.calls[-1:]
389
        self.assertEqual(('get_record_stream', [('rev-one',)],
3849.3.1 by John Arbash Meinel
Part of bug #300289, stop requiring plain fulltexts for revisions.
390
                          target._fetch_order, False),
3606.7.7 by John Arbash Meinel
Add tests for the fetching behavior.
391
                         self.find_get_record_stream(signature_calls))
392
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(
402
                        source.texts)
403
        source.signatures = versionedfile.RecordingVersionedFilesDecorator(
404
                        source.signatures)
405
        source.revisions = versionedfile.RecordingVersionedFilesDecorator(
406
                        source.revisions)
407
        source.inventories = versionedfile.RecordingVersionedFilesDecorator(
408
                        source.inventories)
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
426
        # we care about.
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))
431
3849.3.1 by John Arbash Meinel
Part of bug #300289, stop requiring plain fulltexts for revisions.
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
437
        # file.
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
449
        source.lock_read()
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
456
        target.lock_read()
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)
461
3871.4.2 by John Arbash Meinel
Finally a test case that reproduces bug #304841
462
    def test_fetch_with_fallback_and_merge(self):
463
        builder = self.make_branch_builder('source', format='pack-0.92')
464
        builder.start_series()
465
        # graph
466
        #   A
467
        #   |\
468
        #   B C
469
        #   | |
470
        #   | D
471
        #   | |
472
        #   | E
473
        #    \|
474
        #     F
475
        # A & B are present in the base (stacked-on) repository, A-E are
476
        # present in the source.
3871.4.4 by John Arbash Meinel
Another fix for bug #304841. As a broad-spectrum solution,
477
        # This reproduces bug #304841
3871.4.2 by John Arbash Meinel
Finally a test case that 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.
482
        to_add = [
483
            ('add', ('', 'TREE_ROOT', 'directory', None))]
484
        for i in xrange(10):
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
500
        source.lock_read()
501
        self.addCleanup(source.unlock)
502
        source.inventories = versionedfile.OrderingVersionedFilesDecorator(
503
                        source.inventories,
504
                        key_priority={('E',): 1, ('D',): 2, ('C',): 4,
505
                                      ('F',): 3})
506
        # Ensure that the content is yielded in the proper order, and given as
507
        # the expected kinds
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')],
513
                          records)
514
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
520
        # deltas
521
        stream = target.inventories.get_record_stream(
522
            [('C',), ('D',), ('E',), ('F',)],
523
            'unordered', False)
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'},
527
                         kinds)
3849.3.1 by John Arbash Meinel
Part of bug #300289, stop requiring plain fulltexts for revisions.
528
3606.7.7 by John Arbash Meinel
Add tests for the fetching behavior.
529
3380.1.1 by Aaron Bentley
Fix inventory insertion to work in topological order
530
class Test1To2Fetch(TestCaseWithTransport):
3380.1.3 by Aaron Bentley
Fix model-change fetching with ghosts and when fetch is resumed
531
    """Tests for Model1To2 failure modes"""
3380.1.1 by Aaron Bentley
Fix inventory insertion to work in topological order
532
3380.2.4 by Aaron Bentley
Updates from review
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)
538
539
    def do_fetch_order_test(self, first, second):
3380.1.3 by Aaron Bentley
Fix model-change fetching with ghosts and when fetch is resumed
540
        """Test that fetch works no matter what the set order of revision is.
3380.1.1 by Aaron Bentley
Fix inventory insertion to work in topological order
541
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.
544
        """
3380.1.3 by Aaron Bentley
Fix model-change fetching with ghosts and when fetch is resumed
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)
549
3380.2.4 by Aaron Bentley
Updates from review
550
    def test_fetch_order_AB(self):
3380.2.7 by Aaron Bentley
Update docs
551
        """See do_fetch_order_test"""
3380.2.4 by Aaron Bentley
Updates from review
552
        self.do_fetch_order_test('A', 'B')
553
554
    def test_fetch_order_BA(self):
3380.2.7 by Aaron Bentley
Update docs
555
        """See do_fetch_order_test"""
3380.2.4 by Aaron Bentley
Updates from review
556
        self.do_fetch_order_test('B', 'A')
557
3380.1.3 by Aaron Bentley
Fix model-change fetching with ghosts and when fetch is resumed
558
    def get_parents(self, file_id, revision_id):
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
559
        self.repo.lock_read()
560
        try:
561
            parent_map = self.repo.texts.get_parent_map([(file_id, revision_id)])
562
            return parent_map[(file_id, revision_id)]
563
        finally:
564
            self.repo.unlock()
3380.1.3 by Aaron Bentley
Fix model-change fetching with ghosts and when fetch is resumed
565
3380.1.2 by Aaron Bentley
Improve handling ghosts and changing root_ids
566
    def test_fetch_ghosts(self):
3380.1.3 by Aaron Bentley
Fix model-change fetching with ghosts and when fetch is resumed
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()
3380.1.2 by Aaron Bentley
Improve handling ghosts and changing root_ids
571
        fork.commit('not a ghost', rev_id='not-ghost-parent')
3380.1.3 by Aaron Bentley
Fix model-change fetching with ghosts and when fetch is resumed
572
        self.tree.branch.repository.fetch(fork.branch.repository,
3380.1.2 by Aaron Bentley
Improve handling ghosts and changing root_ids
573
                                     'not-ghost-parent')
3380.1.3 by Aaron Bentley
Fix model-change fetching with ghosts and when fetch is resumed
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()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
578
        self.assertEqual(
579
            ((root_id, 'left-parent'), (root_id, 'ghost-parent'),
580
             (root_id, 'not-ghost-parent')),
581
            self.get_parents(root_id, 'second-id'))
3380.1.3 by Aaron Bentley
Fix model-change fetching with ghosts and when fetch is resumed
582
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')
586
        if change_root:
587
            self.tree.set_root_id('unique-id')
588
        self.tree.commit('second commit', rev_id='second-id')
589
        if fetch_twice:
590
            self.repo.fetch(self.tree.branch.repository, 'first-id')
591
        self.repo.fetch(self.tree.branch.repository, 'second-id')
3380.1.2 by Aaron Bentley
Improve handling ghosts and changing root_ids
592
593
    def test_fetch_changed_root(self):
3380.1.3 by Aaron Bentley
Fix model-change fetching with ghosts and when fetch is resumed
594
        self.make_two_commits(change_root=True, fetch_twice=False)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
595
        self.assertEqual((), self.get_parents('unique-id', 'second-id'))
3380.1.3 by Aaron Bentley
Fix model-change fetching with ghosts and when fetch is resumed
596
597
    def test_two_fetch_changed_root(self):
598
        self.make_two_commits(change_root=True, fetch_twice=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
599
        self.assertEqual((), self.get_parents('unique-id', 'second-id'))
3380.1.3 by Aaron Bentley
Fix model-change fetching with ghosts and when fetch is resumed
600
601
    def test_two_fetches(self):
602
        self.make_two_commits(change_root=False, fetch_twice=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
603
        self.assertEqual((('TREE_ROOT', 'first-id'),),
604
            self.get_parents('TREE_ROOT', 'second-id'))