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