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