##// END OF EJS Templates
phase: generate a push-race detection part on push...
Boris Feld -
r34822:aa5e7b4a default
parent child Browse files
Show More
@@ -1,2081 +1,2106 b''
1 1 # exchange.py - utility to exchange data between repos.
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
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 collections
11 11 import errno
12 12 import hashlib
13 13
14 14 from .i18n import _
15 15 from .node import (
16 16 hex,
17 17 nullid,
18 18 )
19 19 from . import (
20 20 bookmarks as bookmod,
21 21 bundle2,
22 22 changegroup,
23 23 discovery,
24 24 error,
25 25 lock as lockmod,
26 26 obsolete,
27 27 phases,
28 28 pushkey,
29 29 pycompat,
30 30 scmutil,
31 31 sslutil,
32 32 streamclone,
33 33 url as urlmod,
34 34 util,
35 35 )
36 36
37 37 urlerr = util.urlerr
38 38 urlreq = util.urlreq
39 39
40 40 # Maps bundle version human names to changegroup versions.
41 41 _bundlespeccgversions = {'v1': '01',
42 42 'v2': '02',
43 43 'packed1': 's1',
44 44 'bundle2': '02', #legacy
45 45 }
46 46
47 47 # Compression engines allowed in version 1. THIS SHOULD NEVER CHANGE.
48 48 _bundlespecv1compengines = {'gzip', 'bzip2', 'none'}
49 49
50 50 def parsebundlespec(repo, spec, strict=True, externalnames=False):
51 51 """Parse a bundle string specification into parts.
52 52
53 53 Bundle specifications denote a well-defined bundle/exchange format.
54 54 The content of a given specification should not change over time in
55 55 order to ensure that bundles produced by a newer version of Mercurial are
56 56 readable from an older version.
57 57
58 58 The string currently has the form:
59 59
60 60 <compression>-<type>[;<parameter0>[;<parameter1>]]
61 61
62 62 Where <compression> is one of the supported compression formats
63 63 and <type> is (currently) a version string. A ";" can follow the type and
64 64 all text afterwards is interpreted as URI encoded, ";" delimited key=value
65 65 pairs.
66 66
67 67 If ``strict`` is True (the default) <compression> is required. Otherwise,
68 68 it is optional.
69 69
70 70 If ``externalnames`` is False (the default), the human-centric names will
71 71 be converted to their internal representation.
72 72
73 73 Returns a 3-tuple of (compression, version, parameters). Compression will
74 74 be ``None`` if not in strict mode and a compression isn't defined.
75 75
76 76 An ``InvalidBundleSpecification`` is raised when the specification is
77 77 not syntactically well formed.
78 78
79 79 An ``UnsupportedBundleSpecification`` is raised when the compression or
80 80 bundle type/version is not recognized.
81 81
82 82 Note: this function will likely eventually return a more complex data
83 83 structure, including bundle2 part information.
84 84 """
85 85 def parseparams(s):
86 86 if ';' not in s:
87 87 return s, {}
88 88
89 89 params = {}
90 90 version, paramstr = s.split(';', 1)
91 91
92 92 for p in paramstr.split(';'):
93 93 if '=' not in p:
94 94 raise error.InvalidBundleSpecification(
95 95 _('invalid bundle specification: '
96 96 'missing "=" in parameter: %s') % p)
97 97
98 98 key, value = p.split('=', 1)
99 99 key = urlreq.unquote(key)
100 100 value = urlreq.unquote(value)
101 101 params[key] = value
102 102
103 103 return version, params
104 104
105 105
106 106 if strict and '-' not in spec:
107 107 raise error.InvalidBundleSpecification(
108 108 _('invalid bundle specification; '
109 109 'must be prefixed with compression: %s') % spec)
110 110
111 111 if '-' in spec:
112 112 compression, version = spec.split('-', 1)
113 113
114 114 if compression not in util.compengines.supportedbundlenames:
115 115 raise error.UnsupportedBundleSpecification(
116 116 _('%s compression is not supported') % compression)
117 117
118 118 version, params = parseparams(version)
119 119
120 120 if version not in _bundlespeccgversions:
121 121 raise error.UnsupportedBundleSpecification(
122 122 _('%s is not a recognized bundle version') % version)
123 123 else:
124 124 # Value could be just the compression or just the version, in which
125 125 # case some defaults are assumed (but only when not in strict mode).
126 126 assert not strict
127 127
128 128 spec, params = parseparams(spec)
129 129
130 130 if spec in util.compengines.supportedbundlenames:
131 131 compression = spec
132 132 version = 'v1'
133 133 # Generaldelta repos require v2.
134 134 if 'generaldelta' in repo.requirements:
135 135 version = 'v2'
136 136 # Modern compression engines require v2.
137 137 if compression not in _bundlespecv1compengines:
138 138 version = 'v2'
139 139 elif spec in _bundlespeccgversions:
140 140 if spec == 'packed1':
141 141 compression = 'none'
142 142 else:
143 143 compression = 'bzip2'
144 144 version = spec
145 145 else:
146 146 raise error.UnsupportedBundleSpecification(
147 147 _('%s is not a recognized bundle specification') % spec)
148 148
149 149 # Bundle version 1 only supports a known set of compression engines.
150 150 if version == 'v1' and compression not in _bundlespecv1compengines:
151 151 raise error.UnsupportedBundleSpecification(
152 152 _('compression engine %s is not supported on v1 bundles') %
153 153 compression)
154 154
155 155 # The specification for packed1 can optionally declare the data formats
156 156 # required to apply it. If we see this metadata, compare against what the
157 157 # repo supports and error if the bundle isn't compatible.
158 158 if version == 'packed1' and 'requirements' in params:
159 159 requirements = set(params['requirements'].split(','))
160 160 missingreqs = requirements - repo.supportedformats
161 161 if missingreqs:
162 162 raise error.UnsupportedBundleSpecification(
163 163 _('missing support for repository features: %s') %
164 164 ', '.join(sorted(missingreqs)))
165 165
166 166 if not externalnames:
167 167 engine = util.compengines.forbundlename(compression)
168 168 compression = engine.bundletype()[1]
169 169 version = _bundlespeccgversions[version]
170 170 return compression, version, params
171 171
172 172 def readbundle(ui, fh, fname, vfs=None):
173 173 header = changegroup.readexactly(fh, 4)
174 174
175 175 alg = None
176 176 if not fname:
177 177 fname = "stream"
178 178 if not header.startswith('HG') and header.startswith('\0'):
179 179 fh = changegroup.headerlessfixup(fh, header)
180 180 header = "HG10"
181 181 alg = 'UN'
182 182 elif vfs:
183 183 fname = vfs.join(fname)
184 184
185 185 magic, version = header[0:2], header[2:4]
186 186
187 187 if magic != 'HG':
188 188 raise error.Abort(_('%s: not a Mercurial bundle') % fname)
189 189 if version == '10':
190 190 if alg is None:
191 191 alg = changegroup.readexactly(fh, 2)
192 192 return changegroup.cg1unpacker(fh, alg)
193 193 elif version.startswith('2'):
194 194 return bundle2.getunbundler(ui, fh, magicstring=magic + version)
195 195 elif version == 'S1':
196 196 return streamclone.streamcloneapplier(fh)
197 197 else:
198 198 raise error.Abort(_('%s: unknown bundle version %s') % (fname, version))
199 199
200 200 def getbundlespec(ui, fh):
201 201 """Infer the bundlespec from a bundle file handle.
202 202
203 203 The input file handle is seeked and the original seek position is not
204 204 restored.
205 205 """
206 206 def speccompression(alg):
207 207 try:
208 208 return util.compengines.forbundletype(alg).bundletype()[0]
209 209 except KeyError:
210 210 return None
211 211
212 212 b = readbundle(ui, fh, None)
213 213 if isinstance(b, changegroup.cg1unpacker):
214 214 alg = b._type
215 215 if alg == '_truncatedBZ':
216 216 alg = 'BZ'
217 217 comp = speccompression(alg)
218 218 if not comp:
219 219 raise error.Abort(_('unknown compression algorithm: %s') % alg)
220 220 return '%s-v1' % comp
221 221 elif isinstance(b, bundle2.unbundle20):
222 222 if 'Compression' in b.params:
223 223 comp = speccompression(b.params['Compression'])
224 224 if not comp:
225 225 raise error.Abort(_('unknown compression algorithm: %s') % comp)
226 226 else:
227 227 comp = 'none'
228 228
229 229 version = None
230 230 for part in b.iterparts():
231 231 if part.type == 'changegroup':
232 232 version = part.params['version']
233 233 if version in ('01', '02'):
234 234 version = 'v2'
235 235 else:
236 236 raise error.Abort(_('changegroup version %s does not have '
237 237 'a known bundlespec') % version,
238 238 hint=_('try upgrading your Mercurial '
239 239 'client'))
240 240
241 241 if not version:
242 242 raise error.Abort(_('could not identify changegroup version in '
243 243 'bundle'))
244 244
245 245 return '%s-%s' % (comp, version)
246 246 elif isinstance(b, streamclone.streamcloneapplier):
247 247 requirements = streamclone.readbundle1header(fh)[2]
248 248 params = 'requirements=%s' % ','.join(sorted(requirements))
249 249 return 'none-packed1;%s' % urlreq.quote(params)
250 250 else:
251 251 raise error.Abort(_('unknown bundle type: %s') % b)
252 252
253 253 def _computeoutgoing(repo, heads, common):
254 254 """Computes which revs are outgoing given a set of common
255 255 and a set of heads.
256 256
257 257 This is a separate function so extensions can have access to
258 258 the logic.
259 259
260 260 Returns a discovery.outgoing object.
261 261 """
262 262 cl = repo.changelog
263 263 if common:
264 264 hasnode = cl.hasnode
265 265 common = [n for n in common if hasnode(n)]
266 266 else:
267 267 common = [nullid]
268 268 if not heads:
269 269 heads = cl.heads()
270 270 return discovery.outgoing(repo, common, heads)
271 271
272 272 def _forcebundle1(op):
273 273 """return true if a pull/push must use bundle1
274 274
275 275 This function is used to allow testing of the older bundle version"""
276 276 ui = op.repo.ui
277 277 forcebundle1 = False
278 278 # The goal is this config is to allow developer to choose the bundle
279 279 # version used during exchanged. This is especially handy during test.
280 280 # Value is a list of bundle version to be picked from, highest version
281 281 # should be used.
282 282 #
283 283 # developer config: devel.legacy.exchange
284 284 exchange = ui.configlist('devel', 'legacy.exchange')
285 285 forcebundle1 = 'bundle2' not in exchange and 'bundle1' in exchange
286 286 return forcebundle1 or not op.remote.capable('bundle2')
287 287
288 288 class pushoperation(object):
289 289 """A object that represent a single push operation
290 290
291 291 Its purpose is to carry push related state and very common operations.
292 292
293 293 A new pushoperation should be created at the beginning of each push and
294 294 discarded afterward.
295 295 """
296 296
297 297 def __init__(self, repo, remote, force=False, revs=None, newbranch=False,
298 298 bookmarks=(), pushvars=None):
299 299 # repo we push from
300 300 self.repo = repo
301 301 self.ui = repo.ui
302 302 # repo we push to
303 303 self.remote = remote
304 304 # force option provided
305 305 self.force = force
306 306 # revs to be pushed (None is "all")
307 307 self.revs = revs
308 308 # bookmark explicitly pushed
309 309 self.bookmarks = bookmarks
310 310 # allow push of new branch
311 311 self.newbranch = newbranch
312 312 # step already performed
313 313 # (used to check what steps have been already performed through bundle2)
314 314 self.stepsdone = set()
315 315 # Integer version of the changegroup push result
316 316 # - None means nothing to push
317 317 # - 0 means HTTP error
318 318 # - 1 means we pushed and remote head count is unchanged *or*
319 319 # we have outgoing changesets but refused to push
320 320 # - other values as described by addchangegroup()
321 321 self.cgresult = None
322 322 # Boolean value for the bookmark push
323 323 self.bkresult = None
324 324 # discover.outgoing object (contains common and outgoing data)
325 325 self.outgoing = None
326 326 # all remote topological heads before the push
327 327 self.remoteheads = None
328 328 # Details of the remote branch pre and post push
329 329 #
330 330 # mapping: {'branch': ([remoteheads],
331 331 # [newheads],
332 332 # [unsyncedheads],
333 333 # [discardedheads])}
334 334 # - branch: the branch name
335 335 # - remoteheads: the list of remote heads known locally
336 336 # None if the branch is new
337 337 # - newheads: the new remote heads (known locally) with outgoing pushed
338 338 # - unsyncedheads: the list of remote heads unknown locally.
339 339 # - discardedheads: the list of remote heads made obsolete by the push
340 340 self.pushbranchmap = None
341 341 # testable as a boolean indicating if any nodes are missing locally.
342 342 self.incoming = None
343 343 # summary of the remote phase situation
344 344 self.remotephases = None
345 345 # phases changes that must be pushed along side the changesets
346 346 self.outdatedphases = None
347 347 # phases changes that must be pushed if changeset push fails
348 348 self.fallbackoutdatedphases = None
349 349 # outgoing obsmarkers
350 350 self.outobsmarkers = set()
351 351 # outgoing bookmarks
352 352 self.outbookmarks = []
353 353 # transaction manager
354 354 self.trmanager = None
355 355 # map { pushkey partid -> callback handling failure}
356 356 # used to handle exception from mandatory pushkey part failure
357 357 self.pkfailcb = {}
358 358 # an iterable of pushvars or None
359 359 self.pushvars = pushvars
360 360
361 361 @util.propertycache
362 362 def futureheads(self):
363 363 """future remote heads if the changeset push succeeds"""
364 364 return self.outgoing.missingheads
365 365
366 366 @util.propertycache
367 367 def fallbackheads(self):
368 368 """future remote heads if the changeset push fails"""
369 369 if self.revs is None:
370 370 # not target to push, all common are relevant
371 371 return self.outgoing.commonheads
372 372 unfi = self.repo.unfiltered()
373 373 # I want cheads = heads(::missingheads and ::commonheads)
374 374 # (missingheads is revs with secret changeset filtered out)
375 375 #
376 376 # This can be expressed as:
377 377 # cheads = ( (missingheads and ::commonheads)
378 378 # + (commonheads and ::missingheads))"
379 379 # )
380 380 #
381 381 # while trying to push we already computed the following:
382 382 # common = (::commonheads)
383 383 # missing = ((commonheads::missingheads) - commonheads)
384 384 #
385 385 # We can pick:
386 386 # * missingheads part of common (::commonheads)
387 387 common = self.outgoing.common
388 388 nm = self.repo.changelog.nodemap
389 389 cheads = [node for node in self.revs if nm[node] in common]
390 390 # and
391 391 # * commonheads parents on missing
392 392 revset = unfi.set('%ln and parents(roots(%ln))',
393 393 self.outgoing.commonheads,
394 394 self.outgoing.missing)
395 395 cheads.extend(c.node() for c in revset)
396 396 return cheads
397 397
398 398 @property
399 399 def commonheads(self):
400 400 """set of all common heads after changeset bundle push"""
401 401 if self.cgresult:
402 402 return self.futureheads
403 403 else:
404 404 return self.fallbackheads
405 405
406 406 # mapping of message used when pushing bookmark
407 407 bookmsgmap = {'update': (_("updating bookmark %s\n"),
408 408 _('updating bookmark %s failed!\n')),
409 409 'export': (_("exporting bookmark %s\n"),
410 410 _('exporting bookmark %s failed!\n')),
411 411 'delete': (_("deleting remote bookmark %s\n"),
412 412 _('deleting remote bookmark %s failed!\n')),
413 413 }
414 414
415 415
416 416 def push(repo, remote, force=False, revs=None, newbranch=False, bookmarks=(),
417 417 opargs=None):
418 418 '''Push outgoing changesets (limited by revs) from a local
419 419 repository to remote. Return an integer:
420 420 - None means nothing to push
421 421 - 0 means HTTP error
422 422 - 1 means we pushed and remote head count is unchanged *or*
423 423 we have outgoing changesets but refused to push
424 424 - other values as described by addchangegroup()
425 425 '''
426 426 if opargs is None:
427 427 opargs = {}
428 428 pushop = pushoperation(repo, remote, force, revs, newbranch, bookmarks,
429 429 **pycompat.strkwargs(opargs))
430 430 if pushop.remote.local():
431 431 missing = (set(pushop.repo.requirements)
432 432 - pushop.remote.local().supported)
433 433 if missing:
434 434 msg = _("required features are not"
435 435 " supported in the destination:"
436 436 " %s") % (', '.join(sorted(missing)))
437 437 raise error.Abort(msg)
438 438
439 439 if not pushop.remote.canpush():
440 440 raise error.Abort(_("destination does not support push"))
441 441
442 442 if not pushop.remote.capable('unbundle'):
443 443 raise error.Abort(_('cannot push: destination does not support the '
444 444 'unbundle wire protocol command'))
445 445
446 446 # get lock as we might write phase data
447 447 wlock = lock = None
448 448 try:
449 449 # bundle2 push may receive a reply bundle touching bookmarks or other
450 450 # things requiring the wlock. Take it now to ensure proper ordering.
451 451 maypushback = pushop.ui.configbool('experimental', 'bundle2.pushback')
452 452 if (not _forcebundle1(pushop)) and maypushback:
453 453 wlock = pushop.repo.wlock()
454 454 lock = pushop.repo.lock()
455 455 pushop.trmanager = transactionmanager(pushop.repo,
456 456 'push-response',
457 457 pushop.remote.url())
458 458 except IOError as err:
459 459 if err.errno != errno.EACCES:
460 460 raise
461 461 # source repo cannot be locked.
462 462 # We do not abort the push, but just disable the local phase
463 463 # synchronisation.
464 464 msg = 'cannot lock source repository: %s\n' % err
465 465 pushop.ui.debug(msg)
466 466
467 467 with wlock or util.nullcontextmanager(), \
468 468 lock or util.nullcontextmanager(), \
469 469 pushop.trmanager or util.nullcontextmanager():
470 470 pushop.repo.checkpush(pushop)
471 471 _pushdiscovery(pushop)
472 472 if not _forcebundle1(pushop):
473 473 _pushbundle2(pushop)
474 474 _pushchangeset(pushop)
475 475 _pushsyncphase(pushop)
476 476 _pushobsolete(pushop)
477 477 _pushbookmark(pushop)
478 478
479 479 return pushop
480 480
481 481 # list of steps to perform discovery before push
482 482 pushdiscoveryorder = []
483 483
484 484 # Mapping between step name and function
485 485 #
486 486 # This exists to help extensions wrap steps if necessary
487 487 pushdiscoverymapping = {}
488 488
489 489 def pushdiscovery(stepname):
490 490 """decorator for function performing discovery before push
491 491
492 492 The function is added to the step -> function mapping and appended to the
493 493 list of steps. Beware that decorated function will be added in order (this
494 494 may matter).
495 495
496 496 You can only use this decorator for a new step, if you want to wrap a step
497 497 from an extension, change the pushdiscovery dictionary directly."""
498 498 def dec(func):
499 499 assert stepname not in pushdiscoverymapping
500 500 pushdiscoverymapping[stepname] = func
501 501 pushdiscoveryorder.append(stepname)
502 502 return func
503 503 return dec
504 504
505 505 def _pushdiscovery(pushop):
506 506 """Run all discovery steps"""
507 507 for stepname in pushdiscoveryorder:
508 508 step = pushdiscoverymapping[stepname]
509 509 step(pushop)
510 510
511 511 @pushdiscovery('changeset')
512 512 def _pushdiscoverychangeset(pushop):
513 513 """discover the changeset that need to be pushed"""
514 514 fci = discovery.findcommonincoming
515 515 commoninc = fci(pushop.repo, pushop.remote, force=pushop.force)
516 516 common, inc, remoteheads = commoninc
517 517 fco = discovery.findcommonoutgoing
518 518 outgoing = fco(pushop.repo, pushop.remote, onlyheads=pushop.revs,
519 519 commoninc=commoninc, force=pushop.force)
520 520 pushop.outgoing = outgoing
521 521 pushop.remoteheads = remoteheads
522 522 pushop.incoming = inc
523 523
524 524 @pushdiscovery('phase')
525 525 def _pushdiscoveryphase(pushop):
526 526 """discover the phase that needs to be pushed
527 527
528 528 (computed for both success and failure case for changesets push)"""
529 529 outgoing = pushop.outgoing
530 530 unfi = pushop.repo.unfiltered()
531 531 remotephases = pushop.remote.listkeys('phases')
532 532 if (pushop.ui.configbool('ui', '_usedassubrepo')
533 533 and remotephases # server supports phases
534 534 and not pushop.outgoing.missing # no changesets to be pushed
535 535 and remotephases.get('publishing', False)):
536 536 # When:
537 537 # - this is a subrepo push
538 538 # - and remote support phase
539 539 # - and no changeset are to be pushed
540 540 # - and remote is publishing
541 541 # We may be in issue 3781 case!
542 542 # We drop the possible phase synchronisation done by
543 543 # courtesy to publish changesets possibly locally draft
544 544 # on the remote.
545 545 pushop.outdatedphases = []
546 546 pushop.fallbackoutdatedphases = []
547 547 return
548 548
549 549 pushop.remotephases = phases.remotephasessummary(pushop.repo,
550 550 pushop.fallbackheads,
551 551 remotephases)
552 552 droots = pushop.remotephases.draftroots
553 553
554 554 extracond = ''
555 555 if not pushop.remotephases.publishing:
556 556 extracond = ' and public()'
557 557 revset = 'heads((%%ln::%%ln) %s)' % extracond
558 558 # Get the list of all revs draft on remote by public here.
559 559 # XXX Beware that revset break if droots is not strictly
560 560 # XXX root we may want to ensure it is but it is costly
561 561 fallback = list(unfi.set(revset, droots, pushop.fallbackheads))
562 562 if not outgoing.missing:
563 563 future = fallback
564 564 else:
565 565 # adds changeset we are going to push as draft
566 566 #
567 567 # should not be necessary for publishing server, but because of an
568 568 # issue fixed in xxxxx we have to do it anyway.
569 569 fdroots = list(unfi.set('roots(%ln + %ln::)',
570 570 outgoing.missing, droots))
571 571 fdroots = [f.node() for f in fdroots]
572 572 future = list(unfi.set(revset, fdroots, pushop.futureheads))
573 573 pushop.outdatedphases = future
574 574 pushop.fallbackoutdatedphases = fallback
575 575
576 576 @pushdiscovery('obsmarker')
577 577 def _pushdiscoveryobsmarkers(pushop):
578 578 if (obsolete.isenabled(pushop.repo, obsolete.exchangeopt)
579 579 and pushop.repo.obsstore
580 580 and 'obsolete' in pushop.remote.listkeys('namespaces')):
581 581 repo = pushop.repo
582 582 # very naive computation, that can be quite expensive on big repo.
583 583 # However: evolution is currently slow on them anyway.
584 584 nodes = (c.node() for c in repo.set('::%ln', pushop.futureheads))
585 585 pushop.outobsmarkers = pushop.repo.obsstore.relevantmarkers(nodes)
586 586
587 587 @pushdiscovery('bookmarks')
588 588 def _pushdiscoverybookmarks(pushop):
589 589 ui = pushop.ui
590 590 repo = pushop.repo.unfiltered()
591 591 remote = pushop.remote
592 592 ui.debug("checking for updated bookmarks\n")
593 593 ancestors = ()
594 594 if pushop.revs:
595 595 revnums = map(repo.changelog.rev, pushop.revs)
596 596 ancestors = repo.changelog.ancestors(revnums, inclusive=True)
597 597 remotebookmark = remote.listkeys('bookmarks')
598 598
599 599 explicit = set([repo._bookmarks.expandname(bookmark)
600 600 for bookmark in pushop.bookmarks])
601 601
602 602 remotebookmark = bookmod.unhexlifybookmarks(remotebookmark)
603 603 comp = bookmod.comparebookmarks(repo, repo._bookmarks, remotebookmark)
604 604
605 605 def safehex(x):
606 606 if x is None:
607 607 return x
608 608 return hex(x)
609 609
610 610 def hexifycompbookmarks(bookmarks):
611 611 for b, scid, dcid in bookmarks:
612 612 yield b, safehex(scid), safehex(dcid)
613 613
614 614 comp = [hexifycompbookmarks(marks) for marks in comp]
615 615 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = comp
616 616
617 617 for b, scid, dcid in advsrc:
618 618 if b in explicit:
619 619 explicit.remove(b)
620 620 if not ancestors or repo[scid].rev() in ancestors:
621 621 pushop.outbookmarks.append((b, dcid, scid))
622 622 # search added bookmark
623 623 for b, scid, dcid in addsrc:
624 624 if b in explicit:
625 625 explicit.remove(b)
626 626 pushop.outbookmarks.append((b, '', scid))
627 627 # search for overwritten bookmark
628 628 for b, scid, dcid in list(advdst) + list(diverge) + list(differ):
629 629 if b in explicit:
630 630 explicit.remove(b)
631 631 pushop.outbookmarks.append((b, dcid, scid))
632 632 # search for bookmark to delete
633 633 for b, scid, dcid in adddst:
634 634 if b in explicit:
635 635 explicit.remove(b)
636 636 # treat as "deleted locally"
637 637 pushop.outbookmarks.append((b, dcid, ''))
638 638 # identical bookmarks shouldn't get reported
639 639 for b, scid, dcid in same:
640 640 if b in explicit:
641 641 explicit.remove(b)
642 642
643 643 if explicit:
644 644 explicit = sorted(explicit)
645 645 # we should probably list all of them
646 646 ui.warn(_('bookmark %s does not exist on the local '
647 647 'or remote repository!\n') % explicit[0])
648 648 pushop.bkresult = 2
649 649
650 650 pushop.outbookmarks.sort()
651 651
652 652 def _pushcheckoutgoing(pushop):
653 653 outgoing = pushop.outgoing
654 654 unfi = pushop.repo.unfiltered()
655 655 if not outgoing.missing:
656 656 # nothing to push
657 657 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
658 658 return False
659 659 # something to push
660 660 if not pushop.force:
661 661 # if repo.obsstore == False --> no obsolete
662 662 # then, save the iteration
663 663 if unfi.obsstore:
664 664 # this message are here for 80 char limit reason
665 665 mso = _("push includes obsolete changeset: %s!")
666 666 mspd = _("push includes phase-divergent changeset: %s!")
667 667 mscd = _("push includes content-divergent changeset: %s!")
668 668 mst = {"orphan": _("push includes orphan changeset: %s!"),
669 669 "phase-divergent": mspd,
670 670 "content-divergent": mscd}
671 671 # If we are to push if there is at least one
672 672 # obsolete or unstable changeset in missing, at
673 673 # least one of the missinghead will be obsolete or
674 674 # unstable. So checking heads only is ok
675 675 for node in outgoing.missingheads:
676 676 ctx = unfi[node]
677 677 if ctx.obsolete():
678 678 raise error.Abort(mso % ctx)
679 679 elif ctx.isunstable():
680 680 # TODO print more than one instability in the abort
681 681 # message
682 682 raise error.Abort(mst[ctx.instabilities()[0]] % ctx)
683 683
684 684 discovery.checkheads(pushop)
685 685 return True
686 686
687 687 # List of names of steps to perform for an outgoing bundle2, order matters.
688 688 b2partsgenorder = []
689 689
690 690 # Mapping between step name and function
691 691 #
692 692 # This exists to help extensions wrap steps if necessary
693 693 b2partsgenmapping = {}
694 694
695 695 def b2partsgenerator(stepname, idx=None):
696 696 """decorator for function generating bundle2 part
697 697
698 698 The function is added to the step -> function mapping and appended to the
699 699 list of steps. Beware that decorated functions will be added in order
700 700 (this may matter).
701 701
702 702 You can only use this decorator for new steps, if you want to wrap a step
703 703 from an extension, attack the b2partsgenmapping dictionary directly."""
704 704 def dec(func):
705 705 assert stepname not in b2partsgenmapping
706 706 b2partsgenmapping[stepname] = func
707 707 if idx is None:
708 708 b2partsgenorder.append(stepname)
709 709 else:
710 710 b2partsgenorder.insert(idx, stepname)
711 711 return func
712 712 return dec
713 713
714 714 def _pushb2ctxcheckheads(pushop, bundler):
715 715 """Generate race condition checking parts
716 716
717 717 Exists as an independent function to aid extensions
718 718 """
719 719 # * 'force' do not check for push race,
720 720 # * if we don't push anything, there are nothing to check.
721 721 if not pushop.force and pushop.outgoing.missingheads:
722 722 allowunrelated = 'related' in bundler.capabilities.get('checkheads', ())
723 723 emptyremote = pushop.pushbranchmap is None
724 724 if not allowunrelated or emptyremote:
725 725 bundler.newpart('check:heads', data=iter(pushop.remoteheads))
726 726 else:
727 727 affected = set()
728 728 for branch, heads in pushop.pushbranchmap.iteritems():
729 729 remoteheads, newheads, unsyncedheads, discardedheads = heads
730 730 if remoteheads is not None:
731 731 remote = set(remoteheads)
732 732 affected |= set(discardedheads) & remote
733 733 affected |= remote - set(newheads)
734 734 if affected:
735 735 data = iter(sorted(affected))
736 736 bundler.newpart('check:updated-heads', data=data)
737 737
738 def _pushing(pushop):
739 """return True if we are pushing anything"""
740 return bool(pushop.outgoing.missing
741 or pushop.outdatedphases
742 or pushop.outobsmarkers
743 or pushop.outbookmarks)
744
745 @b2partsgenerator('check-phases')
746 def _pushb2checkphases(pushop, bundler):
747 """insert phase move checking"""
748 if not _pushing(pushop) or pushop.force:
749 return
750 b2caps = bundle2.bundle2caps(pushop.remote)
751 hasphaseheads = 'heads' in b2caps.get('phases', ())
752 if pushop.remotephases is not None and hasphaseheads:
753 # check that the remote phase has not changed
754 checks = [[] for p in phases.allphases]
755 checks[phases.public].extend(pushop.remotephases.publicheads)
756 checks[phases.draft].extend(pushop.remotephases.draftroots)
757 if any(checks):
758 for nodes in checks:
759 nodes.sort()
760 checkdata = phases.binaryencode(checks)
761 bundler.newpart('check:phases', data=checkdata)
762
738 763 @b2partsgenerator('changeset')
739 764 def _pushb2ctx(pushop, bundler):
740 765 """handle changegroup push through bundle2
741 766
742 767 addchangegroup result is stored in the ``pushop.cgresult`` attribute.
743 768 """
744 769 if 'changesets' in pushop.stepsdone:
745 770 return
746 771 pushop.stepsdone.add('changesets')
747 772 # Send known heads to the server for race detection.
748 773 if not _pushcheckoutgoing(pushop):
749 774 return
750 775 pushop.repo.prepushoutgoinghooks(pushop)
751 776
752 777 _pushb2ctxcheckheads(pushop, bundler)
753 778
754 779 b2caps = bundle2.bundle2caps(pushop.remote)
755 780 version = '01'
756 781 cgversions = b2caps.get('changegroup')
757 782 if cgversions: # 3.1 and 3.2 ship with an empty value
758 783 cgversions = [v for v in cgversions
759 784 if v in changegroup.supportedoutgoingversions(
760 785 pushop.repo)]
761 786 if not cgversions:
762 787 raise ValueError(_('no common changegroup version'))
763 788 version = max(cgversions)
764 789 cgstream = changegroup.makestream(pushop.repo, pushop.outgoing, version,
765 790 'push')
766 791 cgpart = bundler.newpart('changegroup', data=cgstream)
767 792 if cgversions:
768 793 cgpart.addparam('version', version)
769 794 if 'treemanifest' in pushop.repo.requirements:
770 795 cgpart.addparam('treemanifest', '1')
771 796 def handlereply(op):
772 797 """extract addchangegroup returns from server reply"""
773 798 cgreplies = op.records.getreplies(cgpart.id)
774 799 assert len(cgreplies['changegroup']) == 1
775 800 pushop.cgresult = cgreplies['changegroup'][0]['return']
776 801 return handlereply
777 802
778 803 @b2partsgenerator('phase')
779 804 def _pushb2phases(pushop, bundler):
780 805 """handle phase push through bundle2"""
781 806 if 'phases' in pushop.stepsdone:
782 807 return
783 808 b2caps = bundle2.bundle2caps(pushop.remote)
784 809 if not 'pushkey' in b2caps:
785 810 return
786 811 pushop.stepsdone.add('phases')
787 812 part2node = []
788 813
789 814 def handlefailure(pushop, exc):
790 815 targetid = int(exc.partid)
791 816 for partid, node in part2node:
792 817 if partid == targetid:
793 818 raise error.Abort(_('updating %s to public failed') % node)
794 819
795 820 enc = pushkey.encode
796 821 for newremotehead in pushop.outdatedphases:
797 822 part = bundler.newpart('pushkey')
798 823 part.addparam('namespace', enc('phases'))
799 824 part.addparam('key', enc(newremotehead.hex()))
800 825 part.addparam('old', enc('%d' % phases.draft))
801 826 part.addparam('new', enc('%d' % phases.public))
802 827 part2node.append((part.id, newremotehead))
803 828 pushop.pkfailcb[part.id] = handlefailure
804 829
805 830 def handlereply(op):
806 831 for partid, node in part2node:
807 832 partrep = op.records.getreplies(partid)
808 833 results = partrep['pushkey']
809 834 assert len(results) <= 1
810 835 msg = None
811 836 if not results:
812 837 msg = _('server ignored update of %s to public!\n') % node
813 838 elif not int(results[0]['return']):
814 839 msg = _('updating %s to public failed!\n') % node
815 840 if msg is not None:
816 841 pushop.ui.warn(msg)
817 842 return handlereply
818 843
819 844 @b2partsgenerator('obsmarkers')
820 845 def _pushb2obsmarkers(pushop, bundler):
821 846 if 'obsmarkers' in pushop.stepsdone:
822 847 return
823 848 remoteversions = bundle2.obsmarkersversion(bundler.capabilities)
824 849 if obsolete.commonversion(remoteversions) is None:
825 850 return
826 851 pushop.stepsdone.add('obsmarkers')
827 852 if pushop.outobsmarkers:
828 853 markers = sorted(pushop.outobsmarkers)
829 854 bundle2.buildobsmarkerspart(bundler, markers)
830 855
831 856 @b2partsgenerator('bookmarks')
832 857 def _pushb2bookmarks(pushop, bundler):
833 858 """handle bookmark push through bundle2"""
834 859 if 'bookmarks' in pushop.stepsdone:
835 860 return
836 861 b2caps = bundle2.bundle2caps(pushop.remote)
837 862 if 'pushkey' not in b2caps:
838 863 return
839 864 pushop.stepsdone.add('bookmarks')
840 865 part2book = []
841 866 enc = pushkey.encode
842 867
843 868 def handlefailure(pushop, exc):
844 869 targetid = int(exc.partid)
845 870 for partid, book, action in part2book:
846 871 if partid == targetid:
847 872 raise error.Abort(bookmsgmap[action][1].rstrip() % book)
848 873 # we should not be called for part we did not generated
849 874 assert False
850 875
851 876 for book, old, new in pushop.outbookmarks:
852 877 part = bundler.newpart('pushkey')
853 878 part.addparam('namespace', enc('bookmarks'))
854 879 part.addparam('key', enc(book))
855 880 part.addparam('old', enc(old))
856 881 part.addparam('new', enc(new))
857 882 action = 'update'
858 883 if not old:
859 884 action = 'export'
860 885 elif not new:
861 886 action = 'delete'
862 887 part2book.append((part.id, book, action))
863 888 pushop.pkfailcb[part.id] = handlefailure
864 889
865 890 def handlereply(op):
866 891 ui = pushop.ui
867 892 for partid, book, action in part2book:
868 893 partrep = op.records.getreplies(partid)
869 894 results = partrep['pushkey']
870 895 assert len(results) <= 1
871 896 if not results:
872 897 pushop.ui.warn(_('server ignored bookmark %s update\n') % book)
873 898 else:
874 899 ret = int(results[0]['return'])
875 900 if ret:
876 901 ui.status(bookmsgmap[action][0] % book)
877 902 else:
878 903 ui.warn(bookmsgmap[action][1] % book)
879 904 if pushop.bkresult is not None:
880 905 pushop.bkresult = 1
881 906 return handlereply
882 907
883 908 @b2partsgenerator('pushvars', idx=0)
884 909 def _getbundlesendvars(pushop, bundler):
885 910 '''send shellvars via bundle2'''
886 911 pushvars = pushop.pushvars
887 912 if pushvars:
888 913 shellvars = {}
889 914 for raw in pushvars:
890 915 if '=' not in raw:
891 916 msg = ("unable to parse variable '%s', should follow "
892 917 "'KEY=VALUE' or 'KEY=' format")
893 918 raise error.Abort(msg % raw)
894 919 k, v = raw.split('=', 1)
895 920 shellvars[k] = v
896 921
897 922 part = bundler.newpart('pushvars')
898 923
899 924 for key, value in shellvars.iteritems():
900 925 part.addparam(key, value, mandatory=False)
901 926
902 927 def _pushbundle2(pushop):
903 928 """push data to the remote using bundle2
904 929
905 930 The only currently supported type of data is changegroup but this will
906 931 evolve in the future."""
907 932 bundler = bundle2.bundle20(pushop.ui, bundle2.bundle2caps(pushop.remote))
908 933 pushback = (pushop.trmanager
909 934 and pushop.ui.configbool('experimental', 'bundle2.pushback'))
910 935
911 936 # create reply capability
912 937 capsblob = bundle2.encodecaps(bundle2.getrepocaps(pushop.repo,
913 938 allowpushback=pushback))
914 939 bundler.newpart('replycaps', data=capsblob)
915 940 replyhandlers = []
916 941 for partgenname in b2partsgenorder:
917 942 partgen = b2partsgenmapping[partgenname]
918 943 ret = partgen(pushop, bundler)
919 944 if callable(ret):
920 945 replyhandlers.append(ret)
921 946 # do not push if nothing to push
922 947 if bundler.nbparts <= 1:
923 948 return
924 949 stream = util.chunkbuffer(bundler.getchunks())
925 950 try:
926 951 try:
927 952 reply = pushop.remote.unbundle(
928 953 stream, ['force'], pushop.remote.url())
929 954 except error.BundleValueError as exc:
930 955 raise error.Abort(_('missing support for %s') % exc)
931 956 try:
932 957 trgetter = None
933 958 if pushback:
934 959 trgetter = pushop.trmanager.transaction
935 960 op = bundle2.processbundle(pushop.repo, reply, trgetter)
936 961 except error.BundleValueError as exc:
937 962 raise error.Abort(_('missing support for %s') % exc)
938 963 except bundle2.AbortFromPart as exc:
939 964 pushop.ui.status(_('remote: %s\n') % exc)
940 965 if exc.hint is not None:
941 966 pushop.ui.status(_('remote: %s\n') % ('(%s)' % exc.hint))
942 967 raise error.Abort(_('push failed on remote'))
943 968 except error.PushkeyFailed as exc:
944 969 partid = int(exc.partid)
945 970 if partid not in pushop.pkfailcb:
946 971 raise
947 972 pushop.pkfailcb[partid](pushop, exc)
948 973 for rephand in replyhandlers:
949 974 rephand(op)
950 975
951 976 def _pushchangeset(pushop):
952 977 """Make the actual push of changeset bundle to remote repo"""
953 978 if 'changesets' in pushop.stepsdone:
954 979 return
955 980 pushop.stepsdone.add('changesets')
956 981 if not _pushcheckoutgoing(pushop):
957 982 return
958 983
959 984 # Should have verified this in push().
960 985 assert pushop.remote.capable('unbundle')
961 986
962 987 pushop.repo.prepushoutgoinghooks(pushop)
963 988 outgoing = pushop.outgoing
964 989 # TODO: get bundlecaps from remote
965 990 bundlecaps = None
966 991 # create a changegroup from local
967 992 if pushop.revs is None and not (outgoing.excluded
968 993 or pushop.repo.changelog.filteredrevs):
969 994 # push everything,
970 995 # use the fast path, no race possible on push
971 996 cg = changegroup.makechangegroup(pushop.repo, outgoing, '01', 'push',
972 997 fastpath=True, bundlecaps=bundlecaps)
973 998 else:
974 999 cg = changegroup.makechangegroup(pushop.repo, outgoing, '01',
975 1000 'push', bundlecaps=bundlecaps)
976 1001
977 1002 # apply changegroup to remote
978 1003 # local repo finds heads on server, finds out what
979 1004 # revs it must push. once revs transferred, if server
980 1005 # finds it has different heads (someone else won
981 1006 # commit/push race), server aborts.
982 1007 if pushop.force:
983 1008 remoteheads = ['force']
984 1009 else:
985 1010 remoteheads = pushop.remoteheads
986 1011 # ssh: return remote's addchangegroup()
987 1012 # http: return remote's addchangegroup() or 0 for error
988 1013 pushop.cgresult = pushop.remote.unbundle(cg, remoteheads,
989 1014 pushop.repo.url())
990 1015
991 1016 def _pushsyncphase(pushop):
992 1017 """synchronise phase information locally and remotely"""
993 1018 cheads = pushop.commonheads
994 1019 # even when we don't push, exchanging phase data is useful
995 1020 remotephases = pushop.remote.listkeys('phases')
996 1021 if (pushop.ui.configbool('ui', '_usedassubrepo')
997 1022 and remotephases # server supports phases
998 1023 and pushop.cgresult is None # nothing was pushed
999 1024 and remotephases.get('publishing', False)):
1000 1025 # When:
1001 1026 # - this is a subrepo push
1002 1027 # - and remote support phase
1003 1028 # - and no changeset was pushed
1004 1029 # - and remote is publishing
1005 1030 # We may be in issue 3871 case!
1006 1031 # We drop the possible phase synchronisation done by
1007 1032 # courtesy to publish changesets possibly locally draft
1008 1033 # on the remote.
1009 1034 remotephases = {'publishing': 'True'}
1010 1035 if not remotephases: # old server or public only reply from non-publishing
1011 1036 _localphasemove(pushop, cheads)
1012 1037 # don't push any phase data as there is nothing to push
1013 1038 else:
1014 1039 ana = phases.analyzeremotephases(pushop.repo, cheads,
1015 1040 remotephases)
1016 1041 pheads, droots = ana
1017 1042 ### Apply remote phase on local
1018 1043 if remotephases.get('publishing', False):
1019 1044 _localphasemove(pushop, cheads)
1020 1045 else: # publish = False
1021 1046 _localphasemove(pushop, pheads)
1022 1047 _localphasemove(pushop, cheads, phases.draft)
1023 1048 ### Apply local phase on remote
1024 1049
1025 1050 if pushop.cgresult:
1026 1051 if 'phases' in pushop.stepsdone:
1027 1052 # phases already pushed though bundle2
1028 1053 return
1029 1054 outdated = pushop.outdatedphases
1030 1055 else:
1031 1056 outdated = pushop.fallbackoutdatedphases
1032 1057
1033 1058 pushop.stepsdone.add('phases')
1034 1059
1035 1060 # filter heads already turned public by the push
1036 1061 outdated = [c for c in outdated if c.node() not in pheads]
1037 1062 # fallback to independent pushkey command
1038 1063 for newremotehead in outdated:
1039 1064 r = pushop.remote.pushkey('phases',
1040 1065 newremotehead.hex(),
1041 1066 str(phases.draft),
1042 1067 str(phases.public))
1043 1068 if not r:
1044 1069 pushop.ui.warn(_('updating %s to public failed!\n')
1045 1070 % newremotehead)
1046 1071
1047 1072 def _localphasemove(pushop, nodes, phase=phases.public):
1048 1073 """move <nodes> to <phase> in the local source repo"""
1049 1074 if pushop.trmanager:
1050 1075 phases.advanceboundary(pushop.repo,
1051 1076 pushop.trmanager.transaction(),
1052 1077 phase,
1053 1078 nodes)
1054 1079 else:
1055 1080 # repo is not locked, do not change any phases!
1056 1081 # Informs the user that phases should have been moved when
1057 1082 # applicable.
1058 1083 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
1059 1084 phasestr = phases.phasenames[phase]
1060 1085 if actualmoves:
1061 1086 pushop.ui.status(_('cannot lock source repo, skipping '
1062 1087 'local %s phase update\n') % phasestr)
1063 1088
1064 1089 def _pushobsolete(pushop):
1065 1090 """utility function to push obsolete markers to a remote"""
1066 1091 if 'obsmarkers' in pushop.stepsdone:
1067 1092 return
1068 1093 repo = pushop.repo
1069 1094 remote = pushop.remote
1070 1095 pushop.stepsdone.add('obsmarkers')
1071 1096 if pushop.outobsmarkers:
1072 1097 pushop.ui.debug('try to push obsolete markers to remote\n')
1073 1098 rslts = []
1074 1099 remotedata = obsolete._pushkeyescape(sorted(pushop.outobsmarkers))
1075 1100 for key in sorted(remotedata, reverse=True):
1076 1101 # reverse sort to ensure we end with dump0
1077 1102 data = remotedata[key]
1078 1103 rslts.append(remote.pushkey('obsolete', key, '', data))
1079 1104 if [r for r in rslts if not r]:
1080 1105 msg = _('failed to push some obsolete markers!\n')
1081 1106 repo.ui.warn(msg)
1082 1107
1083 1108 def _pushbookmark(pushop):
1084 1109 """Update bookmark position on remote"""
1085 1110 if pushop.cgresult == 0 or 'bookmarks' in pushop.stepsdone:
1086 1111 return
1087 1112 pushop.stepsdone.add('bookmarks')
1088 1113 ui = pushop.ui
1089 1114 remote = pushop.remote
1090 1115
1091 1116 for b, old, new in pushop.outbookmarks:
1092 1117 action = 'update'
1093 1118 if not old:
1094 1119 action = 'export'
1095 1120 elif not new:
1096 1121 action = 'delete'
1097 1122 if remote.pushkey('bookmarks', b, old, new):
1098 1123 ui.status(bookmsgmap[action][0] % b)
1099 1124 else:
1100 1125 ui.warn(bookmsgmap[action][1] % b)
1101 1126 # discovery can have set the value form invalid entry
1102 1127 if pushop.bkresult is not None:
1103 1128 pushop.bkresult = 1
1104 1129
1105 1130 class pulloperation(object):
1106 1131 """A object that represent a single pull operation
1107 1132
1108 1133 It purpose is to carry pull related state and very common operation.
1109 1134
1110 1135 A new should be created at the beginning of each pull and discarded
1111 1136 afterward.
1112 1137 """
1113 1138
1114 1139 def __init__(self, repo, remote, heads=None, force=False, bookmarks=(),
1115 1140 remotebookmarks=None, streamclonerequested=None):
1116 1141 # repo we pull into
1117 1142 self.repo = repo
1118 1143 # repo we pull from
1119 1144 self.remote = remote
1120 1145 # revision we try to pull (None is "all")
1121 1146 self.heads = heads
1122 1147 # bookmark pulled explicitly
1123 1148 self.explicitbookmarks = [repo._bookmarks.expandname(bookmark)
1124 1149 for bookmark in bookmarks]
1125 1150 # do we force pull?
1126 1151 self.force = force
1127 1152 # whether a streaming clone was requested
1128 1153 self.streamclonerequested = streamclonerequested
1129 1154 # transaction manager
1130 1155 self.trmanager = None
1131 1156 # set of common changeset between local and remote before pull
1132 1157 self.common = None
1133 1158 # set of pulled head
1134 1159 self.rheads = None
1135 1160 # list of missing changeset to fetch remotely
1136 1161 self.fetch = None
1137 1162 # remote bookmarks data
1138 1163 self.remotebookmarks = remotebookmarks
1139 1164 # result of changegroup pulling (used as return code by pull)
1140 1165 self.cgresult = None
1141 1166 # list of step already done
1142 1167 self.stepsdone = set()
1143 1168 # Whether we attempted a clone from pre-generated bundles.
1144 1169 self.clonebundleattempted = False
1145 1170
1146 1171 @util.propertycache
1147 1172 def pulledsubset(self):
1148 1173 """heads of the set of changeset target by the pull"""
1149 1174 # compute target subset
1150 1175 if self.heads is None:
1151 1176 # We pulled every thing possible
1152 1177 # sync on everything common
1153 1178 c = set(self.common)
1154 1179 ret = list(self.common)
1155 1180 for n in self.rheads:
1156 1181 if n not in c:
1157 1182 ret.append(n)
1158 1183 return ret
1159 1184 else:
1160 1185 # We pulled a specific subset
1161 1186 # sync on this subset
1162 1187 return self.heads
1163 1188
1164 1189 @util.propertycache
1165 1190 def canusebundle2(self):
1166 1191 return not _forcebundle1(self)
1167 1192
1168 1193 @util.propertycache
1169 1194 def remotebundle2caps(self):
1170 1195 return bundle2.bundle2caps(self.remote)
1171 1196
1172 1197 def gettransaction(self):
1173 1198 # deprecated; talk to trmanager directly
1174 1199 return self.trmanager.transaction()
1175 1200
1176 1201 class transactionmanager(util.transactional):
1177 1202 """An object to manage the life cycle of a transaction
1178 1203
1179 1204 It creates the transaction on demand and calls the appropriate hooks when
1180 1205 closing the transaction."""
1181 1206 def __init__(self, repo, source, url):
1182 1207 self.repo = repo
1183 1208 self.source = source
1184 1209 self.url = url
1185 1210 self._tr = None
1186 1211
1187 1212 def transaction(self):
1188 1213 """Return an open transaction object, constructing if necessary"""
1189 1214 if not self._tr:
1190 1215 trname = '%s\n%s' % (self.source, util.hidepassword(self.url))
1191 1216 self._tr = self.repo.transaction(trname)
1192 1217 self._tr.hookargs['source'] = self.source
1193 1218 self._tr.hookargs['url'] = self.url
1194 1219 return self._tr
1195 1220
1196 1221 def close(self):
1197 1222 """close transaction if created"""
1198 1223 if self._tr is not None:
1199 1224 self._tr.close()
1200 1225
1201 1226 def release(self):
1202 1227 """release transaction if created"""
1203 1228 if self._tr is not None:
1204 1229 self._tr.release()
1205 1230
1206 1231 def pull(repo, remote, heads=None, force=False, bookmarks=(), opargs=None,
1207 1232 streamclonerequested=None):
1208 1233 """Fetch repository data from a remote.
1209 1234
1210 1235 This is the main function used to retrieve data from a remote repository.
1211 1236
1212 1237 ``repo`` is the local repository to clone into.
1213 1238 ``remote`` is a peer instance.
1214 1239 ``heads`` is an iterable of revisions we want to pull. ``None`` (the
1215 1240 default) means to pull everything from the remote.
1216 1241 ``bookmarks`` is an iterable of bookmarks requesting to be pulled. By
1217 1242 default, all remote bookmarks are pulled.
1218 1243 ``opargs`` are additional keyword arguments to pass to ``pulloperation``
1219 1244 initialization.
1220 1245 ``streamclonerequested`` is a boolean indicating whether a "streaming
1221 1246 clone" is requested. A "streaming clone" is essentially a raw file copy
1222 1247 of revlogs from the server. This only works when the local repository is
1223 1248 empty. The default value of ``None`` means to respect the server
1224 1249 configuration for preferring stream clones.
1225 1250
1226 1251 Returns the ``pulloperation`` created for this pull.
1227 1252 """
1228 1253 if opargs is None:
1229 1254 opargs = {}
1230 1255 pullop = pulloperation(repo, remote, heads, force, bookmarks=bookmarks,
1231 1256 streamclonerequested=streamclonerequested, **opargs)
1232 1257
1233 1258 peerlocal = pullop.remote.local()
1234 1259 if peerlocal:
1235 1260 missing = set(peerlocal.requirements) - pullop.repo.supported
1236 1261 if missing:
1237 1262 msg = _("required features are not"
1238 1263 " supported in the destination:"
1239 1264 " %s") % (', '.join(sorted(missing)))
1240 1265 raise error.Abort(msg)
1241 1266
1242 1267 wlock = lock = None
1243 1268 try:
1244 1269 wlock = pullop.repo.wlock()
1245 1270 lock = pullop.repo.lock()
1246 1271 pullop.trmanager = transactionmanager(repo, 'pull', remote.url())
1247 1272 # This should ideally be in _pullbundle2(). However, it needs to run
1248 1273 # before discovery to avoid extra work.
1249 1274 _maybeapplyclonebundle(pullop)
1250 1275 streamclone.maybeperformlegacystreamclone(pullop)
1251 1276 _pulldiscovery(pullop)
1252 1277 if pullop.canusebundle2:
1253 1278 _pullbundle2(pullop)
1254 1279 _pullchangeset(pullop)
1255 1280 _pullphase(pullop)
1256 1281 _pullbookmarks(pullop)
1257 1282 _pullobsolete(pullop)
1258 1283 pullop.trmanager.close()
1259 1284 finally:
1260 1285 lockmod.release(pullop.trmanager, lock, wlock)
1261 1286
1262 1287 return pullop
1263 1288
1264 1289 # list of steps to perform discovery before pull
1265 1290 pulldiscoveryorder = []
1266 1291
1267 1292 # Mapping between step name and function
1268 1293 #
1269 1294 # This exists to help extensions wrap steps if necessary
1270 1295 pulldiscoverymapping = {}
1271 1296
1272 1297 def pulldiscovery(stepname):
1273 1298 """decorator for function performing discovery before pull
1274 1299
1275 1300 The function is added to the step -> function mapping and appended to the
1276 1301 list of steps. Beware that decorated function will be added in order (this
1277 1302 may matter).
1278 1303
1279 1304 You can only use this decorator for a new step, if you want to wrap a step
1280 1305 from an extension, change the pulldiscovery dictionary directly."""
1281 1306 def dec(func):
1282 1307 assert stepname not in pulldiscoverymapping
1283 1308 pulldiscoverymapping[stepname] = func
1284 1309 pulldiscoveryorder.append(stepname)
1285 1310 return func
1286 1311 return dec
1287 1312
1288 1313 def _pulldiscovery(pullop):
1289 1314 """Run all discovery steps"""
1290 1315 for stepname in pulldiscoveryorder:
1291 1316 step = pulldiscoverymapping[stepname]
1292 1317 step(pullop)
1293 1318
1294 1319 @pulldiscovery('b1:bookmarks')
1295 1320 def _pullbookmarkbundle1(pullop):
1296 1321 """fetch bookmark data in bundle1 case
1297 1322
1298 1323 If not using bundle2, we have to fetch bookmarks before changeset
1299 1324 discovery to reduce the chance and impact of race conditions."""
1300 1325 if pullop.remotebookmarks is not None:
1301 1326 return
1302 1327 if pullop.canusebundle2 and 'listkeys' in pullop.remotebundle2caps:
1303 1328 # all known bundle2 servers now support listkeys, but lets be nice with
1304 1329 # new implementation.
1305 1330 return
1306 1331 pullop.remotebookmarks = pullop.remote.listkeys('bookmarks')
1307 1332
1308 1333
1309 1334 @pulldiscovery('changegroup')
1310 1335 def _pulldiscoverychangegroup(pullop):
1311 1336 """discovery phase for the pull
1312 1337
1313 1338 Current handle changeset discovery only, will change handle all discovery
1314 1339 at some point."""
1315 1340 tmp = discovery.findcommonincoming(pullop.repo,
1316 1341 pullop.remote,
1317 1342 heads=pullop.heads,
1318 1343 force=pullop.force)
1319 1344 common, fetch, rheads = tmp
1320 1345 nm = pullop.repo.unfiltered().changelog.nodemap
1321 1346 if fetch and rheads:
1322 1347 # If a remote heads is filtered locally, put in back in common.
1323 1348 #
1324 1349 # This is a hackish solution to catch most of "common but locally
1325 1350 # hidden situation". We do not performs discovery on unfiltered
1326 1351 # repository because it end up doing a pathological amount of round
1327 1352 # trip for w huge amount of changeset we do not care about.
1328 1353 #
1329 1354 # If a set of such "common but filtered" changeset exist on the server
1330 1355 # but are not including a remote heads, we'll not be able to detect it,
1331 1356 scommon = set(common)
1332 1357 for n in rheads:
1333 1358 if n in nm:
1334 1359 if n not in scommon:
1335 1360 common.append(n)
1336 1361 if set(rheads).issubset(set(common)):
1337 1362 fetch = []
1338 1363 pullop.common = common
1339 1364 pullop.fetch = fetch
1340 1365 pullop.rheads = rheads
1341 1366
1342 1367 def _pullbundle2(pullop):
1343 1368 """pull data using bundle2
1344 1369
1345 1370 For now, the only supported data are changegroup."""
1346 1371 kwargs = {'bundlecaps': caps20to10(pullop.repo)}
1347 1372
1348 1373 # At the moment we don't do stream clones over bundle2. If that is
1349 1374 # implemented then here's where the check for that will go.
1350 1375 streaming = False
1351 1376
1352 1377 # pulling changegroup
1353 1378 pullop.stepsdone.add('changegroup')
1354 1379
1355 1380 kwargs['common'] = pullop.common
1356 1381 kwargs['heads'] = pullop.heads or pullop.rheads
1357 1382 kwargs['cg'] = pullop.fetch
1358 1383
1359 1384 ui = pullop.repo.ui
1360 1385 legacyphase = 'phases' in ui.configlist('devel', 'legacy.exchange')
1361 1386 hasbinaryphase = 'heads' in pullop.remotebundle2caps.get('phases', ())
1362 1387 if (not legacyphase and hasbinaryphase):
1363 1388 kwargs['phases'] = True
1364 1389 pullop.stepsdone.add('phases')
1365 1390
1366 1391 if 'listkeys' in pullop.remotebundle2caps:
1367 1392 if 'phases' not in pullop.stepsdone:
1368 1393 kwargs['listkeys'] = ['phases']
1369 1394 if pullop.remotebookmarks is None:
1370 1395 # make sure to always includes bookmark data when migrating
1371 1396 # `hg incoming --bundle` to using this function.
1372 1397 kwargs.setdefault('listkeys', []).append('bookmarks')
1373 1398
1374 1399 # If this is a full pull / clone and the server supports the clone bundles
1375 1400 # feature, tell the server whether we attempted a clone bundle. The
1376 1401 # presence of this flag indicates the client supports clone bundles. This
1377 1402 # will enable the server to treat clients that support clone bundles
1378 1403 # differently from those that don't.
1379 1404 if (pullop.remote.capable('clonebundles')
1380 1405 and pullop.heads is None and list(pullop.common) == [nullid]):
1381 1406 kwargs['cbattempted'] = pullop.clonebundleattempted
1382 1407
1383 1408 if streaming:
1384 1409 pullop.repo.ui.status(_('streaming all changes\n'))
1385 1410 elif not pullop.fetch:
1386 1411 pullop.repo.ui.status(_("no changes found\n"))
1387 1412 pullop.cgresult = 0
1388 1413 else:
1389 1414 if pullop.heads is None and list(pullop.common) == [nullid]:
1390 1415 pullop.repo.ui.status(_("requesting all changes\n"))
1391 1416 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
1392 1417 remoteversions = bundle2.obsmarkersversion(pullop.remotebundle2caps)
1393 1418 if obsolete.commonversion(remoteversions) is not None:
1394 1419 kwargs['obsmarkers'] = True
1395 1420 pullop.stepsdone.add('obsmarkers')
1396 1421 _pullbundle2extraprepare(pullop, kwargs)
1397 1422 bundle = pullop.remote.getbundle('pull', **pycompat.strkwargs(kwargs))
1398 1423 try:
1399 1424 op = bundle2.processbundle(pullop.repo, bundle, pullop.gettransaction)
1400 1425 except bundle2.AbortFromPart as exc:
1401 1426 pullop.repo.ui.status(_('remote: abort: %s\n') % exc)
1402 1427 raise error.Abort(_('pull failed on remote'), hint=exc.hint)
1403 1428 except error.BundleValueError as exc:
1404 1429 raise error.Abort(_('missing support for %s') % exc)
1405 1430
1406 1431 if pullop.fetch:
1407 1432 pullop.cgresult = bundle2.combinechangegroupresults(op)
1408 1433
1409 1434 # processing phases change
1410 1435 for namespace, value in op.records['listkeys']:
1411 1436 if namespace == 'phases':
1412 1437 _pullapplyphases(pullop, value)
1413 1438
1414 1439 # processing bookmark update
1415 1440 for namespace, value in op.records['listkeys']:
1416 1441 if namespace == 'bookmarks':
1417 1442 pullop.remotebookmarks = value
1418 1443
1419 1444 # bookmark data were either already there or pulled in the bundle
1420 1445 if pullop.remotebookmarks is not None:
1421 1446 _pullbookmarks(pullop)
1422 1447
1423 1448 def _pullbundle2extraprepare(pullop, kwargs):
1424 1449 """hook function so that extensions can extend the getbundle call"""
1425 1450
1426 1451 def _pullchangeset(pullop):
1427 1452 """pull changeset from unbundle into the local repo"""
1428 1453 # We delay the open of the transaction as late as possible so we
1429 1454 # don't open transaction for nothing or you break future useful
1430 1455 # rollback call
1431 1456 if 'changegroup' in pullop.stepsdone:
1432 1457 return
1433 1458 pullop.stepsdone.add('changegroup')
1434 1459 if not pullop.fetch:
1435 1460 pullop.repo.ui.status(_("no changes found\n"))
1436 1461 pullop.cgresult = 0
1437 1462 return
1438 1463 tr = pullop.gettransaction()
1439 1464 if pullop.heads is None and list(pullop.common) == [nullid]:
1440 1465 pullop.repo.ui.status(_("requesting all changes\n"))
1441 1466 elif pullop.heads is None and pullop.remote.capable('changegroupsubset'):
1442 1467 # issue1320, avoid a race if remote changed after discovery
1443 1468 pullop.heads = pullop.rheads
1444 1469
1445 1470 if pullop.remote.capable('getbundle'):
1446 1471 # TODO: get bundlecaps from remote
1447 1472 cg = pullop.remote.getbundle('pull', common=pullop.common,
1448 1473 heads=pullop.heads or pullop.rheads)
1449 1474 elif pullop.heads is None:
1450 1475 cg = pullop.remote.changegroup(pullop.fetch, 'pull')
1451 1476 elif not pullop.remote.capable('changegroupsubset'):
1452 1477 raise error.Abort(_("partial pull cannot be done because "
1453 1478 "other repository doesn't support "
1454 1479 "changegroupsubset."))
1455 1480 else:
1456 1481 cg = pullop.remote.changegroupsubset(pullop.fetch, pullop.heads, 'pull')
1457 1482 bundleop = bundle2.applybundle(pullop.repo, cg, tr, 'pull',
1458 1483 pullop.remote.url())
1459 1484 pullop.cgresult = bundle2.combinechangegroupresults(bundleop)
1460 1485
1461 1486 def _pullphase(pullop):
1462 1487 # Get remote phases data from remote
1463 1488 if 'phases' in pullop.stepsdone:
1464 1489 return
1465 1490 remotephases = pullop.remote.listkeys('phases')
1466 1491 _pullapplyphases(pullop, remotephases)
1467 1492
1468 1493 def _pullapplyphases(pullop, remotephases):
1469 1494 """apply phase movement from observed remote state"""
1470 1495 if 'phases' in pullop.stepsdone:
1471 1496 return
1472 1497 pullop.stepsdone.add('phases')
1473 1498 publishing = bool(remotephases.get('publishing', False))
1474 1499 if remotephases and not publishing:
1475 1500 # remote is new and non-publishing
1476 1501 pheads, _dr = phases.analyzeremotephases(pullop.repo,
1477 1502 pullop.pulledsubset,
1478 1503 remotephases)
1479 1504 dheads = pullop.pulledsubset
1480 1505 else:
1481 1506 # Remote is old or publishing all common changesets
1482 1507 # should be seen as public
1483 1508 pheads = pullop.pulledsubset
1484 1509 dheads = []
1485 1510 unfi = pullop.repo.unfiltered()
1486 1511 phase = unfi._phasecache.phase
1487 1512 rev = unfi.changelog.nodemap.get
1488 1513 public = phases.public
1489 1514 draft = phases.draft
1490 1515
1491 1516 # exclude changesets already public locally and update the others
1492 1517 pheads = [pn for pn in pheads if phase(unfi, rev(pn)) > public]
1493 1518 if pheads:
1494 1519 tr = pullop.gettransaction()
1495 1520 phases.advanceboundary(pullop.repo, tr, public, pheads)
1496 1521
1497 1522 # exclude changesets already draft locally and update the others
1498 1523 dheads = [pn for pn in dheads if phase(unfi, rev(pn)) > draft]
1499 1524 if dheads:
1500 1525 tr = pullop.gettransaction()
1501 1526 phases.advanceboundary(pullop.repo, tr, draft, dheads)
1502 1527
1503 1528 def _pullbookmarks(pullop):
1504 1529 """process the remote bookmark information to update the local one"""
1505 1530 if 'bookmarks' in pullop.stepsdone:
1506 1531 return
1507 1532 pullop.stepsdone.add('bookmarks')
1508 1533 repo = pullop.repo
1509 1534 remotebookmarks = pullop.remotebookmarks
1510 1535 remotebookmarks = bookmod.unhexlifybookmarks(remotebookmarks)
1511 1536 bookmod.updatefromremote(repo.ui, repo, remotebookmarks,
1512 1537 pullop.remote.url(),
1513 1538 pullop.gettransaction,
1514 1539 explicit=pullop.explicitbookmarks)
1515 1540
1516 1541 def _pullobsolete(pullop):
1517 1542 """utility function to pull obsolete markers from a remote
1518 1543
1519 1544 The `gettransaction` is function that return the pull transaction, creating
1520 1545 one if necessary. We return the transaction to inform the calling code that
1521 1546 a new transaction have been created (when applicable).
1522 1547
1523 1548 Exists mostly to allow overriding for experimentation purpose"""
1524 1549 if 'obsmarkers' in pullop.stepsdone:
1525 1550 return
1526 1551 pullop.stepsdone.add('obsmarkers')
1527 1552 tr = None
1528 1553 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
1529 1554 pullop.repo.ui.debug('fetching remote obsolete markers\n')
1530 1555 remoteobs = pullop.remote.listkeys('obsolete')
1531 1556 if 'dump0' in remoteobs:
1532 1557 tr = pullop.gettransaction()
1533 1558 markers = []
1534 1559 for key in sorted(remoteobs, reverse=True):
1535 1560 if key.startswith('dump'):
1536 1561 data = util.b85decode(remoteobs[key])
1537 1562 version, newmarks = obsolete._readmarkers(data)
1538 1563 markers += newmarks
1539 1564 if markers:
1540 1565 pullop.repo.obsstore.add(tr, markers)
1541 1566 pullop.repo.invalidatevolatilesets()
1542 1567 return tr
1543 1568
1544 1569 def caps20to10(repo):
1545 1570 """return a set with appropriate options to use bundle20 during getbundle"""
1546 1571 caps = {'HG20'}
1547 1572 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
1548 1573 caps.add('bundle2=' + urlreq.quote(capsblob))
1549 1574 return caps
1550 1575
1551 1576 # List of names of steps to perform for a bundle2 for getbundle, order matters.
1552 1577 getbundle2partsorder = []
1553 1578
1554 1579 # Mapping between step name and function
1555 1580 #
1556 1581 # This exists to help extensions wrap steps if necessary
1557 1582 getbundle2partsmapping = {}
1558 1583
1559 1584 def getbundle2partsgenerator(stepname, idx=None):
1560 1585 """decorator for function generating bundle2 part for getbundle
1561 1586
1562 1587 The function is added to the step -> function mapping and appended to the
1563 1588 list of steps. Beware that decorated functions will be added in order
1564 1589 (this may matter).
1565 1590
1566 1591 You can only use this decorator for new steps, if you want to wrap a step
1567 1592 from an extension, attack the getbundle2partsmapping dictionary directly."""
1568 1593 def dec(func):
1569 1594 assert stepname not in getbundle2partsmapping
1570 1595 getbundle2partsmapping[stepname] = func
1571 1596 if idx is None:
1572 1597 getbundle2partsorder.append(stepname)
1573 1598 else:
1574 1599 getbundle2partsorder.insert(idx, stepname)
1575 1600 return func
1576 1601 return dec
1577 1602
1578 1603 def bundle2requested(bundlecaps):
1579 1604 if bundlecaps is not None:
1580 1605 return any(cap.startswith('HG2') for cap in bundlecaps)
1581 1606 return False
1582 1607
1583 1608 def getbundlechunks(repo, source, heads=None, common=None, bundlecaps=None,
1584 1609 **kwargs):
1585 1610 """Return chunks constituting a bundle's raw data.
1586 1611
1587 1612 Could be a bundle HG10 or a bundle HG20 depending on bundlecaps
1588 1613 passed.
1589 1614
1590 1615 Returns an iterator over raw chunks (of varying sizes).
1591 1616 """
1592 1617 kwargs = pycompat.byteskwargs(kwargs)
1593 1618 usebundle2 = bundle2requested(bundlecaps)
1594 1619 # bundle10 case
1595 1620 if not usebundle2:
1596 1621 if bundlecaps and not kwargs.get('cg', True):
1597 1622 raise ValueError(_('request for bundle10 must include changegroup'))
1598 1623
1599 1624 if kwargs:
1600 1625 raise ValueError(_('unsupported getbundle arguments: %s')
1601 1626 % ', '.join(sorted(kwargs.keys())))
1602 1627 outgoing = _computeoutgoing(repo, heads, common)
1603 1628 return changegroup.makestream(repo, outgoing, '01', source,
1604 1629 bundlecaps=bundlecaps)
1605 1630
1606 1631 # bundle20 case
1607 1632 b2caps = {}
1608 1633 for bcaps in bundlecaps:
1609 1634 if bcaps.startswith('bundle2='):
1610 1635 blob = urlreq.unquote(bcaps[len('bundle2='):])
1611 1636 b2caps.update(bundle2.decodecaps(blob))
1612 1637 bundler = bundle2.bundle20(repo.ui, b2caps)
1613 1638
1614 1639 kwargs['heads'] = heads
1615 1640 kwargs['common'] = common
1616 1641
1617 1642 for name in getbundle2partsorder:
1618 1643 func = getbundle2partsmapping[name]
1619 1644 func(bundler, repo, source, bundlecaps=bundlecaps, b2caps=b2caps,
1620 1645 **pycompat.strkwargs(kwargs))
1621 1646
1622 1647 return bundler.getchunks()
1623 1648
1624 1649 @getbundle2partsgenerator('changegroup')
1625 1650 def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None,
1626 1651 b2caps=None, heads=None, common=None, **kwargs):
1627 1652 """add a changegroup part to the requested bundle"""
1628 1653 cgstream = None
1629 1654 if kwargs.get('cg', True):
1630 1655 # build changegroup bundle here.
1631 1656 version = '01'
1632 1657 cgversions = b2caps.get('changegroup')
1633 1658 if cgversions: # 3.1 and 3.2 ship with an empty value
1634 1659 cgversions = [v for v in cgversions
1635 1660 if v in changegroup.supportedoutgoingversions(repo)]
1636 1661 if not cgversions:
1637 1662 raise ValueError(_('no common changegroup version'))
1638 1663 version = max(cgversions)
1639 1664 outgoing = _computeoutgoing(repo, heads, common)
1640 1665 if outgoing.missing:
1641 1666 cgstream = changegroup.makestream(repo, outgoing, version, source,
1642 1667 bundlecaps=bundlecaps)
1643 1668
1644 1669 if cgstream:
1645 1670 part = bundler.newpart('changegroup', data=cgstream)
1646 1671 if cgversions:
1647 1672 part.addparam('version', version)
1648 1673 part.addparam('nbchanges', '%d' % len(outgoing.missing),
1649 1674 mandatory=False)
1650 1675 if 'treemanifest' in repo.requirements:
1651 1676 part.addparam('treemanifest', '1')
1652 1677
1653 1678 @getbundle2partsgenerator('listkeys')
1654 1679 def _getbundlelistkeysparts(bundler, repo, source, bundlecaps=None,
1655 1680 b2caps=None, **kwargs):
1656 1681 """add parts containing listkeys namespaces to the requested bundle"""
1657 1682 listkeys = kwargs.get('listkeys', ())
1658 1683 for namespace in listkeys:
1659 1684 part = bundler.newpart('listkeys')
1660 1685 part.addparam('namespace', namespace)
1661 1686 keys = repo.listkeys(namespace).items()
1662 1687 part.data = pushkey.encodekeys(keys)
1663 1688
1664 1689 @getbundle2partsgenerator('obsmarkers')
1665 1690 def _getbundleobsmarkerpart(bundler, repo, source, bundlecaps=None,
1666 1691 b2caps=None, heads=None, **kwargs):
1667 1692 """add an obsolescence markers part to the requested bundle"""
1668 1693 if kwargs.get('obsmarkers', False):
1669 1694 if heads is None:
1670 1695 heads = repo.heads()
1671 1696 subset = [c.node() for c in repo.set('::%ln', heads)]
1672 1697 markers = repo.obsstore.relevantmarkers(subset)
1673 1698 markers = sorted(markers)
1674 1699 bundle2.buildobsmarkerspart(bundler, markers)
1675 1700
1676 1701 @getbundle2partsgenerator('phases')
1677 1702 def _getbundlephasespart(bundler, repo, source, bundlecaps=None,
1678 1703 b2caps=None, heads=None, **kwargs):
1679 1704 """add phase heads part to the requested bundle"""
1680 1705 if kwargs.get('phases', False):
1681 1706 if not 'heads' in b2caps.get('phases'):
1682 1707 raise ValueError(_('no common phases exchange method'))
1683 1708 if heads is None:
1684 1709 heads = repo.heads()
1685 1710
1686 1711 headsbyphase = collections.defaultdict(set)
1687 1712 if repo.publishing():
1688 1713 headsbyphase[phases.public] = heads
1689 1714 else:
1690 1715 # find the appropriate heads to move
1691 1716
1692 1717 phase = repo._phasecache.phase
1693 1718 node = repo.changelog.node
1694 1719 rev = repo.changelog.rev
1695 1720 for h in heads:
1696 1721 headsbyphase[phase(repo, rev(h))].add(h)
1697 1722 seenphases = list(headsbyphase.keys())
1698 1723
1699 1724 # We do not handle anything but public and draft phase for now)
1700 1725 if seenphases:
1701 1726 assert max(seenphases) <= phases.draft
1702 1727
1703 1728 # if client is pulling non-public changesets, we need to find
1704 1729 # intermediate public heads.
1705 1730 draftheads = headsbyphase.get(phases.draft, set())
1706 1731 if draftheads:
1707 1732 publicheads = headsbyphase.get(phases.public, set())
1708 1733
1709 1734 revset = 'heads(only(%ln, %ln) and public())'
1710 1735 extraheads = repo.revs(revset, draftheads, publicheads)
1711 1736 for r in extraheads:
1712 1737 headsbyphase[phases.public].add(node(r))
1713 1738
1714 1739 # transform data in a format used by the encoding function
1715 1740 phasemapping = []
1716 1741 for phase in phases.allphases:
1717 1742 phasemapping.append(sorted(headsbyphase[phase]))
1718 1743
1719 1744 # generate the actual part
1720 1745 phasedata = phases.binaryencode(phasemapping)
1721 1746 bundler.newpart('phase-heads', data=phasedata)
1722 1747
1723 1748 @getbundle2partsgenerator('hgtagsfnodes')
1724 1749 def _getbundletagsfnodes(bundler, repo, source, bundlecaps=None,
1725 1750 b2caps=None, heads=None, common=None,
1726 1751 **kwargs):
1727 1752 """Transfer the .hgtags filenodes mapping.
1728 1753
1729 1754 Only values for heads in this bundle will be transferred.
1730 1755
1731 1756 The part data consists of pairs of 20 byte changeset node and .hgtags
1732 1757 filenodes raw values.
1733 1758 """
1734 1759 # Don't send unless:
1735 1760 # - changeset are being exchanged,
1736 1761 # - the client supports it.
1737 1762 if not (kwargs.get('cg', True) and 'hgtagsfnodes' in b2caps):
1738 1763 return
1739 1764
1740 1765 outgoing = _computeoutgoing(repo, heads, common)
1741 1766 bundle2.addparttagsfnodescache(repo, bundler, outgoing)
1742 1767
1743 1768 def _getbookmarks(repo, **kwargs):
1744 1769 """Returns bookmark to node mapping.
1745 1770
1746 1771 This function is primarily used to generate `bookmarks` bundle2 part.
1747 1772 It is a separate function in order to make it easy to wrap it
1748 1773 in extensions. Passing `kwargs` to the function makes it easy to
1749 1774 add new parameters in extensions.
1750 1775 """
1751 1776
1752 1777 return dict(bookmod.listbinbookmarks(repo))
1753 1778
1754 1779 def check_heads(repo, their_heads, context):
1755 1780 """check if the heads of a repo have been modified
1756 1781
1757 1782 Used by peer for unbundling.
1758 1783 """
1759 1784 heads = repo.heads()
1760 1785 heads_hash = hashlib.sha1(''.join(sorted(heads))).digest()
1761 1786 if not (their_heads == ['force'] or their_heads == heads or
1762 1787 their_heads == ['hashed', heads_hash]):
1763 1788 # someone else committed/pushed/unbundled while we
1764 1789 # were transferring data
1765 1790 raise error.PushRaced('repository changed while %s - '
1766 1791 'please try again' % context)
1767 1792
1768 1793 def unbundle(repo, cg, heads, source, url):
1769 1794 """Apply a bundle to a repo.
1770 1795
1771 1796 this function makes sure the repo is locked during the application and have
1772 1797 mechanism to check that no push race occurred between the creation of the
1773 1798 bundle and its application.
1774 1799
1775 1800 If the push was raced as PushRaced exception is raised."""
1776 1801 r = 0
1777 1802 # need a transaction when processing a bundle2 stream
1778 1803 # [wlock, lock, tr] - needs to be an array so nested functions can modify it
1779 1804 lockandtr = [None, None, None]
1780 1805 recordout = None
1781 1806 # quick fix for output mismatch with bundle2 in 3.4
1782 1807 captureoutput = repo.ui.configbool('experimental', 'bundle2-output-capture')
1783 1808 if url.startswith('remote:http:') or url.startswith('remote:https:'):
1784 1809 captureoutput = True
1785 1810 try:
1786 1811 # note: outside bundle1, 'heads' is expected to be empty and this
1787 1812 # 'check_heads' call wil be a no-op
1788 1813 check_heads(repo, heads, 'uploading changes')
1789 1814 # push can proceed
1790 1815 if not isinstance(cg, bundle2.unbundle20):
1791 1816 # legacy case: bundle1 (changegroup 01)
1792 1817 txnname = "\n".join([source, util.hidepassword(url)])
1793 1818 with repo.lock(), repo.transaction(txnname) as tr:
1794 1819 op = bundle2.applybundle(repo, cg, tr, source, url)
1795 1820 r = bundle2.combinechangegroupresults(op)
1796 1821 else:
1797 1822 r = None
1798 1823 try:
1799 1824 def gettransaction():
1800 1825 if not lockandtr[2]:
1801 1826 lockandtr[0] = repo.wlock()
1802 1827 lockandtr[1] = repo.lock()
1803 1828 lockandtr[2] = repo.transaction(source)
1804 1829 lockandtr[2].hookargs['source'] = source
1805 1830 lockandtr[2].hookargs['url'] = url
1806 1831 lockandtr[2].hookargs['bundle2'] = '1'
1807 1832 return lockandtr[2]
1808 1833
1809 1834 # Do greedy locking by default until we're satisfied with lazy
1810 1835 # locking.
1811 1836 if not repo.ui.configbool('experimental', 'bundle2lazylocking'):
1812 1837 gettransaction()
1813 1838
1814 1839 op = bundle2.bundleoperation(repo, gettransaction,
1815 1840 captureoutput=captureoutput)
1816 1841 try:
1817 1842 op = bundle2.processbundle(repo, cg, op=op)
1818 1843 finally:
1819 1844 r = op.reply
1820 1845 if captureoutput and r is not None:
1821 1846 repo.ui.pushbuffer(error=True, subproc=True)
1822 1847 def recordout(output):
1823 1848 r.newpart('output', data=output, mandatory=False)
1824 1849 if lockandtr[2] is not None:
1825 1850 lockandtr[2].close()
1826 1851 except BaseException as exc:
1827 1852 exc.duringunbundle2 = True
1828 1853 if captureoutput and r is not None:
1829 1854 parts = exc._bundle2salvagedoutput = r.salvageoutput()
1830 1855 def recordout(output):
1831 1856 part = bundle2.bundlepart('output', data=output,
1832 1857 mandatory=False)
1833 1858 parts.append(part)
1834 1859 raise
1835 1860 finally:
1836 1861 lockmod.release(lockandtr[2], lockandtr[1], lockandtr[0])
1837 1862 if recordout is not None:
1838 1863 recordout(repo.ui.popbuffer())
1839 1864 return r
1840 1865
1841 1866 def _maybeapplyclonebundle(pullop):
1842 1867 """Apply a clone bundle from a remote, if possible."""
1843 1868
1844 1869 repo = pullop.repo
1845 1870 remote = pullop.remote
1846 1871
1847 1872 if not repo.ui.configbool('ui', 'clonebundles'):
1848 1873 return
1849 1874
1850 1875 # Only run if local repo is empty.
1851 1876 if len(repo):
1852 1877 return
1853 1878
1854 1879 if pullop.heads:
1855 1880 return
1856 1881
1857 1882 if not remote.capable('clonebundles'):
1858 1883 return
1859 1884
1860 1885 res = remote._call('clonebundles')
1861 1886
1862 1887 # If we call the wire protocol command, that's good enough to record the
1863 1888 # attempt.
1864 1889 pullop.clonebundleattempted = True
1865 1890
1866 1891 entries = parseclonebundlesmanifest(repo, res)
1867 1892 if not entries:
1868 1893 repo.ui.note(_('no clone bundles available on remote; '
1869 1894 'falling back to regular clone\n'))
1870 1895 return
1871 1896
1872 1897 entries = filterclonebundleentries(
1873 1898 repo, entries, streamclonerequested=pullop.streamclonerequested)
1874 1899
1875 1900 if not entries:
1876 1901 # There is a thundering herd concern here. However, if a server
1877 1902 # operator doesn't advertise bundles appropriate for its clients,
1878 1903 # they deserve what's coming. Furthermore, from a client's
1879 1904 # perspective, no automatic fallback would mean not being able to
1880 1905 # clone!
1881 1906 repo.ui.warn(_('no compatible clone bundles available on server; '
1882 1907 'falling back to regular clone\n'))
1883 1908 repo.ui.warn(_('(you may want to report this to the server '
1884 1909 'operator)\n'))
1885 1910 return
1886 1911
1887 1912 entries = sortclonebundleentries(repo.ui, entries)
1888 1913
1889 1914 url = entries[0]['URL']
1890 1915 repo.ui.status(_('applying clone bundle from %s\n') % url)
1891 1916 if trypullbundlefromurl(repo.ui, repo, url):
1892 1917 repo.ui.status(_('finished applying clone bundle\n'))
1893 1918 # Bundle failed.
1894 1919 #
1895 1920 # We abort by default to avoid the thundering herd of
1896 1921 # clients flooding a server that was expecting expensive
1897 1922 # clone load to be offloaded.
1898 1923 elif repo.ui.configbool('ui', 'clonebundlefallback'):
1899 1924 repo.ui.warn(_('falling back to normal clone\n'))
1900 1925 else:
1901 1926 raise error.Abort(_('error applying bundle'),
1902 1927 hint=_('if this error persists, consider contacting '
1903 1928 'the server operator or disable clone '
1904 1929 'bundles via '
1905 1930 '"--config ui.clonebundles=false"'))
1906 1931
1907 1932 def parseclonebundlesmanifest(repo, s):
1908 1933 """Parses the raw text of a clone bundles manifest.
1909 1934
1910 1935 Returns a list of dicts. The dicts have a ``URL`` key corresponding
1911 1936 to the URL and other keys are the attributes for the entry.
1912 1937 """
1913 1938 m = []
1914 1939 for line in s.splitlines():
1915 1940 fields = line.split()
1916 1941 if not fields:
1917 1942 continue
1918 1943 attrs = {'URL': fields[0]}
1919 1944 for rawattr in fields[1:]:
1920 1945 key, value = rawattr.split('=', 1)
1921 1946 key = urlreq.unquote(key)
1922 1947 value = urlreq.unquote(value)
1923 1948 attrs[key] = value
1924 1949
1925 1950 # Parse BUNDLESPEC into components. This makes client-side
1926 1951 # preferences easier to specify since you can prefer a single
1927 1952 # component of the BUNDLESPEC.
1928 1953 if key == 'BUNDLESPEC':
1929 1954 try:
1930 1955 comp, version, params = parsebundlespec(repo, value,
1931 1956 externalnames=True)
1932 1957 attrs['COMPRESSION'] = comp
1933 1958 attrs['VERSION'] = version
1934 1959 except error.InvalidBundleSpecification:
1935 1960 pass
1936 1961 except error.UnsupportedBundleSpecification:
1937 1962 pass
1938 1963
1939 1964 m.append(attrs)
1940 1965
1941 1966 return m
1942 1967
1943 1968 def filterclonebundleentries(repo, entries, streamclonerequested=False):
1944 1969 """Remove incompatible clone bundle manifest entries.
1945 1970
1946 1971 Accepts a list of entries parsed with ``parseclonebundlesmanifest``
1947 1972 and returns a new list consisting of only the entries that this client
1948 1973 should be able to apply.
1949 1974
1950 1975 There is no guarantee we'll be able to apply all returned entries because
1951 1976 the metadata we use to filter on may be missing or wrong.
1952 1977 """
1953 1978 newentries = []
1954 1979 for entry in entries:
1955 1980 spec = entry.get('BUNDLESPEC')
1956 1981 if spec:
1957 1982 try:
1958 1983 comp, version, params = parsebundlespec(repo, spec, strict=True)
1959 1984
1960 1985 # If a stream clone was requested, filter out non-streamclone
1961 1986 # entries.
1962 1987 if streamclonerequested and (comp != 'UN' or version != 's1'):
1963 1988 repo.ui.debug('filtering %s because not a stream clone\n' %
1964 1989 entry['URL'])
1965 1990 continue
1966 1991
1967 1992 except error.InvalidBundleSpecification as e:
1968 1993 repo.ui.debug(str(e) + '\n')
1969 1994 continue
1970 1995 except error.UnsupportedBundleSpecification as e:
1971 1996 repo.ui.debug('filtering %s because unsupported bundle '
1972 1997 'spec: %s\n' % (entry['URL'], str(e)))
1973 1998 continue
1974 1999 # If we don't have a spec and requested a stream clone, we don't know
1975 2000 # what the entry is so don't attempt to apply it.
1976 2001 elif streamclonerequested:
1977 2002 repo.ui.debug('filtering %s because cannot determine if a stream '
1978 2003 'clone bundle\n' % entry['URL'])
1979 2004 continue
1980 2005
1981 2006 if 'REQUIRESNI' in entry and not sslutil.hassni:
1982 2007 repo.ui.debug('filtering %s because SNI not supported\n' %
1983 2008 entry['URL'])
1984 2009 continue
1985 2010
1986 2011 newentries.append(entry)
1987 2012
1988 2013 return newentries
1989 2014
1990 2015 class clonebundleentry(object):
1991 2016 """Represents an item in a clone bundles manifest.
1992 2017
1993 2018 This rich class is needed to support sorting since sorted() in Python 3
1994 2019 doesn't support ``cmp`` and our comparison is complex enough that ``key=``
1995 2020 won't work.
1996 2021 """
1997 2022
1998 2023 def __init__(self, value, prefers):
1999 2024 self.value = value
2000 2025 self.prefers = prefers
2001 2026
2002 2027 def _cmp(self, other):
2003 2028 for prefkey, prefvalue in self.prefers:
2004 2029 avalue = self.value.get(prefkey)
2005 2030 bvalue = other.value.get(prefkey)
2006 2031
2007 2032 # Special case for b missing attribute and a matches exactly.
2008 2033 if avalue is not None and bvalue is None and avalue == prefvalue:
2009 2034 return -1
2010 2035
2011 2036 # Special case for a missing attribute and b matches exactly.
2012 2037 if bvalue is not None and avalue is None and bvalue == prefvalue:
2013 2038 return 1
2014 2039
2015 2040 # We can't compare unless attribute present on both.
2016 2041 if avalue is None or bvalue is None:
2017 2042 continue
2018 2043
2019 2044 # Same values should fall back to next attribute.
2020 2045 if avalue == bvalue:
2021 2046 continue
2022 2047
2023 2048 # Exact matches come first.
2024 2049 if avalue == prefvalue:
2025 2050 return -1
2026 2051 if bvalue == prefvalue:
2027 2052 return 1
2028 2053
2029 2054 # Fall back to next attribute.
2030 2055 continue
2031 2056
2032 2057 # If we got here we couldn't sort by attributes and prefers. Fall
2033 2058 # back to index order.
2034 2059 return 0
2035 2060
2036 2061 def __lt__(self, other):
2037 2062 return self._cmp(other) < 0
2038 2063
2039 2064 def __gt__(self, other):
2040 2065 return self._cmp(other) > 0
2041 2066
2042 2067 def __eq__(self, other):
2043 2068 return self._cmp(other) == 0
2044 2069
2045 2070 def __le__(self, other):
2046 2071 return self._cmp(other) <= 0
2047 2072
2048 2073 def __ge__(self, other):
2049 2074 return self._cmp(other) >= 0
2050 2075
2051 2076 def __ne__(self, other):
2052 2077 return self._cmp(other) != 0
2053 2078
2054 2079 def sortclonebundleentries(ui, entries):
2055 2080 prefers = ui.configlist('ui', 'clonebundleprefers')
2056 2081 if not prefers:
2057 2082 return list(entries)
2058 2083
2059 2084 prefers = [p.split('=', 1) for p in prefers]
2060 2085
2061 2086 items = sorted(clonebundleentry(v, prefers) for v in entries)
2062 2087 return [i.value for i in items]
2063 2088
2064 2089 def trypullbundlefromurl(ui, repo, url):
2065 2090 """Attempt to apply a bundle from a URL."""
2066 2091 with repo.lock(), repo.transaction('bundleurl') as tr:
2067 2092 try:
2068 2093 fh = urlmod.open(ui, url)
2069 2094 cg = readbundle(ui, fh, 'stream')
2070 2095
2071 2096 if isinstance(cg, streamclone.streamcloneapplier):
2072 2097 cg.apply(repo)
2073 2098 else:
2074 2099 bundle2.applybundle(repo, cg, tr, 'clonebundles', url)
2075 2100 return True
2076 2101 except urlerr.httperror as e:
2077 2102 ui.warn(_('HTTP error fetching bundle: %s\n') % str(e))
2078 2103 except urlerr.urlerror as e:
2079 2104 ui.warn(_('error fetching bundle: %s\n') % e.reason)
2080 2105
2081 2106 return False
@@ -1,2133 +1,2214 b''
1 1 > do_push()
2 2 > {
3 3 > user=$1
4 4 > shift
5 5 > echo "Pushing as user $user"
6 6 > echo 'hgrc = """'
7 7 > sed -n '/\[[ha]/,$p' b/.hg/hgrc | grep -v fakegroups.py
8 8 > echo '"""'
9 9 > if test -f acl.config; then
10 10 > echo 'acl.config = """'
11 11 > cat acl.config
12 12 > echo '"""'
13 13 > fi
14 14 > # On AIX /etc/profile sets LOGNAME read-only. So
15 15 > # LOGNAME=$user hg --cws a --debug push ../b
16 16 > # fails with "This variable is read only."
17 17 > # Use env to work around this.
18 18 > env LOGNAME=$user hg --cwd a --debug push ../b
19 19 > hg --cwd b rollback
20 20 > hg --cwd b --quiet tip
21 21 > echo
22 22 > }
23 23
24 24 > init_config()
25 25 > {
26 26 > cat > fakegroups.py <<EOF
27 27 > from hgext import acl
28 28 > def fakegetusers(ui, group):
29 29 > try:
30 30 > return acl._getusersorig(ui, group)
31 31 > except:
32 32 > return ["fred", "betty"]
33 33 > acl._getusersorig = acl._getusers
34 34 > acl._getusers = fakegetusers
35 35 > EOF
36 36 > rm -f acl.config
37 37 > cat > $config <<EOF
38 38 > [hooks]
39 39 > pretxnchangegroup.acl = python:hgext.acl.hook
40 40 > [acl]
41 41 > sources = push
42 42 > [extensions]
43 43 > f=`pwd`/fakegroups.py
44 44 > EOF
45 45 > }
46 46
47 47 $ hg init a
48 48 $ cd a
49 49 $ mkdir foo foo/Bar quux
50 50 $ echo 'in foo' > foo/file.txt
51 51 $ echo 'in foo/Bar' > foo/Bar/file.txt
52 52 $ echo 'in quux' > quux/file.py
53 53 $ hg add -q
54 54 $ hg ci -m 'add files' -d '1000000 0'
55 55 $ echo >> foo/file.txt
56 56 $ hg ci -m 'change foo/file' -d '1000001 0'
57 57 $ echo >> foo/Bar/file.txt
58 58 $ hg ci -m 'change foo/Bar/file' -d '1000002 0'
59 59 $ echo >> quux/file.py
60 60 $ hg ci -m 'change quux/file' -d '1000003 0'
61 61 $ hg tip --quiet
62 62 3:911600dab2ae
63 63
64 64 $ cd ..
65 65 $ hg clone -r 0 a b
66 66 adding changesets
67 67 adding manifests
68 68 adding file changes
69 69 added 1 changesets with 3 changes to 3 files
70 70 new changesets 6675d58eff77
71 71 updating to branch default
72 72 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
73 73
74 74 $ config=b/.hg/hgrc
75 75
76 76 Extension disabled for lack of a hook
77 77
78 78 $ do_push fred
79 79 Pushing as user fred
80 80 hgrc = """
81 81 """
82 82 pushing to ../b
83 83 query 1; heads
84 84 searching for changes
85 85 all remote heads known locally
86 86 listing keys for "phases"
87 87 checking for updated bookmarks
88 88 listing keys for "bookmarks"
89 89 listing keys for "bookmarks"
90 90 3 changesets found
91 91 list of changesets:
92 92 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
93 93 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
94 94 911600dab2ae7a9baff75958b84fe606851ce955
95 bundle2-output-bundle: "HG20", 4 parts total
95 bundle2-output-bundle: "HG20", 5 parts total
96 96 bundle2-output-part: "replycaps" 168 bytes payload
97 bundle2-output-part: "check:phases" 24 bytes payload
97 98 bundle2-output-part: "check:heads" streamed payload
98 99 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
99 100 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
100 101 bundle2-input-bundle: with-transaction
101 102 bundle2-input-part: "replycaps" supported
102 103 bundle2-input-part: total payload size 168
104 bundle2-input-part: "check:phases" supported
105 bundle2-input-part: total payload size 24
103 106 bundle2-input-part: "check:heads" supported
104 107 bundle2-input-part: total payload size 20
105 108 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
106 109 adding changesets
107 110 add changeset ef1ea85a6374
108 111 add changeset f9cafe1212c8
109 112 add changeset 911600dab2ae
110 113 adding manifests
111 114 adding file changes
112 115 adding foo/Bar/file.txt revisions
113 116 adding foo/file.txt revisions
114 117 adding quux/file.py revisions
115 118 added 3 changesets with 3 changes to 3 files
116 119 bundle2-input-part: total payload size 1553
117 120 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
118 121 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
119 bundle2-input-bundle: 3 parts total
122 bundle2-input-bundle: 4 parts total
120 123 updating the branch cache
121 124 bundle2-output-bundle: "HG20", 2 parts total
122 125 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
123 126 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
124 127 bundle2-input-bundle: no-transaction
125 128 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
126 129 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
127 130 bundle2-input-bundle: 1 parts total
128 131 listing keys for "phases"
129 132 repository tip rolled back to revision 0 (undo push)
130 133 0:6675d58eff77
131 134
132 135
133 136 $ echo '[hooks]' >> $config
134 137 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
135 138
136 139 Extension disabled for lack of acl.sources
137 140
138 141 $ do_push fred
139 142 Pushing as user fred
140 143 hgrc = """
141 144 [hooks]
142 145 pretxnchangegroup.acl = python:hgext.acl.hook
143 146 """
144 147 pushing to ../b
145 148 query 1; heads
146 149 searching for changes
147 150 all remote heads known locally
148 151 listing keys for "phases"
149 152 checking for updated bookmarks
150 153 listing keys for "bookmarks"
151 154 listing keys for "bookmarks"
152 155 3 changesets found
153 156 list of changesets:
154 157 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
155 158 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
156 159 911600dab2ae7a9baff75958b84fe606851ce955
157 bundle2-output-bundle: "HG20", 4 parts total
160 bundle2-output-bundle: "HG20", 5 parts total
158 161 bundle2-output-part: "replycaps" 168 bytes payload
162 bundle2-output-part: "check:phases" 24 bytes payload
159 163 bundle2-output-part: "check:heads" streamed payload
160 164 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
161 165 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
162 166 bundle2-input-bundle: with-transaction
163 167 bundle2-input-part: "replycaps" supported
164 168 bundle2-input-part: total payload size 168
169 bundle2-input-part: "check:phases" supported
170 bundle2-input-part: total payload size 24
165 171 bundle2-input-part: "check:heads" supported
166 172 bundle2-input-part: total payload size 20
167 173 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
168 174 adding changesets
169 175 add changeset ef1ea85a6374
170 176 add changeset f9cafe1212c8
171 177 add changeset 911600dab2ae
172 178 adding manifests
173 179 adding file changes
174 180 adding foo/Bar/file.txt revisions
175 181 adding foo/file.txt revisions
176 182 adding quux/file.py revisions
177 183 added 3 changesets with 3 changes to 3 files
178 184 calling hook pretxnchangegroup.acl: hgext.acl.hook
179 185 acl: changes have source "push" - skipping
180 186 bundle2-input-part: total payload size 1553
181 187 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
182 188 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
183 bundle2-input-bundle: 3 parts total
189 bundle2-input-bundle: 4 parts total
184 190 updating the branch cache
185 191 bundle2-output-bundle: "HG20", 2 parts total
186 192 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
187 193 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
188 194 bundle2-input-bundle: no-transaction
189 195 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
190 196 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
191 197 bundle2-input-bundle: 1 parts total
192 198 listing keys for "phases"
193 199 repository tip rolled back to revision 0 (undo push)
194 200 0:6675d58eff77
195 201
196 202
197 203 No [acl.allow]/[acl.deny]
198 204
199 205 $ echo '[acl]' >> $config
200 206 $ echo 'sources = push' >> $config
201 207 $ do_push fred
202 208 Pushing as user fred
203 209 hgrc = """
204 210 [hooks]
205 211 pretxnchangegroup.acl = python:hgext.acl.hook
206 212 [acl]
207 213 sources = push
208 214 """
209 215 pushing to ../b
210 216 query 1; heads
211 217 searching for changes
212 218 all remote heads known locally
213 219 listing keys for "phases"
214 220 checking for updated bookmarks
215 221 listing keys for "bookmarks"
216 222 listing keys for "bookmarks"
217 223 3 changesets found
218 224 list of changesets:
219 225 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
220 226 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
221 227 911600dab2ae7a9baff75958b84fe606851ce955
222 bundle2-output-bundle: "HG20", 4 parts total
228 bundle2-output-bundle: "HG20", 5 parts total
223 229 bundle2-output-part: "replycaps" 168 bytes payload
230 bundle2-output-part: "check:phases" 24 bytes payload
224 231 bundle2-output-part: "check:heads" streamed payload
225 232 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
226 233 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
227 234 bundle2-input-bundle: with-transaction
228 235 bundle2-input-part: "replycaps" supported
229 236 bundle2-input-part: total payload size 168
237 bundle2-input-part: "check:phases" supported
238 bundle2-input-part: total payload size 24
230 239 bundle2-input-part: "check:heads" supported
231 240 bundle2-input-part: total payload size 20
232 241 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
233 242 adding changesets
234 243 add changeset ef1ea85a6374
235 244 add changeset f9cafe1212c8
236 245 add changeset 911600dab2ae
237 246 adding manifests
238 247 adding file changes
239 248 adding foo/Bar/file.txt revisions
240 249 adding foo/file.txt revisions
241 250 adding quux/file.py revisions
242 251 added 3 changesets with 3 changes to 3 files
243 252 calling hook pretxnchangegroup.acl: hgext.acl.hook
244 253 acl: checking access for user "fred"
245 254 acl: acl.allow.branches not enabled
246 255 acl: acl.deny.branches not enabled
247 256 acl: acl.allow not enabled
248 257 acl: acl.deny not enabled
249 258 acl: branch access granted: "ef1ea85a6374" on branch "default"
250 259 acl: path access granted: "ef1ea85a6374"
251 260 acl: branch access granted: "f9cafe1212c8" on branch "default"
252 261 acl: path access granted: "f9cafe1212c8"
253 262 acl: branch access granted: "911600dab2ae" on branch "default"
254 263 acl: path access granted: "911600dab2ae"
255 264 bundle2-input-part: total payload size 1553
256 265 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
257 266 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
258 bundle2-input-bundle: 3 parts total
267 bundle2-input-bundle: 4 parts total
259 268 updating the branch cache
260 269 bundle2-output-bundle: "HG20", 2 parts total
261 270 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
262 271 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
263 272 bundle2-input-bundle: no-transaction
264 273 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
265 274 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
266 275 bundle2-input-bundle: 1 parts total
267 276 listing keys for "phases"
268 277 repository tip rolled back to revision 0 (undo push)
269 278 0:6675d58eff77
270 279
271 280
272 281 Empty [acl.allow]
273 282
274 283 $ echo '[acl.allow]' >> $config
275 284 $ do_push fred
276 285 Pushing as user fred
277 286 hgrc = """
278 287 [hooks]
279 288 pretxnchangegroup.acl = python:hgext.acl.hook
280 289 [acl]
281 290 sources = push
282 291 [acl.allow]
283 292 """
284 293 pushing to ../b
285 294 query 1; heads
286 295 searching for changes
287 296 all remote heads known locally
288 297 listing keys for "phases"
289 298 checking for updated bookmarks
290 299 listing keys for "bookmarks"
291 300 listing keys for "bookmarks"
292 301 3 changesets found
293 302 list of changesets:
294 303 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
295 304 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
296 305 911600dab2ae7a9baff75958b84fe606851ce955
297 bundle2-output-bundle: "HG20", 4 parts total
306 bundle2-output-bundle: "HG20", 5 parts total
298 307 bundle2-output-part: "replycaps" 168 bytes payload
308 bundle2-output-part: "check:phases" 24 bytes payload
299 309 bundle2-output-part: "check:heads" streamed payload
300 310 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
301 311 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
302 312 bundle2-input-bundle: with-transaction
303 313 bundle2-input-part: "replycaps" supported
304 314 bundle2-input-part: total payload size 168
315 bundle2-input-part: "check:phases" supported
316 bundle2-input-part: total payload size 24
305 317 bundle2-input-part: "check:heads" supported
306 318 bundle2-input-part: total payload size 20
307 319 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
308 320 adding changesets
309 321 add changeset ef1ea85a6374
310 322 add changeset f9cafe1212c8
311 323 add changeset 911600dab2ae
312 324 adding manifests
313 325 adding file changes
314 326 adding foo/Bar/file.txt revisions
315 327 adding foo/file.txt revisions
316 328 adding quux/file.py revisions
317 329 added 3 changesets with 3 changes to 3 files
318 330 calling hook pretxnchangegroup.acl: hgext.acl.hook
319 331 acl: checking access for user "fred"
320 332 acl: acl.allow.branches not enabled
321 333 acl: acl.deny.branches not enabled
322 334 acl: acl.allow enabled, 0 entries for user fred
323 335 acl: acl.deny not enabled
324 336 acl: branch access granted: "ef1ea85a6374" on branch "default"
325 337 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
326 338 bundle2-input-part: total payload size 1553
327 bundle2-input-bundle: 3 parts total
339 bundle2-input-bundle: 4 parts total
328 340 transaction abort!
329 341 rollback completed
330 342 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
331 343 no rollback information available
332 344 0:6675d58eff77
333 345
334 346
335 347 fred is allowed inside foo/
336 348
337 349 $ echo 'foo/** = fred' >> $config
338 350 $ do_push fred
339 351 Pushing as user fred
340 352 hgrc = """
341 353 [hooks]
342 354 pretxnchangegroup.acl = python:hgext.acl.hook
343 355 [acl]
344 356 sources = push
345 357 [acl.allow]
346 358 foo/** = fred
347 359 """
348 360 pushing to ../b
349 361 query 1; heads
350 362 searching for changes
351 363 all remote heads known locally
352 364 listing keys for "phases"
353 365 checking for updated bookmarks
354 366 listing keys for "bookmarks"
355 367 listing keys for "bookmarks"
356 368 3 changesets found
357 369 list of changesets:
358 370 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
359 371 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
360 372 911600dab2ae7a9baff75958b84fe606851ce955
361 bundle2-output-bundle: "HG20", 4 parts total
373 bundle2-output-bundle: "HG20", 5 parts total
362 374 bundle2-output-part: "replycaps" 168 bytes payload
375 bundle2-output-part: "check:phases" 24 bytes payload
363 376 bundle2-output-part: "check:heads" streamed payload
364 377 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
365 378 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
366 379 bundle2-input-bundle: with-transaction
367 380 bundle2-input-part: "replycaps" supported
368 381 bundle2-input-part: total payload size 168
382 bundle2-input-part: "check:phases" supported
383 bundle2-input-part: total payload size 24
369 384 bundle2-input-part: "check:heads" supported
370 385 bundle2-input-part: total payload size 20
371 386 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
372 387 adding changesets
373 388 add changeset ef1ea85a6374
374 389 add changeset f9cafe1212c8
375 390 add changeset 911600dab2ae
376 391 adding manifests
377 392 adding file changes
378 393 adding foo/Bar/file.txt revisions
379 394 adding foo/file.txt revisions
380 395 adding quux/file.py revisions
381 396 added 3 changesets with 3 changes to 3 files
382 397 calling hook pretxnchangegroup.acl: hgext.acl.hook
383 398 acl: checking access for user "fred"
384 399 acl: acl.allow.branches not enabled
385 400 acl: acl.deny.branches not enabled
386 401 acl: acl.allow enabled, 1 entries for user fred
387 402 acl: acl.deny not enabled
388 403 acl: branch access granted: "ef1ea85a6374" on branch "default"
389 404 acl: path access granted: "ef1ea85a6374"
390 405 acl: branch access granted: "f9cafe1212c8" on branch "default"
391 406 acl: path access granted: "f9cafe1212c8"
392 407 acl: branch access granted: "911600dab2ae" on branch "default"
393 408 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
394 409 bundle2-input-part: total payload size 1553
395 bundle2-input-bundle: 3 parts total
410 bundle2-input-bundle: 4 parts total
396 411 transaction abort!
397 412 rollback completed
398 413 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
399 414 no rollback information available
400 415 0:6675d58eff77
401 416
402 417
403 418 Empty [acl.deny]
404 419
405 420 $ echo '[acl.deny]' >> $config
406 421 $ do_push barney
407 422 Pushing as user barney
408 423 hgrc = """
409 424 [hooks]
410 425 pretxnchangegroup.acl = python:hgext.acl.hook
411 426 [acl]
412 427 sources = push
413 428 [acl.allow]
414 429 foo/** = fred
415 430 [acl.deny]
416 431 """
417 432 pushing to ../b
418 433 query 1; heads
419 434 searching for changes
420 435 all remote heads known locally
421 436 listing keys for "phases"
422 437 checking for updated bookmarks
423 438 listing keys for "bookmarks"
424 439 listing keys for "bookmarks"
425 440 3 changesets found
426 441 list of changesets:
427 442 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
428 443 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
429 444 911600dab2ae7a9baff75958b84fe606851ce955
430 bundle2-output-bundle: "HG20", 4 parts total
445 bundle2-output-bundle: "HG20", 5 parts total
431 446 bundle2-output-part: "replycaps" 168 bytes payload
447 bundle2-output-part: "check:phases" 24 bytes payload
432 448 bundle2-output-part: "check:heads" streamed payload
433 449 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
434 450 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
435 451 bundle2-input-bundle: with-transaction
436 452 bundle2-input-part: "replycaps" supported
437 453 bundle2-input-part: total payload size 168
454 bundle2-input-part: "check:phases" supported
455 bundle2-input-part: total payload size 24
438 456 bundle2-input-part: "check:heads" supported
439 457 bundle2-input-part: total payload size 20
440 458 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
441 459 adding changesets
442 460 add changeset ef1ea85a6374
443 461 add changeset f9cafe1212c8
444 462 add changeset 911600dab2ae
445 463 adding manifests
446 464 adding file changes
447 465 adding foo/Bar/file.txt revisions
448 466 adding foo/file.txt revisions
449 467 adding quux/file.py revisions
450 468 added 3 changesets with 3 changes to 3 files
451 469 calling hook pretxnchangegroup.acl: hgext.acl.hook
452 470 acl: checking access for user "barney"
453 471 acl: acl.allow.branches not enabled
454 472 acl: acl.deny.branches not enabled
455 473 acl: acl.allow enabled, 0 entries for user barney
456 474 acl: acl.deny enabled, 0 entries for user barney
457 475 acl: branch access granted: "ef1ea85a6374" on branch "default"
458 476 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
459 477 bundle2-input-part: total payload size 1553
460 bundle2-input-bundle: 3 parts total
478 bundle2-input-bundle: 4 parts total
461 479 transaction abort!
462 480 rollback completed
463 481 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
464 482 no rollback information available
465 483 0:6675d58eff77
466 484
467 485
468 486 fred is allowed inside foo/, but not foo/bar/ (case matters)
469 487
470 488 $ echo 'foo/bar/** = fred' >> $config
471 489 $ do_push fred
472 490 Pushing as user fred
473 491 hgrc = """
474 492 [hooks]
475 493 pretxnchangegroup.acl = python:hgext.acl.hook
476 494 [acl]
477 495 sources = push
478 496 [acl.allow]
479 497 foo/** = fred
480 498 [acl.deny]
481 499 foo/bar/** = fred
482 500 """
483 501 pushing to ../b
484 502 query 1; heads
485 503 searching for changes
486 504 all remote heads known locally
487 505 listing keys for "phases"
488 506 checking for updated bookmarks
489 507 listing keys for "bookmarks"
490 508 listing keys for "bookmarks"
491 509 3 changesets found
492 510 list of changesets:
493 511 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
494 512 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
495 513 911600dab2ae7a9baff75958b84fe606851ce955
496 bundle2-output-bundle: "HG20", 4 parts total
514 bundle2-output-bundle: "HG20", 5 parts total
497 515 bundle2-output-part: "replycaps" 168 bytes payload
516 bundle2-output-part: "check:phases" 24 bytes payload
498 517 bundle2-output-part: "check:heads" streamed payload
499 518 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
500 519 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
501 520 bundle2-input-bundle: with-transaction
502 521 bundle2-input-part: "replycaps" supported
503 522 bundle2-input-part: total payload size 168
523 bundle2-input-part: "check:phases" supported
524 bundle2-input-part: total payload size 24
504 525 bundle2-input-part: "check:heads" supported
505 526 bundle2-input-part: total payload size 20
506 527 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
507 528 adding changesets
508 529 add changeset ef1ea85a6374
509 530 add changeset f9cafe1212c8
510 531 add changeset 911600dab2ae
511 532 adding manifests
512 533 adding file changes
513 534 adding foo/Bar/file.txt revisions
514 535 adding foo/file.txt revisions
515 536 adding quux/file.py revisions
516 537 added 3 changesets with 3 changes to 3 files
517 538 calling hook pretxnchangegroup.acl: hgext.acl.hook
518 539 acl: checking access for user "fred"
519 540 acl: acl.allow.branches not enabled
520 541 acl: acl.deny.branches not enabled
521 542 acl: acl.allow enabled, 1 entries for user fred
522 543 acl: acl.deny enabled, 1 entries for user fred
523 544 acl: branch access granted: "ef1ea85a6374" on branch "default"
524 545 acl: path access granted: "ef1ea85a6374"
525 546 acl: branch access granted: "f9cafe1212c8" on branch "default"
526 547 acl: path access granted: "f9cafe1212c8"
527 548 acl: branch access granted: "911600dab2ae" on branch "default"
528 549 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
529 550 bundle2-input-part: total payload size 1553
530 bundle2-input-bundle: 3 parts total
551 bundle2-input-bundle: 4 parts total
531 552 transaction abort!
532 553 rollback completed
533 554 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
534 555 no rollback information available
535 556 0:6675d58eff77
536 557
537 558
538 559 fred is allowed inside foo/, but not foo/Bar/
539 560
540 561 $ echo 'foo/Bar/** = fred' >> $config
541 562 $ do_push fred
542 563 Pushing as user fred
543 564 hgrc = """
544 565 [hooks]
545 566 pretxnchangegroup.acl = python:hgext.acl.hook
546 567 [acl]
547 568 sources = push
548 569 [acl.allow]
549 570 foo/** = fred
550 571 [acl.deny]
551 572 foo/bar/** = fred
552 573 foo/Bar/** = fred
553 574 """
554 575 pushing to ../b
555 576 query 1; heads
556 577 searching for changes
557 578 all remote heads known locally
558 579 listing keys for "phases"
559 580 checking for updated bookmarks
560 581 listing keys for "bookmarks"
561 582 listing keys for "bookmarks"
562 583 3 changesets found
563 584 list of changesets:
564 585 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
565 586 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
566 587 911600dab2ae7a9baff75958b84fe606851ce955
567 bundle2-output-bundle: "HG20", 4 parts total
588 bundle2-output-bundle: "HG20", 5 parts total
568 589 bundle2-output-part: "replycaps" 168 bytes payload
590 bundle2-output-part: "check:phases" 24 bytes payload
569 591 bundle2-output-part: "check:heads" streamed payload
570 592 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
571 593 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
572 594 bundle2-input-bundle: with-transaction
573 595 bundle2-input-part: "replycaps" supported
574 596 bundle2-input-part: total payload size 168
597 bundle2-input-part: "check:phases" supported
598 bundle2-input-part: total payload size 24
575 599 bundle2-input-part: "check:heads" supported
576 600 bundle2-input-part: total payload size 20
577 601 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
578 602 adding changesets
579 603 add changeset ef1ea85a6374
580 604 add changeset f9cafe1212c8
581 605 add changeset 911600dab2ae
582 606 adding manifests
583 607 adding file changes
584 608 adding foo/Bar/file.txt revisions
585 609 adding foo/file.txt revisions
586 610 adding quux/file.py revisions
587 611 added 3 changesets with 3 changes to 3 files
588 612 calling hook pretxnchangegroup.acl: hgext.acl.hook
589 613 acl: checking access for user "fred"
590 614 acl: acl.allow.branches not enabled
591 615 acl: acl.deny.branches not enabled
592 616 acl: acl.allow enabled, 1 entries for user fred
593 617 acl: acl.deny enabled, 2 entries for user fred
594 618 acl: branch access granted: "ef1ea85a6374" on branch "default"
595 619 acl: path access granted: "ef1ea85a6374"
596 620 acl: branch access granted: "f9cafe1212c8" on branch "default"
597 621 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
598 622 bundle2-input-part: total payload size 1553
599 bundle2-input-bundle: 3 parts total
623 bundle2-input-bundle: 4 parts total
600 624 transaction abort!
601 625 rollback completed
602 626 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
603 627 no rollback information available
604 628 0:6675d58eff77
605 629
606 630
607 631 $ echo 'barney is not mentioned => not allowed anywhere'
608 632 barney is not mentioned => not allowed anywhere
609 633 $ do_push barney
610 634 Pushing as user barney
611 635 hgrc = """
612 636 [hooks]
613 637 pretxnchangegroup.acl = python:hgext.acl.hook
614 638 [acl]
615 639 sources = push
616 640 [acl.allow]
617 641 foo/** = fred
618 642 [acl.deny]
619 643 foo/bar/** = fred
620 644 foo/Bar/** = fred
621 645 """
622 646 pushing to ../b
623 647 query 1; heads
624 648 searching for changes
625 649 all remote heads known locally
626 650 listing keys for "phases"
627 651 checking for updated bookmarks
628 652 listing keys for "bookmarks"
629 653 listing keys for "bookmarks"
630 654 3 changesets found
631 655 list of changesets:
632 656 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
633 657 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
634 658 911600dab2ae7a9baff75958b84fe606851ce955
635 bundle2-output-bundle: "HG20", 4 parts total
659 bundle2-output-bundle: "HG20", 5 parts total
636 660 bundle2-output-part: "replycaps" 168 bytes payload
661 bundle2-output-part: "check:phases" 24 bytes payload
637 662 bundle2-output-part: "check:heads" streamed payload
638 663 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
639 664 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
640 665 bundle2-input-bundle: with-transaction
641 666 bundle2-input-part: "replycaps" supported
642 667 bundle2-input-part: total payload size 168
668 bundle2-input-part: "check:phases" supported
669 bundle2-input-part: total payload size 24
643 670 bundle2-input-part: "check:heads" supported
644 671 bundle2-input-part: total payload size 20
645 672 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
646 673 adding changesets
647 674 add changeset ef1ea85a6374
648 675 add changeset f9cafe1212c8
649 676 add changeset 911600dab2ae
650 677 adding manifests
651 678 adding file changes
652 679 adding foo/Bar/file.txt revisions
653 680 adding foo/file.txt revisions
654 681 adding quux/file.py revisions
655 682 added 3 changesets with 3 changes to 3 files
656 683 calling hook pretxnchangegroup.acl: hgext.acl.hook
657 684 acl: checking access for user "barney"
658 685 acl: acl.allow.branches not enabled
659 686 acl: acl.deny.branches not enabled
660 687 acl: acl.allow enabled, 0 entries for user barney
661 688 acl: acl.deny enabled, 0 entries for user barney
662 689 acl: branch access granted: "ef1ea85a6374" on branch "default"
663 690 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
664 691 bundle2-input-part: total payload size 1553
665 bundle2-input-bundle: 3 parts total
692 bundle2-input-bundle: 4 parts total
666 693 transaction abort!
667 694 rollback completed
668 695 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
669 696 no rollback information available
670 697 0:6675d58eff77
671 698
672 699
673 700 barney is allowed everywhere
674 701
675 702 $ echo '[acl.allow]' >> $config
676 703 $ echo '** = barney' >> $config
677 704 $ do_push barney
678 705 Pushing as user barney
679 706 hgrc = """
680 707 [hooks]
681 708 pretxnchangegroup.acl = python:hgext.acl.hook
682 709 [acl]
683 710 sources = push
684 711 [acl.allow]
685 712 foo/** = fred
686 713 [acl.deny]
687 714 foo/bar/** = fred
688 715 foo/Bar/** = fred
689 716 [acl.allow]
690 717 ** = barney
691 718 """
692 719 pushing to ../b
693 720 query 1; heads
694 721 searching for changes
695 722 all remote heads known locally
696 723 listing keys for "phases"
697 724 checking for updated bookmarks
698 725 listing keys for "bookmarks"
699 726 listing keys for "bookmarks"
700 727 3 changesets found
701 728 list of changesets:
702 729 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
703 730 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
704 731 911600dab2ae7a9baff75958b84fe606851ce955
705 bundle2-output-bundle: "HG20", 4 parts total
732 bundle2-output-bundle: "HG20", 5 parts total
706 733 bundle2-output-part: "replycaps" 168 bytes payload
734 bundle2-output-part: "check:phases" 24 bytes payload
707 735 bundle2-output-part: "check:heads" streamed payload
708 736 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
709 737 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
710 738 bundle2-input-bundle: with-transaction
711 739 bundle2-input-part: "replycaps" supported
712 740 bundle2-input-part: total payload size 168
741 bundle2-input-part: "check:phases" supported
742 bundle2-input-part: total payload size 24
713 743 bundle2-input-part: "check:heads" supported
714 744 bundle2-input-part: total payload size 20
715 745 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
716 746 adding changesets
717 747 add changeset ef1ea85a6374
718 748 add changeset f9cafe1212c8
719 749 add changeset 911600dab2ae
720 750 adding manifests
721 751 adding file changes
722 752 adding foo/Bar/file.txt revisions
723 753 adding foo/file.txt revisions
724 754 adding quux/file.py revisions
725 755 added 3 changesets with 3 changes to 3 files
726 756 calling hook pretxnchangegroup.acl: hgext.acl.hook
727 757 acl: checking access for user "barney"
728 758 acl: acl.allow.branches not enabled
729 759 acl: acl.deny.branches not enabled
730 760 acl: acl.allow enabled, 1 entries for user barney
731 761 acl: acl.deny enabled, 0 entries for user barney
732 762 acl: branch access granted: "ef1ea85a6374" on branch "default"
733 763 acl: path access granted: "ef1ea85a6374"
734 764 acl: branch access granted: "f9cafe1212c8" on branch "default"
735 765 acl: path access granted: "f9cafe1212c8"
736 766 acl: branch access granted: "911600dab2ae" on branch "default"
737 767 acl: path access granted: "911600dab2ae"
738 768 bundle2-input-part: total payload size 1553
739 769 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
740 770 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
741 bundle2-input-bundle: 3 parts total
771 bundle2-input-bundle: 4 parts total
742 772 updating the branch cache
743 773 bundle2-output-bundle: "HG20", 2 parts total
744 774 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
745 775 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
746 776 bundle2-input-bundle: no-transaction
747 777 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
748 778 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
749 779 bundle2-input-bundle: 1 parts total
750 780 listing keys for "phases"
751 781 repository tip rolled back to revision 0 (undo push)
752 782 0:6675d58eff77
753 783
754 784
755 785 wilma can change files with a .txt extension
756 786
757 787 $ echo '**/*.txt = wilma' >> $config
758 788 $ do_push wilma
759 789 Pushing as user wilma
760 790 hgrc = """
761 791 [hooks]
762 792 pretxnchangegroup.acl = python:hgext.acl.hook
763 793 [acl]
764 794 sources = push
765 795 [acl.allow]
766 796 foo/** = fred
767 797 [acl.deny]
768 798 foo/bar/** = fred
769 799 foo/Bar/** = fred
770 800 [acl.allow]
771 801 ** = barney
772 802 **/*.txt = wilma
773 803 """
774 804 pushing to ../b
775 805 query 1; heads
776 806 searching for changes
777 807 all remote heads known locally
778 808 listing keys for "phases"
779 809 checking for updated bookmarks
780 810 listing keys for "bookmarks"
781 811 listing keys for "bookmarks"
782 812 3 changesets found
783 813 list of changesets:
784 814 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
785 815 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
786 816 911600dab2ae7a9baff75958b84fe606851ce955
787 bundle2-output-bundle: "HG20", 4 parts total
817 bundle2-output-bundle: "HG20", 5 parts total
788 818 bundle2-output-part: "replycaps" 168 bytes payload
819 bundle2-output-part: "check:phases" 24 bytes payload
789 820 bundle2-output-part: "check:heads" streamed payload
790 821 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
791 822 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
792 823 bundle2-input-bundle: with-transaction
793 824 bundle2-input-part: "replycaps" supported
794 825 bundle2-input-part: total payload size 168
826 bundle2-input-part: "check:phases" supported
827 bundle2-input-part: total payload size 24
795 828 bundle2-input-part: "check:heads" supported
796 829 bundle2-input-part: total payload size 20
797 830 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
798 831 adding changesets
799 832 add changeset ef1ea85a6374
800 833 add changeset f9cafe1212c8
801 834 add changeset 911600dab2ae
802 835 adding manifests
803 836 adding file changes
804 837 adding foo/Bar/file.txt revisions
805 838 adding foo/file.txt revisions
806 839 adding quux/file.py revisions
807 840 added 3 changesets with 3 changes to 3 files
808 841 calling hook pretxnchangegroup.acl: hgext.acl.hook
809 842 acl: checking access for user "wilma"
810 843 acl: acl.allow.branches not enabled
811 844 acl: acl.deny.branches not enabled
812 845 acl: acl.allow enabled, 1 entries for user wilma
813 846 acl: acl.deny enabled, 0 entries for user wilma
814 847 acl: branch access granted: "ef1ea85a6374" on branch "default"
815 848 acl: path access granted: "ef1ea85a6374"
816 849 acl: branch access granted: "f9cafe1212c8" on branch "default"
817 850 acl: path access granted: "f9cafe1212c8"
818 851 acl: branch access granted: "911600dab2ae" on branch "default"
819 852 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
820 853 bundle2-input-part: total payload size 1553
821 bundle2-input-bundle: 3 parts total
854 bundle2-input-bundle: 4 parts total
822 855 transaction abort!
823 856 rollback completed
824 857 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
825 858 no rollback information available
826 859 0:6675d58eff77
827 860
828 861
829 862 file specified by acl.config does not exist
830 863
831 864 $ echo '[acl]' >> $config
832 865 $ echo 'config = ../acl.config' >> $config
833 866 $ do_push barney
834 867 Pushing as user barney
835 868 hgrc = """
836 869 [hooks]
837 870 pretxnchangegroup.acl = python:hgext.acl.hook
838 871 [acl]
839 872 sources = push
840 873 [acl.allow]
841 874 foo/** = fred
842 875 [acl.deny]
843 876 foo/bar/** = fred
844 877 foo/Bar/** = fred
845 878 [acl.allow]
846 879 ** = barney
847 880 **/*.txt = wilma
848 881 [acl]
849 882 config = ../acl.config
850 883 """
851 884 pushing to ../b
852 885 query 1; heads
853 886 searching for changes
854 887 all remote heads known locally
855 888 listing keys for "phases"
856 889 checking for updated bookmarks
857 890 listing keys for "bookmarks"
858 891 listing keys for "bookmarks"
859 892 3 changesets found
860 893 list of changesets:
861 894 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
862 895 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
863 896 911600dab2ae7a9baff75958b84fe606851ce955
864 bundle2-output-bundle: "HG20", 4 parts total
897 bundle2-output-bundle: "HG20", 5 parts total
865 898 bundle2-output-part: "replycaps" 168 bytes payload
899 bundle2-output-part: "check:phases" 24 bytes payload
866 900 bundle2-output-part: "check:heads" streamed payload
867 901 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
868 902 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
869 903 bundle2-input-bundle: with-transaction
870 904 bundle2-input-part: "replycaps" supported
871 905 bundle2-input-part: total payload size 168
906 bundle2-input-part: "check:phases" supported
907 bundle2-input-part: total payload size 24
872 908 bundle2-input-part: "check:heads" supported
873 909 bundle2-input-part: total payload size 20
874 910 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
875 911 adding changesets
876 912 add changeset ef1ea85a6374
877 913 add changeset f9cafe1212c8
878 914 add changeset 911600dab2ae
879 915 adding manifests
880 916 adding file changes
881 917 adding foo/Bar/file.txt revisions
882 918 adding foo/file.txt revisions
883 919 adding quux/file.py revisions
884 920 added 3 changesets with 3 changes to 3 files
885 921 calling hook pretxnchangegroup.acl: hgext.acl.hook
886 922 acl: checking access for user "barney"
887 923 error: pretxnchangegroup.acl hook raised an exception: [Errno *] * (glob)
888 924 bundle2-input-part: total payload size 1553
889 bundle2-input-bundle: 3 parts total
925 bundle2-input-bundle: 4 parts total
890 926 transaction abort!
891 927 rollback completed
892 928 abort: No such file or directory: ../acl.config
893 929 no rollback information available
894 930 0:6675d58eff77
895 931
896 932
897 933 betty is allowed inside foo/ by a acl.config file
898 934
899 935 $ echo '[acl.allow]' >> acl.config
900 936 $ echo 'foo/** = betty' >> acl.config
901 937 $ do_push betty
902 938 Pushing as user betty
903 939 hgrc = """
904 940 [hooks]
905 941 pretxnchangegroup.acl = python:hgext.acl.hook
906 942 [acl]
907 943 sources = push
908 944 [acl.allow]
909 945 foo/** = fred
910 946 [acl.deny]
911 947 foo/bar/** = fred
912 948 foo/Bar/** = fred
913 949 [acl.allow]
914 950 ** = barney
915 951 **/*.txt = wilma
916 952 [acl]
917 953 config = ../acl.config
918 954 """
919 955 acl.config = """
920 956 [acl.allow]
921 957 foo/** = betty
922 958 """
923 959 pushing to ../b
924 960 query 1; heads
925 961 searching for changes
926 962 all remote heads known locally
927 963 listing keys for "phases"
928 964 checking for updated bookmarks
929 965 listing keys for "bookmarks"
930 966 listing keys for "bookmarks"
931 967 3 changesets found
932 968 list of changesets:
933 969 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
934 970 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
935 971 911600dab2ae7a9baff75958b84fe606851ce955
936 bundle2-output-bundle: "HG20", 4 parts total
972 bundle2-output-bundle: "HG20", 5 parts total
937 973 bundle2-output-part: "replycaps" 168 bytes payload
974 bundle2-output-part: "check:phases" 24 bytes payload
938 975 bundle2-output-part: "check:heads" streamed payload
939 976 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
940 977 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
941 978 bundle2-input-bundle: with-transaction
942 979 bundle2-input-part: "replycaps" supported
943 980 bundle2-input-part: total payload size 168
981 bundle2-input-part: "check:phases" supported
982 bundle2-input-part: total payload size 24
944 983 bundle2-input-part: "check:heads" supported
945 984 bundle2-input-part: total payload size 20
946 985 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
947 986 adding changesets
948 987 add changeset ef1ea85a6374
949 988 add changeset f9cafe1212c8
950 989 add changeset 911600dab2ae
951 990 adding manifests
952 991 adding file changes
953 992 adding foo/Bar/file.txt revisions
954 993 adding foo/file.txt revisions
955 994 adding quux/file.py revisions
956 995 added 3 changesets with 3 changes to 3 files
957 996 calling hook pretxnchangegroup.acl: hgext.acl.hook
958 997 acl: checking access for user "betty"
959 998 acl: acl.allow.branches not enabled
960 999 acl: acl.deny.branches not enabled
961 1000 acl: acl.allow enabled, 1 entries for user betty
962 1001 acl: acl.deny enabled, 0 entries for user betty
963 1002 acl: branch access granted: "ef1ea85a6374" on branch "default"
964 1003 acl: path access granted: "ef1ea85a6374"
965 1004 acl: branch access granted: "f9cafe1212c8" on branch "default"
966 1005 acl: path access granted: "f9cafe1212c8"
967 1006 acl: branch access granted: "911600dab2ae" on branch "default"
968 1007 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
969 1008 bundle2-input-part: total payload size 1553
970 bundle2-input-bundle: 3 parts total
1009 bundle2-input-bundle: 4 parts total
971 1010 transaction abort!
972 1011 rollback completed
973 1012 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
974 1013 no rollback information available
975 1014 0:6675d58eff77
976 1015
977 1016
978 1017 acl.config can set only [acl.allow]/[acl.deny]
979 1018
980 1019 $ echo '[hooks]' >> acl.config
981 1020 $ echo 'changegroup.acl = false' >> acl.config
982 1021 $ do_push barney
983 1022 Pushing as user barney
984 1023 hgrc = """
985 1024 [hooks]
986 1025 pretxnchangegroup.acl = python:hgext.acl.hook
987 1026 [acl]
988 1027 sources = push
989 1028 [acl.allow]
990 1029 foo/** = fred
991 1030 [acl.deny]
992 1031 foo/bar/** = fred
993 1032 foo/Bar/** = fred
994 1033 [acl.allow]
995 1034 ** = barney
996 1035 **/*.txt = wilma
997 1036 [acl]
998 1037 config = ../acl.config
999 1038 """
1000 1039 acl.config = """
1001 1040 [acl.allow]
1002 1041 foo/** = betty
1003 1042 [hooks]
1004 1043 changegroup.acl = false
1005 1044 """
1006 1045 pushing to ../b
1007 1046 query 1; heads
1008 1047 searching for changes
1009 1048 all remote heads known locally
1010 1049 listing keys for "phases"
1011 1050 checking for updated bookmarks
1012 1051 listing keys for "bookmarks"
1013 1052 listing keys for "bookmarks"
1014 1053 3 changesets found
1015 1054 list of changesets:
1016 1055 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1017 1056 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1018 1057 911600dab2ae7a9baff75958b84fe606851ce955
1019 bundle2-output-bundle: "HG20", 4 parts total
1058 bundle2-output-bundle: "HG20", 5 parts total
1020 1059 bundle2-output-part: "replycaps" 168 bytes payload
1060 bundle2-output-part: "check:phases" 24 bytes payload
1021 1061 bundle2-output-part: "check:heads" streamed payload
1022 1062 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1023 1063 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1024 1064 bundle2-input-bundle: with-transaction
1025 1065 bundle2-input-part: "replycaps" supported
1026 1066 bundle2-input-part: total payload size 168
1067 bundle2-input-part: "check:phases" supported
1068 bundle2-input-part: total payload size 24
1027 1069 bundle2-input-part: "check:heads" supported
1028 1070 bundle2-input-part: total payload size 20
1029 1071 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1030 1072 adding changesets
1031 1073 add changeset ef1ea85a6374
1032 1074 add changeset f9cafe1212c8
1033 1075 add changeset 911600dab2ae
1034 1076 adding manifests
1035 1077 adding file changes
1036 1078 adding foo/Bar/file.txt revisions
1037 1079 adding foo/file.txt revisions
1038 1080 adding quux/file.py revisions
1039 1081 added 3 changesets with 3 changes to 3 files
1040 1082 calling hook pretxnchangegroup.acl: hgext.acl.hook
1041 1083 acl: checking access for user "barney"
1042 1084 acl: acl.allow.branches not enabled
1043 1085 acl: acl.deny.branches not enabled
1044 1086 acl: acl.allow enabled, 1 entries for user barney
1045 1087 acl: acl.deny enabled, 0 entries for user barney
1046 1088 acl: branch access granted: "ef1ea85a6374" on branch "default"
1047 1089 acl: path access granted: "ef1ea85a6374"
1048 1090 acl: branch access granted: "f9cafe1212c8" on branch "default"
1049 1091 acl: path access granted: "f9cafe1212c8"
1050 1092 acl: branch access granted: "911600dab2ae" on branch "default"
1051 1093 acl: path access granted: "911600dab2ae"
1052 1094 bundle2-input-part: total payload size 1553
1053 1095 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1054 1096 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1055 bundle2-input-bundle: 3 parts total
1097 bundle2-input-bundle: 4 parts total
1056 1098 updating the branch cache
1057 1099 bundle2-output-bundle: "HG20", 2 parts total
1058 1100 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1059 1101 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1060 1102 bundle2-input-bundle: no-transaction
1061 1103 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1062 1104 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1063 1105 bundle2-input-bundle: 1 parts total
1064 1106 listing keys for "phases"
1065 1107 repository tip rolled back to revision 0 (undo push)
1066 1108 0:6675d58eff77
1067 1109
1068 1110
1069 1111 asterisk
1070 1112
1071 1113 $ init_config
1072 1114
1073 1115 asterisk test
1074 1116
1075 1117 $ echo '[acl.allow]' >> $config
1076 1118 $ echo "** = fred" >> $config
1077 1119
1078 1120 fred is always allowed
1079 1121
1080 1122 $ do_push fred
1081 1123 Pushing as user fred
1082 1124 hgrc = """
1083 1125 [hooks]
1084 1126 pretxnchangegroup.acl = python:hgext.acl.hook
1085 1127 [acl]
1086 1128 sources = push
1087 1129 [extensions]
1088 1130 [acl.allow]
1089 1131 ** = fred
1090 1132 """
1091 1133 pushing to ../b
1092 1134 query 1; heads
1093 1135 searching for changes
1094 1136 all remote heads known locally
1095 1137 listing keys for "phases"
1096 1138 checking for updated bookmarks
1097 1139 listing keys for "bookmarks"
1098 1140 listing keys for "bookmarks"
1099 1141 3 changesets found
1100 1142 list of changesets:
1101 1143 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1102 1144 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1103 1145 911600dab2ae7a9baff75958b84fe606851ce955
1104 bundle2-output-bundle: "HG20", 4 parts total
1146 bundle2-output-bundle: "HG20", 5 parts total
1105 1147 bundle2-output-part: "replycaps" 168 bytes payload
1148 bundle2-output-part: "check:phases" 24 bytes payload
1106 1149 bundle2-output-part: "check:heads" streamed payload
1107 1150 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1108 1151 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1109 1152 bundle2-input-bundle: with-transaction
1110 1153 bundle2-input-part: "replycaps" supported
1111 1154 bundle2-input-part: total payload size 168
1155 bundle2-input-part: "check:phases" supported
1156 bundle2-input-part: total payload size 24
1112 1157 bundle2-input-part: "check:heads" supported
1113 1158 bundle2-input-part: total payload size 20
1114 1159 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1115 1160 adding changesets
1116 1161 add changeset ef1ea85a6374
1117 1162 add changeset f9cafe1212c8
1118 1163 add changeset 911600dab2ae
1119 1164 adding manifests
1120 1165 adding file changes
1121 1166 adding foo/Bar/file.txt revisions
1122 1167 adding foo/file.txt revisions
1123 1168 adding quux/file.py revisions
1124 1169 added 3 changesets with 3 changes to 3 files
1125 1170 calling hook pretxnchangegroup.acl: hgext.acl.hook
1126 1171 acl: checking access for user "fred"
1127 1172 acl: acl.allow.branches not enabled
1128 1173 acl: acl.deny.branches not enabled
1129 1174 acl: acl.allow enabled, 1 entries for user fred
1130 1175 acl: acl.deny not enabled
1131 1176 acl: branch access granted: "ef1ea85a6374" on branch "default"
1132 1177 acl: path access granted: "ef1ea85a6374"
1133 1178 acl: branch access granted: "f9cafe1212c8" on branch "default"
1134 1179 acl: path access granted: "f9cafe1212c8"
1135 1180 acl: branch access granted: "911600dab2ae" on branch "default"
1136 1181 acl: path access granted: "911600dab2ae"
1137 1182 bundle2-input-part: total payload size 1553
1138 1183 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1139 1184 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1140 bundle2-input-bundle: 3 parts total
1185 bundle2-input-bundle: 4 parts total
1141 1186 updating the branch cache
1142 1187 bundle2-output-bundle: "HG20", 2 parts total
1143 1188 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1144 1189 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1145 1190 bundle2-input-bundle: no-transaction
1146 1191 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1147 1192 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1148 1193 bundle2-input-bundle: 1 parts total
1149 1194 listing keys for "phases"
1150 1195 repository tip rolled back to revision 0 (undo push)
1151 1196 0:6675d58eff77
1152 1197
1153 1198
1154 1199 $ echo '[acl.deny]' >> $config
1155 1200 $ echo "foo/Bar/** = *" >> $config
1156 1201
1157 1202 no one is allowed inside foo/Bar/
1158 1203
1159 1204 $ do_push fred
1160 1205 Pushing as user fred
1161 1206 hgrc = """
1162 1207 [hooks]
1163 1208 pretxnchangegroup.acl = python:hgext.acl.hook
1164 1209 [acl]
1165 1210 sources = push
1166 1211 [extensions]
1167 1212 [acl.allow]
1168 1213 ** = fred
1169 1214 [acl.deny]
1170 1215 foo/Bar/** = *
1171 1216 """
1172 1217 pushing to ../b
1173 1218 query 1; heads
1174 1219 searching for changes
1175 1220 all remote heads known locally
1176 1221 listing keys for "phases"
1177 1222 checking for updated bookmarks
1178 1223 listing keys for "bookmarks"
1179 1224 listing keys for "bookmarks"
1180 1225 3 changesets found
1181 1226 list of changesets:
1182 1227 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1183 1228 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1184 1229 911600dab2ae7a9baff75958b84fe606851ce955
1185 bundle2-output-bundle: "HG20", 4 parts total
1230 bundle2-output-bundle: "HG20", 5 parts total
1186 1231 bundle2-output-part: "replycaps" 168 bytes payload
1232 bundle2-output-part: "check:phases" 24 bytes payload
1187 1233 bundle2-output-part: "check:heads" streamed payload
1188 1234 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1189 1235 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1190 1236 bundle2-input-bundle: with-transaction
1191 1237 bundle2-input-part: "replycaps" supported
1192 1238 bundle2-input-part: total payload size 168
1239 bundle2-input-part: "check:phases" supported
1240 bundle2-input-part: total payload size 24
1193 1241 bundle2-input-part: "check:heads" supported
1194 1242 bundle2-input-part: total payload size 20
1195 1243 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1196 1244 adding changesets
1197 1245 add changeset ef1ea85a6374
1198 1246 add changeset f9cafe1212c8
1199 1247 add changeset 911600dab2ae
1200 1248 adding manifests
1201 1249 adding file changes
1202 1250 adding foo/Bar/file.txt revisions
1203 1251 adding foo/file.txt revisions
1204 1252 adding quux/file.py revisions
1205 1253 added 3 changesets with 3 changes to 3 files
1206 1254 calling hook pretxnchangegroup.acl: hgext.acl.hook
1207 1255 acl: checking access for user "fred"
1208 1256 acl: acl.allow.branches not enabled
1209 1257 acl: acl.deny.branches not enabled
1210 1258 acl: acl.allow enabled, 1 entries for user fred
1211 1259 acl: acl.deny enabled, 1 entries for user fred
1212 1260 acl: branch access granted: "ef1ea85a6374" on branch "default"
1213 1261 acl: path access granted: "ef1ea85a6374"
1214 1262 acl: branch access granted: "f9cafe1212c8" on branch "default"
1215 1263 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1216 1264 bundle2-input-part: total payload size 1553
1217 bundle2-input-bundle: 3 parts total
1265 bundle2-input-bundle: 4 parts total
1218 1266 transaction abort!
1219 1267 rollback completed
1220 1268 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1221 1269 no rollback information available
1222 1270 0:6675d58eff77
1223 1271
1224 1272
1225 1273 Groups
1226 1274
1227 1275 $ init_config
1228 1276
1229 1277 OS-level groups
1230 1278
1231 1279 $ echo '[acl.allow]' >> $config
1232 1280 $ echo "** = @group1" >> $config
1233 1281
1234 1282 @group1 is always allowed
1235 1283
1236 1284 $ do_push fred
1237 1285 Pushing as user fred
1238 1286 hgrc = """
1239 1287 [hooks]
1240 1288 pretxnchangegroup.acl = python:hgext.acl.hook
1241 1289 [acl]
1242 1290 sources = push
1243 1291 [extensions]
1244 1292 [acl.allow]
1245 1293 ** = @group1
1246 1294 """
1247 1295 pushing to ../b
1248 1296 query 1; heads
1249 1297 searching for changes
1250 1298 all remote heads known locally
1251 1299 listing keys for "phases"
1252 1300 checking for updated bookmarks
1253 1301 listing keys for "bookmarks"
1254 1302 listing keys for "bookmarks"
1255 1303 3 changesets found
1256 1304 list of changesets:
1257 1305 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1258 1306 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1259 1307 911600dab2ae7a9baff75958b84fe606851ce955
1260 bundle2-output-bundle: "HG20", 4 parts total
1308 bundle2-output-bundle: "HG20", 5 parts total
1261 1309 bundle2-output-part: "replycaps" 168 bytes payload
1310 bundle2-output-part: "check:phases" 24 bytes payload
1262 1311 bundle2-output-part: "check:heads" streamed payload
1263 1312 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1264 1313 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1265 1314 bundle2-input-bundle: with-transaction
1266 1315 bundle2-input-part: "replycaps" supported
1267 1316 bundle2-input-part: total payload size 168
1317 bundle2-input-part: "check:phases" supported
1318 bundle2-input-part: total payload size 24
1268 1319 bundle2-input-part: "check:heads" supported
1269 1320 bundle2-input-part: total payload size 20
1270 1321 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1271 1322 adding changesets
1272 1323 add changeset ef1ea85a6374
1273 1324 add changeset f9cafe1212c8
1274 1325 add changeset 911600dab2ae
1275 1326 adding manifests
1276 1327 adding file changes
1277 1328 adding foo/Bar/file.txt revisions
1278 1329 adding foo/file.txt revisions
1279 1330 adding quux/file.py revisions
1280 1331 added 3 changesets with 3 changes to 3 files
1281 1332 calling hook pretxnchangegroup.acl: hgext.acl.hook
1282 1333 acl: checking access for user "fred"
1283 1334 acl: acl.allow.branches not enabled
1284 1335 acl: acl.deny.branches not enabled
1285 1336 acl: "group1" not defined in [acl.groups]
1286 1337 acl: acl.allow enabled, 1 entries for user fred
1287 1338 acl: acl.deny not enabled
1288 1339 acl: branch access granted: "ef1ea85a6374" on branch "default"
1289 1340 acl: path access granted: "ef1ea85a6374"
1290 1341 acl: branch access granted: "f9cafe1212c8" on branch "default"
1291 1342 acl: path access granted: "f9cafe1212c8"
1292 1343 acl: branch access granted: "911600dab2ae" on branch "default"
1293 1344 acl: path access granted: "911600dab2ae"
1294 1345 bundle2-input-part: total payload size 1553
1295 1346 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1296 1347 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1297 bundle2-input-bundle: 3 parts total
1348 bundle2-input-bundle: 4 parts total
1298 1349 updating the branch cache
1299 1350 bundle2-output-bundle: "HG20", 2 parts total
1300 1351 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1301 1352 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1302 1353 bundle2-input-bundle: no-transaction
1303 1354 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1304 1355 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1305 1356 bundle2-input-bundle: 1 parts total
1306 1357 listing keys for "phases"
1307 1358 repository tip rolled back to revision 0 (undo push)
1308 1359 0:6675d58eff77
1309 1360
1310 1361
1311 1362 $ echo '[acl.deny]' >> $config
1312 1363 $ echo "foo/Bar/** = @group1" >> $config
1313 1364
1314 1365 @group is allowed inside anything but foo/Bar/
1315 1366
1316 1367 $ do_push fred
1317 1368 Pushing as user fred
1318 1369 hgrc = """
1319 1370 [hooks]
1320 1371 pretxnchangegroup.acl = python:hgext.acl.hook
1321 1372 [acl]
1322 1373 sources = push
1323 1374 [extensions]
1324 1375 [acl.allow]
1325 1376 ** = @group1
1326 1377 [acl.deny]
1327 1378 foo/Bar/** = @group1
1328 1379 """
1329 1380 pushing to ../b
1330 1381 query 1; heads
1331 1382 searching for changes
1332 1383 all remote heads known locally
1333 1384 listing keys for "phases"
1334 1385 checking for updated bookmarks
1335 1386 listing keys for "bookmarks"
1336 1387 listing keys for "bookmarks"
1337 1388 3 changesets found
1338 1389 list of changesets:
1339 1390 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1340 1391 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1341 1392 911600dab2ae7a9baff75958b84fe606851ce955
1342 bundle2-output-bundle: "HG20", 4 parts total
1393 bundle2-output-bundle: "HG20", 5 parts total
1343 1394 bundle2-output-part: "replycaps" 168 bytes payload
1395 bundle2-output-part: "check:phases" 24 bytes payload
1344 1396 bundle2-output-part: "check:heads" streamed payload
1345 1397 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1346 1398 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1347 1399 bundle2-input-bundle: with-transaction
1348 1400 bundle2-input-part: "replycaps" supported
1349 1401 bundle2-input-part: total payload size 168
1402 bundle2-input-part: "check:phases" supported
1403 bundle2-input-part: total payload size 24
1350 1404 bundle2-input-part: "check:heads" supported
1351 1405 bundle2-input-part: total payload size 20
1352 1406 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1353 1407 adding changesets
1354 1408 add changeset ef1ea85a6374
1355 1409 add changeset f9cafe1212c8
1356 1410 add changeset 911600dab2ae
1357 1411 adding manifests
1358 1412 adding file changes
1359 1413 adding foo/Bar/file.txt revisions
1360 1414 adding foo/file.txt revisions
1361 1415 adding quux/file.py revisions
1362 1416 added 3 changesets with 3 changes to 3 files
1363 1417 calling hook pretxnchangegroup.acl: hgext.acl.hook
1364 1418 acl: checking access for user "fred"
1365 1419 acl: acl.allow.branches not enabled
1366 1420 acl: acl.deny.branches not enabled
1367 1421 acl: "group1" not defined in [acl.groups]
1368 1422 acl: acl.allow enabled, 1 entries for user fred
1369 1423 acl: "group1" not defined in [acl.groups]
1370 1424 acl: acl.deny enabled, 1 entries for user fred
1371 1425 acl: branch access granted: "ef1ea85a6374" on branch "default"
1372 1426 acl: path access granted: "ef1ea85a6374"
1373 1427 acl: branch access granted: "f9cafe1212c8" on branch "default"
1374 1428 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1375 1429 bundle2-input-part: total payload size 1553
1376 bundle2-input-bundle: 3 parts total
1430 bundle2-input-bundle: 4 parts total
1377 1431 transaction abort!
1378 1432 rollback completed
1379 1433 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1380 1434 no rollback information available
1381 1435 0:6675d58eff77
1382 1436
1383 1437
1384 1438 Invalid group
1385 1439
1386 1440 Disable the fakegroups trick to get real failures
1387 1441
1388 1442 $ grep -v fakegroups $config > config.tmp
1389 1443 $ mv config.tmp $config
1390 1444 $ echo '[acl.allow]' >> $config
1391 1445 $ echo "** = @unlikelytoexist" >> $config
1392 1446 $ do_push fred 2>&1 | grep unlikelytoexist
1393 1447 ** = @unlikelytoexist
1394 1448 acl: "unlikelytoexist" not defined in [acl.groups]
1395 1449 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1396 1450 abort: group 'unlikelytoexist' is undefined
1397 1451
1398 1452
1399 1453 Branch acl tests setup
1400 1454
1401 1455 $ init_config
1402 1456 $ cd b
1403 1457 $ hg up
1404 1458 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1405 1459 $ hg branch foobar
1406 1460 marked working directory as branch foobar
1407 1461 (branches are permanent and global, did you want a bookmark?)
1408 1462 $ hg commit -m 'create foobar'
1409 1463 $ echo 'foo contents' > abc.txt
1410 1464 $ hg add abc.txt
1411 1465 $ hg commit -m 'foobar contents'
1412 1466 $ cd ..
1413 1467 $ hg --cwd a pull ../b
1414 1468 pulling from ../b
1415 1469 searching for changes
1416 1470 adding changesets
1417 1471 adding manifests
1418 1472 adding file changes
1419 1473 added 2 changesets with 1 changes to 1 files (+1 heads)
1420 1474 new changesets 81fbf4469322:fb35475503ef
1421 1475 (run 'hg heads' to see heads)
1422 1476
1423 1477 Create additional changeset on foobar branch
1424 1478
1425 1479 $ cd a
1426 1480 $ hg up -C foobar
1427 1481 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1428 1482 $ echo 'foo contents2' > abc.txt
1429 1483 $ hg commit -m 'foobar contents2'
1430 1484 $ cd ..
1431 1485
1432 1486
1433 1487 No branch acls specified
1434 1488
1435 1489 $ do_push astro
1436 1490 Pushing as user astro
1437 1491 hgrc = """
1438 1492 [hooks]
1439 1493 pretxnchangegroup.acl = python:hgext.acl.hook
1440 1494 [acl]
1441 1495 sources = push
1442 1496 [extensions]
1443 1497 """
1444 1498 pushing to ../b
1445 1499 query 1; heads
1446 1500 searching for changes
1447 1501 all remote heads known locally
1448 1502 listing keys for "phases"
1449 1503 checking for updated bookmarks
1450 1504 listing keys for "bookmarks"
1451 1505 listing keys for "bookmarks"
1452 1506 4 changesets found
1453 1507 list of changesets:
1454 1508 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1455 1509 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1456 1510 911600dab2ae7a9baff75958b84fe606851ce955
1457 1511 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1458 bundle2-output-bundle: "HG20", 5 parts total
1512 bundle2-output-bundle: "HG20", 6 parts total
1459 1513 bundle2-output-part: "replycaps" 168 bytes payload
1514 bundle2-output-part: "check:phases" 48 bytes payload
1460 1515 bundle2-output-part: "check:heads" streamed payload
1461 1516 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1462 1517 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1463 1518 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1464 1519 bundle2-input-bundle: with-transaction
1465 1520 bundle2-input-part: "replycaps" supported
1466 1521 bundle2-input-part: total payload size 168
1522 bundle2-input-part: "check:phases" supported
1523 bundle2-input-part: total payload size 48
1467 1524 bundle2-input-part: "check:heads" supported
1468 1525 bundle2-input-part: total payload size 20
1469 1526 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1470 1527 adding changesets
1471 1528 add changeset ef1ea85a6374
1472 1529 add changeset f9cafe1212c8
1473 1530 add changeset 911600dab2ae
1474 1531 add changeset e8fc755d4d82
1475 1532 adding manifests
1476 1533 adding file changes
1477 1534 adding abc.txt revisions
1478 1535 adding foo/Bar/file.txt revisions
1479 1536 adding foo/file.txt revisions
1480 1537 adding quux/file.py revisions
1481 1538 added 4 changesets with 4 changes to 4 files (+1 heads)
1482 1539 calling hook pretxnchangegroup.acl: hgext.acl.hook
1483 1540 acl: checking access for user "astro"
1484 1541 acl: acl.allow.branches not enabled
1485 1542 acl: acl.deny.branches not enabled
1486 1543 acl: acl.allow not enabled
1487 1544 acl: acl.deny not enabled
1488 1545 acl: branch access granted: "ef1ea85a6374" on branch "default"
1489 1546 acl: path access granted: "ef1ea85a6374"
1490 1547 acl: branch access granted: "f9cafe1212c8" on branch "default"
1491 1548 acl: path access granted: "f9cafe1212c8"
1492 1549 acl: branch access granted: "911600dab2ae" on branch "default"
1493 1550 acl: path access granted: "911600dab2ae"
1494 1551 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1495 1552 acl: path access granted: "e8fc755d4d82"
1496 1553 bundle2-input-part: total payload size 2068
1497 1554 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1498 1555 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1499 1556 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1500 1557 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
1501 bundle2-input-bundle: 4 parts total
1558 bundle2-input-bundle: 5 parts total
1502 1559 updating the branch cache
1503 1560 bundle2-output-bundle: "HG20", 3 parts total
1504 1561 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1505 1562 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1506 1563 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1507 1564 bundle2-input-bundle: no-transaction
1508 1565 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1509 1566 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1510 1567 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1511 1568 bundle2-input-bundle: 2 parts total
1512 1569 listing keys for "phases"
1513 1570 repository tip rolled back to revision 2 (undo push)
1514 1571 2:fb35475503ef
1515 1572
1516 1573
1517 1574 Branch acl deny test
1518 1575
1519 1576 $ echo "[acl.deny.branches]" >> $config
1520 1577 $ echo "foobar = *" >> $config
1521 1578 $ do_push astro
1522 1579 Pushing as user astro
1523 1580 hgrc = """
1524 1581 [hooks]
1525 1582 pretxnchangegroup.acl = python:hgext.acl.hook
1526 1583 [acl]
1527 1584 sources = push
1528 1585 [extensions]
1529 1586 [acl.deny.branches]
1530 1587 foobar = *
1531 1588 """
1532 1589 pushing to ../b
1533 1590 query 1; heads
1534 1591 searching for changes
1535 1592 all remote heads known locally
1536 1593 listing keys for "phases"
1537 1594 checking for updated bookmarks
1538 1595 listing keys for "bookmarks"
1539 1596 listing keys for "bookmarks"
1540 1597 4 changesets found
1541 1598 list of changesets:
1542 1599 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1543 1600 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1544 1601 911600dab2ae7a9baff75958b84fe606851ce955
1545 1602 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1546 bundle2-output-bundle: "HG20", 5 parts total
1603 bundle2-output-bundle: "HG20", 6 parts total
1547 1604 bundle2-output-part: "replycaps" 168 bytes payload
1605 bundle2-output-part: "check:phases" 48 bytes payload
1548 1606 bundle2-output-part: "check:heads" streamed payload
1549 1607 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1550 1608 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1551 1609 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1552 1610 bundle2-input-bundle: with-transaction
1553 1611 bundle2-input-part: "replycaps" supported
1554 1612 bundle2-input-part: total payload size 168
1613 bundle2-input-part: "check:phases" supported
1614 bundle2-input-part: total payload size 48
1555 1615 bundle2-input-part: "check:heads" supported
1556 1616 bundle2-input-part: total payload size 20
1557 1617 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1558 1618 adding changesets
1559 1619 add changeset ef1ea85a6374
1560 1620 add changeset f9cafe1212c8
1561 1621 add changeset 911600dab2ae
1562 1622 add changeset e8fc755d4d82
1563 1623 adding manifests
1564 1624 adding file changes
1565 1625 adding abc.txt revisions
1566 1626 adding foo/Bar/file.txt revisions
1567 1627 adding foo/file.txt revisions
1568 1628 adding quux/file.py revisions
1569 1629 added 4 changesets with 4 changes to 4 files (+1 heads)
1570 1630 calling hook pretxnchangegroup.acl: hgext.acl.hook
1571 1631 acl: checking access for user "astro"
1572 1632 acl: acl.allow.branches not enabled
1573 1633 acl: acl.deny.branches enabled, 1 entries for user astro
1574 1634 acl: acl.allow not enabled
1575 1635 acl: acl.deny not enabled
1576 1636 acl: branch access granted: "ef1ea85a6374" on branch "default"
1577 1637 acl: path access granted: "ef1ea85a6374"
1578 1638 acl: branch access granted: "f9cafe1212c8" on branch "default"
1579 1639 acl: path access granted: "f9cafe1212c8"
1580 1640 acl: branch access granted: "911600dab2ae" on branch "default"
1581 1641 acl: path access granted: "911600dab2ae"
1582 1642 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1583 1643 bundle2-input-part: total payload size 2068
1584 bundle2-input-bundle: 4 parts total
1644 bundle2-input-bundle: 5 parts total
1585 1645 transaction abort!
1586 1646 rollback completed
1587 1647 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1588 1648 no rollback information available
1589 1649 2:fb35475503ef
1590 1650
1591 1651
1592 1652 Branch acl empty allow test
1593 1653
1594 1654 $ init_config
1595 1655 $ echo "[acl.allow.branches]" >> $config
1596 1656 $ do_push astro
1597 1657 Pushing as user astro
1598 1658 hgrc = """
1599 1659 [hooks]
1600 1660 pretxnchangegroup.acl = python:hgext.acl.hook
1601 1661 [acl]
1602 1662 sources = push
1603 1663 [extensions]
1604 1664 [acl.allow.branches]
1605 1665 """
1606 1666 pushing to ../b
1607 1667 query 1; heads
1608 1668 searching for changes
1609 1669 all remote heads known locally
1610 1670 listing keys for "phases"
1611 1671 checking for updated bookmarks
1612 1672 listing keys for "bookmarks"
1613 1673 listing keys for "bookmarks"
1614 1674 4 changesets found
1615 1675 list of changesets:
1616 1676 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1617 1677 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1618 1678 911600dab2ae7a9baff75958b84fe606851ce955
1619 1679 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1620 bundle2-output-bundle: "HG20", 5 parts total
1680 bundle2-output-bundle: "HG20", 6 parts total
1621 1681 bundle2-output-part: "replycaps" 168 bytes payload
1682 bundle2-output-part: "check:phases" 48 bytes payload
1622 1683 bundle2-output-part: "check:heads" streamed payload
1623 1684 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1624 1685 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1625 1686 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1626 1687 bundle2-input-bundle: with-transaction
1627 1688 bundle2-input-part: "replycaps" supported
1628 1689 bundle2-input-part: total payload size 168
1690 bundle2-input-part: "check:phases" supported
1691 bundle2-input-part: total payload size 48
1629 1692 bundle2-input-part: "check:heads" supported
1630 1693 bundle2-input-part: total payload size 20
1631 1694 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1632 1695 adding changesets
1633 1696 add changeset ef1ea85a6374
1634 1697 add changeset f9cafe1212c8
1635 1698 add changeset 911600dab2ae
1636 1699 add changeset e8fc755d4d82
1637 1700 adding manifests
1638 1701 adding file changes
1639 1702 adding abc.txt revisions
1640 1703 adding foo/Bar/file.txt revisions
1641 1704 adding foo/file.txt revisions
1642 1705 adding quux/file.py revisions
1643 1706 added 4 changesets with 4 changes to 4 files (+1 heads)
1644 1707 calling hook pretxnchangegroup.acl: hgext.acl.hook
1645 1708 acl: checking access for user "astro"
1646 1709 acl: acl.allow.branches enabled, 0 entries for user astro
1647 1710 acl: acl.deny.branches not enabled
1648 1711 acl: acl.allow not enabled
1649 1712 acl: acl.deny not enabled
1650 1713 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1651 1714 bundle2-input-part: total payload size 2068
1652 bundle2-input-bundle: 4 parts total
1715 bundle2-input-bundle: 5 parts total
1653 1716 transaction abort!
1654 1717 rollback completed
1655 1718 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1656 1719 no rollback information available
1657 1720 2:fb35475503ef
1658 1721
1659 1722
1660 1723 Branch acl allow other
1661 1724
1662 1725 $ init_config
1663 1726 $ echo "[acl.allow.branches]" >> $config
1664 1727 $ echo "* = george" >> $config
1665 1728 $ do_push astro
1666 1729 Pushing as user astro
1667 1730 hgrc = """
1668 1731 [hooks]
1669 1732 pretxnchangegroup.acl = python:hgext.acl.hook
1670 1733 [acl]
1671 1734 sources = push
1672 1735 [extensions]
1673 1736 [acl.allow.branches]
1674 1737 * = george
1675 1738 """
1676 1739 pushing to ../b
1677 1740 query 1; heads
1678 1741 searching for changes
1679 1742 all remote heads known locally
1680 1743 listing keys for "phases"
1681 1744 checking for updated bookmarks
1682 1745 listing keys for "bookmarks"
1683 1746 listing keys for "bookmarks"
1684 1747 4 changesets found
1685 1748 list of changesets:
1686 1749 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1687 1750 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1688 1751 911600dab2ae7a9baff75958b84fe606851ce955
1689 1752 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1690 bundle2-output-bundle: "HG20", 5 parts total
1753 bundle2-output-bundle: "HG20", 6 parts total
1691 1754 bundle2-output-part: "replycaps" 168 bytes payload
1755 bundle2-output-part: "check:phases" 48 bytes payload
1692 1756 bundle2-output-part: "check:heads" streamed payload
1693 1757 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1694 1758 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1695 1759 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1696 1760 bundle2-input-bundle: with-transaction
1697 1761 bundle2-input-part: "replycaps" supported
1698 1762 bundle2-input-part: total payload size 168
1763 bundle2-input-part: "check:phases" supported
1764 bundle2-input-part: total payload size 48
1699 1765 bundle2-input-part: "check:heads" supported
1700 1766 bundle2-input-part: total payload size 20
1701 1767 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1702 1768 adding changesets
1703 1769 add changeset ef1ea85a6374
1704 1770 add changeset f9cafe1212c8
1705 1771 add changeset 911600dab2ae
1706 1772 add changeset e8fc755d4d82
1707 1773 adding manifests
1708 1774 adding file changes
1709 1775 adding abc.txt revisions
1710 1776 adding foo/Bar/file.txt revisions
1711 1777 adding foo/file.txt revisions
1712 1778 adding quux/file.py revisions
1713 1779 added 4 changesets with 4 changes to 4 files (+1 heads)
1714 1780 calling hook pretxnchangegroup.acl: hgext.acl.hook
1715 1781 acl: checking access for user "astro"
1716 1782 acl: acl.allow.branches enabled, 0 entries for user astro
1717 1783 acl: acl.deny.branches not enabled
1718 1784 acl: acl.allow not enabled
1719 1785 acl: acl.deny not enabled
1720 1786 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1721 1787 bundle2-input-part: total payload size 2068
1722 bundle2-input-bundle: 4 parts total
1788 bundle2-input-bundle: 5 parts total
1723 1789 transaction abort!
1724 1790 rollback completed
1725 1791 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1726 1792 no rollback information available
1727 1793 2:fb35475503ef
1728 1794
1729 1795 $ do_push george
1730 1796 Pushing as user george
1731 1797 hgrc = """
1732 1798 [hooks]
1733 1799 pretxnchangegroup.acl = python:hgext.acl.hook
1734 1800 [acl]
1735 1801 sources = push
1736 1802 [extensions]
1737 1803 [acl.allow.branches]
1738 1804 * = george
1739 1805 """
1740 1806 pushing to ../b
1741 1807 query 1; heads
1742 1808 searching for changes
1743 1809 all remote heads known locally
1744 1810 listing keys for "phases"
1745 1811 checking for updated bookmarks
1746 1812 listing keys for "bookmarks"
1747 1813 listing keys for "bookmarks"
1748 1814 4 changesets found
1749 1815 list of changesets:
1750 1816 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1751 1817 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1752 1818 911600dab2ae7a9baff75958b84fe606851ce955
1753 1819 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1754 bundle2-output-bundle: "HG20", 5 parts total
1820 bundle2-output-bundle: "HG20", 6 parts total
1755 1821 bundle2-output-part: "replycaps" 168 bytes payload
1822 bundle2-output-part: "check:phases" 48 bytes payload
1756 1823 bundle2-output-part: "check:heads" streamed payload
1757 1824 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1758 1825 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1759 1826 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1760 1827 bundle2-input-bundle: with-transaction
1761 1828 bundle2-input-part: "replycaps" supported
1762 1829 bundle2-input-part: total payload size 168
1830 bundle2-input-part: "check:phases" supported
1831 bundle2-input-part: total payload size 48
1763 1832 bundle2-input-part: "check:heads" supported
1764 1833 bundle2-input-part: total payload size 20
1765 1834 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1766 1835 adding changesets
1767 1836 add changeset ef1ea85a6374
1768 1837 add changeset f9cafe1212c8
1769 1838 add changeset 911600dab2ae
1770 1839 add changeset e8fc755d4d82
1771 1840 adding manifests
1772 1841 adding file changes
1773 1842 adding abc.txt revisions
1774 1843 adding foo/Bar/file.txt revisions
1775 1844 adding foo/file.txt revisions
1776 1845 adding quux/file.py revisions
1777 1846 added 4 changesets with 4 changes to 4 files (+1 heads)
1778 1847 calling hook pretxnchangegroup.acl: hgext.acl.hook
1779 1848 acl: checking access for user "george"
1780 1849 acl: acl.allow.branches enabled, 1 entries for user george
1781 1850 acl: acl.deny.branches not enabled
1782 1851 acl: acl.allow not enabled
1783 1852 acl: acl.deny not enabled
1784 1853 acl: branch access granted: "ef1ea85a6374" on branch "default"
1785 1854 acl: path access granted: "ef1ea85a6374"
1786 1855 acl: branch access granted: "f9cafe1212c8" on branch "default"
1787 1856 acl: path access granted: "f9cafe1212c8"
1788 1857 acl: branch access granted: "911600dab2ae" on branch "default"
1789 1858 acl: path access granted: "911600dab2ae"
1790 1859 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1791 1860 acl: path access granted: "e8fc755d4d82"
1792 1861 bundle2-input-part: total payload size 2068
1793 1862 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1794 1863 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1795 1864 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1796 1865 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
1797 bundle2-input-bundle: 4 parts total
1866 bundle2-input-bundle: 5 parts total
1798 1867 updating the branch cache
1799 1868 bundle2-output-bundle: "HG20", 3 parts total
1800 1869 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1801 1870 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1802 1871 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1803 1872 bundle2-input-bundle: no-transaction
1804 1873 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1805 1874 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1806 1875 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1807 1876 bundle2-input-bundle: 2 parts total
1808 1877 listing keys for "phases"
1809 1878 repository tip rolled back to revision 2 (undo push)
1810 1879 2:fb35475503ef
1811 1880
1812 1881
1813 1882 Branch acl conflicting allow
1814 1883 asterisk ends up applying to all branches and allowing george to
1815 1884 push foobar into the remote
1816 1885
1817 1886 $ init_config
1818 1887 $ echo "[acl.allow.branches]" >> $config
1819 1888 $ echo "foobar = astro" >> $config
1820 1889 $ echo "* = george" >> $config
1821 1890 $ do_push george
1822 1891 Pushing as user george
1823 1892 hgrc = """
1824 1893 [hooks]
1825 1894 pretxnchangegroup.acl = python:hgext.acl.hook
1826 1895 [acl]
1827 1896 sources = push
1828 1897 [extensions]
1829 1898 [acl.allow.branches]
1830 1899 foobar = astro
1831 1900 * = george
1832 1901 """
1833 1902 pushing to ../b
1834 1903 query 1; heads
1835 1904 searching for changes
1836 1905 all remote heads known locally
1837 1906 listing keys for "phases"
1838 1907 checking for updated bookmarks
1839 1908 listing keys for "bookmarks"
1840 1909 listing keys for "bookmarks"
1841 1910 4 changesets found
1842 1911 list of changesets:
1843 1912 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1844 1913 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1845 1914 911600dab2ae7a9baff75958b84fe606851ce955
1846 1915 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1847 bundle2-output-bundle: "HG20", 5 parts total
1916 bundle2-output-bundle: "HG20", 6 parts total
1848 1917 bundle2-output-part: "replycaps" 168 bytes payload
1918 bundle2-output-part: "check:phases" 48 bytes payload
1849 1919 bundle2-output-part: "check:heads" streamed payload
1850 1920 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1851 1921 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1852 1922 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1853 1923 bundle2-input-bundle: with-transaction
1854 1924 bundle2-input-part: "replycaps" supported
1855 1925 bundle2-input-part: total payload size 168
1926 bundle2-input-part: "check:phases" supported
1927 bundle2-input-part: total payload size 48
1856 1928 bundle2-input-part: "check:heads" supported
1857 1929 bundle2-input-part: total payload size 20
1858 1930 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1859 1931 adding changesets
1860 1932 add changeset ef1ea85a6374
1861 1933 add changeset f9cafe1212c8
1862 1934 add changeset 911600dab2ae
1863 1935 add changeset e8fc755d4d82
1864 1936 adding manifests
1865 1937 adding file changes
1866 1938 adding abc.txt revisions
1867 1939 adding foo/Bar/file.txt revisions
1868 1940 adding foo/file.txt revisions
1869 1941 adding quux/file.py revisions
1870 1942 added 4 changesets with 4 changes to 4 files (+1 heads)
1871 1943 calling hook pretxnchangegroup.acl: hgext.acl.hook
1872 1944 acl: checking access for user "george"
1873 1945 acl: acl.allow.branches enabled, 1 entries for user george
1874 1946 acl: acl.deny.branches not enabled
1875 1947 acl: acl.allow not enabled
1876 1948 acl: acl.deny not enabled
1877 1949 acl: branch access granted: "ef1ea85a6374" on branch "default"
1878 1950 acl: path access granted: "ef1ea85a6374"
1879 1951 acl: branch access granted: "f9cafe1212c8" on branch "default"
1880 1952 acl: path access granted: "f9cafe1212c8"
1881 1953 acl: branch access granted: "911600dab2ae" on branch "default"
1882 1954 acl: path access granted: "911600dab2ae"
1883 1955 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1884 1956 acl: path access granted: "e8fc755d4d82"
1885 1957 bundle2-input-part: total payload size 2068
1886 1958 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1887 1959 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1888 1960 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1889 1961 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
1890 bundle2-input-bundle: 4 parts total
1962 bundle2-input-bundle: 5 parts total
1891 1963 updating the branch cache
1892 1964 bundle2-output-bundle: "HG20", 3 parts total
1893 1965 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1894 1966 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1895 1967 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1896 1968 bundle2-input-bundle: no-transaction
1897 1969 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1898 1970 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1899 1971 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1900 1972 bundle2-input-bundle: 2 parts total
1901 1973 listing keys for "phases"
1902 1974 repository tip rolled back to revision 2 (undo push)
1903 1975 2:fb35475503ef
1904 1976
1905 1977 Branch acl conflicting deny
1906 1978
1907 1979 $ init_config
1908 1980 $ echo "[acl.deny.branches]" >> $config
1909 1981 $ echo "foobar = astro" >> $config
1910 1982 $ echo "default = astro" >> $config
1911 1983 $ echo "* = george" >> $config
1912 1984 $ do_push george
1913 1985 Pushing as user george
1914 1986 hgrc = """
1915 1987 [hooks]
1916 1988 pretxnchangegroup.acl = python:hgext.acl.hook
1917 1989 [acl]
1918 1990 sources = push
1919 1991 [extensions]
1920 1992 [acl.deny.branches]
1921 1993 foobar = astro
1922 1994 default = astro
1923 1995 * = george
1924 1996 """
1925 1997 pushing to ../b
1926 1998 query 1; heads
1927 1999 searching for changes
1928 2000 all remote heads known locally
1929 2001 listing keys for "phases"
1930 2002 checking for updated bookmarks
1931 2003 listing keys for "bookmarks"
1932 2004 listing keys for "bookmarks"
1933 2005 4 changesets found
1934 2006 list of changesets:
1935 2007 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1936 2008 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1937 2009 911600dab2ae7a9baff75958b84fe606851ce955
1938 2010 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1939 bundle2-output-bundle: "HG20", 5 parts total
2011 bundle2-output-bundle: "HG20", 6 parts total
1940 2012 bundle2-output-part: "replycaps" 168 bytes payload
2013 bundle2-output-part: "check:phases" 48 bytes payload
1941 2014 bundle2-output-part: "check:heads" streamed payload
1942 2015 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1943 2016 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1944 2017 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1945 2018 bundle2-input-bundle: with-transaction
1946 2019 bundle2-input-part: "replycaps" supported
1947 2020 bundle2-input-part: total payload size 168
2021 bundle2-input-part: "check:phases" supported
2022 bundle2-input-part: total payload size 48
1948 2023 bundle2-input-part: "check:heads" supported
1949 2024 bundle2-input-part: total payload size 20
1950 2025 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1951 2026 adding changesets
1952 2027 add changeset ef1ea85a6374
1953 2028 add changeset f9cafe1212c8
1954 2029 add changeset 911600dab2ae
1955 2030 add changeset e8fc755d4d82
1956 2031 adding manifests
1957 2032 adding file changes
1958 2033 adding abc.txt revisions
1959 2034 adding foo/Bar/file.txt revisions
1960 2035 adding foo/file.txt revisions
1961 2036 adding quux/file.py revisions
1962 2037 added 4 changesets with 4 changes to 4 files (+1 heads)
1963 2038 calling hook pretxnchangegroup.acl: hgext.acl.hook
1964 2039 acl: checking access for user "george"
1965 2040 acl: acl.allow.branches not enabled
1966 2041 acl: acl.deny.branches enabled, 1 entries for user george
1967 2042 acl: acl.allow not enabled
1968 2043 acl: acl.deny not enabled
1969 2044 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1970 2045 bundle2-input-part: total payload size 2068
1971 bundle2-input-bundle: 4 parts total
2046 bundle2-input-bundle: 5 parts total
1972 2047 transaction abort!
1973 2048 rollback completed
1974 2049 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1975 2050 no rollback information available
1976 2051 2:fb35475503ef
1977 2052
1978 2053 User 'astro' must not be denied
1979 2054
1980 2055 $ init_config
1981 2056 $ echo "[acl.deny.branches]" >> $config
1982 2057 $ echo "default = !astro" >> $config
1983 2058 $ do_push astro
1984 2059 Pushing as user astro
1985 2060 hgrc = """
1986 2061 [hooks]
1987 2062 pretxnchangegroup.acl = python:hgext.acl.hook
1988 2063 [acl]
1989 2064 sources = push
1990 2065 [extensions]
1991 2066 [acl.deny.branches]
1992 2067 default = !astro
1993 2068 """
1994 2069 pushing to ../b
1995 2070 query 1; heads
1996 2071 searching for changes
1997 2072 all remote heads known locally
1998 2073 listing keys for "phases"
1999 2074 checking for updated bookmarks
2000 2075 listing keys for "bookmarks"
2001 2076 listing keys for "bookmarks"
2002 2077 4 changesets found
2003 2078 list of changesets:
2004 2079 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2005 2080 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2006 2081 911600dab2ae7a9baff75958b84fe606851ce955
2007 2082 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2008 bundle2-output-bundle: "HG20", 5 parts total
2083 bundle2-output-bundle: "HG20", 6 parts total
2009 2084 bundle2-output-part: "replycaps" 168 bytes payload
2085 bundle2-output-part: "check:phases" 48 bytes payload
2010 2086 bundle2-output-part: "check:heads" streamed payload
2011 2087 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2012 2088 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
2013 2089 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
2014 2090 bundle2-input-bundle: with-transaction
2015 2091 bundle2-input-part: "replycaps" supported
2016 2092 bundle2-input-part: total payload size 168
2093 bundle2-input-part: "check:phases" supported
2094 bundle2-input-part: total payload size 48
2017 2095 bundle2-input-part: "check:heads" supported
2018 2096 bundle2-input-part: total payload size 20
2019 2097 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2020 2098 adding changesets
2021 2099 add changeset ef1ea85a6374
2022 2100 add changeset f9cafe1212c8
2023 2101 add changeset 911600dab2ae
2024 2102 add changeset e8fc755d4d82
2025 2103 adding manifests
2026 2104 adding file changes
2027 2105 adding abc.txt revisions
2028 2106 adding foo/Bar/file.txt revisions
2029 2107 adding foo/file.txt revisions
2030 2108 adding quux/file.py revisions
2031 2109 added 4 changesets with 4 changes to 4 files (+1 heads)
2032 2110 calling hook pretxnchangegroup.acl: hgext.acl.hook
2033 2111 acl: checking access for user "astro"
2034 2112 acl: acl.allow.branches not enabled
2035 2113 acl: acl.deny.branches enabled, 0 entries for user astro
2036 2114 acl: acl.allow not enabled
2037 2115 acl: acl.deny not enabled
2038 2116 acl: branch access granted: "ef1ea85a6374" on branch "default"
2039 2117 acl: path access granted: "ef1ea85a6374"
2040 2118 acl: branch access granted: "f9cafe1212c8" on branch "default"
2041 2119 acl: path access granted: "f9cafe1212c8"
2042 2120 acl: branch access granted: "911600dab2ae" on branch "default"
2043 2121 acl: path access granted: "911600dab2ae"
2044 2122 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2045 2123 acl: path access granted: "e8fc755d4d82"
2046 2124 bundle2-input-part: total payload size 2068
2047 2125 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
2048 2126 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
2049 2127 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
2050 2128 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
2051 bundle2-input-bundle: 4 parts total
2129 bundle2-input-bundle: 5 parts total
2052 2130 updating the branch cache
2053 2131 bundle2-output-bundle: "HG20", 3 parts total
2054 2132 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
2055 2133 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
2056 2134 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
2057 2135 bundle2-input-bundle: no-transaction
2058 2136 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
2059 2137 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
2060 2138 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
2061 2139 bundle2-input-bundle: 2 parts total
2062 2140 listing keys for "phases"
2063 2141 repository tip rolled back to revision 2 (undo push)
2064 2142 2:fb35475503ef
2065 2143
2066 2144
2067 2145 Non-astro users must be denied
2068 2146
2069 2147 $ do_push george
2070 2148 Pushing as user george
2071 2149 hgrc = """
2072 2150 [hooks]
2073 2151 pretxnchangegroup.acl = python:hgext.acl.hook
2074 2152 [acl]
2075 2153 sources = push
2076 2154 [extensions]
2077 2155 [acl.deny.branches]
2078 2156 default = !astro
2079 2157 """
2080 2158 pushing to ../b
2081 2159 query 1; heads
2082 2160 searching for changes
2083 2161 all remote heads known locally
2084 2162 listing keys for "phases"
2085 2163 checking for updated bookmarks
2086 2164 listing keys for "bookmarks"
2087 2165 listing keys for "bookmarks"
2088 2166 4 changesets found
2089 2167 list of changesets:
2090 2168 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2091 2169 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2092 2170 911600dab2ae7a9baff75958b84fe606851ce955
2093 2171 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2094 bundle2-output-bundle: "HG20", 5 parts total
2172 bundle2-output-bundle: "HG20", 6 parts total
2095 2173 bundle2-output-part: "replycaps" 168 bytes payload
2174 bundle2-output-part: "check:phases" 48 bytes payload
2096 2175 bundle2-output-part: "check:heads" streamed payload
2097 2176 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2098 2177 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
2099 2178 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
2100 2179 bundle2-input-bundle: with-transaction
2101 2180 bundle2-input-part: "replycaps" supported
2102 2181 bundle2-input-part: total payload size 168
2182 bundle2-input-part: "check:phases" supported
2183 bundle2-input-part: total payload size 48
2103 2184 bundle2-input-part: "check:heads" supported
2104 2185 bundle2-input-part: total payload size 20
2105 2186 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2106 2187 adding changesets
2107 2188 add changeset ef1ea85a6374
2108 2189 add changeset f9cafe1212c8
2109 2190 add changeset 911600dab2ae
2110 2191 add changeset e8fc755d4d82
2111 2192 adding manifests
2112 2193 adding file changes
2113 2194 adding abc.txt revisions
2114 2195 adding foo/Bar/file.txt revisions
2115 2196 adding foo/file.txt revisions
2116 2197 adding quux/file.py revisions
2117 2198 added 4 changesets with 4 changes to 4 files (+1 heads)
2118 2199 calling hook pretxnchangegroup.acl: hgext.acl.hook
2119 2200 acl: checking access for user "george"
2120 2201 acl: acl.allow.branches not enabled
2121 2202 acl: acl.deny.branches enabled, 1 entries for user george
2122 2203 acl: acl.allow not enabled
2123 2204 acl: acl.deny not enabled
2124 2205 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2125 2206 bundle2-input-part: total payload size 2068
2126 bundle2-input-bundle: 4 parts total
2207 bundle2-input-bundle: 5 parts total
2127 2208 transaction abort!
2128 2209 rollback completed
2129 2210 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2130 2211 no rollback information available
2131 2212 2:fb35475503ef
2132 2213
2133 2214
General Comments 0
You need to be logged in to leave comments. Login now