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