##// END OF EJS Templates
widening: remove always-true condition in widening code...
Martin von Zweigbergk -
r43538:c5adf480 default
parent child Browse files
Show More
@@ -1,386 +1,385 b''
1 1 # narrowbundle2.py - bundle2 extensions for narrow repository support
2 2 #
3 3 # Copyright 2017 Google, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import struct
12 12
13 13 from mercurial.i18n import _
14 14 from mercurial.node import nullid
15 15 from mercurial import (
16 16 bundle2,
17 17 changegroup,
18 18 error,
19 19 exchange,
20 20 localrepo,
21 21 narrowspec,
22 22 repair,
23 23 util,
24 24 wireprototypes,
25 25 )
26 26 from mercurial.interfaces import repository
27 27 from mercurial.utils import stringutil
28 28
29 29 _NARROWACL_SECTION = b'narrowacl'
30 30 _CHANGESPECPART = b'narrow:changespec'
31 31 _RESSPECS = b'narrow:responsespec'
32 32 _SPECPART = b'narrow:spec'
33 33 _SPECPART_INCLUDE = b'include'
34 34 _SPECPART_EXCLUDE = b'exclude'
35 35 _KILLNODESIGNAL = b'KILL'
36 36 _DONESIGNAL = b'DONE'
37 37 _ELIDEDCSHEADER = b'>20s20s20sl' # cset id, p1, p2, len(text)
38 38 _ELIDEDMFHEADER = b'>20s20s20s20sl' # manifest id, p1, p2, link id, len(text)
39 39 _CSHEADERSIZE = struct.calcsize(_ELIDEDCSHEADER)
40 40 _MFHEADERSIZE = struct.calcsize(_ELIDEDMFHEADER)
41 41
42 42 # Serve a changegroup for a client with a narrow clone.
43 43 def getbundlechangegrouppart_narrow(
44 44 bundler,
45 45 repo,
46 46 source,
47 47 bundlecaps=None,
48 48 b2caps=None,
49 49 heads=None,
50 50 common=None,
51 51 **kwargs
52 52 ):
53 53 assert repo.ui.configbool(b'experimental', b'narrowservebrokenellipses')
54 54
55 55 cgversions = b2caps.get(b'changegroup')
56 56 cgversions = [
57 57 v
58 58 for v in cgversions
59 59 if v in changegroup.supportedoutgoingversions(repo)
60 60 ]
61 61 if not cgversions:
62 62 raise ValueError(_(b'no common changegroup version'))
63 63 version = max(cgversions)
64 64
65 65 include = sorted(filter(bool, kwargs.get(r'includepats', [])))
66 66 exclude = sorted(filter(bool, kwargs.get(r'excludepats', [])))
67 67 generateellipsesbundle2(
68 68 bundler,
69 69 repo,
70 70 include,
71 71 exclude,
72 72 version,
73 73 common,
74 74 heads,
75 75 kwargs.get(r'depth', None),
76 76 )
77 77
78 78
79 79 def generateellipsesbundle2(
80 80 bundler, repo, include, exclude, version, common, heads, depth,
81 81 ):
82 82 match = narrowspec.match(repo.root, include=include, exclude=exclude)
83 83 if depth is not None:
84 84 depth = int(depth)
85 85 if depth < 1:
86 86 raise error.Abort(_(b'depth must be positive, got %d') % depth)
87 87
88 88 heads = set(heads or repo.heads())
89 89 common = set(common or [nullid])
90 90
91 91 visitnodes, relevant_nodes, ellipsisroots = exchange._computeellipsis(
92 92 repo, common, heads, set(), match, depth=depth
93 93 )
94 94
95 95 repo.ui.debug(b'Found %d relevant revs\n' % len(relevant_nodes))
96 96 if visitnodes:
97 97 packer = changegroup.getbundler(
98 98 version,
99 99 repo,
100 100 matcher=match,
101 101 ellipses=True,
102 102 shallow=depth is not None,
103 103 ellipsisroots=ellipsisroots,
104 104 fullnodes=relevant_nodes,
105 105 )
106 106 cgdata = packer.generate(common, visitnodes, False, b'narrow_widen')
107 107
108 108 part = bundler.newpart(b'changegroup', data=cgdata)
109 109 part.addparam(b'version', version)
110 110 if b'treemanifest' in repo.requirements:
111 111 part.addparam(b'treemanifest', b'1')
112 112
113 113
114 114 def generate_ellipses_bundle2_for_widening(
115 115 bundler,
116 116 repo,
117 117 oldinclude,
118 118 oldexclude,
119 119 newinclude,
120 120 newexclude,
121 121 version,
122 122 common,
123 123 heads,
124 124 known,
125 125 depth,
126 126 ):
127 127 newmatch = narrowspec.match(
128 128 repo.root, include=newinclude, exclude=newexclude
129 129 )
130 130 if depth is not None:
131 131 depth = int(depth)
132 132 if depth < 1:
133 133 raise error.Abort(_(b'depth must be positive, got %d') % depth)
134 134
135 135 heads = set(heads or repo.heads())
136 136 common = set(common or [nullid])
137 if known and (oldinclude != newinclude or oldexclude != newexclude):
138 # Steps:
139 # 1. Send kill for "$known & ::common"
140 #
141 # 2. Send changegroup for ::common
142 #
143 # 3. Proceed.
144 #
145 # In the future, we can send kills for only the specific
146 # nodes we know should go away or change shape, and then
147 # send a data stream that tells the client something like this:
148 #
149 # a) apply this changegroup
150 # b) apply nodes XXX, YYY, ZZZ that you already have
151 # c) goto a
152 #
153 # until they've built up the full new state.
154 # Convert to revnums and intersect with "common". The client should
155 # have made it a subset of "common" already, but let's be safe.
156 known = set(repo.revs(b"%ln & ::%ln", known, common))
157 # TODO: we could send only roots() of this set, and the
158 # list of nodes in common, and the client could work out
159 # what to strip, instead of us explicitly sending every
160 # single node.
161 deadrevs = known
137 # Steps:
138 # 1. Send kill for "$known & ::common"
139 #
140 # 2. Send changegroup for ::common
141 #
142 # 3. Proceed.
143 #
144 # In the future, we can send kills for only the specific
145 # nodes we know should go away or change shape, and then
146 # send a data stream that tells the client something like this:
147 #
148 # a) apply this changegroup
149 # b) apply nodes XXX, YYY, ZZZ that you already have
150 # c) goto a
151 #
152 # until they've built up the full new state.
153 # Convert to revnums and intersect with "common". The client should
154 # have made it a subset of "common" already, but let's be safe.
155 known = set(repo.revs(b"%ln & ::%ln", known, common))
156 # TODO: we could send only roots() of this set, and the
157 # list of nodes in common, and the client could work out
158 # what to strip, instead of us explicitly sending every
159 # single node.
160 deadrevs = known
162 161
163 def genkills():
164 for r in deadrevs:
165 yield _KILLNODESIGNAL
166 yield repo.changelog.node(r)
167 yield _DONESIGNAL
162 def genkills():
163 for r in deadrevs:
164 yield _KILLNODESIGNAL
165 yield repo.changelog.node(r)
166 yield _DONESIGNAL
168 167
169 bundler.newpart(_CHANGESPECPART, data=genkills())
170 newvisit, newfull, newellipsis = exchange._computeellipsis(
171 repo, set(), common, known, newmatch
168 bundler.newpart(_CHANGESPECPART, data=genkills())
169 newvisit, newfull, newellipsis = exchange._computeellipsis(
170 repo, set(), common, known, newmatch
171 )
172 if newvisit:
173 packer = changegroup.getbundler(
174 version,
175 repo,
176 matcher=newmatch,
177 ellipses=True,
178 shallow=depth is not None,
179 ellipsisroots=newellipsis,
180 fullnodes=newfull,
172 181 )
173 if newvisit:
174 packer = changegroup.getbundler(
175 version,
176 repo,
177 matcher=newmatch,
178 ellipses=True,
179 shallow=depth is not None,
180 ellipsisroots=newellipsis,
181 fullnodes=newfull,
182 )
183 cgdata = packer.generate(common, newvisit, False, b'narrow_widen')
182 cgdata = packer.generate(common, newvisit, False, b'narrow_widen')
184 183
185 part = bundler.newpart(b'changegroup', data=cgdata)
186 part.addparam(b'version', version)
187 if b'treemanifest' in repo.requirements:
188 part.addparam(b'treemanifest', b'1')
184 part = bundler.newpart(b'changegroup', data=cgdata)
185 part.addparam(b'version', version)
186 if b'treemanifest' in repo.requirements:
187 part.addparam(b'treemanifest', b'1')
189 188
190 189 visitnodes, relevant_nodes, ellipsisroots = exchange._computeellipsis(
191 190 repo, common, heads, set(), newmatch, depth=depth
192 191 )
193 192
194 193 repo.ui.debug(b'Found %d relevant revs\n' % len(relevant_nodes))
195 194 if visitnodes:
196 195 packer = changegroup.getbundler(
197 196 version,
198 197 repo,
199 198 matcher=newmatch,
200 199 ellipses=True,
201 200 shallow=depth is not None,
202 201 ellipsisroots=ellipsisroots,
203 202 fullnodes=relevant_nodes,
204 203 )
205 204 cgdata = packer.generate(common, visitnodes, False, b'narrow_widen')
206 205
207 206 part = bundler.newpart(b'changegroup', data=cgdata)
208 207 part.addparam(b'version', version)
209 208 if b'treemanifest' in repo.requirements:
210 209 part.addparam(b'treemanifest', b'1')
211 210
212 211
213 212 @bundle2.parthandler(_SPECPART, (_SPECPART_INCLUDE, _SPECPART_EXCLUDE))
214 213 def _handlechangespec_2(op, inpart):
215 214 # XXX: This bundle2 handling is buggy and should be removed after hg5.2 is
216 215 # released. New servers will send a mandatory bundle2 part named
217 216 # 'Narrowspec' and will send specs as data instead of params.
218 217 # Refer to issue5952 and 6019
219 218 includepats = set(inpart.params.get(_SPECPART_INCLUDE, b'').splitlines())
220 219 excludepats = set(inpart.params.get(_SPECPART_EXCLUDE, b'').splitlines())
221 220 narrowspec.validatepatterns(includepats)
222 221 narrowspec.validatepatterns(excludepats)
223 222
224 223 if not repository.NARROW_REQUIREMENT in op.repo.requirements:
225 224 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
226 225 op.repo._writerequirements()
227 226 op.repo.setnarrowpats(includepats, excludepats)
228 227 narrowspec.copytoworkingcopy(op.repo)
229 228
230 229
231 230 @bundle2.parthandler(_RESSPECS)
232 231 def _handlenarrowspecs(op, inpart):
233 232 data = inpart.read()
234 233 inc, exc = data.split(b'\0')
235 234 includepats = set(inc.splitlines())
236 235 excludepats = set(exc.splitlines())
237 236 narrowspec.validatepatterns(includepats)
238 237 narrowspec.validatepatterns(excludepats)
239 238
240 239 if repository.NARROW_REQUIREMENT not in op.repo.requirements:
241 240 op.repo.requirements.add(repository.NARROW_REQUIREMENT)
242 241 op.repo._writerequirements()
243 242 op.repo.setnarrowpats(includepats, excludepats)
244 243 narrowspec.copytoworkingcopy(op.repo)
245 244
246 245
247 246 @bundle2.parthandler(_CHANGESPECPART)
248 247 def _handlechangespec(op, inpart):
249 248 repo = op.repo
250 249 cl = repo.changelog
251 250
252 251 # changesets which need to be stripped entirely. either they're no longer
253 252 # needed in the new narrow spec, or the server is sending a replacement
254 253 # in the changegroup part.
255 254 clkills = set()
256 255
257 256 # A changespec part contains all the updates to ellipsis nodes
258 257 # that will happen as a result of widening or narrowing a
259 258 # repo. All the changes that this block encounters are ellipsis
260 259 # nodes or flags to kill an existing ellipsis.
261 260 chunksignal = changegroup.readexactly(inpart, 4)
262 261 while chunksignal != _DONESIGNAL:
263 262 if chunksignal == _KILLNODESIGNAL:
264 263 # a node used to be an ellipsis but isn't anymore
265 264 ck = changegroup.readexactly(inpart, 20)
266 265 if cl.hasnode(ck):
267 266 clkills.add(ck)
268 267 else:
269 268 raise error.Abort(
270 269 _(b'unexpected changespec node chunk type: %s') % chunksignal
271 270 )
272 271 chunksignal = changegroup.readexactly(inpart, 4)
273 272
274 273 if clkills:
275 274 # preserve bookmarks that repair.strip() would otherwise strip
276 275 op._bookmarksbackup = repo._bookmarks
277 276
278 277 class dummybmstore(dict):
279 278 def applychanges(self, repo, tr, changes):
280 279 pass
281 280
282 281 localrepo.localrepository._bookmarks.set(repo, dummybmstore())
283 282 chgrpfile = repair.strip(
284 283 op.ui, repo, list(clkills), backup=True, topic=b'widen'
285 284 )
286 285 if chgrpfile:
287 286 op._widen_uninterr = repo.ui.uninterruptible()
288 287 op._widen_uninterr.__enter__()
289 288 # presence of _widen_bundle attribute activates widen handler later
290 289 op._widen_bundle = chgrpfile
291 290 # Set the new narrowspec if we're widening. The setnewnarrowpats() method
292 291 # will currently always be there when using the core+narrowhg server, but
293 292 # other servers may include a changespec part even when not widening (e.g.
294 293 # because we're deepening a shallow repo).
295 294 if util.safehasattr(repo, 'setnewnarrowpats'):
296 295 repo.setnewnarrowpats()
297 296
298 297
299 298 def handlechangegroup_widen(op, inpart):
300 299 """Changegroup exchange handler which restores temporarily-stripped nodes"""
301 300 # We saved a bundle with stripped node data we must now restore.
302 301 # This approach is based on mercurial/repair.py@6ee26a53c111.
303 302 repo = op.repo
304 303 ui = op.ui
305 304
306 305 chgrpfile = op._widen_bundle
307 306 del op._widen_bundle
308 307 vfs = repo.vfs
309 308
310 309 ui.note(_(b"adding branch\n"))
311 310 f = vfs.open(chgrpfile, b"rb")
312 311 try:
313 312 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
314 313 # silence internal shuffling chatter
315 314 override = {(b'ui', b'quiet'): True}
316 315 if ui.verbose:
317 316 override = {}
318 317 with ui.configoverride(override):
319 318 if isinstance(gen, bundle2.unbundle20):
320 319 with repo.transaction(b'strip') as tr:
321 320 bundle2.processbundle(repo, gen, lambda: tr)
322 321 else:
323 322 gen.apply(
324 323 repo, b'strip', b'bundle:' + vfs.join(chgrpfile), True
325 324 )
326 325 finally:
327 326 f.close()
328 327
329 328 # remove undo files
330 329 for undovfs, undofile in repo.undofiles():
331 330 try:
332 331 undovfs.unlink(undofile)
333 332 except OSError as e:
334 333 if e.errno != errno.ENOENT:
335 334 ui.warn(
336 335 _(b'error removing %s: %s\n')
337 336 % (undovfs.join(undofile), stringutil.forcebytestr(e))
338 337 )
339 338
340 339 # Remove partial backup only if there were no exceptions
341 340 op._widen_uninterr.__exit__(None, None, None)
342 341 vfs.unlink(chgrpfile)
343 342
344 343
345 344 def setup():
346 345 """Enable narrow repo support in bundle2-related extension points."""
347 346 getbundleargs = wireprototypes.GETBUNDLE_ARGUMENTS
348 347
349 348 getbundleargs[b'narrow'] = b'boolean'
350 349 getbundleargs[b'depth'] = b'plain'
351 350 getbundleargs[b'oldincludepats'] = b'csv'
352 351 getbundleargs[b'oldexcludepats'] = b'csv'
353 352 getbundleargs[b'known'] = b'csv'
354 353
355 354 # Extend changegroup serving to handle requests from narrow clients.
356 355 origcgfn = exchange.getbundle2partsmapping[b'changegroup']
357 356
358 357 def wrappedcgfn(*args, **kwargs):
359 358 repo = args[1]
360 359 if repo.ui.has_section(_NARROWACL_SECTION):
361 360 kwargs = exchange.applynarrowacl(repo, kwargs)
362 361
363 362 if kwargs.get(r'narrow', False) and repo.ui.configbool(
364 363 b'experimental', b'narrowservebrokenellipses'
365 364 ):
366 365 getbundlechangegrouppart_narrow(*args, **kwargs)
367 366 else:
368 367 origcgfn(*args, **kwargs)
369 368
370 369 exchange.getbundle2partsmapping[b'changegroup'] = wrappedcgfn
371 370
372 371 # Extend changegroup receiver so client can fixup after widen requests.
373 372 origcghandler = bundle2.parthandlermapping[b'changegroup']
374 373
375 374 def wrappedcghandler(op, inpart):
376 375 origcghandler(op, inpart)
377 376 if util.safehasattr(op, '_widen_bundle'):
378 377 handlechangegroup_widen(op, inpart)
379 378 if util.safehasattr(op, '_bookmarksbackup'):
380 379 localrepo.localrepository._bookmarks.set(
381 380 op.repo, op._bookmarksbackup
382 381 )
383 382 del op._bookmarksbackup
384 383
385 384 wrappedcghandler.params = origcghandler.params
386 385 bundle2.parthandlermapping[b'changegroup'] = wrappedcghandler
General Comments 0
You need to be logged in to leave comments. Login now