Merge lp:~jameinel/bzr/quick-fix into lp:~bzr/bzr/trunk-old
- quick-fix
- Merge into trunk-old
Proposed by
John A Meinel
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~jameinel/bzr/quick-fix |
Merge into: | lp:~bzr/bzr/trunk-old |
Diff against target: | 252 lines |
To merge this branch: | bzr merge lp:~jameinel/bzr/quick-fix |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Bennetts | Approve | ||
Review via email: mp+6948@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
John A Meinel (jameinel) wrote : | # |
Revision history for this message
Andrew Bennetts (spiv) wrote : | # |
I think this looks good. One change you should make: if it does fix more than my previous ghosts+stacking fix as you say, then it deserves a NEWS entry. :)
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'NEWS' |
2 | --- NEWS 2009-05-29 11:26:30 +0000 |
3 | +++ NEWS 2009-06-02 02:35:12 +0000 |
4 | @@ -18,6 +18,11 @@ |
5 | Improvements |
6 | ************ |
7 | |
8 | + |
9 | +* ``--development6-rich-root`` can now stack. (Modulo some smart-server |
10 | + bugs with stacking and non default formats.) |
11 | + (John Arbash Meinel, #373455) |
12 | + |
13 | * Numerous operations are now faster for huge projects, i.e. those |
14 | with a large number of files and/or a large number of revisions, |
15 | particularly when the latest development format is used. These |
16 | @@ -30,10 +35,6 @@ |
17 | |
18 | (Ian Clatworthy) |
19 | |
20 | -* ``--development6-rich-root`` can now stack. (Modulo some smart-server |
21 | - bugs with stacking and non default formats.) |
22 | - (John Arbash Meinel, #373455) |
23 | - |
24 | |
25 | Bug Fixes |
26 | ********* |
27 | @@ -45,6 +46,11 @@ |
28 | ``RemoteRepository`` was handling fallbacks along with the |
29 | ``_real_repository``. (Andrew Bennetts, John Arbash Meinel, #375496) |
30 | |
31 | +* Fix a small bug with fetching revisions w/ ghosts into a new stacked |
32 | + branch. Not often triggered, because it required ghosts to be part of |
33 | + the fetched revisions, not in the stacked-on ancestry. |
34 | + (John Arbash Meinel) |
35 | + |
36 | |
37 | Documentation |
38 | ************* |
39 | |
40 | === modified file 'bzrlib/repofmt/groupcompress_repo.py' |
41 | --- bzrlib/repofmt/groupcompress_repo.py 2009-05-29 10:25:37 +0000 |
42 | +++ bzrlib/repofmt/groupcompress_repo.py 2009-06-02 02:35:12 +0000 |
43 | @@ -857,7 +857,7 @@ |
44 | self._chk_id_roots = None |
45 | self._chk_p_id_roots = None |
46 | |
47 | - def _get_inventory_stream(self, inventory_keys): |
48 | + def _get_inventory_stream(self, inventory_keys, allow_absent=False): |
49 | """Get a stream of inventory texts. |
50 | |
51 | When this function returns, self._chk_id_roots and self._chk_p_id_roots |
52 | @@ -872,6 +872,11 @@ |
53 | stream = source_vf.get_record_stream(inventory_keys, |
54 | 'groupcompress', True) |
55 | for record in stream: |
56 | + if record.storage_kind == 'absent': |
57 | + if allow_absent: |
58 | + continue |
59 | + else: |
60 | + raise errors.NoSuchRevision(self, record.key) |
61 | bytes = record.get_bytes_as('fulltext') |
62 | chk_inv = inventory.CHKInventory.deserialise(None, bytes, |
63 | record.key) |
64 | @@ -981,7 +986,11 @@ |
65 | raise AssertionError('Cannot call get_stream_for_missing_keys' |
66 | ' untill all of get_stream() has been consumed.') |
67 | # Yield the inventory stream, so we can find the chk stream |
68 | - yield self._get_inventory_stream(missing_inventory_keys) |
69 | + # Some of the missing_keys will be missing because they are ghosts. |
70 | + # As such, we can ignore them. The Sink is required to verify there are |
71 | + # no unavailable texts when the ghost inventories are not filled in. |
72 | + yield self._get_inventory_stream(missing_inventory_keys, |
73 | + allow_absent=True) |
74 | # We use the empty set for excluded_revision_ids, to make it clear that |
75 | # we want to transmit all referenced chk pages. |
76 | for stream_info in self._get_filtered_chk_streams(set()): |
77 | |
78 | === modified file 'bzrlib/repository.py' |
79 | --- bzrlib/repository.py 2009-05-27 15:39:12 +0000 |
80 | +++ bzrlib/repository.py 2009-06-02 02:35:12 +0000 |
81 | @@ -4265,8 +4265,12 @@ |
82 | continue |
83 | # Ask for full texts always so that we don't need more round trips |
84 | # after this stream. |
85 | - stream = vf.get_record_stream(keys, |
86 | - self.to_format._fetch_order, True) |
87 | + # Some of the missing keys are genuinely ghosts, so filter absent |
88 | + # records. The Sink is responsible for doing another check to |
89 | + # ensure that ghosts don't introduce missing data for future |
90 | + # fetches. |
91 | + stream = versionedfile.filter_absent(vf.get_record_stream(keys, |
92 | + self.to_format._fetch_order, True)) |
93 | yield substream_kind, stream |
94 | |
95 | def inventory_fetch_order(self): |
96 | |
97 | === modified file 'bzrlib/smart/repository.py' |
98 | --- bzrlib/smart/repository.py 2009-05-07 05:08:46 +0000 |
99 | +++ bzrlib/smart/repository.py 2009-06-02 02:35:12 +0000 |
100 | @@ -436,6 +436,8 @@ |
101 | for record in substream: |
102 | if record.storage_kind in ('chunked', 'fulltext'): |
103 | serialised = record_to_fulltext_bytes(record) |
104 | + elif record.storage_kind == 'absent': |
105 | + raise ValueError("Absent factory for %s" % (record.key,)) |
106 | else: |
107 | serialised = record.get_bytes_as(record.storage_kind) |
108 | if serialised: |
109 | |
110 | === modified file 'bzrlib/tests/per_repository/test_fetch.py' |
111 | --- bzrlib/tests/per_repository/test_fetch.py 2009-05-23 04:55:52 +0000 |
112 | +++ bzrlib/tests/per_repository/test_fetch.py 2009-06-02 02:35:12 +0000 |
113 | @@ -23,6 +23,7 @@ |
114 | graph, |
115 | remote, |
116 | repository, |
117 | + tests, |
118 | ) |
119 | from bzrlib.inventory import ROOT_ID |
120 | from bzrlib.tests import TestSkipped |
121 | @@ -145,7 +146,7 @@ |
122 | ]) |
123 | |
124 | def test_fetch_to_rich_root_set_parent_1_parent(self): |
125 | - # 1 parent rev -> 1 parent |
126 | + # 1 parent rev -> 1 parent |
127 | self.do_test_fetch_to_rich_root_sets_parents_correctly( |
128 | ((ROOT_ID, 'base'),), |
129 | [('base', None, [('add', ('', ROOT_ID, 'directory', ''))]), |
130 | @@ -300,6 +301,55 @@ |
131 | repo.fetch(tree.branch.repository) |
132 | repo.fetch(tree.branch.repository) |
133 | |
134 | + def make_simple_branch_with_ghost(self): |
135 | + builder = self.make_branch_builder('source') |
136 | + builder.start_series() |
137 | + builder.build_snapshot('A-id', None, [ |
138 | + ('add', ('', 'root-id', 'directory', None)), |
139 | + ('add', ('file', 'file-id', 'file', 'content\n'))]) |
140 | + builder.build_snapshot('B-id', ['A-id', 'ghost-id'], []) |
141 | + builder.finish_series() |
142 | + source_b = builder.get_branch() |
143 | + source_b.lock_read() |
144 | + self.addCleanup(source_b.unlock) |
145 | + return source_b |
146 | + |
147 | + def test_fetch_with_ghost(self): |
148 | + source_b = self.make_simple_branch_with_ghost() |
149 | + target = self.make_repository('target') |
150 | + target.lock_write() |
151 | + self.addCleanup(target.unlock) |
152 | + target.fetch(source_b.repository, revision_id='B-id') |
153 | + |
154 | + def test_fetch_into_smart_with_ghost(self): |
155 | + trans = self.make_smart_server('target') |
156 | + source_b = self.make_simple_branch_with_ghost() |
157 | + target = self.make_repository('target') |
158 | + # Re-open the repository over the smart protocol |
159 | + target = repository.Repository.open(trans.base) |
160 | + target.lock_write() |
161 | + self.addCleanup(target.unlock) |
162 | + try: |
163 | + target.fetch(source_b.repository, revision_id='B-id') |
164 | + except errors.TokenLockingNotSupported: |
165 | + # The code inside fetch() that tries to lock and then fails, also |
166 | + # causes weird problems with 'lock_not_held' later on... |
167 | + target.lock_read() |
168 | + raise tests.KnownFailure('some repositories fail to fetch' |
169 | + ' via the smart server because of locking issues.') |
170 | + |
171 | + def test_fetch_from_smart_with_ghost(self): |
172 | + trans = self.make_smart_server('source') |
173 | + source_b = self.make_simple_branch_with_ghost() |
174 | + target = self.make_repository('target') |
175 | + target.lock_write() |
176 | + self.addCleanup(target.unlock) |
177 | + # Re-open the repository over the smart protocol |
178 | + source = repository.Repository.open(trans.base) |
179 | + source.lock_read() |
180 | + self.addCleanup(source.unlock) |
181 | + target.fetch(source, revision_id='B-id') |
182 | + |
183 | |
184 | class TestSource(TestCaseWithRepository): |
185 | """Tests for/about the results of Repository._get_source.""" |
186 | |
187 | === modified file 'bzrlib/tests/per_repository_reference/test_fetch.py' |
188 | --- bzrlib/tests/per_repository_reference/test_fetch.py 2009-05-29 09:56:12 +0000 |
189 | +++ bzrlib/tests/per_repository_reference/test_fetch.py 2009-06-02 02:35:12 +0000 |
190 | @@ -15,7 +15,13 @@ |
191 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
192 | |
193 | |
194 | -from bzrlib.smart import server |
195 | +from bzrlib import ( |
196 | + branch, |
197 | + errors, |
198 | + ) |
199 | +from bzrlib.smart import ( |
200 | + server, |
201 | + ) |
202 | from bzrlib.tests.per_repository import TestCaseWithRepository |
203 | |
204 | |
205 | @@ -99,3 +105,47 @@ |
206 | final2_b = target_b.bzrdir.sprout('final2', |
207 | revision_id='C-id').open_branch() |
208 | self.assertEqual('C-id', final_b.last_revision()) |
209 | + |
210 | + def make_source_with_ghost_and_stacked_target(self): |
211 | + builder = self.make_branch_builder('source') |
212 | + builder.start_series() |
213 | + builder.build_snapshot('A-id', None, [ |
214 | + ('add', ('', 'root-id', 'directory', None)), |
215 | + ('add', ('file', 'file-id', 'file', 'content\n'))]) |
216 | + builder.build_snapshot('B-id', ['A-id', 'ghost-id'], []) |
217 | + builder.finish_series() |
218 | + source_b = builder.get_branch() |
219 | + source_b.lock_read() |
220 | + self.addCleanup(source_b.unlock) |
221 | + base = self.make_branch('base') |
222 | + base.pull(source_b, stop_revision='A-id') |
223 | + stacked = self.make_branch('stacked') |
224 | + stacked.set_stacked_on_url('../base') |
225 | + return source_b, base, stacked |
226 | + |
227 | + def test_fetch_with_ghost_stacked(self): |
228 | + (source_b, base, |
229 | + stacked) = self.make_source_with_ghost_and_stacked_target() |
230 | + stacked.pull(source_b, stop_revision='B-id') |
231 | + |
232 | + def test_fetch_into_smart_stacked_with_ghost(self): |
233 | + (source_b, base, |
234 | + stacked) = self.make_source_with_ghost_and_stacked_target() |
235 | + # Now, create a smart server on 'stacked' and re-open to force the |
236 | + # target to be a smart target |
237 | + trans = self.make_smart_server('stacked') |
238 | + stacked = branch.Branch.open(trans.base) |
239 | + stacked.lock_write() |
240 | + self.addCleanup(stacked.unlock) |
241 | + stacked.pull(source_b, stop_revision='B-id') |
242 | + |
243 | + def test_fetch_to_stacked_from_smart_with_ghost(self): |
244 | + (source_b, base, |
245 | + stacked) = self.make_source_with_ghost_and_stacked_target() |
246 | + # Now, create a smart server on 'source' and re-open to force the |
247 | + # target to be a smart target |
248 | + trans = self.make_smart_server('source') |
249 | + source_b = branch.Branch.open(trans.base) |
250 | + source_b.lock_read() |
251 | + self.addCleanup(source_b.unlock) |
252 | + stacked.pull(source_b, stop_revision='B-id') |
Fixes some issues with fetch + ghosts + smart server
Turns out that my update for gc stacking slightly regressed our ability to push 'bzr.dev' via the smart server, because of how missing inventories and ghosts interact.
This patch adds tests that assert we can fetch via smart server for *all* formats with a revision that has a ghost parent (a test which was missing w/ Andrew's fix). It also adds similar tests for fetching revisions w/ ghosts into a stacked branch. (Which Andrew's original fix did *not* address.)
Both StreamSource and GroupCHKStreamS ource were affected by this, though there are still more failures with --dev6 + stacking + smart server code to be fixed. (Namely 'bzr push bzr+ssh: //.../new- branch' fails because the smart remote client tries to 'upgrade' the default branch to a hard-coded 1.6.1/1.6-rich-root format repository)