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