##// END OF EJS Templates
transaction: include txnname in the hookargs dictionary...
marmoute -
r42062:94faa2e8 default
parent child Browse files
Show More
@@ -1,3087 +1,3087 b''
1 1 # localrepo.py - read/write repository class for mercurial
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 errno
11 11 import hashlib
12 12 import os
13 13 import random
14 14 import sys
15 15 import time
16 16 import weakref
17 17
18 18 from .i18n import _
19 19 from .node import (
20 20 bin,
21 21 hex,
22 22 nullid,
23 23 nullrev,
24 24 short,
25 25 )
26 26 from . import (
27 27 bookmarks,
28 28 branchmap,
29 29 bundle2,
30 30 changegroup,
31 31 changelog,
32 32 color,
33 33 context,
34 34 dirstate,
35 35 dirstateguard,
36 36 discovery,
37 37 encoding,
38 38 error,
39 39 exchange,
40 40 extensions,
41 41 filelog,
42 42 hook,
43 43 lock as lockmod,
44 44 manifest,
45 45 match as matchmod,
46 46 merge as mergemod,
47 47 mergeutil,
48 48 namespaces,
49 49 narrowspec,
50 50 obsolete,
51 51 pathutil,
52 52 phases,
53 53 pushkey,
54 54 pycompat,
55 55 repository,
56 56 repoview,
57 57 revset,
58 58 revsetlang,
59 59 scmutil,
60 60 sparse,
61 61 store as storemod,
62 62 subrepoutil,
63 63 tags as tagsmod,
64 64 transaction,
65 65 txnutil,
66 66 util,
67 67 vfs as vfsmod,
68 68 )
69 69 from .utils import (
70 70 interfaceutil,
71 71 procutil,
72 72 stringutil,
73 73 )
74 74
75 75 from .revlogutils import (
76 76 constants as revlogconst,
77 77 )
78 78
79 79 release = lockmod.release
80 80 urlerr = util.urlerr
81 81 urlreq = util.urlreq
82 82
83 83 # set of (path, vfs-location) tuples. vfs-location is:
84 84 # - 'plain for vfs relative paths
85 85 # - '' for svfs relative paths
86 86 _cachedfiles = set()
87 87
88 88 class _basefilecache(scmutil.filecache):
89 89 """All filecache usage on repo are done for logic that should be unfiltered
90 90 """
91 91 def __get__(self, repo, type=None):
92 92 if repo is None:
93 93 return self
94 94 # proxy to unfiltered __dict__ since filtered repo has no entry
95 95 unfi = repo.unfiltered()
96 96 try:
97 97 return unfi.__dict__[self.sname]
98 98 except KeyError:
99 99 pass
100 100 return super(_basefilecache, self).__get__(unfi, type)
101 101
102 102 def set(self, repo, value):
103 103 return super(_basefilecache, self).set(repo.unfiltered(), value)
104 104
105 105 class repofilecache(_basefilecache):
106 106 """filecache for files in .hg but outside of .hg/store"""
107 107 def __init__(self, *paths):
108 108 super(repofilecache, self).__init__(*paths)
109 109 for path in paths:
110 110 _cachedfiles.add((path, 'plain'))
111 111
112 112 def join(self, obj, fname):
113 113 return obj.vfs.join(fname)
114 114
115 115 class storecache(_basefilecache):
116 116 """filecache for files in the store"""
117 117 def __init__(self, *paths):
118 118 super(storecache, self).__init__(*paths)
119 119 for path in paths:
120 120 _cachedfiles.add((path, ''))
121 121
122 122 def join(self, obj, fname):
123 123 return obj.sjoin(fname)
124 124
125 125 def isfilecached(repo, name):
126 126 """check if a repo has already cached "name" filecache-ed property
127 127
128 128 This returns (cachedobj-or-None, iscached) tuple.
129 129 """
130 130 cacheentry = repo.unfiltered()._filecache.get(name, None)
131 131 if not cacheentry:
132 132 return None, False
133 133 return cacheentry.obj, True
134 134
135 135 class unfilteredpropertycache(util.propertycache):
136 136 """propertycache that apply to unfiltered repo only"""
137 137
138 138 def __get__(self, repo, type=None):
139 139 unfi = repo.unfiltered()
140 140 if unfi is repo:
141 141 return super(unfilteredpropertycache, self).__get__(unfi)
142 142 return getattr(unfi, self.name)
143 143
144 144 class filteredpropertycache(util.propertycache):
145 145 """propertycache that must take filtering in account"""
146 146
147 147 def cachevalue(self, obj, value):
148 148 object.__setattr__(obj, self.name, value)
149 149
150 150
151 151 def hasunfilteredcache(repo, name):
152 152 """check if a repo has an unfilteredpropertycache value for <name>"""
153 153 return name in vars(repo.unfiltered())
154 154
155 155 def unfilteredmethod(orig):
156 156 """decorate method that always need to be run on unfiltered version"""
157 157 def wrapper(repo, *args, **kwargs):
158 158 return orig(repo.unfiltered(), *args, **kwargs)
159 159 return wrapper
160 160
161 161 moderncaps = {'lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
162 162 'unbundle'}
163 163 legacycaps = moderncaps.union({'changegroupsubset'})
164 164
165 165 @interfaceutil.implementer(repository.ipeercommandexecutor)
166 166 class localcommandexecutor(object):
167 167 def __init__(self, peer):
168 168 self._peer = peer
169 169 self._sent = False
170 170 self._closed = False
171 171
172 172 def __enter__(self):
173 173 return self
174 174
175 175 def __exit__(self, exctype, excvalue, exctb):
176 176 self.close()
177 177
178 178 def callcommand(self, command, args):
179 179 if self._sent:
180 180 raise error.ProgrammingError('callcommand() cannot be used after '
181 181 'sendcommands()')
182 182
183 183 if self._closed:
184 184 raise error.ProgrammingError('callcommand() cannot be used after '
185 185 'close()')
186 186
187 187 # We don't need to support anything fancy. Just call the named
188 188 # method on the peer and return a resolved future.
189 189 fn = getattr(self._peer, pycompat.sysstr(command))
190 190
191 191 f = pycompat.futures.Future()
192 192
193 193 try:
194 194 result = fn(**pycompat.strkwargs(args))
195 195 except Exception:
196 196 pycompat.future_set_exception_info(f, sys.exc_info()[1:])
197 197 else:
198 198 f.set_result(result)
199 199
200 200 return f
201 201
202 202 def sendcommands(self):
203 203 self._sent = True
204 204
205 205 def close(self):
206 206 self._closed = True
207 207
208 208 @interfaceutil.implementer(repository.ipeercommands)
209 209 class localpeer(repository.peer):
210 210 '''peer for a local repo; reflects only the most recent API'''
211 211
212 212 def __init__(self, repo, caps=None):
213 213 super(localpeer, self).__init__()
214 214
215 215 if caps is None:
216 216 caps = moderncaps.copy()
217 217 self._repo = repo.filtered('served')
218 218 self.ui = repo.ui
219 219 self._caps = repo._restrictcapabilities(caps)
220 220
221 221 # Begin of _basepeer interface.
222 222
223 223 def url(self):
224 224 return self._repo.url()
225 225
226 226 def local(self):
227 227 return self._repo
228 228
229 229 def peer(self):
230 230 return self
231 231
232 232 def canpush(self):
233 233 return True
234 234
235 235 def close(self):
236 236 self._repo.close()
237 237
238 238 # End of _basepeer interface.
239 239
240 240 # Begin of _basewirecommands interface.
241 241
242 242 def branchmap(self):
243 243 return self._repo.branchmap()
244 244
245 245 def capabilities(self):
246 246 return self._caps
247 247
248 248 def clonebundles(self):
249 249 return self._repo.tryread('clonebundles.manifest')
250 250
251 251 def debugwireargs(self, one, two, three=None, four=None, five=None):
252 252 """Used to test argument passing over the wire"""
253 253 return "%s %s %s %s %s" % (one, two, pycompat.bytestr(three),
254 254 pycompat.bytestr(four),
255 255 pycompat.bytestr(five))
256 256
257 257 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
258 258 **kwargs):
259 259 chunks = exchange.getbundlechunks(self._repo, source, heads=heads,
260 260 common=common, bundlecaps=bundlecaps,
261 261 **kwargs)[1]
262 262 cb = util.chunkbuffer(chunks)
263 263
264 264 if exchange.bundle2requested(bundlecaps):
265 265 # When requesting a bundle2, getbundle returns a stream to make the
266 266 # wire level function happier. We need to build a proper object
267 267 # from it in local peer.
268 268 return bundle2.getunbundler(self.ui, cb)
269 269 else:
270 270 return changegroup.getunbundler('01', cb, None)
271 271
272 272 def heads(self):
273 273 return self._repo.heads()
274 274
275 275 def known(self, nodes):
276 276 return self._repo.known(nodes)
277 277
278 278 def listkeys(self, namespace):
279 279 return self._repo.listkeys(namespace)
280 280
281 281 def lookup(self, key):
282 282 return self._repo.lookup(key)
283 283
284 284 def pushkey(self, namespace, key, old, new):
285 285 return self._repo.pushkey(namespace, key, old, new)
286 286
287 287 def stream_out(self):
288 288 raise error.Abort(_('cannot perform stream clone against local '
289 289 'peer'))
290 290
291 291 def unbundle(self, bundle, heads, url):
292 292 """apply a bundle on a repo
293 293
294 294 This function handles the repo locking itself."""
295 295 try:
296 296 try:
297 297 bundle = exchange.readbundle(self.ui, bundle, None)
298 298 ret = exchange.unbundle(self._repo, bundle, heads, 'push', url)
299 299 if util.safehasattr(ret, 'getchunks'):
300 300 # This is a bundle20 object, turn it into an unbundler.
301 301 # This little dance should be dropped eventually when the
302 302 # API is finally improved.
303 303 stream = util.chunkbuffer(ret.getchunks())
304 304 ret = bundle2.getunbundler(self.ui, stream)
305 305 return ret
306 306 except Exception as exc:
307 307 # If the exception contains output salvaged from a bundle2
308 308 # reply, we need to make sure it is printed before continuing
309 309 # to fail. So we build a bundle2 with such output and consume
310 310 # it directly.
311 311 #
312 312 # This is not very elegant but allows a "simple" solution for
313 313 # issue4594
314 314 output = getattr(exc, '_bundle2salvagedoutput', ())
315 315 if output:
316 316 bundler = bundle2.bundle20(self._repo.ui)
317 317 for out in output:
318 318 bundler.addpart(out)
319 319 stream = util.chunkbuffer(bundler.getchunks())
320 320 b = bundle2.getunbundler(self.ui, stream)
321 321 bundle2.processbundle(self._repo, b)
322 322 raise
323 323 except error.PushRaced as exc:
324 324 raise error.ResponseError(_('push failed:'),
325 325 stringutil.forcebytestr(exc))
326 326
327 327 # End of _basewirecommands interface.
328 328
329 329 # Begin of peer interface.
330 330
331 331 def commandexecutor(self):
332 332 return localcommandexecutor(self)
333 333
334 334 # End of peer interface.
335 335
336 336 @interfaceutil.implementer(repository.ipeerlegacycommands)
337 337 class locallegacypeer(localpeer):
338 338 '''peer extension which implements legacy methods too; used for tests with
339 339 restricted capabilities'''
340 340
341 341 def __init__(self, repo):
342 342 super(locallegacypeer, self).__init__(repo, caps=legacycaps)
343 343
344 344 # Begin of baselegacywirecommands interface.
345 345
346 346 def between(self, pairs):
347 347 return self._repo.between(pairs)
348 348
349 349 def branches(self, nodes):
350 350 return self._repo.branches(nodes)
351 351
352 352 def changegroup(self, nodes, source):
353 353 outgoing = discovery.outgoing(self._repo, missingroots=nodes,
354 354 missingheads=self._repo.heads())
355 355 return changegroup.makechangegroup(self._repo, outgoing, '01', source)
356 356
357 357 def changegroupsubset(self, bases, heads, source):
358 358 outgoing = discovery.outgoing(self._repo, missingroots=bases,
359 359 missingheads=heads)
360 360 return changegroup.makechangegroup(self._repo, outgoing, '01', source)
361 361
362 362 # End of baselegacywirecommands interface.
363 363
364 364 # Increment the sub-version when the revlog v2 format changes to lock out old
365 365 # clients.
366 366 REVLOGV2_REQUIREMENT = 'exp-revlogv2.1'
367 367
368 368 # A repository with the sparserevlog feature will have delta chains that
369 369 # can spread over a larger span. Sparse reading cuts these large spans into
370 370 # pieces, so that each piece isn't too big.
371 371 # Without the sparserevlog capability, reading from the repository could use
372 372 # huge amounts of memory, because the whole span would be read at once,
373 373 # including all the intermediate revisions that aren't pertinent for the chain.
374 374 # This is why once a repository has enabled sparse-read, it becomes required.
375 375 SPARSEREVLOG_REQUIREMENT = 'sparserevlog'
376 376
377 377 # Functions receiving (ui, features) that extensions can register to impact
378 378 # the ability to load repositories with custom requirements. Only
379 379 # functions defined in loaded extensions are called.
380 380 #
381 381 # The function receives a set of requirement strings that the repository
382 382 # is capable of opening. Functions will typically add elements to the
383 383 # set to reflect that the extension knows how to handle that requirements.
384 384 featuresetupfuncs = set()
385 385
386 386 def makelocalrepository(baseui, path, intents=None):
387 387 """Create a local repository object.
388 388
389 389 Given arguments needed to construct a local repository, this function
390 390 performs various early repository loading functionality (such as
391 391 reading the ``.hg/requires`` and ``.hg/hgrc`` files), validates that
392 392 the repository can be opened, derives a type suitable for representing
393 393 that repository, and returns an instance of it.
394 394
395 395 The returned object conforms to the ``repository.completelocalrepository``
396 396 interface.
397 397
398 398 The repository type is derived by calling a series of factory functions
399 399 for each aspect/interface of the final repository. These are defined by
400 400 ``REPO_INTERFACES``.
401 401
402 402 Each factory function is called to produce a type implementing a specific
403 403 interface. The cumulative list of returned types will be combined into a
404 404 new type and that type will be instantiated to represent the local
405 405 repository.
406 406
407 407 The factory functions each receive various state that may be consulted
408 408 as part of deriving a type.
409 409
410 410 Extensions should wrap these factory functions to customize repository type
411 411 creation. Note that an extension's wrapped function may be called even if
412 412 that extension is not loaded for the repo being constructed. Extensions
413 413 should check if their ``__name__`` appears in the
414 414 ``extensionmodulenames`` set passed to the factory function and no-op if
415 415 not.
416 416 """
417 417 ui = baseui.copy()
418 418 # Prevent copying repo configuration.
419 419 ui.copy = baseui.copy
420 420
421 421 # Working directory VFS rooted at repository root.
422 422 wdirvfs = vfsmod.vfs(path, expandpath=True, realpath=True)
423 423
424 424 # Main VFS for .hg/ directory.
425 425 hgpath = wdirvfs.join(b'.hg')
426 426 hgvfs = vfsmod.vfs(hgpath, cacheaudited=True)
427 427
428 428 # The .hg/ path should exist and should be a directory. All other
429 429 # cases are errors.
430 430 if not hgvfs.isdir():
431 431 try:
432 432 hgvfs.stat()
433 433 except OSError as e:
434 434 if e.errno != errno.ENOENT:
435 435 raise
436 436
437 437 raise error.RepoError(_(b'repository %s not found') % path)
438 438
439 439 # .hg/requires file contains a newline-delimited list of
440 440 # features/capabilities the opener (us) must have in order to use
441 441 # the repository. This file was introduced in Mercurial 0.9.2,
442 442 # which means very old repositories may not have one. We assume
443 443 # a missing file translates to no requirements.
444 444 try:
445 445 requirements = set(hgvfs.read(b'requires').splitlines())
446 446 except IOError as e:
447 447 if e.errno != errno.ENOENT:
448 448 raise
449 449 requirements = set()
450 450
451 451 # The .hg/hgrc file may load extensions or contain config options
452 452 # that influence repository construction. Attempt to load it and
453 453 # process any new extensions that it may have pulled in.
454 454 if loadhgrc(ui, wdirvfs, hgvfs, requirements):
455 455 afterhgrcload(ui, wdirvfs, hgvfs, requirements)
456 456 extensions.loadall(ui)
457 457 extensions.populateui(ui)
458 458
459 459 # Set of module names of extensions loaded for this repository.
460 460 extensionmodulenames = {m.__name__ for n, m in extensions.extensions(ui)}
461 461
462 462 supportedrequirements = gathersupportedrequirements(ui)
463 463
464 464 # We first validate the requirements are known.
465 465 ensurerequirementsrecognized(requirements, supportedrequirements)
466 466
467 467 # Then we validate that the known set is reasonable to use together.
468 468 ensurerequirementscompatible(ui, requirements)
469 469
470 470 # TODO there are unhandled edge cases related to opening repositories with
471 471 # shared storage. If storage is shared, we should also test for requirements
472 472 # compatibility in the pointed-to repo. This entails loading the .hg/hgrc in
473 473 # that repo, as that repo may load extensions needed to open it. This is a
474 474 # bit complicated because we don't want the other hgrc to overwrite settings
475 475 # in this hgrc.
476 476 #
477 477 # This bug is somewhat mitigated by the fact that we copy the .hg/requires
478 478 # file when sharing repos. But if a requirement is added after the share is
479 479 # performed, thereby introducing a new requirement for the opener, we may
480 480 # will not see that and could encounter a run-time error interacting with
481 481 # that shared store since it has an unknown-to-us requirement.
482 482
483 483 # At this point, we know we should be capable of opening the repository.
484 484 # Now get on with doing that.
485 485
486 486 features = set()
487 487
488 488 # The "store" part of the repository holds versioned data. How it is
489 489 # accessed is determined by various requirements. The ``shared`` or
490 490 # ``relshared`` requirements indicate the store lives in the path contained
491 491 # in the ``.hg/sharedpath`` file. This is an absolute path for
492 492 # ``shared`` and relative to ``.hg/`` for ``relshared``.
493 493 if b'shared' in requirements or b'relshared' in requirements:
494 494 sharedpath = hgvfs.read(b'sharedpath').rstrip(b'\n')
495 495 if b'relshared' in requirements:
496 496 sharedpath = hgvfs.join(sharedpath)
497 497
498 498 sharedvfs = vfsmod.vfs(sharedpath, realpath=True)
499 499
500 500 if not sharedvfs.exists():
501 501 raise error.RepoError(_(b'.hg/sharedpath points to nonexistent '
502 502 b'directory %s') % sharedvfs.base)
503 503
504 504 features.add(repository.REPO_FEATURE_SHARED_STORAGE)
505 505
506 506 storebasepath = sharedvfs.base
507 507 cachepath = sharedvfs.join(b'cache')
508 508 else:
509 509 storebasepath = hgvfs.base
510 510 cachepath = hgvfs.join(b'cache')
511 511 wcachepath = hgvfs.join(b'wcache')
512 512
513 513
514 514 # The store has changed over time and the exact layout is dictated by
515 515 # requirements. The store interface abstracts differences across all
516 516 # of them.
517 517 store = makestore(requirements, storebasepath,
518 518 lambda base: vfsmod.vfs(base, cacheaudited=True))
519 519 hgvfs.createmode = store.createmode
520 520
521 521 storevfs = store.vfs
522 522 storevfs.options = resolvestorevfsoptions(ui, requirements, features)
523 523
524 524 # The cache vfs is used to manage cache files.
525 525 cachevfs = vfsmod.vfs(cachepath, cacheaudited=True)
526 526 cachevfs.createmode = store.createmode
527 527 # The cache vfs is used to manage cache files related to the working copy
528 528 wcachevfs = vfsmod.vfs(wcachepath, cacheaudited=True)
529 529 wcachevfs.createmode = store.createmode
530 530
531 531 # Now resolve the type for the repository object. We do this by repeatedly
532 532 # calling a factory function to produces types for specific aspects of the
533 533 # repo's operation. The aggregate returned types are used as base classes
534 534 # for a dynamically-derived type, which will represent our new repository.
535 535
536 536 bases = []
537 537 extrastate = {}
538 538
539 539 for iface, fn in REPO_INTERFACES:
540 540 # We pass all potentially useful state to give extensions tons of
541 541 # flexibility.
542 542 typ = fn()(ui=ui,
543 543 intents=intents,
544 544 requirements=requirements,
545 545 features=features,
546 546 wdirvfs=wdirvfs,
547 547 hgvfs=hgvfs,
548 548 store=store,
549 549 storevfs=storevfs,
550 550 storeoptions=storevfs.options,
551 551 cachevfs=cachevfs,
552 552 wcachevfs=wcachevfs,
553 553 extensionmodulenames=extensionmodulenames,
554 554 extrastate=extrastate,
555 555 baseclasses=bases)
556 556
557 557 if not isinstance(typ, type):
558 558 raise error.ProgrammingError('unable to construct type for %s' %
559 559 iface)
560 560
561 561 bases.append(typ)
562 562
563 563 # type() allows you to use characters in type names that wouldn't be
564 564 # recognized as Python symbols in source code. We abuse that to add
565 565 # rich information about our constructed repo.
566 566 name = pycompat.sysstr(b'derivedrepo:%s<%s>' % (
567 567 wdirvfs.base,
568 568 b','.join(sorted(requirements))))
569 569
570 570 cls = type(name, tuple(bases), {})
571 571
572 572 return cls(
573 573 baseui=baseui,
574 574 ui=ui,
575 575 origroot=path,
576 576 wdirvfs=wdirvfs,
577 577 hgvfs=hgvfs,
578 578 requirements=requirements,
579 579 supportedrequirements=supportedrequirements,
580 580 sharedpath=storebasepath,
581 581 store=store,
582 582 cachevfs=cachevfs,
583 583 wcachevfs=wcachevfs,
584 584 features=features,
585 585 intents=intents)
586 586
587 587 def loadhgrc(ui, wdirvfs, hgvfs, requirements):
588 588 """Load hgrc files/content into a ui instance.
589 589
590 590 This is called during repository opening to load any additional
591 591 config files or settings relevant to the current repository.
592 592
593 593 Returns a bool indicating whether any additional configs were loaded.
594 594
595 595 Extensions should monkeypatch this function to modify how per-repo
596 596 configs are loaded. For example, an extension may wish to pull in
597 597 configs from alternate files or sources.
598 598 """
599 599 try:
600 600 ui.readconfig(hgvfs.join(b'hgrc'), root=wdirvfs.base)
601 601 return True
602 602 except IOError:
603 603 return False
604 604
605 605 def afterhgrcload(ui, wdirvfs, hgvfs, requirements):
606 606 """Perform additional actions after .hg/hgrc is loaded.
607 607
608 608 This function is called during repository loading immediately after
609 609 the .hg/hgrc file is loaded and before per-repo extensions are loaded.
610 610
611 611 The function can be used to validate configs, automatically add
612 612 options (including extensions) based on requirements, etc.
613 613 """
614 614
615 615 # Map of requirements to list of extensions to load automatically when
616 616 # requirement is present.
617 617 autoextensions = {
618 618 b'largefiles': [b'largefiles'],
619 619 b'lfs': [b'lfs'],
620 620 }
621 621
622 622 for requirement, names in sorted(autoextensions.items()):
623 623 if requirement not in requirements:
624 624 continue
625 625
626 626 for name in names:
627 627 if not ui.hasconfig(b'extensions', name):
628 628 ui.setconfig(b'extensions', name, b'', source='autoload')
629 629
630 630 def gathersupportedrequirements(ui):
631 631 """Determine the complete set of recognized requirements."""
632 632 # Start with all requirements supported by this file.
633 633 supported = set(localrepository._basesupported)
634 634
635 635 # Execute ``featuresetupfuncs`` entries if they belong to an extension
636 636 # relevant to this ui instance.
637 637 modules = {m.__name__ for n, m in extensions.extensions(ui)}
638 638
639 639 for fn in featuresetupfuncs:
640 640 if fn.__module__ in modules:
641 641 fn(ui, supported)
642 642
643 643 # Add derived requirements from registered compression engines.
644 644 for name in util.compengines:
645 645 engine = util.compengines[name]
646 646 if engine.revlogheader():
647 647 supported.add(b'exp-compression-%s' % name)
648 648
649 649 return supported
650 650
651 651 def ensurerequirementsrecognized(requirements, supported):
652 652 """Validate that a set of local requirements is recognized.
653 653
654 654 Receives a set of requirements. Raises an ``error.RepoError`` if there
655 655 exists any requirement in that set that currently loaded code doesn't
656 656 recognize.
657 657
658 658 Returns a set of supported requirements.
659 659 """
660 660 missing = set()
661 661
662 662 for requirement in requirements:
663 663 if requirement in supported:
664 664 continue
665 665
666 666 if not requirement or not requirement[0:1].isalnum():
667 667 raise error.RequirementError(_(b'.hg/requires file is corrupt'))
668 668
669 669 missing.add(requirement)
670 670
671 671 if missing:
672 672 raise error.RequirementError(
673 673 _(b'repository requires features unknown to this Mercurial: %s') %
674 674 b' '.join(sorted(missing)),
675 675 hint=_(b'see https://mercurial-scm.org/wiki/MissingRequirement '
676 676 b'for more information'))
677 677
678 678 def ensurerequirementscompatible(ui, requirements):
679 679 """Validates that a set of recognized requirements is mutually compatible.
680 680
681 681 Some requirements may not be compatible with others or require
682 682 config options that aren't enabled. This function is called during
683 683 repository opening to ensure that the set of requirements needed
684 684 to open a repository is sane and compatible with config options.
685 685
686 686 Extensions can monkeypatch this function to perform additional
687 687 checking.
688 688
689 689 ``error.RepoError`` should be raised on failure.
690 690 """
691 691 if b'exp-sparse' in requirements and not sparse.enabled:
692 692 raise error.RepoError(_(b'repository is using sparse feature but '
693 693 b'sparse is not enabled; enable the '
694 694 b'"sparse" extensions to access'))
695 695
696 696 def makestore(requirements, path, vfstype):
697 697 """Construct a storage object for a repository."""
698 698 if b'store' in requirements:
699 699 if b'fncache' in requirements:
700 700 return storemod.fncachestore(path, vfstype,
701 701 b'dotencode' in requirements)
702 702
703 703 return storemod.encodedstore(path, vfstype)
704 704
705 705 return storemod.basicstore(path, vfstype)
706 706
707 707 def resolvestorevfsoptions(ui, requirements, features):
708 708 """Resolve the options to pass to the store vfs opener.
709 709
710 710 The returned dict is used to influence behavior of the storage layer.
711 711 """
712 712 options = {}
713 713
714 714 if b'treemanifest' in requirements:
715 715 options[b'treemanifest'] = True
716 716
717 717 # experimental config: format.manifestcachesize
718 718 manifestcachesize = ui.configint(b'format', b'manifestcachesize')
719 719 if manifestcachesize is not None:
720 720 options[b'manifestcachesize'] = manifestcachesize
721 721
722 722 # In the absence of another requirement superseding a revlog-related
723 723 # requirement, we have to assume the repo is using revlog version 0.
724 724 # This revlog format is super old and we don't bother trying to parse
725 725 # opener options for it because those options wouldn't do anything
726 726 # meaningful on such old repos.
727 727 if b'revlogv1' in requirements or REVLOGV2_REQUIREMENT in requirements:
728 728 options.update(resolverevlogstorevfsoptions(ui, requirements, features))
729 729
730 730 return options
731 731
732 732 def resolverevlogstorevfsoptions(ui, requirements, features):
733 733 """Resolve opener options specific to revlogs."""
734 734
735 735 options = {}
736 736 options[b'flagprocessors'] = {}
737 737
738 738 if b'revlogv1' in requirements:
739 739 options[b'revlogv1'] = True
740 740 if REVLOGV2_REQUIREMENT in requirements:
741 741 options[b'revlogv2'] = True
742 742
743 743 if b'generaldelta' in requirements:
744 744 options[b'generaldelta'] = True
745 745
746 746 # experimental config: format.chunkcachesize
747 747 chunkcachesize = ui.configint(b'format', b'chunkcachesize')
748 748 if chunkcachesize is not None:
749 749 options[b'chunkcachesize'] = chunkcachesize
750 750
751 751 deltabothparents = ui.configbool(b'storage',
752 752 b'revlog.optimize-delta-parent-choice')
753 753 options[b'deltabothparents'] = deltabothparents
754 754
755 755 lazydelta = ui.configbool(b'storage', b'revlog.reuse-external-delta')
756 756 lazydeltabase = False
757 757 if lazydelta:
758 758 lazydeltabase = ui.configbool(b'storage',
759 759 b'revlog.reuse-external-delta-parent')
760 760 if lazydeltabase is None:
761 761 lazydeltabase = not scmutil.gddeltaconfig(ui)
762 762 options[b'lazydelta'] = lazydelta
763 763 options[b'lazydeltabase'] = lazydeltabase
764 764
765 765 chainspan = ui.configbytes(b'experimental', b'maxdeltachainspan')
766 766 if 0 <= chainspan:
767 767 options[b'maxdeltachainspan'] = chainspan
768 768
769 769 mmapindexthreshold = ui.configbytes(b'experimental',
770 770 b'mmapindexthreshold')
771 771 if mmapindexthreshold is not None:
772 772 options[b'mmapindexthreshold'] = mmapindexthreshold
773 773
774 774 withsparseread = ui.configbool(b'experimental', b'sparse-read')
775 775 srdensitythres = float(ui.config(b'experimental',
776 776 b'sparse-read.density-threshold'))
777 777 srmingapsize = ui.configbytes(b'experimental',
778 778 b'sparse-read.min-gap-size')
779 779 options[b'with-sparse-read'] = withsparseread
780 780 options[b'sparse-read-density-threshold'] = srdensitythres
781 781 options[b'sparse-read-min-gap-size'] = srmingapsize
782 782
783 783 sparserevlog = SPARSEREVLOG_REQUIREMENT in requirements
784 784 options[b'sparse-revlog'] = sparserevlog
785 785 if sparserevlog:
786 786 options[b'generaldelta'] = True
787 787
788 788 maxchainlen = None
789 789 if sparserevlog:
790 790 maxchainlen = revlogconst.SPARSE_REVLOG_MAX_CHAIN_LENGTH
791 791 # experimental config: format.maxchainlen
792 792 maxchainlen = ui.configint(b'format', b'maxchainlen', maxchainlen)
793 793 if maxchainlen is not None:
794 794 options[b'maxchainlen'] = maxchainlen
795 795
796 796 for r in requirements:
797 797 if r.startswith(b'exp-compression-'):
798 798 options[b'compengine'] = r[len(b'exp-compression-'):]
799 799
800 800 if repository.NARROW_REQUIREMENT in requirements:
801 801 options[b'enableellipsis'] = True
802 802
803 803 return options
804 804
805 805 def makemain(**kwargs):
806 806 """Produce a type conforming to ``ilocalrepositorymain``."""
807 807 return localrepository
808 808
809 809 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage)
810 810 class revlogfilestorage(object):
811 811 """File storage when using revlogs."""
812 812
813 813 def file(self, path):
814 814 if path[0] == b'/':
815 815 path = path[1:]
816 816
817 817 return filelog.filelog(self.svfs, path)
818 818
819 819 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage)
820 820 class revlognarrowfilestorage(object):
821 821 """File storage when using revlogs and narrow files."""
822 822
823 823 def file(self, path):
824 824 if path[0] == b'/':
825 825 path = path[1:]
826 826
827 827 return filelog.narrowfilelog(self.svfs, path, self._storenarrowmatch)
828 828
829 829 def makefilestorage(requirements, features, **kwargs):
830 830 """Produce a type conforming to ``ilocalrepositoryfilestorage``."""
831 831 features.add(repository.REPO_FEATURE_REVLOG_FILE_STORAGE)
832 832 features.add(repository.REPO_FEATURE_STREAM_CLONE)
833 833
834 834 if repository.NARROW_REQUIREMENT in requirements:
835 835 return revlognarrowfilestorage
836 836 else:
837 837 return revlogfilestorage
838 838
839 839 # List of repository interfaces and factory functions for them. Each
840 840 # will be called in order during ``makelocalrepository()`` to iteratively
841 841 # derive the final type for a local repository instance. We capture the
842 842 # function as a lambda so we don't hold a reference and the module-level
843 843 # functions can be wrapped.
844 844 REPO_INTERFACES = [
845 845 (repository.ilocalrepositorymain, lambda: makemain),
846 846 (repository.ilocalrepositoryfilestorage, lambda: makefilestorage),
847 847 ]
848 848
849 849 @interfaceutil.implementer(repository.ilocalrepositorymain)
850 850 class localrepository(object):
851 851 """Main class for representing local repositories.
852 852
853 853 All local repositories are instances of this class.
854 854
855 855 Constructed on its own, instances of this class are not usable as
856 856 repository objects. To obtain a usable repository object, call
857 857 ``hg.repository()``, ``localrepo.instance()``, or
858 858 ``localrepo.makelocalrepository()``. The latter is the lowest-level.
859 859 ``instance()`` adds support for creating new repositories.
860 860 ``hg.repository()`` adds more extension integration, including calling
861 861 ``reposetup()``. Generally speaking, ``hg.repository()`` should be
862 862 used.
863 863 """
864 864
865 865 # obsolete experimental requirements:
866 866 # - manifestv2: An experimental new manifest format that allowed
867 867 # for stem compression of long paths. Experiment ended up not
868 868 # being successful (repository sizes went up due to worse delta
869 869 # chains), and the code was deleted in 4.6.
870 870 supportedformats = {
871 871 'revlogv1',
872 872 'generaldelta',
873 873 'treemanifest',
874 874 REVLOGV2_REQUIREMENT,
875 875 SPARSEREVLOG_REQUIREMENT,
876 876 }
877 877 _basesupported = supportedformats | {
878 878 'store',
879 879 'fncache',
880 880 'shared',
881 881 'relshared',
882 882 'dotencode',
883 883 'exp-sparse',
884 884 'internal-phase'
885 885 }
886 886
887 887 # list of prefix for file which can be written without 'wlock'
888 888 # Extensions should extend this list when needed
889 889 _wlockfreeprefix = {
890 890 # We migh consider requiring 'wlock' for the next
891 891 # two, but pretty much all the existing code assume
892 892 # wlock is not needed so we keep them excluded for
893 893 # now.
894 894 'hgrc',
895 895 'requires',
896 896 # XXX cache is a complicatged business someone
897 897 # should investigate this in depth at some point
898 898 'cache/',
899 899 # XXX shouldn't be dirstate covered by the wlock?
900 900 'dirstate',
901 901 # XXX bisect was still a bit too messy at the time
902 902 # this changeset was introduced. Someone should fix
903 903 # the remainig bit and drop this line
904 904 'bisect.state',
905 905 }
906 906
907 907 def __init__(self, baseui, ui, origroot, wdirvfs, hgvfs, requirements,
908 908 supportedrequirements, sharedpath, store, cachevfs, wcachevfs,
909 909 features, intents=None):
910 910 """Create a new local repository instance.
911 911
912 912 Most callers should use ``hg.repository()``, ``localrepo.instance()``,
913 913 or ``localrepo.makelocalrepository()`` for obtaining a new repository
914 914 object.
915 915
916 916 Arguments:
917 917
918 918 baseui
919 919 ``ui.ui`` instance that ``ui`` argument was based off of.
920 920
921 921 ui
922 922 ``ui.ui`` instance for use by the repository.
923 923
924 924 origroot
925 925 ``bytes`` path to working directory root of this repository.
926 926
927 927 wdirvfs
928 928 ``vfs.vfs`` rooted at the working directory.
929 929
930 930 hgvfs
931 931 ``vfs.vfs`` rooted at .hg/
932 932
933 933 requirements
934 934 ``set`` of bytestrings representing repository opening requirements.
935 935
936 936 supportedrequirements
937 937 ``set`` of bytestrings representing repository requirements that we
938 938 know how to open. May be a supetset of ``requirements``.
939 939
940 940 sharedpath
941 941 ``bytes`` Defining path to storage base directory. Points to a
942 942 ``.hg/`` directory somewhere.
943 943
944 944 store
945 945 ``store.basicstore`` (or derived) instance providing access to
946 946 versioned storage.
947 947
948 948 cachevfs
949 949 ``vfs.vfs`` used for cache files.
950 950
951 951 wcachevfs
952 952 ``vfs.vfs`` used for cache files related to the working copy.
953 953
954 954 features
955 955 ``set`` of bytestrings defining features/capabilities of this
956 956 instance.
957 957
958 958 intents
959 959 ``set`` of system strings indicating what this repo will be used
960 960 for.
961 961 """
962 962 self.baseui = baseui
963 963 self.ui = ui
964 964 self.origroot = origroot
965 965 # vfs rooted at working directory.
966 966 self.wvfs = wdirvfs
967 967 self.root = wdirvfs.base
968 968 # vfs rooted at .hg/. Used to access most non-store paths.
969 969 self.vfs = hgvfs
970 970 self.path = hgvfs.base
971 971 self.requirements = requirements
972 972 self.supported = supportedrequirements
973 973 self.sharedpath = sharedpath
974 974 self.store = store
975 975 self.cachevfs = cachevfs
976 976 self.wcachevfs = wcachevfs
977 977 self.features = features
978 978
979 979 self.filtername = None
980 980
981 981 if (self.ui.configbool('devel', 'all-warnings') or
982 982 self.ui.configbool('devel', 'check-locks')):
983 983 self.vfs.audit = self._getvfsward(self.vfs.audit)
984 984 # A list of callback to shape the phase if no data were found.
985 985 # Callback are in the form: func(repo, roots) --> processed root.
986 986 # This list it to be filled by extension during repo setup
987 987 self._phasedefaults = []
988 988
989 989 color.setup(self.ui)
990 990
991 991 self.spath = self.store.path
992 992 self.svfs = self.store.vfs
993 993 self.sjoin = self.store.join
994 994 if (self.ui.configbool('devel', 'all-warnings') or
995 995 self.ui.configbool('devel', 'check-locks')):
996 996 if util.safehasattr(self.svfs, 'vfs'): # this is filtervfs
997 997 self.svfs.vfs.audit = self._getsvfsward(self.svfs.vfs.audit)
998 998 else: # standard vfs
999 999 self.svfs.audit = self._getsvfsward(self.svfs.audit)
1000 1000
1001 1001 self._dirstatevalidatewarned = False
1002 1002
1003 1003 self._branchcaches = branchmap.BranchMapCache()
1004 1004 self._revbranchcache = None
1005 1005 self._filterpats = {}
1006 1006 self._datafilters = {}
1007 1007 self._transref = self._lockref = self._wlockref = None
1008 1008
1009 1009 # A cache for various files under .hg/ that tracks file changes,
1010 1010 # (used by the filecache decorator)
1011 1011 #
1012 1012 # Maps a property name to its util.filecacheentry
1013 1013 self._filecache = {}
1014 1014
1015 1015 # hold sets of revision to be filtered
1016 1016 # should be cleared when something might have changed the filter value:
1017 1017 # - new changesets,
1018 1018 # - phase change,
1019 1019 # - new obsolescence marker,
1020 1020 # - working directory parent change,
1021 1021 # - bookmark changes
1022 1022 self.filteredrevcache = {}
1023 1023
1024 1024 # post-dirstate-status hooks
1025 1025 self._postdsstatus = []
1026 1026
1027 1027 # generic mapping between names and nodes
1028 1028 self.names = namespaces.namespaces()
1029 1029
1030 1030 # Key to signature value.
1031 1031 self._sparsesignaturecache = {}
1032 1032 # Signature to cached matcher instance.
1033 1033 self._sparsematchercache = {}
1034 1034
1035 1035 def _getvfsward(self, origfunc):
1036 1036 """build a ward for self.vfs"""
1037 1037 rref = weakref.ref(self)
1038 1038 def checkvfs(path, mode=None):
1039 1039 ret = origfunc(path, mode=mode)
1040 1040 repo = rref()
1041 1041 if (repo is None
1042 1042 or not util.safehasattr(repo, '_wlockref')
1043 1043 or not util.safehasattr(repo, '_lockref')):
1044 1044 return
1045 1045 if mode in (None, 'r', 'rb'):
1046 1046 return
1047 1047 if path.startswith(repo.path):
1048 1048 # truncate name relative to the repository (.hg)
1049 1049 path = path[len(repo.path) + 1:]
1050 1050 if path.startswith('cache/'):
1051 1051 msg = 'accessing cache with vfs instead of cachevfs: "%s"'
1052 1052 repo.ui.develwarn(msg % path, stacklevel=3, config="cache-vfs")
1053 1053 if path.startswith('journal.') or path.startswith('undo.'):
1054 1054 # journal is covered by 'lock'
1055 1055 if repo._currentlock(repo._lockref) is None:
1056 1056 repo.ui.develwarn('write with no lock: "%s"' % path,
1057 1057 stacklevel=3, config='check-locks')
1058 1058 elif repo._currentlock(repo._wlockref) is None:
1059 1059 # rest of vfs files are covered by 'wlock'
1060 1060 #
1061 1061 # exclude special files
1062 1062 for prefix in self._wlockfreeprefix:
1063 1063 if path.startswith(prefix):
1064 1064 return
1065 1065 repo.ui.develwarn('write with no wlock: "%s"' % path,
1066 1066 stacklevel=3, config='check-locks')
1067 1067 return ret
1068 1068 return checkvfs
1069 1069
1070 1070 def _getsvfsward(self, origfunc):
1071 1071 """build a ward for self.svfs"""
1072 1072 rref = weakref.ref(self)
1073 1073 def checksvfs(path, mode=None):
1074 1074 ret = origfunc(path, mode=mode)
1075 1075 repo = rref()
1076 1076 if repo is None or not util.safehasattr(repo, '_lockref'):
1077 1077 return
1078 1078 if mode in (None, 'r', 'rb'):
1079 1079 return
1080 1080 if path.startswith(repo.sharedpath):
1081 1081 # truncate name relative to the repository (.hg)
1082 1082 path = path[len(repo.sharedpath) + 1:]
1083 1083 if repo._currentlock(repo._lockref) is None:
1084 1084 repo.ui.develwarn('write with no lock: "%s"' % path,
1085 1085 stacklevel=4)
1086 1086 return ret
1087 1087 return checksvfs
1088 1088
1089 1089 def close(self):
1090 1090 self._writecaches()
1091 1091
1092 1092 def _writecaches(self):
1093 1093 if self._revbranchcache:
1094 1094 self._revbranchcache.write()
1095 1095
1096 1096 def _restrictcapabilities(self, caps):
1097 1097 if self.ui.configbool('experimental', 'bundle2-advertise'):
1098 1098 caps = set(caps)
1099 1099 capsblob = bundle2.encodecaps(bundle2.getrepocaps(self,
1100 1100 role='client'))
1101 1101 caps.add('bundle2=' + urlreq.quote(capsblob))
1102 1102 return caps
1103 1103
1104 1104 def _writerequirements(self):
1105 1105 scmutil.writerequires(self.vfs, self.requirements)
1106 1106
1107 1107 # Don't cache auditor/nofsauditor, or you'll end up with reference cycle:
1108 1108 # self -> auditor -> self._checknested -> self
1109 1109
1110 1110 @property
1111 1111 def auditor(self):
1112 1112 # This is only used by context.workingctx.match in order to
1113 1113 # detect files in subrepos.
1114 1114 return pathutil.pathauditor(self.root, callback=self._checknested)
1115 1115
1116 1116 @property
1117 1117 def nofsauditor(self):
1118 1118 # This is only used by context.basectx.match in order to detect
1119 1119 # files in subrepos.
1120 1120 return pathutil.pathauditor(self.root, callback=self._checknested,
1121 1121 realfs=False, cached=True)
1122 1122
1123 1123 def _checknested(self, path):
1124 1124 """Determine if path is a legal nested repository."""
1125 1125 if not path.startswith(self.root):
1126 1126 return False
1127 1127 subpath = path[len(self.root) + 1:]
1128 1128 normsubpath = util.pconvert(subpath)
1129 1129
1130 1130 # XXX: Checking against the current working copy is wrong in
1131 1131 # the sense that it can reject things like
1132 1132 #
1133 1133 # $ hg cat -r 10 sub/x.txt
1134 1134 #
1135 1135 # if sub/ is no longer a subrepository in the working copy
1136 1136 # parent revision.
1137 1137 #
1138 1138 # However, it can of course also allow things that would have
1139 1139 # been rejected before, such as the above cat command if sub/
1140 1140 # is a subrepository now, but was a normal directory before.
1141 1141 # The old path auditor would have rejected by mistake since it
1142 1142 # panics when it sees sub/.hg/.
1143 1143 #
1144 1144 # All in all, checking against the working copy seems sensible
1145 1145 # since we want to prevent access to nested repositories on
1146 1146 # the filesystem *now*.
1147 1147 ctx = self[None]
1148 1148 parts = util.splitpath(subpath)
1149 1149 while parts:
1150 1150 prefix = '/'.join(parts)
1151 1151 if prefix in ctx.substate:
1152 1152 if prefix == normsubpath:
1153 1153 return True
1154 1154 else:
1155 1155 sub = ctx.sub(prefix)
1156 1156 return sub.checknested(subpath[len(prefix) + 1:])
1157 1157 else:
1158 1158 parts.pop()
1159 1159 return False
1160 1160
1161 1161 def peer(self):
1162 1162 return localpeer(self) # not cached to avoid reference cycle
1163 1163
1164 1164 def unfiltered(self):
1165 1165 """Return unfiltered version of the repository
1166 1166
1167 1167 Intended to be overwritten by filtered repo."""
1168 1168 return self
1169 1169
1170 1170 def filtered(self, name, visibilityexceptions=None):
1171 1171 """Return a filtered version of a repository"""
1172 1172 cls = repoview.newtype(self.unfiltered().__class__)
1173 1173 return cls(self, name, visibilityexceptions)
1174 1174
1175 1175 @repofilecache('bookmarks', 'bookmarks.current')
1176 1176 def _bookmarks(self):
1177 1177 return bookmarks.bmstore(self)
1178 1178
1179 1179 @property
1180 1180 def _activebookmark(self):
1181 1181 return self._bookmarks.active
1182 1182
1183 1183 # _phasesets depend on changelog. what we need is to call
1184 1184 # _phasecache.invalidate() if '00changelog.i' was changed, but it
1185 1185 # can't be easily expressed in filecache mechanism.
1186 1186 @storecache('phaseroots', '00changelog.i')
1187 1187 def _phasecache(self):
1188 1188 return phases.phasecache(self, self._phasedefaults)
1189 1189
1190 1190 @storecache('obsstore')
1191 1191 def obsstore(self):
1192 1192 return obsolete.makestore(self.ui, self)
1193 1193
1194 1194 @storecache('00changelog.i')
1195 1195 def changelog(self):
1196 1196 return changelog.changelog(self.svfs,
1197 1197 trypending=txnutil.mayhavepending(self.root))
1198 1198
1199 1199 @storecache('00manifest.i')
1200 1200 def manifestlog(self):
1201 1201 rootstore = manifest.manifestrevlog(self.svfs)
1202 1202 return manifest.manifestlog(self.svfs, self, rootstore,
1203 1203 self._storenarrowmatch)
1204 1204
1205 1205 @repofilecache('dirstate')
1206 1206 def dirstate(self):
1207 1207 return self._makedirstate()
1208 1208
1209 1209 def _makedirstate(self):
1210 1210 """Extension point for wrapping the dirstate per-repo."""
1211 1211 sparsematchfn = lambda: sparse.matcher(self)
1212 1212
1213 1213 return dirstate.dirstate(self.vfs, self.ui, self.root,
1214 1214 self._dirstatevalidate, sparsematchfn)
1215 1215
1216 1216 def _dirstatevalidate(self, node):
1217 1217 try:
1218 1218 self.changelog.rev(node)
1219 1219 return node
1220 1220 except error.LookupError:
1221 1221 if not self._dirstatevalidatewarned:
1222 1222 self._dirstatevalidatewarned = True
1223 1223 self.ui.warn(_("warning: ignoring unknown"
1224 1224 " working parent %s!\n") % short(node))
1225 1225 return nullid
1226 1226
1227 1227 @storecache(narrowspec.FILENAME)
1228 1228 def narrowpats(self):
1229 1229 """matcher patterns for this repository's narrowspec
1230 1230
1231 1231 A tuple of (includes, excludes).
1232 1232 """
1233 1233 return narrowspec.load(self)
1234 1234
1235 1235 @storecache(narrowspec.FILENAME)
1236 1236 def _storenarrowmatch(self):
1237 1237 if repository.NARROW_REQUIREMENT not in self.requirements:
1238 1238 return matchmod.always()
1239 1239 include, exclude = self.narrowpats
1240 1240 return narrowspec.match(self.root, include=include, exclude=exclude)
1241 1241
1242 1242 @storecache(narrowspec.FILENAME)
1243 1243 def _narrowmatch(self):
1244 1244 if repository.NARROW_REQUIREMENT not in self.requirements:
1245 1245 return matchmod.always()
1246 1246 narrowspec.checkworkingcopynarrowspec(self)
1247 1247 include, exclude = self.narrowpats
1248 1248 return narrowspec.match(self.root, include=include, exclude=exclude)
1249 1249
1250 1250 def narrowmatch(self, match=None, includeexact=False):
1251 1251 """matcher corresponding the the repo's narrowspec
1252 1252
1253 1253 If `match` is given, then that will be intersected with the narrow
1254 1254 matcher.
1255 1255
1256 1256 If `includeexact` is True, then any exact matches from `match` will
1257 1257 be included even if they're outside the narrowspec.
1258 1258 """
1259 1259 if match:
1260 1260 if includeexact and not self._narrowmatch.always():
1261 1261 # do not exclude explicitly-specified paths so that they can
1262 1262 # be warned later on
1263 1263 em = matchmod.exact(match.files())
1264 1264 nm = matchmod.unionmatcher([self._narrowmatch, em])
1265 1265 return matchmod.intersectmatchers(match, nm)
1266 1266 return matchmod.intersectmatchers(match, self._narrowmatch)
1267 1267 return self._narrowmatch
1268 1268
1269 1269 def setnarrowpats(self, newincludes, newexcludes):
1270 1270 narrowspec.save(self, newincludes, newexcludes)
1271 1271 self.invalidate(clearfilecache=True)
1272 1272
1273 1273 def __getitem__(self, changeid):
1274 1274 if changeid is None:
1275 1275 return context.workingctx(self)
1276 1276 if isinstance(changeid, context.basectx):
1277 1277 return changeid
1278 1278 if isinstance(changeid, slice):
1279 1279 # wdirrev isn't contiguous so the slice shouldn't include it
1280 1280 return [self[i]
1281 1281 for i in pycompat.xrange(*changeid.indices(len(self)))
1282 1282 if i not in self.changelog.filteredrevs]
1283 1283 try:
1284 1284 if isinstance(changeid, int):
1285 1285 node = self.changelog.node(changeid)
1286 1286 rev = changeid
1287 1287 elif changeid == 'null':
1288 1288 node = nullid
1289 1289 rev = nullrev
1290 1290 elif changeid == 'tip':
1291 1291 node = self.changelog.tip()
1292 1292 rev = self.changelog.rev(node)
1293 1293 elif changeid == '.':
1294 1294 # this is a hack to delay/avoid loading obsmarkers
1295 1295 # when we know that '.' won't be hidden
1296 1296 node = self.dirstate.p1()
1297 1297 rev = self.unfiltered().changelog.rev(node)
1298 1298 elif len(changeid) == 20:
1299 1299 try:
1300 1300 node = changeid
1301 1301 rev = self.changelog.rev(changeid)
1302 1302 except error.FilteredLookupError:
1303 1303 changeid = hex(changeid) # for the error message
1304 1304 raise
1305 1305 except LookupError:
1306 1306 # check if it might have come from damaged dirstate
1307 1307 #
1308 1308 # XXX we could avoid the unfiltered if we had a recognizable
1309 1309 # exception for filtered changeset access
1310 1310 if (self.local()
1311 1311 and changeid in self.unfiltered().dirstate.parents()):
1312 1312 msg = _("working directory has unknown parent '%s'!")
1313 1313 raise error.Abort(msg % short(changeid))
1314 1314 changeid = hex(changeid) # for the error message
1315 1315 raise
1316 1316
1317 1317 elif len(changeid) == 40:
1318 1318 node = bin(changeid)
1319 1319 rev = self.changelog.rev(node)
1320 1320 else:
1321 1321 raise error.ProgrammingError(
1322 1322 "unsupported changeid '%s' of type %s" %
1323 1323 (changeid, type(changeid)))
1324 1324
1325 1325 return context.changectx(self, rev, node)
1326 1326
1327 1327 except (error.FilteredIndexError, error.FilteredLookupError):
1328 1328 raise error.FilteredRepoLookupError(_("filtered revision '%s'")
1329 1329 % pycompat.bytestr(changeid))
1330 1330 except (IndexError, LookupError):
1331 1331 raise error.RepoLookupError(
1332 1332 _("unknown revision '%s'") % pycompat.bytestr(changeid))
1333 1333 except error.WdirUnsupported:
1334 1334 return context.workingctx(self)
1335 1335
1336 1336 def __contains__(self, changeid):
1337 1337 """True if the given changeid exists
1338 1338
1339 1339 error.AmbiguousPrefixLookupError is raised if an ambiguous node
1340 1340 specified.
1341 1341 """
1342 1342 try:
1343 1343 self[changeid]
1344 1344 return True
1345 1345 except error.RepoLookupError:
1346 1346 return False
1347 1347
1348 1348 def __nonzero__(self):
1349 1349 return True
1350 1350
1351 1351 __bool__ = __nonzero__
1352 1352
1353 1353 def __len__(self):
1354 1354 # no need to pay the cost of repoview.changelog
1355 1355 unfi = self.unfiltered()
1356 1356 return len(unfi.changelog)
1357 1357
1358 1358 def __iter__(self):
1359 1359 return iter(self.changelog)
1360 1360
1361 1361 def revs(self, expr, *args):
1362 1362 '''Find revisions matching a revset.
1363 1363
1364 1364 The revset is specified as a string ``expr`` that may contain
1365 1365 %-formatting to escape certain types. See ``revsetlang.formatspec``.
1366 1366
1367 1367 Revset aliases from the configuration are not expanded. To expand
1368 1368 user aliases, consider calling ``scmutil.revrange()`` or
1369 1369 ``repo.anyrevs([expr], user=True)``.
1370 1370
1371 1371 Returns a revset.abstractsmartset, which is a list-like interface
1372 1372 that contains integer revisions.
1373 1373 '''
1374 1374 tree = revsetlang.spectree(expr, *args)
1375 1375 return revset.makematcher(tree)(self)
1376 1376
1377 1377 def set(self, expr, *args):
1378 1378 '''Find revisions matching a revset and emit changectx instances.
1379 1379
1380 1380 This is a convenience wrapper around ``revs()`` that iterates the
1381 1381 result and is a generator of changectx instances.
1382 1382
1383 1383 Revset aliases from the configuration are not expanded. To expand
1384 1384 user aliases, consider calling ``scmutil.revrange()``.
1385 1385 '''
1386 1386 for r in self.revs(expr, *args):
1387 1387 yield self[r]
1388 1388
1389 1389 def anyrevs(self, specs, user=False, localalias=None):
1390 1390 '''Find revisions matching one of the given revsets.
1391 1391
1392 1392 Revset aliases from the configuration are not expanded by default. To
1393 1393 expand user aliases, specify ``user=True``. To provide some local
1394 1394 definitions overriding user aliases, set ``localalias`` to
1395 1395 ``{name: definitionstring}``.
1396 1396 '''
1397 1397 if user:
1398 1398 m = revset.matchany(self.ui, specs,
1399 1399 lookup=revset.lookupfn(self),
1400 1400 localalias=localalias)
1401 1401 else:
1402 1402 m = revset.matchany(None, specs, localalias=localalias)
1403 1403 return m(self)
1404 1404
1405 1405 def url(self):
1406 1406 return 'file:' + self.root
1407 1407
1408 1408 def hook(self, name, throw=False, **args):
1409 1409 """Call a hook, passing this repo instance.
1410 1410
1411 1411 This a convenience method to aid invoking hooks. Extensions likely
1412 1412 won't call this unless they have registered a custom hook or are
1413 1413 replacing code that is expected to call a hook.
1414 1414 """
1415 1415 return hook.hook(self.ui, self, name, throw, **args)
1416 1416
1417 1417 @filteredpropertycache
1418 1418 def _tagscache(self):
1419 1419 '''Returns a tagscache object that contains various tags related
1420 1420 caches.'''
1421 1421
1422 1422 # This simplifies its cache management by having one decorated
1423 1423 # function (this one) and the rest simply fetch things from it.
1424 1424 class tagscache(object):
1425 1425 def __init__(self):
1426 1426 # These two define the set of tags for this repository. tags
1427 1427 # maps tag name to node; tagtypes maps tag name to 'global' or
1428 1428 # 'local'. (Global tags are defined by .hgtags across all
1429 1429 # heads, and local tags are defined in .hg/localtags.)
1430 1430 # They constitute the in-memory cache of tags.
1431 1431 self.tags = self.tagtypes = None
1432 1432
1433 1433 self.nodetagscache = self.tagslist = None
1434 1434
1435 1435 cache = tagscache()
1436 1436 cache.tags, cache.tagtypes = self._findtags()
1437 1437
1438 1438 return cache
1439 1439
1440 1440 def tags(self):
1441 1441 '''return a mapping of tag to node'''
1442 1442 t = {}
1443 1443 if self.changelog.filteredrevs:
1444 1444 tags, tt = self._findtags()
1445 1445 else:
1446 1446 tags = self._tagscache.tags
1447 1447 rev = self.changelog.rev
1448 1448 for k, v in tags.iteritems():
1449 1449 try:
1450 1450 # ignore tags to unknown nodes
1451 1451 rev(v)
1452 1452 t[k] = v
1453 1453 except (error.LookupError, ValueError):
1454 1454 pass
1455 1455 return t
1456 1456
1457 1457 def _findtags(self):
1458 1458 '''Do the hard work of finding tags. Return a pair of dicts
1459 1459 (tags, tagtypes) where tags maps tag name to node, and tagtypes
1460 1460 maps tag name to a string like \'global\' or \'local\'.
1461 1461 Subclasses or extensions are free to add their own tags, but
1462 1462 should be aware that the returned dicts will be retained for the
1463 1463 duration of the localrepo object.'''
1464 1464
1465 1465 # XXX what tagtype should subclasses/extensions use? Currently
1466 1466 # mq and bookmarks add tags, but do not set the tagtype at all.
1467 1467 # Should each extension invent its own tag type? Should there
1468 1468 # be one tagtype for all such "virtual" tags? Or is the status
1469 1469 # quo fine?
1470 1470
1471 1471
1472 1472 # map tag name to (node, hist)
1473 1473 alltags = tagsmod.findglobaltags(self.ui, self)
1474 1474 # map tag name to tag type
1475 1475 tagtypes = dict((tag, 'global') for tag in alltags)
1476 1476
1477 1477 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
1478 1478
1479 1479 # Build the return dicts. Have to re-encode tag names because
1480 1480 # the tags module always uses UTF-8 (in order not to lose info
1481 1481 # writing to the cache), but the rest of Mercurial wants them in
1482 1482 # local encoding.
1483 1483 tags = {}
1484 1484 for (name, (node, hist)) in alltags.iteritems():
1485 1485 if node != nullid:
1486 1486 tags[encoding.tolocal(name)] = node
1487 1487 tags['tip'] = self.changelog.tip()
1488 1488 tagtypes = dict([(encoding.tolocal(name), value)
1489 1489 for (name, value) in tagtypes.iteritems()])
1490 1490 return (tags, tagtypes)
1491 1491
1492 1492 def tagtype(self, tagname):
1493 1493 '''
1494 1494 return the type of the given tag. result can be:
1495 1495
1496 1496 'local' : a local tag
1497 1497 'global' : a global tag
1498 1498 None : tag does not exist
1499 1499 '''
1500 1500
1501 1501 return self._tagscache.tagtypes.get(tagname)
1502 1502
1503 1503 def tagslist(self):
1504 1504 '''return a list of tags ordered by revision'''
1505 1505 if not self._tagscache.tagslist:
1506 1506 l = []
1507 1507 for t, n in self.tags().iteritems():
1508 1508 l.append((self.changelog.rev(n), t, n))
1509 1509 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
1510 1510
1511 1511 return self._tagscache.tagslist
1512 1512
1513 1513 def nodetags(self, node):
1514 1514 '''return the tags associated with a node'''
1515 1515 if not self._tagscache.nodetagscache:
1516 1516 nodetagscache = {}
1517 1517 for t, n in self._tagscache.tags.iteritems():
1518 1518 nodetagscache.setdefault(n, []).append(t)
1519 1519 for tags in nodetagscache.itervalues():
1520 1520 tags.sort()
1521 1521 self._tagscache.nodetagscache = nodetagscache
1522 1522 return self._tagscache.nodetagscache.get(node, [])
1523 1523
1524 1524 def nodebookmarks(self, node):
1525 1525 """return the list of bookmarks pointing to the specified node"""
1526 1526 return self._bookmarks.names(node)
1527 1527
1528 1528 def branchmap(self):
1529 1529 '''returns a dictionary {branch: [branchheads]} with branchheads
1530 1530 ordered by increasing revision number'''
1531 1531 return self._branchcaches[self]
1532 1532
1533 1533 @unfilteredmethod
1534 1534 def revbranchcache(self):
1535 1535 if not self._revbranchcache:
1536 1536 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
1537 1537 return self._revbranchcache
1538 1538
1539 1539 def branchtip(self, branch, ignoremissing=False):
1540 1540 '''return the tip node for a given branch
1541 1541
1542 1542 If ignoremissing is True, then this method will not raise an error.
1543 1543 This is helpful for callers that only expect None for a missing branch
1544 1544 (e.g. namespace).
1545 1545
1546 1546 '''
1547 1547 try:
1548 1548 return self.branchmap().branchtip(branch)
1549 1549 except KeyError:
1550 1550 if not ignoremissing:
1551 1551 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
1552 1552 else:
1553 1553 pass
1554 1554
1555 1555 def lookup(self, key):
1556 1556 return scmutil.revsymbol(self, key).node()
1557 1557
1558 1558 def lookupbranch(self, key):
1559 1559 if key in self.branchmap():
1560 1560 return key
1561 1561
1562 1562 return scmutil.revsymbol(self, key).branch()
1563 1563
1564 1564 def known(self, nodes):
1565 1565 cl = self.changelog
1566 1566 nm = cl.nodemap
1567 1567 filtered = cl.filteredrevs
1568 1568 result = []
1569 1569 for n in nodes:
1570 1570 r = nm.get(n)
1571 1571 resp = not (r is None or r in filtered)
1572 1572 result.append(resp)
1573 1573 return result
1574 1574
1575 1575 def local(self):
1576 1576 return self
1577 1577
1578 1578 def publishing(self):
1579 1579 # it's safe (and desirable) to trust the publish flag unconditionally
1580 1580 # so that we don't finalize changes shared between users via ssh or nfs
1581 1581 return self.ui.configbool('phases', 'publish', untrusted=True)
1582 1582
1583 1583 def cancopy(self):
1584 1584 # so statichttprepo's override of local() works
1585 1585 if not self.local():
1586 1586 return False
1587 1587 if not self.publishing():
1588 1588 return True
1589 1589 # if publishing we can't copy if there is filtered content
1590 1590 return not self.filtered('visible').changelog.filteredrevs
1591 1591
1592 1592 def shared(self):
1593 1593 '''the type of shared repository (None if not shared)'''
1594 1594 if self.sharedpath != self.path:
1595 1595 return 'store'
1596 1596 return None
1597 1597
1598 1598 def wjoin(self, f, *insidef):
1599 1599 return self.vfs.reljoin(self.root, f, *insidef)
1600 1600
1601 1601 def setparents(self, p1, p2=nullid):
1602 1602 with self.dirstate.parentchange():
1603 1603 copies = self.dirstate.setparents(p1, p2)
1604 1604 pctx = self[p1]
1605 1605 if copies:
1606 1606 # Adjust copy records, the dirstate cannot do it, it
1607 1607 # requires access to parents manifests. Preserve them
1608 1608 # only for entries added to first parent.
1609 1609 for f in copies:
1610 1610 if f not in pctx and copies[f] in pctx:
1611 1611 self.dirstate.copy(copies[f], f)
1612 1612 if p2 == nullid:
1613 1613 for f, s in sorted(self.dirstate.copies().items()):
1614 1614 if f not in pctx and s not in pctx:
1615 1615 self.dirstate.copy(None, f)
1616 1616
1617 1617 def filectx(self, path, changeid=None, fileid=None, changectx=None):
1618 1618 """changeid must be a changeset revision, if specified.
1619 1619 fileid can be a file revision or node."""
1620 1620 return context.filectx(self, path, changeid, fileid,
1621 1621 changectx=changectx)
1622 1622
1623 1623 def getcwd(self):
1624 1624 return self.dirstate.getcwd()
1625 1625
1626 1626 def pathto(self, f, cwd=None):
1627 1627 return self.dirstate.pathto(f, cwd)
1628 1628
1629 1629 def _loadfilter(self, filter):
1630 1630 if filter not in self._filterpats:
1631 1631 l = []
1632 1632 for pat, cmd in self.ui.configitems(filter):
1633 1633 if cmd == '!':
1634 1634 continue
1635 1635 mf = matchmod.match(self.root, '', [pat])
1636 1636 fn = None
1637 1637 params = cmd
1638 1638 for name, filterfn in self._datafilters.iteritems():
1639 1639 if cmd.startswith(name):
1640 1640 fn = filterfn
1641 1641 params = cmd[len(name):].lstrip()
1642 1642 break
1643 1643 if not fn:
1644 1644 fn = lambda s, c, **kwargs: procutil.filter(s, c)
1645 1645 # Wrap old filters not supporting keyword arguments
1646 1646 if not pycompat.getargspec(fn)[2]:
1647 1647 oldfn = fn
1648 1648 fn = lambda s, c, **kwargs: oldfn(s, c)
1649 1649 l.append((mf, fn, params))
1650 1650 self._filterpats[filter] = l
1651 1651 return self._filterpats[filter]
1652 1652
1653 1653 def _filter(self, filterpats, filename, data):
1654 1654 for mf, fn, cmd in filterpats:
1655 1655 if mf(filename):
1656 1656 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
1657 1657 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
1658 1658 break
1659 1659
1660 1660 return data
1661 1661
1662 1662 @unfilteredpropertycache
1663 1663 def _encodefilterpats(self):
1664 1664 return self._loadfilter('encode')
1665 1665
1666 1666 @unfilteredpropertycache
1667 1667 def _decodefilterpats(self):
1668 1668 return self._loadfilter('decode')
1669 1669
1670 1670 def adddatafilter(self, name, filter):
1671 1671 self._datafilters[name] = filter
1672 1672
1673 1673 def wread(self, filename):
1674 1674 if self.wvfs.islink(filename):
1675 1675 data = self.wvfs.readlink(filename)
1676 1676 else:
1677 1677 data = self.wvfs.read(filename)
1678 1678 return self._filter(self._encodefilterpats, filename, data)
1679 1679
1680 1680 def wwrite(self, filename, data, flags, backgroundclose=False, **kwargs):
1681 1681 """write ``data`` into ``filename`` in the working directory
1682 1682
1683 1683 This returns length of written (maybe decoded) data.
1684 1684 """
1685 1685 data = self._filter(self._decodefilterpats, filename, data)
1686 1686 if 'l' in flags:
1687 1687 self.wvfs.symlink(data, filename)
1688 1688 else:
1689 1689 self.wvfs.write(filename, data, backgroundclose=backgroundclose,
1690 1690 **kwargs)
1691 1691 if 'x' in flags:
1692 1692 self.wvfs.setflags(filename, False, True)
1693 1693 else:
1694 1694 self.wvfs.setflags(filename, False, False)
1695 1695 return len(data)
1696 1696
1697 1697 def wwritedata(self, filename, data):
1698 1698 return self._filter(self._decodefilterpats, filename, data)
1699 1699
1700 1700 def currenttransaction(self):
1701 1701 """return the current transaction or None if non exists"""
1702 1702 if self._transref:
1703 1703 tr = self._transref()
1704 1704 else:
1705 1705 tr = None
1706 1706
1707 1707 if tr and tr.running():
1708 1708 return tr
1709 1709 return None
1710 1710
1711 1711 def transaction(self, desc, report=None):
1712 1712 if (self.ui.configbool('devel', 'all-warnings')
1713 1713 or self.ui.configbool('devel', 'check-locks')):
1714 1714 if self._currentlock(self._lockref) is None:
1715 1715 raise error.ProgrammingError('transaction requires locking')
1716 1716 tr = self.currenttransaction()
1717 1717 if tr is not None:
1718 1718 return tr.nest(name=desc)
1719 1719
1720 1720 # abort here if the journal already exists
1721 1721 if self.svfs.exists("journal"):
1722 1722 raise error.RepoError(
1723 1723 _("abandoned transaction found"),
1724 1724 hint=_("run 'hg recover' to clean up transaction"))
1725 1725
1726 1726 idbase = "%.40f#%f" % (random.random(), time.time())
1727 1727 ha = hex(hashlib.sha1(idbase).digest())
1728 1728 txnid = 'TXN:' + ha
1729 1729 self.hook('pretxnopen', throw=True, txnname=desc, txnid=txnid)
1730 1730
1731 1731 self._writejournal(desc)
1732 1732 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
1733 1733 if report:
1734 1734 rp = report
1735 1735 else:
1736 1736 rp = self.ui.warn
1737 1737 vfsmap = {'plain': self.vfs, 'store': self.svfs} # root of .hg/
1738 1738 # we must avoid cyclic reference between repo and transaction.
1739 1739 reporef = weakref.ref(self)
1740 1740 # Code to track tag movement
1741 1741 #
1742 1742 # Since tags are all handled as file content, it is actually quite hard
1743 1743 # to track these movement from a code perspective. So we fallback to a
1744 1744 # tracking at the repository level. One could envision to track changes
1745 1745 # to the '.hgtags' file through changegroup apply but that fails to
1746 1746 # cope with case where transaction expose new heads without changegroup
1747 1747 # being involved (eg: phase movement).
1748 1748 #
1749 1749 # For now, We gate the feature behind a flag since this likely comes
1750 1750 # with performance impacts. The current code run more often than needed
1751 1751 # and do not use caches as much as it could. The current focus is on
1752 1752 # the behavior of the feature so we disable it by default. The flag
1753 1753 # will be removed when we are happy with the performance impact.
1754 1754 #
1755 1755 # Once this feature is no longer experimental move the following
1756 1756 # documentation to the appropriate help section:
1757 1757 #
1758 1758 # The ``HG_TAG_MOVED`` variable will be set if the transaction touched
1759 1759 # tags (new or changed or deleted tags). In addition the details of
1760 1760 # these changes are made available in a file at:
1761 1761 # ``REPOROOT/.hg/changes/tags.changes``.
1762 1762 # Make sure you check for HG_TAG_MOVED before reading that file as it
1763 1763 # might exist from a previous transaction even if no tag were touched
1764 1764 # in this one. Changes are recorded in a line base format::
1765 1765 #
1766 1766 # <action> <hex-node> <tag-name>\n
1767 1767 #
1768 1768 # Actions are defined as follow:
1769 1769 # "-R": tag is removed,
1770 1770 # "+A": tag is added,
1771 1771 # "-M": tag is moved (old value),
1772 1772 # "+M": tag is moved (new value),
1773 1773 tracktags = lambda x: None
1774 1774 # experimental config: experimental.hook-track-tags
1775 1775 shouldtracktags = self.ui.configbool('experimental', 'hook-track-tags')
1776 1776 if desc != 'strip' and shouldtracktags:
1777 1777 oldheads = self.changelog.headrevs()
1778 1778 def tracktags(tr2):
1779 1779 repo = reporef()
1780 1780 oldfnodes = tagsmod.fnoderevs(repo.ui, repo, oldheads)
1781 1781 newheads = repo.changelog.headrevs()
1782 1782 newfnodes = tagsmod.fnoderevs(repo.ui, repo, newheads)
1783 1783 # notes: we compare lists here.
1784 1784 # As we do it only once buiding set would not be cheaper
1785 1785 changes = tagsmod.difftags(repo.ui, repo, oldfnodes, newfnodes)
1786 1786 if changes:
1787 1787 tr2.hookargs['tag_moved'] = '1'
1788 1788 with repo.vfs('changes/tags.changes', 'w',
1789 1789 atomictemp=True) as changesfile:
1790 1790 # note: we do not register the file to the transaction
1791 1791 # because we needs it to still exist on the transaction
1792 1792 # is close (for txnclose hooks)
1793 1793 tagsmod.writediff(changesfile, changes)
1794 1794 def validate(tr2):
1795 1795 """will run pre-closing hooks"""
1796 1796 # XXX the transaction API is a bit lacking here so we take a hacky
1797 1797 # path for now
1798 1798 #
1799 1799 # We cannot add this as a "pending" hooks since the 'tr.hookargs'
1800 1800 # dict is copied before these run. In addition we needs the data
1801 1801 # available to in memory hooks too.
1802 1802 #
1803 1803 # Moreover, we also need to make sure this runs before txnclose
1804 1804 # hooks and there is no "pending" mechanism that would execute
1805 1805 # logic only if hooks are about to run.
1806 1806 #
1807 1807 # Fixing this limitation of the transaction is also needed to track
1808 1808 # other families of changes (bookmarks, phases, obsolescence).
1809 1809 #
1810 1810 # This will have to be fixed before we remove the experimental
1811 1811 # gating.
1812 1812 tracktags(tr2)
1813 1813 repo = reporef()
1814 1814 if repo.ui.configbool('experimental', 'single-head-per-branch'):
1815 1815 scmutil.enforcesinglehead(repo, tr2, desc)
1816 1816 if hook.hashook(repo.ui, 'pretxnclose-bookmark'):
1817 1817 for name, (old, new) in sorted(tr.changes['bookmarks'].items()):
1818 1818 args = tr.hookargs.copy()
1819 1819 args.update(bookmarks.preparehookargs(name, old, new))
1820 1820 repo.hook('pretxnclose-bookmark', throw=True,
1821 txnname=desc,
1822 1821 **pycompat.strkwargs(args))
1823 1822 if hook.hashook(repo.ui, 'pretxnclose-phase'):
1824 1823 cl = repo.unfiltered().changelog
1825 1824 for rev, (old, new) in tr.changes['phases'].items():
1826 1825 args = tr.hookargs.copy()
1827 1826 node = hex(cl.node(rev))
1828 1827 args.update(phases.preparehookargs(node, old, new))
1829 repo.hook('pretxnclose-phase', throw=True, txnname=desc,
1828 repo.hook('pretxnclose-phase', throw=True,
1830 1829 **pycompat.strkwargs(args))
1831 1830
1832 1831 repo.hook('pretxnclose', throw=True,
1833 txnname=desc, **pycompat.strkwargs(tr.hookargs))
1832 **pycompat.strkwargs(tr.hookargs))
1834 1833 def releasefn(tr, success):
1835 1834 repo = reporef()
1836 1835 if success:
1837 1836 # this should be explicitly invoked here, because
1838 1837 # in-memory changes aren't written out at closing
1839 1838 # transaction, if tr.addfilegenerator (via
1840 1839 # dirstate.write or so) isn't invoked while
1841 1840 # transaction running
1842 1841 repo.dirstate.write(None)
1843 1842 else:
1844 1843 # discard all changes (including ones already written
1845 1844 # out) in this transaction
1846 1845 narrowspec.restorebackup(self, 'journal.narrowspec')
1847 1846 narrowspec.restorewcbackup(self, 'journal.narrowspec.dirstate')
1848 1847 repo.dirstate.restorebackup(None, 'journal.dirstate')
1849 1848
1850 1849 repo.invalidate(clearfilecache=True)
1851 1850
1852 1851 tr = transaction.transaction(rp, self.svfs, vfsmap,
1853 1852 "journal",
1854 1853 "undo",
1855 1854 aftertrans(renames),
1856 1855 self.store.createmode,
1857 1856 validator=validate,
1858 1857 releasefn=releasefn,
1859 1858 checkambigfiles=_cachedfiles,
1860 1859 name=desc)
1861 1860 tr.changes['origrepolen'] = len(self)
1862 1861 tr.changes['obsmarkers'] = set()
1863 1862 tr.changes['phases'] = {}
1864 1863 tr.changes['bookmarks'] = {}
1865 1864
1866 1865 tr.hookargs['txnid'] = txnid
1866 tr.hookargs['txnname'] = desc
1867 1867 # note: writing the fncache only during finalize mean that the file is
1868 1868 # outdated when running hooks. As fncache is used for streaming clone,
1869 1869 # this is not expected to break anything that happen during the hooks.
1870 1870 tr.addfinalize('flush-fncache', self.store.write)
1871 1871 def txnclosehook(tr2):
1872 1872 """To be run if transaction is successful, will schedule a hook run
1873 1873 """
1874 1874 # Don't reference tr2 in hook() so we don't hold a reference.
1875 1875 # This reduces memory consumption when there are multiple
1876 1876 # transactions per lock. This can likely go away if issue5045
1877 1877 # fixes the function accumulation.
1878 1878 hookargs = tr2.hookargs
1879 1879
1880 1880 def hookfunc():
1881 1881 repo = reporef()
1882 1882 if hook.hashook(repo.ui, 'txnclose-bookmark'):
1883 1883 bmchanges = sorted(tr.changes['bookmarks'].items())
1884 1884 for name, (old, new) in bmchanges:
1885 1885 args = tr.hookargs.copy()
1886 1886 args.update(bookmarks.preparehookargs(name, old, new))
1887 1887 repo.hook('txnclose-bookmark', throw=False,
1888 txnname=desc, **pycompat.strkwargs(args))
1888 **pycompat.strkwargs(args))
1889 1889
1890 1890 if hook.hashook(repo.ui, 'txnclose-phase'):
1891 1891 cl = repo.unfiltered().changelog
1892 1892 phasemv = sorted(tr.changes['phases'].items())
1893 1893 for rev, (old, new) in phasemv:
1894 1894 args = tr.hookargs.copy()
1895 1895 node = hex(cl.node(rev))
1896 1896 args.update(phases.preparehookargs(node, old, new))
1897 repo.hook('txnclose-phase', throw=False, txnname=desc,
1897 repo.hook('txnclose-phase', throw=False,
1898 1898 **pycompat.strkwargs(args))
1899 1899
1900 repo.hook('txnclose', throw=False, txnname=desc,
1900 repo.hook('txnclose', throw=False,
1901 1901 **pycompat.strkwargs(hookargs))
1902 1902 reporef()._afterlock(hookfunc)
1903 1903 tr.addfinalize('txnclose-hook', txnclosehook)
1904 1904 # Include a leading "-" to make it happen before the transaction summary
1905 1905 # reports registered via scmutil.registersummarycallback() whose names
1906 1906 # are 00-txnreport etc. That way, the caches will be warm when the
1907 1907 # callbacks run.
1908 1908 tr.addpostclose('-warm-cache', self._buildcacheupdater(tr))
1909 1909 def txnaborthook(tr2):
1910 1910 """To be run if transaction is aborted
1911 1911 """
1912 reporef().hook('txnabort', throw=False, txnname=desc,
1912 reporef().hook('txnabort', throw=False,
1913 1913 **pycompat.strkwargs(tr2.hookargs))
1914 1914 tr.addabort('txnabort-hook', txnaborthook)
1915 1915 # avoid eager cache invalidation. in-memory data should be identical
1916 1916 # to stored data if transaction has no error.
1917 1917 tr.addpostclose('refresh-filecachestats', self._refreshfilecachestats)
1918 1918 self._transref = weakref.ref(tr)
1919 1919 scmutil.registersummarycallback(self, tr, desc)
1920 1920 return tr
1921 1921
1922 1922 def _journalfiles(self):
1923 1923 return ((self.svfs, 'journal'),
1924 1924 (self.svfs, 'journal.narrowspec'),
1925 1925 (self.vfs, 'journal.narrowspec.dirstate'),
1926 1926 (self.vfs, 'journal.dirstate'),
1927 1927 (self.vfs, 'journal.branch'),
1928 1928 (self.vfs, 'journal.desc'),
1929 1929 (self.vfs, 'journal.bookmarks'),
1930 1930 (self.svfs, 'journal.phaseroots'))
1931 1931
1932 1932 def undofiles(self):
1933 1933 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
1934 1934
1935 1935 @unfilteredmethod
1936 1936 def _writejournal(self, desc):
1937 1937 self.dirstate.savebackup(None, 'journal.dirstate')
1938 1938 narrowspec.savewcbackup(self, 'journal.narrowspec.dirstate')
1939 1939 narrowspec.savebackup(self, 'journal.narrowspec')
1940 1940 self.vfs.write("journal.branch",
1941 1941 encoding.fromlocal(self.dirstate.branch()))
1942 1942 self.vfs.write("journal.desc",
1943 1943 "%d\n%s\n" % (len(self), desc))
1944 1944 self.vfs.write("journal.bookmarks",
1945 1945 self.vfs.tryread("bookmarks"))
1946 1946 self.svfs.write("journal.phaseroots",
1947 1947 self.svfs.tryread("phaseroots"))
1948 1948
1949 1949 def recover(self):
1950 1950 with self.lock():
1951 1951 if self.svfs.exists("journal"):
1952 1952 self.ui.status(_("rolling back interrupted transaction\n"))
1953 1953 vfsmap = {'': self.svfs,
1954 1954 'plain': self.vfs,}
1955 1955 transaction.rollback(self.svfs, vfsmap, "journal",
1956 1956 self.ui.warn,
1957 1957 checkambigfiles=_cachedfiles)
1958 1958 self.invalidate()
1959 1959 return True
1960 1960 else:
1961 1961 self.ui.warn(_("no interrupted transaction available\n"))
1962 1962 return False
1963 1963
1964 1964 def rollback(self, dryrun=False, force=False):
1965 1965 wlock = lock = dsguard = None
1966 1966 try:
1967 1967 wlock = self.wlock()
1968 1968 lock = self.lock()
1969 1969 if self.svfs.exists("undo"):
1970 1970 dsguard = dirstateguard.dirstateguard(self, 'rollback')
1971 1971
1972 1972 return self._rollback(dryrun, force, dsguard)
1973 1973 else:
1974 1974 self.ui.warn(_("no rollback information available\n"))
1975 1975 return 1
1976 1976 finally:
1977 1977 release(dsguard, lock, wlock)
1978 1978
1979 1979 @unfilteredmethod # Until we get smarter cache management
1980 1980 def _rollback(self, dryrun, force, dsguard):
1981 1981 ui = self.ui
1982 1982 try:
1983 1983 args = self.vfs.read('undo.desc').splitlines()
1984 1984 (oldlen, desc, detail) = (int(args[0]), args[1], None)
1985 1985 if len(args) >= 3:
1986 1986 detail = args[2]
1987 1987 oldtip = oldlen - 1
1988 1988
1989 1989 if detail and ui.verbose:
1990 1990 msg = (_('repository tip rolled back to revision %d'
1991 1991 ' (undo %s: %s)\n')
1992 1992 % (oldtip, desc, detail))
1993 1993 else:
1994 1994 msg = (_('repository tip rolled back to revision %d'
1995 1995 ' (undo %s)\n')
1996 1996 % (oldtip, desc))
1997 1997 except IOError:
1998 1998 msg = _('rolling back unknown transaction\n')
1999 1999 desc = None
2000 2000
2001 2001 if not force and self['.'] != self['tip'] and desc == 'commit':
2002 2002 raise error.Abort(
2003 2003 _('rollback of last commit while not checked out '
2004 2004 'may lose data'), hint=_('use -f to force'))
2005 2005
2006 2006 ui.status(msg)
2007 2007 if dryrun:
2008 2008 return 0
2009 2009
2010 2010 parents = self.dirstate.parents()
2011 2011 self.destroying()
2012 2012 vfsmap = {'plain': self.vfs, '': self.svfs}
2013 2013 transaction.rollback(self.svfs, vfsmap, 'undo', ui.warn,
2014 2014 checkambigfiles=_cachedfiles)
2015 2015 if self.vfs.exists('undo.bookmarks'):
2016 2016 self.vfs.rename('undo.bookmarks', 'bookmarks', checkambig=True)
2017 2017 if self.svfs.exists('undo.phaseroots'):
2018 2018 self.svfs.rename('undo.phaseroots', 'phaseroots', checkambig=True)
2019 2019 self.invalidate()
2020 2020
2021 2021 parentgone = any(p not in self.changelog.nodemap for p in parents)
2022 2022 if parentgone:
2023 2023 # prevent dirstateguard from overwriting already restored one
2024 2024 dsguard.close()
2025 2025
2026 2026 narrowspec.restorebackup(self, 'undo.narrowspec')
2027 2027 narrowspec.restorewcbackup(self, 'undo.narrowspec.dirstate')
2028 2028 self.dirstate.restorebackup(None, 'undo.dirstate')
2029 2029 try:
2030 2030 branch = self.vfs.read('undo.branch')
2031 2031 self.dirstate.setbranch(encoding.tolocal(branch))
2032 2032 except IOError:
2033 2033 ui.warn(_('named branch could not be reset: '
2034 2034 'current branch is still \'%s\'\n')
2035 2035 % self.dirstate.branch())
2036 2036
2037 2037 parents = tuple([p.rev() for p in self[None].parents()])
2038 2038 if len(parents) > 1:
2039 2039 ui.status(_('working directory now based on '
2040 2040 'revisions %d and %d\n') % parents)
2041 2041 else:
2042 2042 ui.status(_('working directory now based on '
2043 2043 'revision %d\n') % parents)
2044 2044 mergemod.mergestate.clean(self, self['.'].node())
2045 2045
2046 2046 # TODO: if we know which new heads may result from this rollback, pass
2047 2047 # them to destroy(), which will prevent the branchhead cache from being
2048 2048 # invalidated.
2049 2049 self.destroyed()
2050 2050 return 0
2051 2051
2052 2052 def _buildcacheupdater(self, newtransaction):
2053 2053 """called during transaction to build the callback updating cache
2054 2054
2055 2055 Lives on the repository to help extension who might want to augment
2056 2056 this logic. For this purpose, the created transaction is passed to the
2057 2057 method.
2058 2058 """
2059 2059 # we must avoid cyclic reference between repo and transaction.
2060 2060 reporef = weakref.ref(self)
2061 2061 def updater(tr):
2062 2062 repo = reporef()
2063 2063 repo.updatecaches(tr)
2064 2064 return updater
2065 2065
2066 2066 @unfilteredmethod
2067 2067 def updatecaches(self, tr=None, full=False):
2068 2068 """warm appropriate caches
2069 2069
2070 2070 If this function is called after a transaction closed. The transaction
2071 2071 will be available in the 'tr' argument. This can be used to selectively
2072 2072 update caches relevant to the changes in that transaction.
2073 2073
2074 2074 If 'full' is set, make sure all caches the function knows about have
2075 2075 up-to-date data. Even the ones usually loaded more lazily.
2076 2076 """
2077 2077 if tr is not None and tr.hookargs.get('source') == 'strip':
2078 2078 # During strip, many caches are invalid but
2079 2079 # later call to `destroyed` will refresh them.
2080 2080 return
2081 2081
2082 2082 if tr is None or tr.changes['origrepolen'] < len(self):
2083 2083 # accessing the 'ser ved' branchmap should refresh all the others,
2084 2084 self.ui.debug('updating the branch cache\n')
2085 2085 self.filtered('served').branchmap()
2086 2086
2087 2087 if full:
2088 2088 rbc = self.revbranchcache()
2089 2089 for r in self.changelog:
2090 2090 rbc.branchinfo(r)
2091 2091 rbc.write()
2092 2092
2093 2093 # ensure the working copy parents are in the manifestfulltextcache
2094 2094 for ctx in self['.'].parents():
2095 2095 ctx.manifest() # accessing the manifest is enough
2096 2096
2097 2097 def invalidatecaches(self):
2098 2098
2099 2099 if r'_tagscache' in vars(self):
2100 2100 # can't use delattr on proxy
2101 2101 del self.__dict__[r'_tagscache']
2102 2102
2103 2103 self._branchcaches.clear()
2104 2104 self.invalidatevolatilesets()
2105 2105 self._sparsesignaturecache.clear()
2106 2106
2107 2107 def invalidatevolatilesets(self):
2108 2108 self.filteredrevcache.clear()
2109 2109 obsolete.clearobscaches(self)
2110 2110
2111 2111 def invalidatedirstate(self):
2112 2112 '''Invalidates the dirstate, causing the next call to dirstate
2113 2113 to check if it was modified since the last time it was read,
2114 2114 rereading it if it has.
2115 2115
2116 2116 This is different to dirstate.invalidate() that it doesn't always
2117 2117 rereads the dirstate. Use dirstate.invalidate() if you want to
2118 2118 explicitly read the dirstate again (i.e. restoring it to a previous
2119 2119 known good state).'''
2120 2120 if hasunfilteredcache(self, r'dirstate'):
2121 2121 for k in self.dirstate._filecache:
2122 2122 try:
2123 2123 delattr(self.dirstate, k)
2124 2124 except AttributeError:
2125 2125 pass
2126 2126 delattr(self.unfiltered(), r'dirstate')
2127 2127
2128 2128 def invalidate(self, clearfilecache=False):
2129 2129 '''Invalidates both store and non-store parts other than dirstate
2130 2130
2131 2131 If a transaction is running, invalidation of store is omitted,
2132 2132 because discarding in-memory changes might cause inconsistency
2133 2133 (e.g. incomplete fncache causes unintentional failure, but
2134 2134 redundant one doesn't).
2135 2135 '''
2136 2136 unfiltered = self.unfiltered() # all file caches are stored unfiltered
2137 2137 for k in list(self._filecache.keys()):
2138 2138 # dirstate is invalidated separately in invalidatedirstate()
2139 2139 if k == 'dirstate':
2140 2140 continue
2141 2141 if (k == 'changelog' and
2142 2142 self.currenttransaction() and
2143 2143 self.changelog._delayed):
2144 2144 # The changelog object may store unwritten revisions. We don't
2145 2145 # want to lose them.
2146 2146 # TODO: Solve the problem instead of working around it.
2147 2147 continue
2148 2148
2149 2149 if clearfilecache:
2150 2150 del self._filecache[k]
2151 2151 try:
2152 2152 delattr(unfiltered, k)
2153 2153 except AttributeError:
2154 2154 pass
2155 2155 self.invalidatecaches()
2156 2156 if not self.currenttransaction():
2157 2157 # TODO: Changing contents of store outside transaction
2158 2158 # causes inconsistency. We should make in-memory store
2159 2159 # changes detectable, and abort if changed.
2160 2160 self.store.invalidatecaches()
2161 2161
2162 2162 def invalidateall(self):
2163 2163 '''Fully invalidates both store and non-store parts, causing the
2164 2164 subsequent operation to reread any outside changes.'''
2165 2165 # extension should hook this to invalidate its caches
2166 2166 self.invalidate()
2167 2167 self.invalidatedirstate()
2168 2168
2169 2169 @unfilteredmethod
2170 2170 def _refreshfilecachestats(self, tr):
2171 2171 """Reload stats of cached files so that they are flagged as valid"""
2172 2172 for k, ce in self._filecache.items():
2173 2173 k = pycompat.sysstr(k)
2174 2174 if k == r'dirstate' or k not in self.__dict__:
2175 2175 continue
2176 2176 ce.refresh()
2177 2177
2178 2178 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc,
2179 2179 inheritchecker=None, parentenvvar=None):
2180 2180 parentlock = None
2181 2181 # the contents of parentenvvar are used by the underlying lock to
2182 2182 # determine whether it can be inherited
2183 2183 if parentenvvar is not None:
2184 2184 parentlock = encoding.environ.get(parentenvvar)
2185 2185
2186 2186 timeout = 0
2187 2187 warntimeout = 0
2188 2188 if wait:
2189 2189 timeout = self.ui.configint("ui", "timeout")
2190 2190 warntimeout = self.ui.configint("ui", "timeout.warn")
2191 2191 # internal config: ui.signal-safe-lock
2192 2192 signalsafe = self.ui.configbool('ui', 'signal-safe-lock')
2193 2193
2194 2194 l = lockmod.trylock(self.ui, vfs, lockname, timeout, warntimeout,
2195 2195 releasefn=releasefn,
2196 2196 acquirefn=acquirefn, desc=desc,
2197 2197 inheritchecker=inheritchecker,
2198 2198 parentlock=parentlock,
2199 2199 signalsafe=signalsafe)
2200 2200 return l
2201 2201
2202 2202 def _afterlock(self, callback):
2203 2203 """add a callback to be run when the repository is fully unlocked
2204 2204
2205 2205 The callback will be executed when the outermost lock is released
2206 2206 (with wlock being higher level than 'lock')."""
2207 2207 for ref in (self._wlockref, self._lockref):
2208 2208 l = ref and ref()
2209 2209 if l and l.held:
2210 2210 l.postrelease.append(callback)
2211 2211 break
2212 2212 else: # no lock have been found.
2213 2213 callback()
2214 2214
2215 2215 def lock(self, wait=True):
2216 2216 '''Lock the repository store (.hg/store) and return a weak reference
2217 2217 to the lock. Use this before modifying the store (e.g. committing or
2218 2218 stripping). If you are opening a transaction, get a lock as well.)
2219 2219
2220 2220 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
2221 2221 'wlock' first to avoid a dead-lock hazard.'''
2222 2222 l = self._currentlock(self._lockref)
2223 2223 if l is not None:
2224 2224 l.lock()
2225 2225 return l
2226 2226
2227 2227 l = self._lock(vfs=self.svfs,
2228 2228 lockname="lock",
2229 2229 wait=wait,
2230 2230 releasefn=None,
2231 2231 acquirefn=self.invalidate,
2232 2232 desc=_('repository %s') % self.origroot)
2233 2233 self._lockref = weakref.ref(l)
2234 2234 return l
2235 2235
2236 2236 def _wlockchecktransaction(self):
2237 2237 if self.currenttransaction() is not None:
2238 2238 raise error.LockInheritanceContractViolation(
2239 2239 'wlock cannot be inherited in the middle of a transaction')
2240 2240
2241 2241 def wlock(self, wait=True):
2242 2242 '''Lock the non-store parts of the repository (everything under
2243 2243 .hg except .hg/store) and return a weak reference to the lock.
2244 2244
2245 2245 Use this before modifying files in .hg.
2246 2246
2247 2247 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
2248 2248 'wlock' first to avoid a dead-lock hazard.'''
2249 2249 l = self._wlockref and self._wlockref()
2250 2250 if l is not None and l.held:
2251 2251 l.lock()
2252 2252 return l
2253 2253
2254 2254 # We do not need to check for non-waiting lock acquisition. Such
2255 2255 # acquisition would not cause dead-lock as they would just fail.
2256 2256 if wait and (self.ui.configbool('devel', 'all-warnings')
2257 2257 or self.ui.configbool('devel', 'check-locks')):
2258 2258 if self._currentlock(self._lockref) is not None:
2259 2259 self.ui.develwarn('"wlock" acquired after "lock"')
2260 2260
2261 2261 def unlock():
2262 2262 if self.dirstate.pendingparentchange():
2263 2263 self.dirstate.invalidate()
2264 2264 else:
2265 2265 self.dirstate.write(None)
2266 2266
2267 2267 self._filecache['dirstate'].refresh()
2268 2268
2269 2269 l = self._lock(self.vfs, "wlock", wait, unlock,
2270 2270 self.invalidatedirstate, _('working directory of %s') %
2271 2271 self.origroot,
2272 2272 inheritchecker=self._wlockchecktransaction,
2273 2273 parentenvvar='HG_WLOCK_LOCKER')
2274 2274 self._wlockref = weakref.ref(l)
2275 2275 return l
2276 2276
2277 2277 def _currentlock(self, lockref):
2278 2278 """Returns the lock if it's held, or None if it's not."""
2279 2279 if lockref is None:
2280 2280 return None
2281 2281 l = lockref()
2282 2282 if l is None or not l.held:
2283 2283 return None
2284 2284 return l
2285 2285
2286 2286 def currentwlock(self):
2287 2287 """Returns the wlock if it's held, or None if it's not."""
2288 2288 return self._currentlock(self._wlockref)
2289 2289
2290 2290 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
2291 2291 """
2292 2292 commit an individual file as part of a larger transaction
2293 2293 """
2294 2294
2295 2295 fname = fctx.path()
2296 2296 fparent1 = manifest1.get(fname, nullid)
2297 2297 fparent2 = manifest2.get(fname, nullid)
2298 2298 if isinstance(fctx, context.filectx):
2299 2299 node = fctx.filenode()
2300 2300 if node in [fparent1, fparent2]:
2301 2301 self.ui.debug('reusing %s filelog entry\n' % fname)
2302 2302 if manifest1.flags(fname) != fctx.flags():
2303 2303 changelist.append(fname)
2304 2304 return node
2305 2305
2306 2306 flog = self.file(fname)
2307 2307 meta = {}
2308 2308 cfname = fctx.copysource()
2309 2309 if cfname and cfname != fname:
2310 2310 # Mark the new revision of this file as a copy of another
2311 2311 # file. This copy data will effectively act as a parent
2312 2312 # of this new revision. If this is a merge, the first
2313 2313 # parent will be the nullid (meaning "look up the copy data")
2314 2314 # and the second one will be the other parent. For example:
2315 2315 #
2316 2316 # 0 --- 1 --- 3 rev1 changes file foo
2317 2317 # \ / rev2 renames foo to bar and changes it
2318 2318 # \- 2 -/ rev3 should have bar with all changes and
2319 2319 # should record that bar descends from
2320 2320 # bar in rev2 and foo in rev1
2321 2321 #
2322 2322 # this allows this merge to succeed:
2323 2323 #
2324 2324 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
2325 2325 # \ / merging rev3 and rev4 should use bar@rev2
2326 2326 # \- 2 --- 4 as the merge base
2327 2327 #
2328 2328
2329 2329 crev = manifest1.get(cfname)
2330 2330 newfparent = fparent2
2331 2331
2332 2332 if manifest2: # branch merge
2333 2333 if fparent2 == nullid or crev is None: # copied on remote side
2334 2334 if cfname in manifest2:
2335 2335 crev = manifest2[cfname]
2336 2336 newfparent = fparent1
2337 2337
2338 2338 # Here, we used to search backwards through history to try to find
2339 2339 # where the file copy came from if the source of a copy was not in
2340 2340 # the parent directory. However, this doesn't actually make sense to
2341 2341 # do (what does a copy from something not in your working copy even
2342 2342 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
2343 2343 # the user that copy information was dropped, so if they didn't
2344 2344 # expect this outcome it can be fixed, but this is the correct
2345 2345 # behavior in this circumstance.
2346 2346
2347 2347 if crev:
2348 2348 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
2349 2349 meta["copy"] = cfname
2350 2350 meta["copyrev"] = hex(crev)
2351 2351 fparent1, fparent2 = nullid, newfparent
2352 2352 else:
2353 2353 self.ui.warn(_("warning: can't find ancestor for '%s' "
2354 2354 "copied from '%s'!\n") % (fname, cfname))
2355 2355
2356 2356 elif fparent1 == nullid:
2357 2357 fparent1, fparent2 = fparent2, nullid
2358 2358 elif fparent2 != nullid:
2359 2359 # is one parent an ancestor of the other?
2360 2360 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
2361 2361 if fparent1 in fparentancestors:
2362 2362 fparent1, fparent2 = fparent2, nullid
2363 2363 elif fparent2 in fparentancestors:
2364 2364 fparent2 = nullid
2365 2365
2366 2366 # is the file changed?
2367 2367 text = fctx.data()
2368 2368 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
2369 2369 changelist.append(fname)
2370 2370 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
2371 2371 # are just the flags changed during merge?
2372 2372 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
2373 2373 changelist.append(fname)
2374 2374
2375 2375 return fparent1
2376 2376
2377 2377 def checkcommitpatterns(self, wctx, vdirs, match, status, fail):
2378 2378 """check for commit arguments that aren't committable"""
2379 2379 if match.isexact() or match.prefix():
2380 2380 matched = set(status.modified + status.added + status.removed)
2381 2381
2382 2382 for f in match.files():
2383 2383 f = self.dirstate.normalize(f)
2384 2384 if f == '.' or f in matched or f in wctx.substate:
2385 2385 continue
2386 2386 if f in status.deleted:
2387 2387 fail(f, _('file not found!'))
2388 2388 if f in vdirs: # visited directory
2389 2389 d = f + '/'
2390 2390 for mf in matched:
2391 2391 if mf.startswith(d):
2392 2392 break
2393 2393 else:
2394 2394 fail(f, _("no match under directory!"))
2395 2395 elif f not in self.dirstate:
2396 2396 fail(f, _("file not tracked!"))
2397 2397
2398 2398 @unfilteredmethod
2399 2399 def commit(self, text="", user=None, date=None, match=None, force=False,
2400 2400 editor=False, extra=None):
2401 2401 """Add a new revision to current repository.
2402 2402
2403 2403 Revision information is gathered from the working directory,
2404 2404 match can be used to filter the committed files. If editor is
2405 2405 supplied, it is called to get a commit message.
2406 2406 """
2407 2407 if extra is None:
2408 2408 extra = {}
2409 2409
2410 2410 def fail(f, msg):
2411 2411 raise error.Abort('%s: %s' % (f, msg))
2412 2412
2413 2413 if not match:
2414 2414 match = matchmod.always()
2415 2415
2416 2416 if not force:
2417 2417 vdirs = []
2418 2418 match.explicitdir = vdirs.append
2419 2419 match.bad = fail
2420 2420
2421 2421 # lock() for recent changelog (see issue4368)
2422 2422 with self.wlock(), self.lock():
2423 2423 wctx = self[None]
2424 2424 merge = len(wctx.parents()) > 1
2425 2425
2426 2426 if not force and merge and not match.always():
2427 2427 raise error.Abort(_('cannot partially commit a merge '
2428 2428 '(do not specify files or patterns)'))
2429 2429
2430 2430 status = self.status(match=match, clean=force)
2431 2431 if force:
2432 2432 status.modified.extend(status.clean) # mq may commit clean files
2433 2433
2434 2434 # check subrepos
2435 2435 subs, commitsubs, newstate = subrepoutil.precommit(
2436 2436 self.ui, wctx, status, match, force=force)
2437 2437
2438 2438 # make sure all explicit patterns are matched
2439 2439 if not force:
2440 2440 self.checkcommitpatterns(wctx, vdirs, match, status, fail)
2441 2441
2442 2442 cctx = context.workingcommitctx(self, status,
2443 2443 text, user, date, extra)
2444 2444
2445 2445 # internal config: ui.allowemptycommit
2446 2446 allowemptycommit = (wctx.branch() != wctx.p1().branch()
2447 2447 or extra.get('close') or merge or cctx.files()
2448 2448 or self.ui.configbool('ui', 'allowemptycommit'))
2449 2449 if not allowemptycommit:
2450 2450 return None
2451 2451
2452 2452 if merge and cctx.deleted():
2453 2453 raise error.Abort(_("cannot commit merge with missing files"))
2454 2454
2455 2455 ms = mergemod.mergestate.read(self)
2456 2456 mergeutil.checkunresolved(ms)
2457 2457
2458 2458 if editor:
2459 2459 cctx._text = editor(self, cctx, subs)
2460 2460 edited = (text != cctx._text)
2461 2461
2462 2462 # Save commit message in case this transaction gets rolled back
2463 2463 # (e.g. by a pretxncommit hook). Leave the content alone on
2464 2464 # the assumption that the user will use the same editor again.
2465 2465 msgfn = self.savecommitmessage(cctx._text)
2466 2466
2467 2467 # commit subs and write new state
2468 2468 if subs:
2469 2469 uipathfn = scmutil.getuipathfn(self)
2470 2470 for s in sorted(commitsubs):
2471 2471 sub = wctx.sub(s)
2472 2472 self.ui.status(_('committing subrepository %s\n') %
2473 2473 uipathfn(subrepoutil.subrelpath(sub)))
2474 2474 sr = sub.commit(cctx._text, user, date)
2475 2475 newstate[s] = (newstate[s][0], sr)
2476 2476 subrepoutil.writestate(self, newstate)
2477 2477
2478 2478 p1, p2 = self.dirstate.parents()
2479 2479 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
2480 2480 try:
2481 2481 self.hook("precommit", throw=True, parent1=hookp1,
2482 2482 parent2=hookp2)
2483 2483 with self.transaction('commit'):
2484 2484 ret = self.commitctx(cctx, True)
2485 2485 # update bookmarks, dirstate and mergestate
2486 2486 bookmarks.update(self, [p1, p2], ret)
2487 2487 cctx.markcommitted(ret)
2488 2488 ms.reset()
2489 2489 except: # re-raises
2490 2490 if edited:
2491 2491 self.ui.write(
2492 2492 _('note: commit message saved in %s\n') % msgfn)
2493 2493 raise
2494 2494
2495 2495 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
2496 2496 # hack for command that use a temporary commit (eg: histedit)
2497 2497 # temporary commit got stripped before hook release
2498 2498 if self.changelog.hasnode(ret):
2499 2499 self.hook("commit", node=node, parent1=parent1,
2500 2500 parent2=parent2)
2501 2501 self._afterlock(commithook)
2502 2502 return ret
2503 2503
2504 2504 @unfilteredmethod
2505 2505 def commitctx(self, ctx, error=False):
2506 2506 """Add a new revision to current repository.
2507 2507 Revision information is passed via the context argument.
2508 2508
2509 2509 ctx.files() should list all files involved in this commit, i.e.
2510 2510 modified/added/removed files. On merge, it may be wider than the
2511 2511 ctx.files() to be committed, since any file nodes derived directly
2512 2512 from p1 or p2 are excluded from the committed ctx.files().
2513 2513 """
2514 2514
2515 2515 p1, p2 = ctx.p1(), ctx.p2()
2516 2516 user = ctx.user()
2517 2517
2518 2518 with self.lock(), self.transaction("commit") as tr:
2519 2519 trp = weakref.proxy(tr)
2520 2520
2521 2521 if ctx.manifestnode():
2522 2522 # reuse an existing manifest revision
2523 2523 self.ui.debug('reusing known manifest\n')
2524 2524 mn = ctx.manifestnode()
2525 2525 files = ctx.files()
2526 2526 elif ctx.files():
2527 2527 m1ctx = p1.manifestctx()
2528 2528 m2ctx = p2.manifestctx()
2529 2529 mctx = m1ctx.copy()
2530 2530
2531 2531 m = mctx.read()
2532 2532 m1 = m1ctx.read()
2533 2533 m2 = m2ctx.read()
2534 2534
2535 2535 # check in files
2536 2536 added = []
2537 2537 changed = []
2538 2538 removed = list(ctx.removed())
2539 2539 linkrev = len(self)
2540 2540 self.ui.note(_("committing files:\n"))
2541 2541 uipathfn = scmutil.getuipathfn(self)
2542 2542 for f in sorted(ctx.modified() + ctx.added()):
2543 2543 self.ui.note(uipathfn(f) + "\n")
2544 2544 try:
2545 2545 fctx = ctx[f]
2546 2546 if fctx is None:
2547 2547 removed.append(f)
2548 2548 else:
2549 2549 added.append(f)
2550 2550 m[f] = self._filecommit(fctx, m1, m2, linkrev,
2551 2551 trp, changed)
2552 2552 m.setflag(f, fctx.flags())
2553 2553 except OSError:
2554 2554 self.ui.warn(_("trouble committing %s!\n") %
2555 2555 uipathfn(f))
2556 2556 raise
2557 2557 except IOError as inst:
2558 2558 errcode = getattr(inst, 'errno', errno.ENOENT)
2559 2559 if error or errcode and errcode != errno.ENOENT:
2560 2560 self.ui.warn(_("trouble committing %s!\n") %
2561 2561 uipathfn(f))
2562 2562 raise
2563 2563
2564 2564 # update manifest
2565 2565 removed = [f for f in sorted(removed) if f in m1 or f in m2]
2566 2566 drop = [f for f in removed if f in m]
2567 2567 for f in drop:
2568 2568 del m[f]
2569 2569 files = changed + removed
2570 2570 md = None
2571 2571 if not files:
2572 2572 # if no "files" actually changed in terms of the changelog,
2573 2573 # try hard to detect unmodified manifest entry so that the
2574 2574 # exact same commit can be reproduced later on convert.
2575 2575 md = m1.diff(m, scmutil.matchfiles(self, ctx.files()))
2576 2576 if not files and md:
2577 2577 self.ui.debug('not reusing manifest (no file change in '
2578 2578 'changelog, but manifest differs)\n')
2579 2579 if files or md:
2580 2580 self.ui.note(_("committing manifest\n"))
2581 2581 # we're using narrowmatch here since it's already applied at
2582 2582 # other stages (such as dirstate.walk), so we're already
2583 2583 # ignoring things outside of narrowspec in most cases. The
2584 2584 # one case where we might have files outside the narrowspec
2585 2585 # at this point is merges, and we already error out in the
2586 2586 # case where the merge has files outside of the narrowspec,
2587 2587 # so this is safe.
2588 2588 mn = mctx.write(trp, linkrev,
2589 2589 p1.manifestnode(), p2.manifestnode(),
2590 2590 added, drop, match=self.narrowmatch())
2591 2591 else:
2592 2592 self.ui.debug('reusing manifest form p1 (listed files '
2593 2593 'actually unchanged)\n')
2594 2594 mn = p1.manifestnode()
2595 2595 else:
2596 2596 self.ui.debug('reusing manifest from p1 (no file change)\n')
2597 2597 mn = p1.manifestnode()
2598 2598 files = []
2599 2599
2600 2600 # update changelog
2601 2601 self.ui.note(_("committing changelog\n"))
2602 2602 self.changelog.delayupdate(tr)
2603 2603 n = self.changelog.add(mn, files, ctx.description(),
2604 2604 trp, p1.node(), p2.node(),
2605 2605 user, ctx.date(), ctx.extra().copy())
2606 2606 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
2607 2607 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
2608 2608 parent2=xp2)
2609 2609 # set the new commit is proper phase
2610 2610 targetphase = subrepoutil.newcommitphase(self.ui, ctx)
2611 2611 if targetphase:
2612 2612 # retract boundary do not alter parent changeset.
2613 2613 # if a parent have higher the resulting phase will
2614 2614 # be compliant anyway
2615 2615 #
2616 2616 # if minimal phase was 0 we don't need to retract anything
2617 2617 phases.registernew(self, tr, targetphase, [n])
2618 2618 return n
2619 2619
2620 2620 @unfilteredmethod
2621 2621 def destroying(self):
2622 2622 '''Inform the repository that nodes are about to be destroyed.
2623 2623 Intended for use by strip and rollback, so there's a common
2624 2624 place for anything that has to be done before destroying history.
2625 2625
2626 2626 This is mostly useful for saving state that is in memory and waiting
2627 2627 to be flushed when the current lock is released. Because a call to
2628 2628 destroyed is imminent, the repo will be invalidated causing those
2629 2629 changes to stay in memory (waiting for the next unlock), or vanish
2630 2630 completely.
2631 2631 '''
2632 2632 # When using the same lock to commit and strip, the phasecache is left
2633 2633 # dirty after committing. Then when we strip, the repo is invalidated,
2634 2634 # causing those changes to disappear.
2635 2635 if '_phasecache' in vars(self):
2636 2636 self._phasecache.write()
2637 2637
2638 2638 @unfilteredmethod
2639 2639 def destroyed(self):
2640 2640 '''Inform the repository that nodes have been destroyed.
2641 2641 Intended for use by strip and rollback, so there's a common
2642 2642 place for anything that has to be done after destroying history.
2643 2643 '''
2644 2644 # When one tries to:
2645 2645 # 1) destroy nodes thus calling this method (e.g. strip)
2646 2646 # 2) use phasecache somewhere (e.g. commit)
2647 2647 #
2648 2648 # then 2) will fail because the phasecache contains nodes that were
2649 2649 # removed. We can either remove phasecache from the filecache,
2650 2650 # causing it to reload next time it is accessed, or simply filter
2651 2651 # the removed nodes now and write the updated cache.
2652 2652 self._phasecache.filterunknown(self)
2653 2653 self._phasecache.write()
2654 2654
2655 2655 # refresh all repository caches
2656 2656 self.updatecaches()
2657 2657
2658 2658 # Ensure the persistent tag cache is updated. Doing it now
2659 2659 # means that the tag cache only has to worry about destroyed
2660 2660 # heads immediately after a strip/rollback. That in turn
2661 2661 # guarantees that "cachetip == currenttip" (comparing both rev
2662 2662 # and node) always means no nodes have been added or destroyed.
2663 2663
2664 2664 # XXX this is suboptimal when qrefresh'ing: we strip the current
2665 2665 # head, refresh the tag cache, then immediately add a new head.
2666 2666 # But I think doing it this way is necessary for the "instant
2667 2667 # tag cache retrieval" case to work.
2668 2668 self.invalidate()
2669 2669
2670 2670 def status(self, node1='.', node2=None, match=None,
2671 2671 ignored=False, clean=False, unknown=False,
2672 2672 listsubrepos=False):
2673 2673 '''a convenience method that calls node1.status(node2)'''
2674 2674 return self[node1].status(node2, match, ignored, clean, unknown,
2675 2675 listsubrepos)
2676 2676
2677 2677 def addpostdsstatus(self, ps):
2678 2678 """Add a callback to run within the wlock, at the point at which status
2679 2679 fixups happen.
2680 2680
2681 2681 On status completion, callback(wctx, status) will be called with the
2682 2682 wlock held, unless the dirstate has changed from underneath or the wlock
2683 2683 couldn't be grabbed.
2684 2684
2685 2685 Callbacks should not capture and use a cached copy of the dirstate --
2686 2686 it might change in the meanwhile. Instead, they should access the
2687 2687 dirstate via wctx.repo().dirstate.
2688 2688
2689 2689 This list is emptied out after each status run -- extensions should
2690 2690 make sure it adds to this list each time dirstate.status is called.
2691 2691 Extensions should also make sure they don't call this for statuses
2692 2692 that don't involve the dirstate.
2693 2693 """
2694 2694
2695 2695 # The list is located here for uniqueness reasons -- it is actually
2696 2696 # managed by the workingctx, but that isn't unique per-repo.
2697 2697 self._postdsstatus.append(ps)
2698 2698
2699 2699 def postdsstatus(self):
2700 2700 """Used by workingctx to get the list of post-dirstate-status hooks."""
2701 2701 return self._postdsstatus
2702 2702
2703 2703 def clearpostdsstatus(self):
2704 2704 """Used by workingctx to clear post-dirstate-status hooks."""
2705 2705 del self._postdsstatus[:]
2706 2706
2707 2707 def heads(self, start=None):
2708 2708 if start is None:
2709 2709 cl = self.changelog
2710 2710 headrevs = reversed(cl.headrevs())
2711 2711 return [cl.node(rev) for rev in headrevs]
2712 2712
2713 2713 heads = self.changelog.heads(start)
2714 2714 # sort the output in rev descending order
2715 2715 return sorted(heads, key=self.changelog.rev, reverse=True)
2716 2716
2717 2717 def branchheads(self, branch=None, start=None, closed=False):
2718 2718 '''return a (possibly filtered) list of heads for the given branch
2719 2719
2720 2720 Heads are returned in topological order, from newest to oldest.
2721 2721 If branch is None, use the dirstate branch.
2722 2722 If start is not None, return only heads reachable from start.
2723 2723 If closed is True, return heads that are marked as closed as well.
2724 2724 '''
2725 2725 if branch is None:
2726 2726 branch = self[None].branch()
2727 2727 branches = self.branchmap()
2728 2728 if branch not in branches:
2729 2729 return []
2730 2730 # the cache returns heads ordered lowest to highest
2731 2731 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
2732 2732 if start is not None:
2733 2733 # filter out the heads that cannot be reached from startrev
2734 2734 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
2735 2735 bheads = [h for h in bheads if h in fbheads]
2736 2736 return bheads
2737 2737
2738 2738 def branches(self, nodes):
2739 2739 if not nodes:
2740 2740 nodes = [self.changelog.tip()]
2741 2741 b = []
2742 2742 for n in nodes:
2743 2743 t = n
2744 2744 while True:
2745 2745 p = self.changelog.parents(n)
2746 2746 if p[1] != nullid or p[0] == nullid:
2747 2747 b.append((t, n, p[0], p[1]))
2748 2748 break
2749 2749 n = p[0]
2750 2750 return b
2751 2751
2752 2752 def between(self, pairs):
2753 2753 r = []
2754 2754
2755 2755 for top, bottom in pairs:
2756 2756 n, l, i = top, [], 0
2757 2757 f = 1
2758 2758
2759 2759 while n != bottom and n != nullid:
2760 2760 p = self.changelog.parents(n)[0]
2761 2761 if i == f:
2762 2762 l.append(n)
2763 2763 f = f * 2
2764 2764 n = p
2765 2765 i += 1
2766 2766
2767 2767 r.append(l)
2768 2768
2769 2769 return r
2770 2770
2771 2771 def checkpush(self, pushop):
2772 2772 """Extensions can override this function if additional checks have
2773 2773 to be performed before pushing, or call it if they override push
2774 2774 command.
2775 2775 """
2776 2776
2777 2777 @unfilteredpropertycache
2778 2778 def prepushoutgoinghooks(self):
2779 2779 """Return util.hooks consists of a pushop with repo, remote, outgoing
2780 2780 methods, which are called before pushing changesets.
2781 2781 """
2782 2782 return util.hooks()
2783 2783
2784 2784 def pushkey(self, namespace, key, old, new):
2785 2785 try:
2786 2786 tr = self.currenttransaction()
2787 2787 hookargs = {}
2788 2788 if tr is not None:
2789 2789 hookargs.update(tr.hookargs)
2790 2790 hookargs = pycompat.strkwargs(hookargs)
2791 2791 hookargs[r'namespace'] = namespace
2792 2792 hookargs[r'key'] = key
2793 2793 hookargs[r'old'] = old
2794 2794 hookargs[r'new'] = new
2795 2795 self.hook('prepushkey', throw=True, **hookargs)
2796 2796 except error.HookAbort as exc:
2797 2797 self.ui.write_err(_("pushkey-abort: %s\n") % exc)
2798 2798 if exc.hint:
2799 2799 self.ui.write_err(_("(%s)\n") % exc.hint)
2800 2800 return False
2801 2801 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
2802 2802 ret = pushkey.push(self, namespace, key, old, new)
2803 2803 def runhook():
2804 2804 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
2805 2805 ret=ret)
2806 2806 self._afterlock(runhook)
2807 2807 return ret
2808 2808
2809 2809 def listkeys(self, namespace):
2810 2810 self.hook('prelistkeys', throw=True, namespace=namespace)
2811 2811 self.ui.debug('listing keys for "%s"\n' % namespace)
2812 2812 values = pushkey.list(self, namespace)
2813 2813 self.hook('listkeys', namespace=namespace, values=values)
2814 2814 return values
2815 2815
2816 2816 def debugwireargs(self, one, two, three=None, four=None, five=None):
2817 2817 '''used to test argument passing over the wire'''
2818 2818 return "%s %s %s %s %s" % (one, two, pycompat.bytestr(three),
2819 2819 pycompat.bytestr(four),
2820 2820 pycompat.bytestr(five))
2821 2821
2822 2822 def savecommitmessage(self, text):
2823 2823 fp = self.vfs('last-message.txt', 'wb')
2824 2824 try:
2825 2825 fp.write(text)
2826 2826 finally:
2827 2827 fp.close()
2828 2828 return self.pathto(fp.name[len(self.root) + 1:])
2829 2829
2830 2830 # used to avoid circular references so destructors work
2831 2831 def aftertrans(files):
2832 2832 renamefiles = [tuple(t) for t in files]
2833 2833 def a():
2834 2834 for vfs, src, dest in renamefiles:
2835 2835 # if src and dest refer to a same file, vfs.rename is a no-op,
2836 2836 # leaving both src and dest on disk. delete dest to make sure
2837 2837 # the rename couldn't be such a no-op.
2838 2838 vfs.tryunlink(dest)
2839 2839 try:
2840 2840 vfs.rename(src, dest)
2841 2841 except OSError: # journal file does not yet exist
2842 2842 pass
2843 2843 return a
2844 2844
2845 2845 def undoname(fn):
2846 2846 base, name = os.path.split(fn)
2847 2847 assert name.startswith('journal')
2848 2848 return os.path.join(base, name.replace('journal', 'undo', 1))
2849 2849
2850 2850 def instance(ui, path, create, intents=None, createopts=None):
2851 2851 localpath = util.urllocalpath(path)
2852 2852 if create:
2853 2853 createrepository(ui, localpath, createopts=createopts)
2854 2854
2855 2855 return makelocalrepository(ui, localpath, intents=intents)
2856 2856
2857 2857 def islocal(path):
2858 2858 return True
2859 2859
2860 2860 def defaultcreateopts(ui, createopts=None):
2861 2861 """Populate the default creation options for a repository.
2862 2862
2863 2863 A dictionary of explicitly requested creation options can be passed
2864 2864 in. Missing keys will be populated.
2865 2865 """
2866 2866 createopts = dict(createopts or {})
2867 2867
2868 2868 if 'backend' not in createopts:
2869 2869 # experimental config: storage.new-repo-backend
2870 2870 createopts['backend'] = ui.config('storage', 'new-repo-backend')
2871 2871
2872 2872 return createopts
2873 2873
2874 2874 def newreporequirements(ui, createopts):
2875 2875 """Determine the set of requirements for a new local repository.
2876 2876
2877 2877 Extensions can wrap this function to specify custom requirements for
2878 2878 new repositories.
2879 2879 """
2880 2880 # If the repo is being created from a shared repository, we copy
2881 2881 # its requirements.
2882 2882 if 'sharedrepo' in createopts:
2883 2883 requirements = set(createopts['sharedrepo'].requirements)
2884 2884 if createopts.get('sharedrelative'):
2885 2885 requirements.add('relshared')
2886 2886 else:
2887 2887 requirements.add('shared')
2888 2888
2889 2889 return requirements
2890 2890
2891 2891 if 'backend' not in createopts:
2892 2892 raise error.ProgrammingError('backend key not present in createopts; '
2893 2893 'was defaultcreateopts() called?')
2894 2894
2895 2895 if createopts['backend'] != 'revlogv1':
2896 2896 raise error.Abort(_('unable to determine repository requirements for '
2897 2897 'storage backend: %s') % createopts['backend'])
2898 2898
2899 2899 requirements = {'revlogv1'}
2900 2900 if ui.configbool('format', 'usestore'):
2901 2901 requirements.add('store')
2902 2902 if ui.configbool('format', 'usefncache'):
2903 2903 requirements.add('fncache')
2904 2904 if ui.configbool('format', 'dotencode'):
2905 2905 requirements.add('dotencode')
2906 2906
2907 2907 compengine = ui.config('experimental', 'format.compression')
2908 2908 if compengine not in util.compengines:
2909 2909 raise error.Abort(_('compression engine %s defined by '
2910 2910 'experimental.format.compression not available') %
2911 2911 compengine,
2912 2912 hint=_('run "hg debuginstall" to list available '
2913 2913 'compression engines'))
2914 2914
2915 2915 # zlib is the historical default and doesn't need an explicit requirement.
2916 2916 if compengine != 'zlib':
2917 2917 requirements.add('exp-compression-%s' % compengine)
2918 2918
2919 2919 if scmutil.gdinitconfig(ui):
2920 2920 requirements.add('generaldelta')
2921 2921 if ui.configbool('format', 'sparse-revlog'):
2922 2922 requirements.add(SPARSEREVLOG_REQUIREMENT)
2923 2923 if ui.configbool('experimental', 'treemanifest'):
2924 2924 requirements.add('treemanifest')
2925 2925
2926 2926 revlogv2 = ui.config('experimental', 'revlogv2')
2927 2927 if revlogv2 == 'enable-unstable-format-and-corrupt-my-data':
2928 2928 requirements.remove('revlogv1')
2929 2929 # generaldelta is implied by revlogv2.
2930 2930 requirements.discard('generaldelta')
2931 2931 requirements.add(REVLOGV2_REQUIREMENT)
2932 2932 # experimental config: format.internal-phase
2933 2933 if ui.configbool('format', 'internal-phase'):
2934 2934 requirements.add('internal-phase')
2935 2935
2936 2936 if createopts.get('narrowfiles'):
2937 2937 requirements.add(repository.NARROW_REQUIREMENT)
2938 2938
2939 2939 if createopts.get('lfs'):
2940 2940 requirements.add('lfs')
2941 2941
2942 2942 return requirements
2943 2943
2944 2944 def filterknowncreateopts(ui, createopts):
2945 2945 """Filters a dict of repo creation options against options that are known.
2946 2946
2947 2947 Receives a dict of repo creation options and returns a dict of those
2948 2948 options that we don't know how to handle.
2949 2949
2950 2950 This function is called as part of repository creation. If the
2951 2951 returned dict contains any items, repository creation will not
2952 2952 be allowed, as it means there was a request to create a repository
2953 2953 with options not recognized by loaded code.
2954 2954
2955 2955 Extensions can wrap this function to filter out creation options
2956 2956 they know how to handle.
2957 2957 """
2958 2958 known = {
2959 2959 'backend',
2960 2960 'lfs',
2961 2961 'narrowfiles',
2962 2962 'sharedrepo',
2963 2963 'sharedrelative',
2964 2964 'shareditems',
2965 2965 'shallowfilestore',
2966 2966 }
2967 2967
2968 2968 return {k: v for k, v in createopts.items() if k not in known}
2969 2969
2970 2970 def createrepository(ui, path, createopts=None):
2971 2971 """Create a new repository in a vfs.
2972 2972
2973 2973 ``path`` path to the new repo's working directory.
2974 2974 ``createopts`` options for the new repository.
2975 2975
2976 2976 The following keys for ``createopts`` are recognized:
2977 2977
2978 2978 backend
2979 2979 The storage backend to use.
2980 2980 lfs
2981 2981 Repository will be created with ``lfs`` requirement. The lfs extension
2982 2982 will automatically be loaded when the repository is accessed.
2983 2983 narrowfiles
2984 2984 Set up repository to support narrow file storage.
2985 2985 sharedrepo
2986 2986 Repository object from which storage should be shared.
2987 2987 sharedrelative
2988 2988 Boolean indicating if the path to the shared repo should be
2989 2989 stored as relative. By default, the pointer to the "parent" repo
2990 2990 is stored as an absolute path.
2991 2991 shareditems
2992 2992 Set of items to share to the new repository (in addition to storage).
2993 2993 shallowfilestore
2994 2994 Indicates that storage for files should be shallow (not all ancestor
2995 2995 revisions are known).
2996 2996 """
2997 2997 createopts = defaultcreateopts(ui, createopts=createopts)
2998 2998
2999 2999 unknownopts = filterknowncreateopts(ui, createopts)
3000 3000
3001 3001 if not isinstance(unknownopts, dict):
3002 3002 raise error.ProgrammingError('filterknowncreateopts() did not return '
3003 3003 'a dict')
3004 3004
3005 3005 if unknownopts:
3006 3006 raise error.Abort(_('unable to create repository because of unknown '
3007 3007 'creation option: %s') %
3008 3008 ', '.join(sorted(unknownopts)),
3009 3009 hint=_('is a required extension not loaded?'))
3010 3010
3011 3011 requirements = newreporequirements(ui, createopts=createopts)
3012 3012
3013 3013 wdirvfs = vfsmod.vfs(path, expandpath=True, realpath=True)
3014 3014
3015 3015 hgvfs = vfsmod.vfs(wdirvfs.join(b'.hg'))
3016 3016 if hgvfs.exists():
3017 3017 raise error.RepoError(_('repository %s already exists') % path)
3018 3018
3019 3019 if 'sharedrepo' in createopts:
3020 3020 sharedpath = createopts['sharedrepo'].sharedpath
3021 3021
3022 3022 if createopts.get('sharedrelative'):
3023 3023 try:
3024 3024 sharedpath = os.path.relpath(sharedpath, hgvfs.base)
3025 3025 except (IOError, ValueError) as e:
3026 3026 # ValueError is raised on Windows if the drive letters differ
3027 3027 # on each path.
3028 3028 raise error.Abort(_('cannot calculate relative path'),
3029 3029 hint=stringutil.forcebytestr(e))
3030 3030
3031 3031 if not wdirvfs.exists():
3032 3032 wdirvfs.makedirs()
3033 3033
3034 3034 hgvfs.makedir(notindexed=True)
3035 3035 if 'sharedrepo' not in createopts:
3036 3036 hgvfs.mkdir(b'cache')
3037 3037 hgvfs.mkdir(b'wcache')
3038 3038
3039 3039 if b'store' in requirements and 'sharedrepo' not in createopts:
3040 3040 hgvfs.mkdir(b'store')
3041 3041
3042 3042 # We create an invalid changelog outside the store so very old
3043 3043 # Mercurial versions (which didn't know about the requirements
3044 3044 # file) encounter an error on reading the changelog. This
3045 3045 # effectively locks out old clients and prevents them from
3046 3046 # mucking with a repo in an unknown format.
3047 3047 #
3048 3048 # The revlog header has version 2, which won't be recognized by
3049 3049 # such old clients.
3050 3050 hgvfs.append(b'00changelog.i',
3051 3051 b'\0\0\0\2 dummy changelog to prevent using the old repo '
3052 3052 b'layout')
3053 3053
3054 3054 scmutil.writerequires(hgvfs, requirements)
3055 3055
3056 3056 # Write out file telling readers where to find the shared store.
3057 3057 if 'sharedrepo' in createopts:
3058 3058 hgvfs.write(b'sharedpath', sharedpath)
3059 3059
3060 3060 if createopts.get('shareditems'):
3061 3061 shared = b'\n'.join(sorted(createopts['shareditems'])) + b'\n'
3062 3062 hgvfs.write(b'shared', shared)
3063 3063
3064 3064 def poisonrepository(repo):
3065 3065 """Poison a repository instance so it can no longer be used."""
3066 3066 # Perform any cleanup on the instance.
3067 3067 repo.close()
3068 3068
3069 3069 # Our strategy is to replace the type of the object with one that
3070 3070 # has all attribute lookups result in error.
3071 3071 #
3072 3072 # But we have to allow the close() method because some constructors
3073 3073 # of repos call close() on repo references.
3074 3074 class poisonedrepository(object):
3075 3075 def __getattribute__(self, item):
3076 3076 if item == r'close':
3077 3077 return object.__getattribute__(self, item)
3078 3078
3079 3079 raise error.ProgrammingError('repo instances should not be used '
3080 3080 'after unshare')
3081 3081
3082 3082 def close(self):
3083 3083 pass
3084 3084
3085 3085 # We may have a repoview, which intercepts __setattr__. So be sure
3086 3086 # we operate at the lowest level possible.
3087 3087 object.__setattr__(repo, r'__class__', poisonedrepository)
@@ -1,928 +1,932 b''
1 1 Setting up test
2 2
3 3 $ hg init test
4 4 $ cd test
5 5 $ echo 0 > afile
6 6 $ hg add afile
7 7 $ hg commit -m "0.0"
8 8 $ echo 1 >> afile
9 9 $ hg commit -m "0.1"
10 10 $ echo 2 >> afile
11 11 $ hg commit -m "0.2"
12 12 $ echo 3 >> afile
13 13 $ hg commit -m "0.3"
14 14 $ hg update -C 0
15 15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 16 $ echo 1 >> afile
17 17 $ hg commit -m "1.1"
18 18 created new head
19 19 $ echo 2 >> afile
20 20 $ hg commit -m "1.2"
21 21 $ echo "a line" > fred
22 22 $ echo 3 >> afile
23 23 $ hg add fred
24 24 $ hg commit -m "1.3"
25 25 $ hg mv afile adifferentfile
26 26 $ hg commit -m "1.3m"
27 27 $ hg update -C 3
28 28 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
29 29 $ hg mv afile anotherfile
30 30 $ hg commit -m "0.3m"
31 31 $ hg verify
32 32 checking changesets
33 33 checking manifests
34 34 crosschecking files in changesets and manifests
35 35 checking files
36 36 checked 9 changesets with 7 changes to 4 files
37 37 $ cd ..
38 38 $ hg init empty
39 39
40 40 Bundle and phase
41 41
42 42 $ hg -R test phase --force --secret 0
43 43 $ hg -R test bundle phase.hg empty
44 44 searching for changes
45 45 no changes found (ignored 9 secret changesets)
46 46 [1]
47 47 $ hg -R test phase --draft -r 'head()'
48 48
49 49 Bundle --all
50 50
51 51 $ hg -R test bundle --all all.hg
52 52 9 changesets found
53 53
54 54 Bundle test to full.hg
55 55
56 56 $ hg -R test bundle full.hg empty
57 57 searching for changes
58 58 9 changesets found
59 59
60 60 Unbundle full.hg in test
61 61
62 62 $ hg -R test unbundle full.hg
63 63 adding changesets
64 64 adding manifests
65 65 adding file changes
66 66 added 0 changesets with 0 changes to 4 files
67 67 (run 'hg update' to get a working copy)
68 68
69 69 Verify empty
70 70
71 71 $ hg -R empty heads
72 72 [1]
73 73 $ hg -R empty verify
74 74 checking changesets
75 75 checking manifests
76 76 crosschecking files in changesets and manifests
77 77 checking files
78 78 checked 0 changesets with 0 changes to 0 files
79 79
80 80 #if repobundlerepo
81 81
82 82 Pull full.hg into test (using --cwd)
83 83
84 84 $ hg --cwd test pull ../full.hg
85 85 pulling from ../full.hg
86 86 searching for changes
87 87 no changes found
88 88
89 89 Verify that there are no leaked temporary files after pull (issue2797)
90 90
91 91 $ ls test/.hg | grep .hg10un
92 92 [1]
93 93
94 94 Pull full.hg into empty (using --cwd)
95 95
96 96 $ hg --cwd empty pull ../full.hg
97 97 pulling from ../full.hg
98 98 requesting all changes
99 99 adding changesets
100 100 adding manifests
101 101 adding file changes
102 102 added 9 changesets with 7 changes to 4 files (+1 heads)
103 103 new changesets f9ee2f85a263:aa35859c02ea (9 drafts)
104 104 (run 'hg heads' to see heads, 'hg merge' to merge)
105 105
106 106 Rollback empty
107 107
108 108 $ hg -R empty rollback
109 109 repository tip rolled back to revision -1 (undo pull)
110 110
111 111 Pull full.hg into empty again (using --cwd)
112 112
113 113 $ hg --cwd empty pull ../full.hg
114 114 pulling from ../full.hg
115 115 requesting all changes
116 116 adding changesets
117 117 adding manifests
118 118 adding file changes
119 119 added 9 changesets with 7 changes to 4 files (+1 heads)
120 120 new changesets f9ee2f85a263:aa35859c02ea (9 drafts)
121 121 (run 'hg heads' to see heads, 'hg merge' to merge)
122 122
123 123 Pull full.hg into test (using -R)
124 124
125 125 $ hg -R test pull full.hg
126 126 pulling from full.hg
127 127 searching for changes
128 128 no changes found
129 129
130 130 Pull full.hg into empty (using -R)
131 131
132 132 $ hg -R empty pull full.hg
133 133 pulling from full.hg
134 134 searching for changes
135 135 no changes found
136 136
137 137 Rollback empty
138 138
139 139 $ hg -R empty rollback
140 140 repository tip rolled back to revision -1 (undo pull)
141 141
142 142 Pull full.hg into empty again (using -R)
143 143
144 144 $ hg -R empty pull full.hg
145 145 pulling from full.hg
146 146 requesting all changes
147 147 adding changesets
148 148 adding manifests
149 149 adding file changes
150 150 added 9 changesets with 7 changes to 4 files (+1 heads)
151 151 new changesets f9ee2f85a263:aa35859c02ea (9 drafts)
152 152 (run 'hg heads' to see heads, 'hg merge' to merge)
153 153
154 154 Log -R full.hg in fresh empty
155 155
156 156 $ rm -r empty
157 157 $ hg init empty
158 158 $ cd empty
159 159 $ hg -R bundle://../full.hg log
160 160 changeset: 8:aa35859c02ea
161 161 tag: tip
162 162 parent: 3:eebf5a27f8ca
163 163 user: test
164 164 date: Thu Jan 01 00:00:00 1970 +0000
165 165 summary: 0.3m
166 166
167 167 changeset: 7:a6a34bfa0076
168 168 user: test
169 169 date: Thu Jan 01 00:00:00 1970 +0000
170 170 summary: 1.3m
171 171
172 172 changeset: 6:7373c1169842
173 173 user: test
174 174 date: Thu Jan 01 00:00:00 1970 +0000
175 175 summary: 1.3
176 176
177 177 changeset: 5:1bb50a9436a7
178 178 user: test
179 179 date: Thu Jan 01 00:00:00 1970 +0000
180 180 summary: 1.2
181 181
182 182 changeset: 4:095197eb4973
183 183 parent: 0:f9ee2f85a263
184 184 user: test
185 185 date: Thu Jan 01 00:00:00 1970 +0000
186 186 summary: 1.1
187 187
188 188 changeset: 3:eebf5a27f8ca
189 189 user: test
190 190 date: Thu Jan 01 00:00:00 1970 +0000
191 191 summary: 0.3
192 192
193 193 changeset: 2:e38ba6f5b7e0
194 194 user: test
195 195 date: Thu Jan 01 00:00:00 1970 +0000
196 196 summary: 0.2
197 197
198 198 changeset: 1:34c2bf6b0626
199 199 user: test
200 200 date: Thu Jan 01 00:00:00 1970 +0000
201 201 summary: 0.1
202 202
203 203 changeset: 0:f9ee2f85a263
204 204 user: test
205 205 date: Thu Jan 01 00:00:00 1970 +0000
206 206 summary: 0.0
207 207
208 208 Make sure bundlerepo doesn't leak tempfiles (issue2491)
209 209
210 210 $ ls .hg
211 211 00changelog.i
212 212 cache
213 213 requires
214 214 store
215 215 wcache
216 216
217 217 Pull ../full.hg into empty (with hook)
218 218
219 219 $ cat >> .hg/hgrc <<EOF
220 220 > [hooks]
221 221 > changegroup = sh -c "printenv.py --line changegroup"
222 222 > EOF
223 223
224 224 doesn't work (yet ?)
225 225 NOTE: msys is mangling the URL below
226 226
227 227 hg -R bundle://../full.hg verify
228 228
229 229 $ hg pull bundle://../full.hg
230 230 pulling from bundle:../full.hg
231 231 requesting all changes
232 232 adding changesets
233 233 adding manifests
234 234 adding file changes
235 235 added 9 changesets with 7 changes to 4 files (+1 heads)
236 236 new changesets f9ee2f85a263:aa35859c02ea (9 drafts)
237 237 changegroup hook: HG_HOOKNAME=changegroup
238 238 HG_HOOKTYPE=changegroup
239 239 HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735
240 240 HG_NODE_LAST=aa35859c02ea8bd48da5da68cd2740ac71afcbaf
241 241 HG_SOURCE=pull
242 242 HG_TXNID=TXN:$ID$
243 HG_TXNNAME=pull
244 bundle:../full.hg
243 245 HG_URL=bundle:../full.hg (no-msys !)
244 246 HG_URL=bundle;../full.hg (msys !)
245 247
246 248 (run 'hg heads' to see heads, 'hg merge' to merge)
247 249
248 250 Rollback empty
249 251
250 252 $ hg rollback
251 253 repository tip rolled back to revision -1 (undo pull)
252 254 $ cd ..
253 255
254 256 Log -R bundle:empty+full.hg
255 257
256 258 $ hg -R bundle:empty+full.hg log --template="{rev} "; echo ""
257 259 8 7 6 5 4 3 2 1 0
258 260
259 261 Pull full.hg into empty again (using -R; with hook)
260 262
261 263 $ hg -R empty pull full.hg
262 264 pulling from full.hg
263 265 requesting all changes
264 266 adding changesets
265 267 adding manifests
266 268 adding file changes
267 269 added 9 changesets with 7 changes to 4 files (+1 heads)
268 270 new changesets f9ee2f85a263:aa35859c02ea (9 drafts)
269 271 changegroup hook: HG_HOOKNAME=changegroup
270 272 HG_HOOKTYPE=changegroup
271 273 HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735
272 274 HG_NODE_LAST=aa35859c02ea8bd48da5da68cd2740ac71afcbaf
273 275 HG_SOURCE=pull
274 276 HG_TXNID=TXN:$ID$
277 HG_TXNNAME=pull
278 bundle:empty+full.hg
275 279 HG_URL=bundle:empty+full.hg
276 280
277 281 (run 'hg heads' to see heads, 'hg merge' to merge)
278 282
279 283 #endif
280 284
281 285 Cannot produce streaming clone bundles with "hg bundle"
282 286
283 287 $ hg -R test bundle -t packed1 packed.hg
284 288 abort: packed bundles cannot be produced by "hg bundle"
285 289 (use 'hg debugcreatestreamclonebundle')
286 290 [255]
287 291
288 292 packed1 is produced properly
289 293
290 294 #if reporevlogstore
291 295
292 296 $ hg -R test debugcreatestreamclonebundle packed.hg
293 297 writing 2664 bytes for 6 files
294 298 bundle requirements: generaldelta, revlogv1, sparserevlog
295 299
296 300 $ f -B 64 --size --sha1 --hexdump packed.hg
297 301 packed.hg: size=2840, sha1=12bf3eee3eb8a04c503ce2d29b48f0135c7edff5
298 302 0000: 48 47 53 31 55 4e 00 00 00 00 00 00 00 06 00 00 |HGS1UN..........|
299 303 0010: 00 00 00 00 0a 68 00 23 67 65 6e 65 72 61 6c 64 |.....h.#generald|
300 304 0020: 65 6c 74 61 2c 72 65 76 6c 6f 67 76 31 2c 73 70 |elta,revlogv1,sp|
301 305 0030: 61 72 73 65 72 65 76 6c 6f 67 00 64 61 74 61 2f |arserevlog.data/|
302 306
303 307 $ hg debugbundle --spec packed.hg
304 308 none-packed1;requirements%3Dgeneraldelta%2Crevlogv1%2Csparserevlog
305 309
306 310 generaldelta requirement is not listed in stream clone bundles unless used
307 311
308 312 $ hg --config format.usegeneraldelta=false init testnongd
309 313 $ cd testnongd
310 314 $ touch foo
311 315 $ hg -q commit -A -m initial
312 316 $ cd ..
313 317 $ hg -R testnongd debugcreatestreamclonebundle packednongd.hg
314 318 writing 301 bytes for 3 files
315 319 bundle requirements: revlogv1
316 320
317 321 $ f -B 64 --size --sha1 --hexdump packednongd.hg
318 322 packednongd.hg: size=383, sha1=1d9c230238edd5d38907100b729ba72b1831fe6f
319 323 0000: 48 47 53 31 55 4e 00 00 00 00 00 00 00 03 00 00 |HGS1UN..........|
320 324 0010: 00 00 00 00 01 2d 00 09 72 65 76 6c 6f 67 76 31 |.....-..revlogv1|
321 325 0020: 00 64 61 74 61 2f 66 6f 6f 2e 69 00 36 34 0a 00 |.data/foo.i.64..|
322 326 0030: 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
323 327
324 328 $ hg debugbundle --spec packednongd.hg
325 329 none-packed1;requirements%3Drevlogv1
326 330
327 331 Warning emitted when packed bundles contain secret changesets
328 332
329 333 $ hg init testsecret
330 334 $ cd testsecret
331 335 $ touch foo
332 336 $ hg -q commit -A -m initial
333 337 $ hg phase --force --secret -r .
334 338 $ cd ..
335 339
336 340 $ hg -R testsecret debugcreatestreamclonebundle packedsecret.hg
337 341 (warning: stream clone bundle will contain secret revisions)
338 342 writing 301 bytes for 3 files
339 343 bundle requirements: generaldelta, revlogv1, sparserevlog
340 344
341 345 Unpacking packed1 bundles with "hg unbundle" isn't allowed
342 346
343 347 $ hg init packed
344 348 $ hg -R packed unbundle packed.hg
345 349 abort: packed bundles cannot be applied with "hg unbundle"
346 350 (use "hg debugapplystreamclonebundle")
347 351 [255]
348 352
349 353 packed1 can be consumed from debug command
350 354
351 355 (this also confirms that streamclone-ed changes are visible via
352 356 @filecache properties to in-process procedures before closing
353 357 transaction)
354 358
355 359 $ cat > $TESTTMP/showtip.py <<EOF
356 360 > from __future__ import absolute_import
357 361 >
358 362 > def showtip(ui, repo, hooktype, **kwargs):
359 363 > ui.warn(b'%s: %s\n' % (hooktype, repo[b'tip'].hex()[:12]))
360 364 >
361 365 > def reposetup(ui, repo):
362 366 > # this confirms (and ensures) that (empty) 00changelog.i
363 367 > # before streamclone is already cached as repo.changelog
364 368 > ui.setconfig(b'hooks', b'pretxnopen.showtip', showtip)
365 369 >
366 370 > # this confirms that streamclone-ed changes are visible to
367 371 > # in-process procedures before closing transaction
368 372 > ui.setconfig(b'hooks', b'pretxnclose.showtip', showtip)
369 373 >
370 374 > # this confirms that streamclone-ed changes are still visible
371 375 > # after closing transaction
372 376 > ui.setconfig(b'hooks', b'txnclose.showtip', showtip)
373 377 > EOF
374 378 $ cat >> $HGRCPATH <<EOF
375 379 > [extensions]
376 380 > showtip = $TESTTMP/showtip.py
377 381 > EOF
378 382
379 383 $ hg -R packed debugapplystreamclonebundle packed.hg
380 384 6 files to transfer, 2.60 KB of data
381 385 pretxnopen: 000000000000
382 386 pretxnclose: aa35859c02ea
383 387 transferred 2.60 KB in *.* seconds (* */sec) (glob)
384 388 txnclose: aa35859c02ea
385 389
386 390 (for safety, confirm visibility of streamclone-ed changes by another
387 391 process, too)
388 392
389 393 $ hg -R packed tip -T "{node|short}\n"
390 394 aa35859c02ea
391 395
392 396 $ cat >> $HGRCPATH <<EOF
393 397 > [extensions]
394 398 > showtip = !
395 399 > EOF
396 400
397 401 Does not work on non-empty repo
398 402
399 403 $ hg -R packed debugapplystreamclonebundle packed.hg
400 404 abort: cannot apply stream clone bundle on non-empty repo
401 405 [255]
402 406
403 407 #endif
404 408
405 409 Create partial clones
406 410
407 411 $ rm -r empty
408 412 $ hg init empty
409 413 $ hg clone -r 3 test partial
410 414 adding changesets
411 415 adding manifests
412 416 adding file changes
413 417 added 4 changesets with 4 changes to 1 files
414 418 new changesets f9ee2f85a263:eebf5a27f8ca
415 419 updating to branch default
416 420 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
417 421 $ hg clone partial partial2
418 422 updating to branch default
419 423 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
420 424 $ cd partial
421 425
422 426 #if repobundlerepo
423 427
424 428 Log -R full.hg in partial
425 429
426 430 $ hg -R bundle://../full.hg log -T phases
427 431 changeset: 8:aa35859c02ea
428 432 tag: tip
429 433 phase: draft
430 434 parent: 3:eebf5a27f8ca
431 435 user: test
432 436 date: Thu Jan 01 00:00:00 1970 +0000
433 437 summary: 0.3m
434 438
435 439 changeset: 7:a6a34bfa0076
436 440 phase: draft
437 441 user: test
438 442 date: Thu Jan 01 00:00:00 1970 +0000
439 443 summary: 1.3m
440 444
441 445 changeset: 6:7373c1169842
442 446 phase: draft
443 447 user: test
444 448 date: Thu Jan 01 00:00:00 1970 +0000
445 449 summary: 1.3
446 450
447 451 changeset: 5:1bb50a9436a7
448 452 phase: draft
449 453 user: test
450 454 date: Thu Jan 01 00:00:00 1970 +0000
451 455 summary: 1.2
452 456
453 457 changeset: 4:095197eb4973
454 458 phase: draft
455 459 parent: 0:f9ee2f85a263
456 460 user: test
457 461 date: Thu Jan 01 00:00:00 1970 +0000
458 462 summary: 1.1
459 463
460 464 changeset: 3:eebf5a27f8ca
461 465 phase: public
462 466 user: test
463 467 date: Thu Jan 01 00:00:00 1970 +0000
464 468 summary: 0.3
465 469
466 470 changeset: 2:e38ba6f5b7e0
467 471 phase: public
468 472 user: test
469 473 date: Thu Jan 01 00:00:00 1970 +0000
470 474 summary: 0.2
471 475
472 476 changeset: 1:34c2bf6b0626
473 477 phase: public
474 478 user: test
475 479 date: Thu Jan 01 00:00:00 1970 +0000
476 480 summary: 0.1
477 481
478 482 changeset: 0:f9ee2f85a263
479 483 phase: public
480 484 user: test
481 485 date: Thu Jan 01 00:00:00 1970 +0000
482 486 summary: 0.0
483 487
484 488
485 489 Incoming full.hg in partial
486 490
487 491 $ hg incoming bundle://../full.hg
488 492 comparing with bundle:../full.hg
489 493 searching for changes
490 494 changeset: 4:095197eb4973
491 495 parent: 0:f9ee2f85a263
492 496 user: test
493 497 date: Thu Jan 01 00:00:00 1970 +0000
494 498 summary: 1.1
495 499
496 500 changeset: 5:1bb50a9436a7
497 501 user: test
498 502 date: Thu Jan 01 00:00:00 1970 +0000
499 503 summary: 1.2
500 504
501 505 changeset: 6:7373c1169842
502 506 user: test
503 507 date: Thu Jan 01 00:00:00 1970 +0000
504 508 summary: 1.3
505 509
506 510 changeset: 7:a6a34bfa0076
507 511 user: test
508 512 date: Thu Jan 01 00:00:00 1970 +0000
509 513 summary: 1.3m
510 514
511 515 changeset: 8:aa35859c02ea
512 516 tag: tip
513 517 parent: 3:eebf5a27f8ca
514 518 user: test
515 519 date: Thu Jan 01 00:00:00 1970 +0000
516 520 summary: 0.3m
517 521
518 522
519 523 Outgoing -R full.hg vs partial2 in partial
520 524
521 525 $ hg -R bundle://../full.hg outgoing ../partial2
522 526 comparing with ../partial2
523 527 searching for changes
524 528 changeset: 4:095197eb4973
525 529 parent: 0:f9ee2f85a263
526 530 user: test
527 531 date: Thu Jan 01 00:00:00 1970 +0000
528 532 summary: 1.1
529 533
530 534 changeset: 5:1bb50a9436a7
531 535 user: test
532 536 date: Thu Jan 01 00:00:00 1970 +0000
533 537 summary: 1.2
534 538
535 539 changeset: 6:7373c1169842
536 540 user: test
537 541 date: Thu Jan 01 00:00:00 1970 +0000
538 542 summary: 1.3
539 543
540 544 changeset: 7:a6a34bfa0076
541 545 user: test
542 546 date: Thu Jan 01 00:00:00 1970 +0000
543 547 summary: 1.3m
544 548
545 549 changeset: 8:aa35859c02ea
546 550 tag: tip
547 551 parent: 3:eebf5a27f8ca
548 552 user: test
549 553 date: Thu Jan 01 00:00:00 1970 +0000
550 554 summary: 0.3m
551 555
552 556
553 557 Outgoing -R does-not-exist.hg vs partial2 in partial
554 558
555 559 $ hg -R bundle://../does-not-exist.hg outgoing ../partial2
556 560 abort: *../does-not-exist.hg* (glob)
557 561 [255]
558 562
559 563 #endif
560 564
561 565 $ cd ..
562 566
563 567 hide outer repo
564 568 $ hg init
565 569
566 570 Direct clone from bundle (all-history)
567 571
568 572 #if repobundlerepo
569 573
570 574 $ hg clone full.hg full-clone
571 575 requesting all changes
572 576 adding changesets
573 577 adding manifests
574 578 adding file changes
575 579 added 9 changesets with 7 changes to 4 files (+1 heads)
576 580 new changesets f9ee2f85a263:aa35859c02ea (9 drafts)
577 581 updating to branch default
578 582 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
579 583 $ hg -R full-clone heads
580 584 changeset: 8:aa35859c02ea
581 585 tag: tip
582 586 parent: 3:eebf5a27f8ca
583 587 user: test
584 588 date: Thu Jan 01 00:00:00 1970 +0000
585 589 summary: 0.3m
586 590
587 591 changeset: 7:a6a34bfa0076
588 592 user: test
589 593 date: Thu Jan 01 00:00:00 1970 +0000
590 594 summary: 1.3m
591 595
592 596 $ rm -r full-clone
593 597
594 598 When cloning from a non-copiable repository into '', do not
595 599 recurse infinitely (issue2528)
596 600
597 601 $ hg clone full.hg ''
598 602 abort: empty destination path is not valid
599 603 [255]
600 604
601 605 test for https://bz.mercurial-scm.org/216
602 606
603 607 Unbundle incremental bundles into fresh empty in one go
604 608
605 609 $ rm -r empty
606 610 $ hg init empty
607 611 $ hg -R test bundle --base null -r 0 ../0.hg
608 612 1 changesets found
609 613 $ hg -R test bundle --base 0 -r 1 ../1.hg
610 614 1 changesets found
611 615 $ hg -R empty unbundle -u ../0.hg ../1.hg
612 616 adding changesets
613 617 adding manifests
614 618 adding file changes
615 619 added 1 changesets with 1 changes to 1 files
616 620 new changesets f9ee2f85a263 (1 drafts)
617 621 adding changesets
618 622 adding manifests
619 623 adding file changes
620 624 added 1 changesets with 1 changes to 1 files
621 625 new changesets 34c2bf6b0626 (1 drafts)
622 626 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
623 627
624 628 View full contents of the bundle
625 629 $ hg -R test bundle --base null -r 3 ../partial.hg
626 630 4 changesets found
627 631 $ cd test
628 632 $ hg -R ../../partial.hg log -r "bundle()"
629 633 changeset: 0:f9ee2f85a263
630 634 user: test
631 635 date: Thu Jan 01 00:00:00 1970 +0000
632 636 summary: 0.0
633 637
634 638 changeset: 1:34c2bf6b0626
635 639 user: test
636 640 date: Thu Jan 01 00:00:00 1970 +0000
637 641 summary: 0.1
638 642
639 643 changeset: 2:e38ba6f5b7e0
640 644 user: test
641 645 date: Thu Jan 01 00:00:00 1970 +0000
642 646 summary: 0.2
643 647
644 648 changeset: 3:eebf5a27f8ca
645 649 user: test
646 650 date: Thu Jan 01 00:00:00 1970 +0000
647 651 summary: 0.3
648 652
649 653 $ cd ..
650 654
651 655 #endif
652 656
653 657 test for 540d1059c802
654 658
655 659 $ hg init orig
656 660 $ cd orig
657 661 $ echo foo > foo
658 662 $ hg add foo
659 663 $ hg ci -m 'add foo'
660 664
661 665 $ hg clone . ../copy
662 666 updating to branch default
663 667 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
664 668 $ hg tag foo
665 669
666 670 $ cd ../copy
667 671 $ echo >> foo
668 672 $ hg ci -m 'change foo'
669 673 $ hg bundle ../bundle.hg ../orig
670 674 searching for changes
671 675 1 changesets found
672 676
673 677 $ cd ..
674 678
675 679 #if repobundlerepo
676 680 $ cd orig
677 681 $ hg incoming ../bundle.hg
678 682 comparing with ../bundle.hg
679 683 searching for changes
680 684 changeset: 2:ed1b79f46b9a
681 685 tag: tip
682 686 parent: 0:bbd179dfa0a7
683 687 user: test
684 688 date: Thu Jan 01 00:00:00 1970 +0000
685 689 summary: change foo
686 690
687 691 $ cd ..
688 692
689 693 test bundle with # in the filename (issue2154):
690 694
691 695 $ cp bundle.hg 'test#bundle.hg'
692 696 $ cd orig
693 697 $ hg incoming '../test#bundle.hg'
694 698 comparing with ../test
695 699 abort: unknown revision 'bundle.hg'!
696 700 [255]
697 701
698 702 note that percent encoding is not handled:
699 703
700 704 $ hg incoming ../test%23bundle.hg
701 705 abort: repository ../test%23bundle.hg not found!
702 706 [255]
703 707 $ cd ..
704 708
705 709 #endif
706 710
707 711 test to bundle revisions on the newly created branch (issue3828):
708 712
709 713 $ hg -q clone -U test test-clone
710 714 $ cd test
711 715
712 716 $ hg -q branch foo
713 717 $ hg commit -m "create foo branch"
714 718 $ hg -q outgoing ../test-clone
715 719 9:b4f5acb1ee27
716 720 $ hg -q bundle --branch foo foo.hg ../test-clone
717 721 #if repobundlerepo
718 722 $ hg -R foo.hg -q log -r "bundle()"
719 723 9:b4f5acb1ee27
720 724 #endif
721 725
722 726 $ cd ..
723 727
724 728 test for https://bz.mercurial-scm.org/1144
725 729
726 730 test that verify bundle does not traceback
727 731
728 732 partial history bundle, fails w/ unknown parent
729 733
730 734 $ hg -R bundle.hg verify
731 735 abort: 00changelog.i@bbd179dfa0a7: unknown parent!
732 736 [255]
733 737
734 738 full history bundle, refuses to verify non-local repo
735 739
736 740 #if repobundlerepo
737 741 $ hg -R all.hg verify
738 742 abort: cannot verify bundle or remote repos
739 743 [255]
740 744 #endif
741 745
742 746 but, regular verify must continue to work
743 747
744 748 $ hg -R orig verify
745 749 checking changesets
746 750 checking manifests
747 751 crosschecking files in changesets and manifests
748 752 checking files
749 753 checked 2 changesets with 2 changes to 2 files
750 754
751 755 #if repobundlerepo
752 756 diff against bundle
753 757
754 758 $ hg init b
755 759 $ cd b
756 760 $ hg -R ../all.hg diff -r tip
757 761 diff -r aa35859c02ea anotherfile
758 762 --- a/anotherfile Thu Jan 01 00:00:00 1970 +0000
759 763 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
760 764 @@ -1,4 +0,0 @@
761 765 -0
762 766 -1
763 767 -2
764 768 -3
765 769 $ cd ..
766 770 #endif
767 771
768 772 bundle single branch
769 773
770 774 $ hg init branchy
771 775 $ cd branchy
772 776 $ echo a >a
773 777 $ echo x >x
774 778 $ hg ci -Ama
775 779 adding a
776 780 adding x
777 781 $ echo c >c
778 782 $ echo xx >x
779 783 $ hg ci -Amc
780 784 adding c
781 785 $ echo c1 >c1
782 786 $ hg ci -Amc1
783 787 adding c1
784 788 $ hg up 0
785 789 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
786 790 $ echo b >b
787 791 $ hg ci -Amb
788 792 adding b
789 793 created new head
790 794 $ echo b1 >b1
791 795 $ echo xx >x
792 796 $ hg ci -Amb1
793 797 adding b1
794 798 $ hg clone -q -r2 . part
795 799
796 800 == bundling via incoming
797 801
798 802 $ hg in -R part --bundle incoming.hg --template "{node}\n" .
799 803 comparing with .
800 804 searching for changes
801 805 1a38c1b849e8b70c756d2d80b0b9a3ac0b7ea11a
802 806 057f4db07f61970e1c11e83be79e9d08adc4dc31
803 807
804 808 == bundling
805 809
806 810 $ hg bundle bundle.hg part --debug --config progress.debug=true
807 811 query 1; heads
808 812 searching for changes
809 813 all remote heads known locally
810 814 2 changesets found
811 815 list of changesets:
812 816 1a38c1b849e8b70c756d2d80b0b9a3ac0b7ea11a
813 817 057f4db07f61970e1c11e83be79e9d08adc4dc31
814 818 bundle2-output-bundle: "HG20", (1 params) 2 parts total
815 819 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
816 820 changesets: 1/2 chunks (50.00%)
817 821 changesets: 2/2 chunks (100.00%)
818 822 manifests: 1/2 chunks (50.00%)
819 823 manifests: 2/2 chunks (100.00%)
820 824 files: b 1/3 files (33.33%)
821 825 files: b1 2/3 files (66.67%)
822 826 files: x 3/3 files (100.00%)
823 827 bundle2-output-part: "cache:rev-branch-cache" (advisory) streamed payload
824 828
825 829 #if repobundlerepo
826 830 == Test for issue3441
827 831
828 832 $ hg clone -q -r0 . part2
829 833 $ hg -q -R part2 pull bundle.hg
830 834 $ hg -R part2 verify
831 835 checking changesets
832 836 checking manifests
833 837 crosschecking files in changesets and manifests
834 838 checking files
835 839 checked 3 changesets with 5 changes to 4 files
836 840 #endif
837 841
838 842 == Test bundling no commits
839 843
840 844 $ hg bundle -r 'public()' no-output.hg
841 845 abort: no commits to bundle
842 846 [255]
843 847
844 848 $ cd ..
845 849
846 850 When user merges to the revision existing only in the bundle,
847 851 it should show warning that second parent of the working
848 852 directory does not exist
849 853
850 854 $ hg init update2bundled
851 855 $ cd update2bundled
852 856 $ cat <<EOF >> .hg/hgrc
853 857 > [extensions]
854 858 > strip =
855 859 > EOF
856 860 $ echo "aaa" >> a
857 861 $ hg commit -A -m 0
858 862 adding a
859 863 $ echo "bbb" >> b
860 864 $ hg commit -A -m 1
861 865 adding b
862 866 $ echo "ccc" >> c
863 867 $ hg commit -A -m 2
864 868 adding c
865 869 $ hg update -r 1
866 870 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
867 871 $ echo "ddd" >> d
868 872 $ hg commit -A -m 3
869 873 adding d
870 874 created new head
871 875 $ hg update -r 2
872 876 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
873 877 $ hg log -G
874 878 o changeset: 3:8bd3e1f196af
875 879 | tag: tip
876 880 | parent: 1:a01eca7af26d
877 881 | user: test
878 882 | date: Thu Jan 01 00:00:00 1970 +0000
879 883 | summary: 3
880 884 |
881 885 | @ changeset: 2:4652c276ac4f
882 886 |/ user: test
883 887 | date: Thu Jan 01 00:00:00 1970 +0000
884 888 | summary: 2
885 889 |
886 890 o changeset: 1:a01eca7af26d
887 891 | user: test
888 892 | date: Thu Jan 01 00:00:00 1970 +0000
889 893 | summary: 1
890 894 |
891 895 o changeset: 0:4fe08cd4693e
892 896 user: test
893 897 date: Thu Jan 01 00:00:00 1970 +0000
894 898 summary: 0
895 899
896 900
897 901 #if repobundlerepo
898 902 $ hg bundle --base 1 -r 3 ../update2bundled.hg
899 903 1 changesets found
900 904 $ hg strip -r 3
901 905 saved backup bundle to $TESTTMP/update2bundled/.hg/strip-backup/8bd3e1f196af-017e56d8-backup.hg
902 906 $ hg merge -R ../update2bundled.hg -r 3
903 907 setting parent to node 8bd3e1f196af289b2b121be08031e76d7ae92098 that only exists in the bundle
904 908 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
905 909 (branch merge, don't forget to commit)
906 910
907 911 When user updates to the revision existing only in the bundle,
908 912 it should show warning
909 913
910 914 $ hg update -R ../update2bundled.hg --clean -r 3
911 915 setting parent to node 8bd3e1f196af289b2b121be08031e76d7ae92098 that only exists in the bundle
912 916 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
913 917
914 918 When user updates to the revision existing in the local repository
915 919 the warning shouldn't be emitted
916 920
917 921 $ hg update -R ../update2bundled.hg -r 0
918 922 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
919 923 #endif
920 924
921 925 Test the option that create slim bundle
922 926
923 927 $ hg bundle -a --config devel.bundle.delta=p1 ./slim.hg
924 928 3 changesets found
925 929
926 930 Test the option that create and no-delta's bundle
927 931 $ hg bundle -a --config devel.bundle.delta=full ./full.hg
928 932 3 changesets found
@@ -1,418 +1,460 b''
1 1 Create an extension to test bundle2 with multiple changegroups
2 2
3 3 $ cat > bundle2.py <<EOF
4 4 > """
5 5 > """
6 6 > from mercurial import changegroup, discovery, exchange
7 7 >
8 8 > def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None,
9 9 > b2caps=None, heads=None, common=None,
10 10 > **kwargs):
11 11 > # Create two changegroups given the common changesets and heads for the
12 12 > # changegroup part we are being requested. Use the parent of each head
13 13 > # in 'heads' as intermediate heads for the first changegroup.
14 14 > intermediates = [repo[r].p1().node() for r in heads]
15 15 > outgoing = discovery.outgoing(repo, common, intermediates)
16 16 > cg = changegroup.makechangegroup(repo, outgoing, b'01',
17 17 > source, bundlecaps=bundlecaps)
18 18 > bundler.newpart(b'output', data=b'changegroup1')
19 19 > bundler.newpart(b'changegroup', data=cg.getchunks())
20 20 > outgoing = discovery.outgoing(repo, common + intermediates, heads)
21 21 > cg = changegroup.makechangegroup(repo, outgoing, b'01',
22 22 > source, bundlecaps=bundlecaps)
23 23 > bundler.newpart(b'output', data=b'changegroup2')
24 24 > bundler.newpart(b'changegroup', data=cg.getchunks())
25 25 >
26 26 > def _pull(repo, *args, **kwargs):
27 27 > pullop = _orig_pull(repo, *args, **kwargs)
28 28 > repo.ui.write(b'pullop.cgresult is %d\n' % pullop.cgresult)
29 29 > return pullop
30 30 >
31 31 > _orig_pull = exchange.pull
32 32 > exchange.pull = _pull
33 33 > exchange.getbundle2partsmapping[b'changegroup'] = _getbundlechangegrouppart
34 34 > EOF
35 35
36 36 $ cat >> $HGRCPATH << EOF
37 37 > [ui]
38 38 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
39 39 > EOF
40 40
41 41 Start with a simple repository with a single commit
42 42
43 43 $ hg init repo
44 44 $ cd repo
45 45 $ cat > .hg/hgrc << EOF
46 46 > [extensions]
47 47 > bundle2=$TESTTMP/bundle2.py
48 48 > EOF
49 49
50 50 $ echo A > A
51 51 $ hg commit -A -m A -q
52 52 $ cd ..
53 53
54 54 Clone
55 55
56 56 $ hg clone -q repo clone
57 57
58 58 Add two linear commits
59 59
60 60 $ cd repo
61 61 $ echo B > B
62 62 $ hg commit -A -m B -q
63 63 $ echo C > C
64 64 $ hg commit -A -m C -q
65 65
66 66 $ cd ../clone
67 67 $ cat >> .hg/hgrc <<EOF
68 68 > [hooks]
69 69 > pretxnchangegroup = sh -c "printenv.py --line pretxnchangegroup"
70 70 > changegroup = sh -c "printenv.py --line changegroup"
71 71 > incoming = sh -c "printenv.py --line incoming"
72 72 > EOF
73 73
74 74 Pull the new commits in the clone
75 75
76 76 $ hg pull
77 77 pulling from $TESTTMP/repo
78 78 searching for changes
79 79 remote: changegroup1
80 80 adding changesets
81 81 adding manifests
82 82 adding file changes
83 83 added 1 changesets with 1 changes to 1 files
84 84 pretxnchangegroup hook: HG_HOOKNAME=pretxnchangegroup
85 85 HG_HOOKTYPE=pretxnchangegroup
86 86 HG_NODE=27547f69f25460a52fff66ad004e58da7ad3fb56
87 87 HG_NODE_LAST=27547f69f25460a52fff66ad004e58da7ad3fb56
88 88 HG_PENDING=$TESTTMP/clone
89 89 HG_SOURCE=pull
90 90 HG_TXNID=TXN:$ID$
91 HG_TXNNAME=pull
92 file:/*/$TESTTMP/repo (glob)
91 93 HG_URL=file:$TESTTMP/repo
92 94
93 95 remote: changegroup2
94 96 adding changesets
95 97 adding manifests
96 98 adding file changes
97 99 added 1 changesets with 1 changes to 1 files
98 100 pretxnchangegroup hook: HG_HOOKNAME=pretxnchangegroup
99 101 HG_HOOKTYPE=pretxnchangegroup
100 102 HG_NODE=f838bfaca5c7226600ebcfd84f3c3c13a28d3757
101 103 HG_NODE_LAST=f838bfaca5c7226600ebcfd84f3c3c13a28d3757
102 104 HG_PENDING=$TESTTMP/clone
103 105 HG_PHASES_MOVED=1
104 106 HG_SOURCE=pull
105 107 HG_TXNID=TXN:$ID$
108 HG_TXNNAME=pull
109 file:/*/$TESTTMP/repo (glob)
106 110 HG_URL=file:$TESTTMP/repo
107 111
108 112 new changesets 27547f69f254:f838bfaca5c7
109 113 changegroup hook: HG_HOOKNAME=changegroup
110 114 HG_HOOKTYPE=changegroup
111 115 HG_NODE=27547f69f25460a52fff66ad004e58da7ad3fb56
112 116 HG_NODE_LAST=27547f69f25460a52fff66ad004e58da7ad3fb56
113 117 HG_SOURCE=pull
114 118 HG_TXNID=TXN:$ID$
119 HG_TXNNAME=pull
120 file:/*/$TESTTMP/repo (glob)
115 121 HG_URL=file:$TESTTMP/repo
116 122
117 123 incoming hook: HG_HOOKNAME=incoming
118 124 HG_HOOKTYPE=incoming
119 125 HG_NODE=27547f69f25460a52fff66ad004e58da7ad3fb56
120 126 HG_SOURCE=pull
121 127 HG_TXNID=TXN:$ID$
128 HG_TXNNAME=pull
129 file:/*/$TESTTMP/repo (glob)
122 130 HG_URL=file:$TESTTMP/repo
123 131
124 132 changegroup hook: HG_HOOKNAME=changegroup
125 133 HG_HOOKTYPE=changegroup
126 134 HG_NODE=f838bfaca5c7226600ebcfd84f3c3c13a28d3757
127 135 HG_NODE_LAST=f838bfaca5c7226600ebcfd84f3c3c13a28d3757
128 136 HG_PHASES_MOVED=1
129 137 HG_SOURCE=pull
130 138 HG_TXNID=TXN:$ID$
139 HG_TXNNAME=pull
140 file:/*/$TESTTMP/repo (glob)
131 141 HG_URL=file:$TESTTMP/repo
132 142
133 143 incoming hook: HG_HOOKNAME=incoming
134 144 HG_HOOKTYPE=incoming
135 145 HG_NODE=f838bfaca5c7226600ebcfd84f3c3c13a28d3757
136 146 HG_PHASES_MOVED=1
137 147 HG_SOURCE=pull
138 148 HG_TXNID=TXN:$ID$
149 HG_TXNNAME=pull
150 file:/*/$TESTTMP/repo (glob)
139 151 HG_URL=file:$TESTTMP/repo
140 152
141 153 pullop.cgresult is 1
142 154 (run 'hg update' to get a working copy)
143 155 $ hg update
144 156 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
145 157 $ hg log -G
146 158 @ 2:f838bfaca5c7 public test C
147 159 |
148 160 o 1:27547f69f254 public test B
149 161 |
150 162 o 0:4a2df7238c3b public test A
151 163
152 164 Add more changesets with multiple heads to the original repository
153 165
154 166 $ cd ../repo
155 167 $ echo D > D
156 168 $ hg commit -A -m D -q
157 169 $ hg up -r 1
158 170 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
159 171 $ echo E > E
160 172 $ hg commit -A -m E -q
161 173 $ echo F > F
162 174 $ hg commit -A -m F -q
163 175 $ hg up -r 1
164 176 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
165 177 $ echo G > G
166 178 $ hg commit -A -m G -q
167 179 $ hg up -r 3
168 180 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
169 181 $ echo H > H
170 182 $ hg commit -A -m H -q
171 183 $ hg log -G
172 184 @ 7:5cd59d311f65 draft test H
173 185 |
174 186 | o 6:1d14c3ce6ac0 draft test G
175 187 | |
176 188 | | o 5:7f219660301f draft test F
177 189 | | |
178 190 | | o 4:8a5212ebc852 draft test E
179 191 | |/
180 192 o | 3:b3325c91a4d9 draft test D
181 193 | |
182 194 o | 2:f838bfaca5c7 draft test C
183 195 |/
184 196 o 1:27547f69f254 draft test B
185 197 |
186 198 o 0:4a2df7238c3b draft test A
187 199
188 200 New heads are reported during transfer and properly accounted for in
189 201 pullop.cgresult
190 202
191 203 $ cd ../clone
192 204 $ hg pull
193 205 pulling from $TESTTMP/repo
194 206 searching for changes
195 207 remote: changegroup1
196 208 adding changesets
197 209 adding manifests
198 210 adding file changes
199 211 added 2 changesets with 2 changes to 2 files (+1 heads)
200 212 pretxnchangegroup hook: HG_HOOKNAME=pretxnchangegroup
201 213 HG_HOOKTYPE=pretxnchangegroup
202 214 HG_NODE=b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
203 215 HG_NODE_LAST=8a5212ebc8527f9fb821601504794e3eb11a1ed3
204 216 HG_PENDING=$TESTTMP/clone
205 217 HG_SOURCE=pull
206 218 HG_TXNID=TXN:$ID$
219 HG_TXNNAME=pull
220 file:/*/$TESTTMP/repo (glob)
207 221 HG_URL=file:$TESTTMP/repo
208 222
209 223 remote: changegroup2
210 224 adding changesets
211 225 adding manifests
212 226 adding file changes
213 227 added 3 changesets with 3 changes to 3 files (+1 heads)
214 228 pretxnchangegroup hook: HG_HOOKNAME=pretxnchangegroup
215 229 HG_HOOKTYPE=pretxnchangegroup
216 230 HG_NODE=7f219660301fe4c8a116f714df5e769695cc2b46
217 231 HG_NODE_LAST=5cd59d311f6508b8e0ed28a266756c859419c9f1
218 232 HG_PENDING=$TESTTMP/clone
219 233 HG_PHASES_MOVED=1
220 234 HG_SOURCE=pull
221 235 HG_TXNID=TXN:$ID$
236 HG_TXNNAME=pull
237 file:/*/$TESTTMP/repo (glob)
222 238 HG_URL=file:$TESTTMP/repo
223 239
224 240 new changesets b3325c91a4d9:5cd59d311f65
225 241 changegroup hook: HG_HOOKNAME=changegroup
226 242 HG_HOOKTYPE=changegroup
227 243 HG_NODE=b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
228 244 HG_NODE_LAST=8a5212ebc8527f9fb821601504794e3eb11a1ed3
229 245 HG_SOURCE=pull
230 246 HG_TXNID=TXN:$ID$
247 HG_TXNNAME=pull
248 file:/*/$TESTTMP/repo (glob)
231 249 HG_URL=file:$TESTTMP/repo
232 250
233 251 incoming hook: HG_HOOKNAME=incoming
234 252 HG_HOOKTYPE=incoming
235 253 HG_NODE=b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
236 254 HG_SOURCE=pull
237 255 HG_TXNID=TXN:$ID$
256 HG_TXNNAME=pull
257 file:/*/$TESTTMP/repo (glob)
238 258 HG_URL=file:$TESTTMP/repo
239 259
240 260 incoming hook: HG_HOOKNAME=incoming
241 261 HG_HOOKTYPE=incoming
242 262 HG_NODE=8a5212ebc8527f9fb821601504794e3eb11a1ed3
243 263 HG_SOURCE=pull
244 264 HG_TXNID=TXN:$ID$
265 HG_TXNNAME=pull
266 file:/*/$TESTTMP/repo (glob)
245 267 HG_URL=file:$TESTTMP/repo
246 268
247 269 changegroup hook: HG_HOOKNAME=changegroup
248 270 HG_HOOKTYPE=changegroup
249 271 HG_NODE=7f219660301fe4c8a116f714df5e769695cc2b46
250 272 HG_NODE_LAST=5cd59d311f6508b8e0ed28a266756c859419c9f1
251 273 HG_PHASES_MOVED=1
252 274 HG_SOURCE=pull
253 275 HG_TXNID=TXN:$ID$
276 HG_TXNNAME=pull
277 file:/*/$TESTTMP/repo (glob)
254 278 HG_URL=file:$TESTTMP/repo
255 279
256 280 incoming hook: HG_HOOKNAME=incoming
257 281 HG_HOOKTYPE=incoming
258 282 HG_NODE=7f219660301fe4c8a116f714df5e769695cc2b46
259 283 HG_PHASES_MOVED=1
260 284 HG_SOURCE=pull
261 285 HG_TXNID=TXN:$ID$
286 HG_TXNNAME=pull
287 file:/*/$TESTTMP/repo (glob)
262 288 HG_URL=file:$TESTTMP/repo
263 289
264 290 incoming hook: HG_HOOKNAME=incoming
265 291 HG_HOOKTYPE=incoming
266 292 HG_NODE=1d14c3ce6ac0582d2809220d33e8cd7a696e0156
267 293 HG_PHASES_MOVED=1
268 294 HG_SOURCE=pull
269 295 HG_TXNID=TXN:$ID$
296 HG_TXNNAME=pull
297 file:/*/$TESTTMP/repo (glob)
270 298 HG_URL=file:$TESTTMP/repo
271 299
272 300 incoming hook: HG_HOOKNAME=incoming
273 301 HG_HOOKTYPE=incoming
274 302 HG_NODE=5cd59d311f6508b8e0ed28a266756c859419c9f1
275 303 HG_PHASES_MOVED=1
276 304 HG_SOURCE=pull
277 305 HG_TXNID=TXN:$ID$
306 HG_TXNNAME=pull
307 file:/*/$TESTTMP/repo (glob)
278 308 HG_URL=file:$TESTTMP/repo
279 309
280 310 pullop.cgresult is 3
281 311 (run 'hg heads' to see heads, 'hg merge' to merge)
282 312 $ hg log -G
283 313 o 7:5cd59d311f65 public test H
284 314 |
285 315 | o 6:1d14c3ce6ac0 public test G
286 316 | |
287 317 | | o 5:7f219660301f public test F
288 318 | | |
289 319 | | o 4:8a5212ebc852 public test E
290 320 | |/
291 321 o | 3:b3325c91a4d9 public test D
292 322 | |
293 323 @ | 2:f838bfaca5c7 public test C
294 324 |/
295 325 o 1:27547f69f254 public test B
296 326 |
297 327 o 0:4a2df7238c3b public test A
298 328
299 329 Removing a head from the original repository by merging it
300 330
301 331 $ cd ../repo
302 332 $ hg merge -r 6 -q
303 333 $ hg commit -m Merge
304 334 $ echo I > I
305 335 $ hg commit -A -m H -q
306 336 $ hg log -G
307 337 @ 9:9d18e5bd9ab0 draft test H
308 338 |
309 339 o 8:71bd7b46de72 draft test Merge
310 340 |\
311 341 | o 7:5cd59d311f65 draft test H
312 342 | |
313 343 o | 6:1d14c3ce6ac0 draft test G
314 344 | |
315 345 | | o 5:7f219660301f draft test F
316 346 | | |
317 347 +---o 4:8a5212ebc852 draft test E
318 348 | |
319 349 | o 3:b3325c91a4d9 draft test D
320 350 | |
321 351 | o 2:f838bfaca5c7 draft test C
322 352 |/
323 353 o 1:27547f69f254 draft test B
324 354 |
325 355 o 0:4a2df7238c3b draft test A
326 356
327 357 Removed heads are reported during transfer and properly accounted for in
328 358 pullop.cgresult
329 359
330 360 $ cd ../clone
331 361 $ hg pull
332 362 pulling from $TESTTMP/repo
333 363 searching for changes
334 364 remote: changegroup1
335 365 adding changesets
336 366 adding manifests
337 367 adding file changes
338 368 added 1 changesets with 0 changes to 0 files (-1 heads)
339 369 pretxnchangegroup hook: HG_HOOKNAME=pretxnchangegroup
340 370 HG_HOOKTYPE=pretxnchangegroup
341 371 HG_NODE=71bd7b46de72e69a32455bf88d04757d542e6cf4
342 372 HG_NODE_LAST=71bd7b46de72e69a32455bf88d04757d542e6cf4
343 373 HG_PENDING=$TESTTMP/clone
344 374 HG_SOURCE=pull
345 375 HG_TXNID=TXN:$ID$
376 HG_TXNNAME=pull
377 file:/*/$TESTTMP/repo (glob)
346 378 HG_URL=file:$TESTTMP/repo
347 379
348 380 remote: changegroup2
349 381 adding changesets
350 382 adding manifests
351 383 adding file changes
352 384 added 1 changesets with 1 changes to 1 files
353 385 pretxnchangegroup hook: HG_HOOKNAME=pretxnchangegroup
354 386 HG_HOOKTYPE=pretxnchangegroup
355 387 HG_NODE=9d18e5bd9ab09337802595d49f1dad0c98df4d84
356 388 HG_NODE_LAST=9d18e5bd9ab09337802595d49f1dad0c98df4d84
357 389 HG_PENDING=$TESTTMP/clone
358 390 HG_PHASES_MOVED=1
359 391 HG_SOURCE=pull
360 392 HG_TXNID=TXN:$ID$
393 HG_TXNNAME=pull
394 file:/*/$TESTTMP/repo (glob)
361 395 HG_URL=file:$TESTTMP/repo
362 396
363 397 new changesets 71bd7b46de72:9d18e5bd9ab0
364 398 changegroup hook: HG_HOOKNAME=changegroup
365 399 HG_HOOKTYPE=changegroup
366 400 HG_NODE=71bd7b46de72e69a32455bf88d04757d542e6cf4
367 401 HG_NODE_LAST=71bd7b46de72e69a32455bf88d04757d542e6cf4
368 402 HG_SOURCE=pull
369 403 HG_TXNID=TXN:$ID$
404 HG_TXNNAME=pull
405 file:/*/$TESTTMP/repo (glob)
370 406 HG_URL=file:$TESTTMP/repo
371 407
372 408 incoming hook: HG_HOOKNAME=incoming
373 409 HG_HOOKTYPE=incoming
374 410 HG_NODE=71bd7b46de72e69a32455bf88d04757d542e6cf4
375 411 HG_SOURCE=pull
376 412 HG_TXNID=TXN:$ID$
413 HG_TXNNAME=pull
414 file:/*/$TESTTMP/repo (glob)
377 415 HG_URL=file:$TESTTMP/repo
378 416
379 417 changegroup hook: HG_HOOKNAME=changegroup
380 418 HG_HOOKTYPE=changegroup
381 419 HG_NODE=9d18e5bd9ab09337802595d49f1dad0c98df4d84
382 420 HG_NODE_LAST=9d18e5bd9ab09337802595d49f1dad0c98df4d84
383 421 HG_PHASES_MOVED=1
384 422 HG_SOURCE=pull
385 423 HG_TXNID=TXN:$ID$
424 HG_TXNNAME=pull
425 file:/*/$TESTTMP/repo (glob)
386 426 HG_URL=file:$TESTTMP/repo
387 427
388 428 incoming hook: HG_HOOKNAME=incoming
389 429 HG_HOOKTYPE=incoming
390 430 HG_NODE=9d18e5bd9ab09337802595d49f1dad0c98df4d84
391 431 HG_PHASES_MOVED=1
392 432 HG_SOURCE=pull
393 433 HG_TXNID=TXN:$ID$
434 HG_TXNNAME=pull
435 file:/*/$TESTTMP/repo (glob)
394 436 HG_URL=file:$TESTTMP/repo
395 437
396 438 pullop.cgresult is -2
397 439 (run 'hg update' to get a working copy)
398 440 $ hg log -G
399 441 o 9:9d18e5bd9ab0 public test H
400 442 |
401 443 o 8:71bd7b46de72 public test Merge
402 444 |\
403 445 | o 7:5cd59d311f65 public test H
404 446 | |
405 447 o | 6:1d14c3ce6ac0 public test G
406 448 | |
407 449 | | o 5:7f219660301f public test F
408 450 | | |
409 451 +---o 4:8a5212ebc852 public test E
410 452 | |
411 453 | o 3:b3325c91a4d9 public test D
412 454 | |
413 455 | @ 2:f838bfaca5c7 public test C
414 456 |/
415 457 o 1:27547f69f254 public test B
416 458 |
417 459 o 0:4a2df7238c3b public test A
418 460
@@ -1,1352 +1,1368 b''
1 1 commit hooks can see env vars
2 2 (and post-transaction one are run unlocked)
3 3
4 4
5 5 $ cat > $TESTTMP/txnabort.checkargs.py <<EOF
6 6 > from mercurial import pycompat
7 7 > def showargs(ui, repo, hooktype, **kwargs):
8 8 > kwargs = pycompat.byteskwargs(kwargs)
9 9 > ui.write(b'%s Python hook: %s\n' % (hooktype,
10 10 > b','.join(sorted(kwargs))))
11 11 > EOF
12 12
13 13 $ hg init a
14 14 $ cd a
15 15 $ cat > .hg/hgrc <<EOF
16 16 > [hooks]
17 17 > commit = sh -c "HG_LOCAL= HG_TAG= printenv.py --line commit"
18 18 > commit.b = sh -c "HG_LOCAL= HG_TAG= printenv.py --line commit.b"
19 19 > precommit = sh -c "HG_LOCAL= HG_NODE= HG_TAG= printenv.py --line precommit"
20 20 > pretxncommit = sh -c "HG_LOCAL= HG_TAG= printenv.py --line pretxncommit"
21 21 > pretxncommit.tip = hg -q tip
22 22 > pre-identify = sh -c "printenv.py --line pre-identify 1"
23 23 > pre-cat = sh -c "printenv.py --line pre-cat"
24 24 > post-cat = sh -c "printenv.py --line post-cat"
25 25 > pretxnopen = sh -c "HG_LOCAL= HG_TAG= printenv.py --line pretxnopen"
26 26 > pretxnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py --line pretxnclose"
27 27 > txnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py --line txnclose"
28 28 > txnabort.0 = python:$TESTTMP/txnabort.checkargs.py:showargs
29 29 > txnabort.1 = sh -c "HG_LOCAL= HG_TAG= printenv.py --line txnabort"
30 30 > txnclose.checklock = sh -c "hg debuglock > /dev/null"
31 31 > EOF
32 32 $ echo a > a
33 33 $ hg add a
34 34 $ hg commit -m a
35 35 precommit hook: HG_HOOKNAME=precommit
36 36 HG_HOOKTYPE=precommit
37 37 HG_PARENT1=0000000000000000000000000000000000000000
38 38
39 39 pretxnopen hook: HG_HOOKNAME=pretxnopen
40 40 HG_HOOKTYPE=pretxnopen
41 41 HG_TXNID=TXN:$ID$
42 42 HG_TXNNAME=commit
43 43
44 44 pretxncommit hook: HG_HOOKNAME=pretxncommit
45 45 HG_HOOKTYPE=pretxncommit
46 46 HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
47 47 HG_PARENT1=0000000000000000000000000000000000000000
48 48 HG_PENDING=$TESTTMP/a
49 49
50 50 0:cb9a9f314b8b
51 51 pretxnclose hook: HG_HOOKNAME=pretxnclose
52 52 HG_HOOKTYPE=pretxnclose
53 53 HG_PENDING=$TESTTMP/a
54 54 HG_PHASES_MOVED=1
55 55 HG_TXNID=TXN:$ID$
56 56 HG_TXNNAME=commit
57 57
58 58 txnclose hook: HG_HOOKNAME=txnclose
59 59 HG_HOOKTYPE=txnclose
60 60 HG_PHASES_MOVED=1
61 61 HG_TXNID=TXN:$ID$
62 62 HG_TXNNAME=commit
63 63
64 64 commit hook: HG_HOOKNAME=commit
65 65 HG_HOOKTYPE=commit
66 66 HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
67 67 HG_PARENT1=0000000000000000000000000000000000000000
68 68
69 69 commit.b hook: HG_HOOKNAME=commit.b
70 70 HG_HOOKTYPE=commit
71 71 HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
72 72 HG_PARENT1=0000000000000000000000000000000000000000
73 73
74 74
75 75 $ hg clone . ../b
76 76 updating to branch default
77 77 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 78 $ cd ../b
79 79
80 80 changegroup hooks can see env vars
81 81
82 82 $ cat > .hg/hgrc <<EOF
83 83 > [hooks]
84 84 > prechangegroup = sh -c "printenv.py --line prechangegroup"
85 85 > changegroup = sh -c "printenv.py --line changegroup"
86 86 > incoming = sh -c "printenv.py --line incoming"
87 87 > EOF
88 88
89 89 pretxncommit and commit hooks can see both parents of merge
90 90
91 91 $ cd ../a
92 92 $ echo b >> a
93 93 $ hg commit -m a1 -d "1 0"
94 94 precommit hook: HG_HOOKNAME=precommit
95 95 HG_HOOKTYPE=precommit
96 96 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
97 97
98 98 pretxnopen hook: HG_HOOKNAME=pretxnopen
99 99 HG_HOOKTYPE=pretxnopen
100 100 HG_TXNID=TXN:$ID$
101 101 HG_TXNNAME=commit
102 102
103 103 pretxncommit hook: HG_HOOKNAME=pretxncommit
104 104 HG_HOOKTYPE=pretxncommit
105 105 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
106 106 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
107 107 HG_PENDING=$TESTTMP/a
108 108
109 109 1:ab228980c14d
110 110 pretxnclose hook: HG_HOOKNAME=pretxnclose
111 111 HG_HOOKTYPE=pretxnclose
112 112 HG_PENDING=$TESTTMP/a
113 113 HG_TXNID=TXN:$ID$
114 114 HG_TXNNAME=commit
115 115
116 116 txnclose hook: HG_HOOKNAME=txnclose
117 117 HG_HOOKTYPE=txnclose
118 118 HG_TXNID=TXN:$ID$
119 119 HG_TXNNAME=commit
120 120
121 121 commit hook: HG_HOOKNAME=commit
122 122 HG_HOOKTYPE=commit
123 123 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
124 124 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
125 125
126 126 commit.b hook: HG_HOOKNAME=commit.b
127 127 HG_HOOKTYPE=commit
128 128 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
129 129 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
130 130
131 131 $ hg update -C 0
132 132 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
133 133 $ echo b > b
134 134 $ hg add b
135 135 $ hg commit -m b -d '1 0'
136 136 precommit hook: HG_HOOKNAME=precommit
137 137 HG_HOOKTYPE=precommit
138 138 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
139 139
140 140 pretxnopen hook: HG_HOOKNAME=pretxnopen
141 141 HG_HOOKTYPE=pretxnopen
142 142 HG_TXNID=TXN:$ID$
143 143 HG_TXNNAME=commit
144 144
145 145 pretxncommit hook: HG_HOOKNAME=pretxncommit
146 146 HG_HOOKTYPE=pretxncommit
147 147 HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
148 148 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
149 149 HG_PENDING=$TESTTMP/a
150 150
151 151 2:ee9deb46ab31
152 152 pretxnclose hook: HG_HOOKNAME=pretxnclose
153 153 HG_HOOKTYPE=pretxnclose
154 154 HG_PENDING=$TESTTMP/a
155 155 HG_TXNID=TXN:$ID$
156 156 HG_TXNNAME=commit
157 157
158 158 created new head
159 159 txnclose hook: HG_HOOKNAME=txnclose
160 160 HG_HOOKTYPE=txnclose
161 161 HG_TXNID=TXN:$ID$
162 162 HG_TXNNAME=commit
163 163
164 164 commit hook: HG_HOOKNAME=commit
165 165 HG_HOOKTYPE=commit
166 166 HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
167 167 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
168 168
169 169 commit.b hook: HG_HOOKNAME=commit.b
170 170 HG_HOOKTYPE=commit
171 171 HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
172 172 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
173 173
174 174 $ hg merge 1
175 175 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
176 176 (branch merge, don't forget to commit)
177 177 $ hg commit -m merge -d '2 0'
178 178 precommit hook: HG_HOOKNAME=precommit
179 179 HG_HOOKTYPE=precommit
180 180 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
181 181 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
182 182
183 183 pretxnopen hook: HG_HOOKNAME=pretxnopen
184 184 HG_HOOKTYPE=pretxnopen
185 185 HG_TXNID=TXN:$ID$
186 186 HG_TXNNAME=commit
187 187
188 188 pretxncommit hook: HG_HOOKNAME=pretxncommit
189 189 HG_HOOKTYPE=pretxncommit
190 190 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
191 191 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
192 192 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
193 193 HG_PENDING=$TESTTMP/a
194 194
195 195 3:07f3376c1e65
196 196 pretxnclose hook: HG_HOOKNAME=pretxnclose
197 197 HG_HOOKTYPE=pretxnclose
198 198 HG_PENDING=$TESTTMP/a
199 199 HG_TXNID=TXN:$ID$
200 200 HG_TXNNAME=commit
201 201
202 202 txnclose hook: HG_HOOKNAME=txnclose
203 203 HG_HOOKTYPE=txnclose
204 204 HG_TXNID=TXN:$ID$
205 205 HG_TXNNAME=commit
206 206
207 207 commit hook: HG_HOOKNAME=commit
208 208 HG_HOOKTYPE=commit
209 209 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
210 210 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
211 211 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
212 212
213 213 commit.b hook: HG_HOOKNAME=commit.b
214 214 HG_HOOKTYPE=commit
215 215 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
216 216 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
217 217 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
218 218
219 219
220 220 test generic hooks
221 221
222 222 $ hg id
223 223 pre-identify hook: HG_ARGS=id
224 224 HG_HOOKNAME=pre-identify
225 225 HG_HOOKTYPE=pre-identify
226 226 HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None, 'template': ''}
227 227 HG_PATS=[]
228 228
229 229 abort: pre-identify hook exited with status 1
230 230 [255]
231 231 $ hg cat b
232 232 pre-cat hook: HG_ARGS=cat b
233 233 HG_HOOKNAME=pre-cat
234 234 HG_HOOKTYPE=pre-cat
235 235 HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''}
236 236 HG_PATS=['b']
237 237
238 238 b
239 239 post-cat hook: HG_ARGS=cat b
240 240 HG_HOOKNAME=post-cat
241 241 HG_HOOKTYPE=post-cat
242 242 HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''}
243 243 HG_PATS=['b']
244 244 HG_RESULT=0
245 245
246 246
247 247 $ cd ../b
248 248 $ hg pull ../a
249 249 pulling from ../a
250 250 searching for changes
251 251 prechangegroup hook: HG_HOOKNAME=prechangegroup
252 252 HG_HOOKTYPE=prechangegroup
253 253 HG_SOURCE=pull
254 254 HG_TXNID=TXN:$ID$
255 HG_TXNNAME=pull
256 file:/*/$TESTTMP/a (glob)
255 257 HG_URL=file:$TESTTMP/a
256 258
257 259 adding changesets
258 260 adding manifests
259 261 adding file changes
260 262 added 3 changesets with 2 changes to 2 files
261 263 new changesets ab228980c14d:07f3376c1e65
262 264 changegroup hook: HG_HOOKNAME=changegroup
263 265 HG_HOOKTYPE=changegroup
264 266 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
265 267 HG_NODE_LAST=07f3376c1e655977439df2a814e3cc14b27abac2
266 268 HG_SOURCE=pull
267 269 HG_TXNID=TXN:$ID$
270 HG_TXNNAME=pull
271 file:/*/$TESTTMP/a (glob)
268 272 HG_URL=file:$TESTTMP/a
269 273
270 274 incoming hook: HG_HOOKNAME=incoming
271 275 HG_HOOKTYPE=incoming
272 276 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
273 277 HG_SOURCE=pull
274 278 HG_TXNID=TXN:$ID$
279 HG_TXNNAME=pull
280 file:/*/$TESTTMP/a (glob)
275 281 HG_URL=file:$TESTTMP/a
276 282
277 283 incoming hook: HG_HOOKNAME=incoming
278 284 HG_HOOKTYPE=incoming
279 285 HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
280 286 HG_SOURCE=pull
281 287 HG_TXNID=TXN:$ID$
288 HG_TXNNAME=pull
289 file:/*/$TESTTMP/a (glob)
282 290 HG_URL=file:$TESTTMP/a
283 291
284 292 incoming hook: HG_HOOKNAME=incoming
285 293 HG_HOOKTYPE=incoming
286 294 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
287 295 HG_SOURCE=pull
288 296 HG_TXNID=TXN:$ID$
297 HG_TXNNAME=pull
298 file:/*/$TESTTMP/a (glob)
289 299 HG_URL=file:$TESTTMP/a
290 300
291 301 (run 'hg update' to get a working copy)
292 302
293 303 tag hooks can see env vars
294 304
295 305 $ cd ../a
296 306 $ cat >> .hg/hgrc <<EOF
297 307 > pretag = sh -c "printenv.py --line pretag"
298 308 > tag = sh -c "HG_PARENT1= HG_PARENT2= printenv.py --line tag"
299 309 > EOF
300 310 $ hg tag -d '3 0' a
301 311 pretag hook: HG_HOOKNAME=pretag
302 312 HG_HOOKTYPE=pretag
303 313 HG_LOCAL=0
304 314 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
305 315 HG_TAG=a
306 316
307 317 precommit hook: HG_HOOKNAME=precommit
308 318 HG_HOOKTYPE=precommit
309 319 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
310 320
311 321 pretxnopen hook: HG_HOOKNAME=pretxnopen
312 322 HG_HOOKTYPE=pretxnopen
313 323 HG_TXNID=TXN:$ID$
314 324 HG_TXNNAME=commit
315 325
316 326 pretxncommit hook: HG_HOOKNAME=pretxncommit
317 327 HG_HOOKTYPE=pretxncommit
318 328 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
319 329 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
320 330 HG_PENDING=$TESTTMP/a
321 331
322 332 4:539e4b31b6dc
323 333 pretxnclose hook: HG_HOOKNAME=pretxnclose
324 334 HG_HOOKTYPE=pretxnclose
325 335 HG_PENDING=$TESTTMP/a
326 336 HG_TXNID=TXN:$ID$
327 337 HG_TXNNAME=commit
328 338
329 339 tag hook: HG_HOOKNAME=tag
330 340 HG_HOOKTYPE=tag
331 341 HG_LOCAL=0
332 342 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
333 343 HG_TAG=a
334 344
335 345 txnclose hook: HG_HOOKNAME=txnclose
336 346 HG_HOOKTYPE=txnclose
337 347 HG_TXNID=TXN:$ID$
338 348 HG_TXNNAME=commit
339 349
340 350 commit hook: HG_HOOKNAME=commit
341 351 HG_HOOKTYPE=commit
342 352 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
343 353 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
344 354
345 355 commit.b hook: HG_HOOKNAME=commit.b
346 356 HG_HOOKTYPE=commit
347 357 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
348 358 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
349 359
350 360 $ hg tag -l la
351 361 pretag hook: HG_HOOKNAME=pretag
352 362 HG_HOOKTYPE=pretag
353 363 HG_LOCAL=1
354 364 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
355 365 HG_TAG=la
356 366
357 367 tag hook: HG_HOOKNAME=tag
358 368 HG_HOOKTYPE=tag
359 369 HG_LOCAL=1
360 370 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
361 371 HG_TAG=la
362 372
363 373
364 374 pretag hook can forbid tagging
365 375
366 376 $ cat >> .hg/hgrc <<EOF
367 377 > pretag.forbid = sh -c "printenv.py --line pretag.forbid 1"
368 378 > EOF
369 379 $ hg tag -d '4 0' fa
370 380 pretag hook: HG_HOOKNAME=pretag
371 381 HG_HOOKTYPE=pretag
372 382 HG_LOCAL=0
373 383 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
374 384 HG_TAG=fa
375 385
376 386 pretag.forbid hook: HG_HOOKNAME=pretag.forbid
377 387 HG_HOOKTYPE=pretag
378 388 HG_LOCAL=0
379 389 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
380 390 HG_TAG=fa
381 391
382 392 abort: pretag.forbid hook exited with status 1
383 393 [255]
384 394 $ hg tag -l fla
385 395 pretag hook: HG_HOOKNAME=pretag
386 396 HG_HOOKTYPE=pretag
387 397 HG_LOCAL=1
388 398 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
389 399 HG_TAG=fla
390 400
391 401 pretag.forbid hook: HG_HOOKNAME=pretag.forbid
392 402 HG_HOOKTYPE=pretag
393 403 HG_LOCAL=1
394 404 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
395 405 HG_TAG=fla
396 406
397 407 abort: pretag.forbid hook exited with status 1
398 408 [255]
399 409
400 410 pretxncommit hook can see changeset, can roll back txn, changeset no
401 411 more there after
402 412
403 413 $ cat >> .hg/hgrc <<EOF
404 414 > pretxncommit.forbid0 = sh -c "hg tip -q"
405 415 > pretxncommit.forbid1 = sh -c "printenv.py --line pretxncommit.forbid 1"
406 416 > EOF
407 417 $ echo z > z
408 418 $ hg add z
409 419 $ hg -q tip
410 420 4:539e4b31b6dc
411 421 $ hg commit -m 'fail' -d '4 0'
412 422 precommit hook: HG_HOOKNAME=precommit
413 423 HG_HOOKTYPE=precommit
414 424 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
415 425
416 426 pretxnopen hook: HG_HOOKNAME=pretxnopen
417 427 HG_HOOKTYPE=pretxnopen
418 428 HG_TXNID=TXN:$ID$
419 429 HG_TXNNAME=commit
420 430
421 431 pretxncommit hook: HG_HOOKNAME=pretxncommit
422 432 HG_HOOKTYPE=pretxncommit
423 433 HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567
424 434 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
425 435 HG_PENDING=$TESTTMP/a
426 436
427 437 5:6f611f8018c1
428 438 5:6f611f8018c1
429 439 pretxncommit.forbid hook: HG_HOOKNAME=pretxncommit.forbid1
430 440 HG_HOOKTYPE=pretxncommit
431 441 HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567
432 442 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
433 443 HG_PENDING=$TESTTMP/a
434 444
435 445 transaction abort!
436 446 txnabort Python hook: txnid,txnname
437 447 txnabort hook: HG_HOOKNAME=txnabort.1
438 448 HG_HOOKTYPE=txnabort
439 449 HG_TXNID=TXN:$ID$
440 450 HG_TXNNAME=commit
441 451
442 452 rollback completed
443 453 abort: pretxncommit.forbid1 hook exited with status 1
444 454 [255]
445 455 $ hg -q tip
446 456 4:539e4b31b6dc
447 457
448 458 (Check that no 'changelog.i.a' file were left behind)
449 459
450 460 $ ls -1 .hg/store/
451 461 00changelog.i
452 462 00manifest.i
453 463 data
454 464 fncache (repofncache !)
455 465 journal.phaseroots
456 466 phaseroots
457 467 undo
458 468 undo.backup.fncache (repofncache !)
459 469 undo.backupfiles
460 470 undo.phaseroots
461 471
462 472
463 473 precommit hook can prevent commit
464 474
465 475 $ cat >> .hg/hgrc <<EOF
466 476 > precommit.forbid = sh -c "printenv.py --line precommit.forbid 1"
467 477 > EOF
468 478 $ hg commit -m 'fail' -d '4 0'
469 479 precommit hook: HG_HOOKNAME=precommit
470 480 HG_HOOKTYPE=precommit
471 481 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
472 482
473 483 precommit.forbid hook: HG_HOOKNAME=precommit.forbid
474 484 HG_HOOKTYPE=precommit
475 485 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
476 486
477 487 abort: precommit.forbid hook exited with status 1
478 488 [255]
479 489 $ hg -q tip
480 490 4:539e4b31b6dc
481 491
482 492 preupdate hook can prevent update
483 493
484 494 $ cat >> .hg/hgrc <<EOF
485 495 > preupdate = sh -c "printenv.py --line preupdate"
486 496 > EOF
487 497 $ hg update 1
488 498 preupdate hook: HG_HOOKNAME=preupdate
489 499 HG_HOOKTYPE=preupdate
490 500 HG_PARENT1=ab228980c14d
491 501
492 502 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
493 503
494 504 update hook
495 505
496 506 $ cat >> .hg/hgrc <<EOF
497 507 > update = sh -c "printenv.py --line update"
498 508 > EOF
499 509 $ hg update
500 510 preupdate hook: HG_HOOKNAME=preupdate
501 511 HG_HOOKTYPE=preupdate
502 512 HG_PARENT1=539e4b31b6dc
503 513
504 514 update hook: HG_ERROR=0
505 515 HG_HOOKNAME=update
506 516 HG_HOOKTYPE=update
507 517 HG_PARENT1=539e4b31b6dc
508 518
509 519 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
510 520
511 521 pushkey hook
512 522
513 523 $ cat >> .hg/hgrc <<EOF
514 524 > pushkey = sh -c "printenv.py --line pushkey"
515 525 > EOF
516 526 $ cd ../b
517 527 $ hg bookmark -r null foo
518 528 $ hg push -B foo ../a
519 529 pushing to ../a
520 530 searching for changes
521 531 no changes found
522 532 pretxnopen hook: HG_HOOKNAME=pretxnopen
523 533 HG_HOOKTYPE=pretxnopen
524 534 HG_TXNID=TXN:$ID$
525 535 HG_TXNNAME=push
526 536
527 537 pretxnclose hook: HG_BOOKMARK_MOVED=1
528 538 HG_BUNDLE2=1
529 539 HG_HOOKNAME=pretxnclose
530 540 HG_HOOKTYPE=pretxnclose
531 541 HG_PENDING=$TESTTMP/a
532 542 HG_SOURCE=push
533 543 HG_TXNID=TXN:$ID$
534 544 HG_TXNNAME=push
535 545 HG_URL=file:$TESTTMP/a
536 546
537 547 pushkey hook: HG_BUNDLE2=1
538 548 HG_HOOKNAME=pushkey
539 549 HG_HOOKTYPE=pushkey
540 550 HG_KEY=foo
541 551 HG_NAMESPACE=bookmarks
542 552 HG_NEW=0000000000000000000000000000000000000000
543 553 HG_PUSHKEYCOMPAT=1
544 554 HG_SOURCE=push
545 555 HG_TXNID=TXN:$ID$
556 HG_TXNNAME=push
546 557 HG_URL=file:$TESTTMP/a
547 558
548 559 txnclose hook: HG_BOOKMARK_MOVED=1
549 560 HG_BUNDLE2=1
550 561 HG_HOOKNAME=txnclose
551 562 HG_HOOKTYPE=txnclose
552 563 HG_SOURCE=push
553 564 HG_TXNID=TXN:$ID$
554 565 HG_TXNNAME=push
555 566 HG_URL=file:$TESTTMP/a
556 567
557 568 exporting bookmark foo
558 569 [1]
559 570 $ cd ../a
560 571
561 572 listkeys hook
562 573
563 574 $ cat >> .hg/hgrc <<EOF
564 575 > listkeys = sh -c "printenv.py --line listkeys"
565 576 > EOF
566 577 $ hg bookmark -r null bar
567 578 pretxnopen hook: HG_HOOKNAME=pretxnopen
568 579 HG_HOOKTYPE=pretxnopen
569 580 HG_TXNID=TXN:$ID$
570 581 HG_TXNNAME=bookmark
571 582
572 583 pretxnclose hook: HG_BOOKMARK_MOVED=1
573 584 HG_HOOKNAME=pretxnclose
574 585 HG_HOOKTYPE=pretxnclose
575 586 HG_PENDING=$TESTTMP/a
576 587 HG_TXNID=TXN:$ID$
577 588 HG_TXNNAME=bookmark
578 589
579 590 txnclose hook: HG_BOOKMARK_MOVED=1
580 591 HG_HOOKNAME=txnclose
581 592 HG_HOOKTYPE=txnclose
582 593 HG_TXNID=TXN:$ID$
583 594 HG_TXNNAME=bookmark
584 595
585 596 $ cd ../b
586 597 $ hg pull -B bar ../a
587 598 pulling from ../a
588 599 listkeys hook: HG_HOOKNAME=listkeys
589 600 HG_HOOKTYPE=listkeys
590 601 HG_NAMESPACE=bookmarks
591 602 HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
592 603
593 604 no changes found
594 605 adding remote bookmark bar
595 606 $ cd ../a
596 607
597 608 test that prepushkey can prevent incoming keys
598 609
599 610 $ cat >> .hg/hgrc <<EOF
600 611 > prepushkey = sh -c "printenv.py --line prepushkey.forbid 1"
601 612 > EOF
602 613 $ cd ../b
603 614 $ hg bookmark -r null baz
604 615 $ hg push -B baz ../a
605 616 pushing to ../a
606 617 searching for changes
607 618 listkeys hook: HG_HOOKNAME=listkeys
608 619 HG_HOOKTYPE=listkeys
609 620 HG_NAMESPACE=phases
610 621 HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
611 622
612 623 listkeys hook: HG_HOOKNAME=listkeys
613 624 HG_HOOKTYPE=listkeys
614 625 HG_NAMESPACE=bookmarks
615 626 HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
616 627
617 628 no changes found
618 629 pretxnopen hook: HG_HOOKNAME=pretxnopen
619 630 HG_HOOKTYPE=pretxnopen
620 631 HG_TXNID=TXN:$ID$
621 632 HG_TXNNAME=push
622 633
623 634 prepushkey.forbid hook: HG_BUNDLE2=1
624 635 HG_HOOKNAME=prepushkey
625 636 HG_HOOKTYPE=prepushkey
626 637 HG_KEY=baz
627 638 HG_NAMESPACE=bookmarks
628 639 HG_NEW=0000000000000000000000000000000000000000
629 640 HG_PUSHKEYCOMPAT=1
630 641 HG_SOURCE=push
631 642 HG_TXNID=TXN:$ID$
643 HG_TXNNAME=push
632 644 HG_URL=file:$TESTTMP/a
633 645
634 646 abort: prepushkey hook exited with status 1
635 647 [255]
636 648 $ cd ../a
637 649
638 650 test that prelistkeys can prevent listing keys
639 651
640 652 $ cat >> .hg/hgrc <<EOF
641 653 > prelistkeys = sh -c "printenv.py --line prelistkeys.forbid 1"
642 654 > EOF
643 655 $ hg bookmark -r null quux
644 656 pretxnopen hook: HG_HOOKNAME=pretxnopen
645 657 HG_HOOKTYPE=pretxnopen
646 658 HG_TXNID=TXN:$ID$
647 659 HG_TXNNAME=bookmark
648 660
649 661 pretxnclose hook: HG_BOOKMARK_MOVED=1
650 662 HG_HOOKNAME=pretxnclose
651 663 HG_HOOKTYPE=pretxnclose
652 664 HG_PENDING=$TESTTMP/a
653 665 HG_TXNID=TXN:$ID$
654 666 HG_TXNNAME=bookmark
655 667
656 668 txnclose hook: HG_BOOKMARK_MOVED=1
657 669 HG_HOOKNAME=txnclose
658 670 HG_HOOKTYPE=txnclose
659 671 HG_TXNID=TXN:$ID$
660 672 HG_TXNNAME=bookmark
661 673
662 674 $ cd ../b
663 675 $ hg pull -B quux ../a
664 676 pulling from ../a
665 677 prelistkeys.forbid hook: HG_HOOKNAME=prelistkeys
666 678 HG_HOOKTYPE=prelistkeys
667 679 HG_NAMESPACE=bookmarks
668 680
669 681 abort: prelistkeys hook exited with status 1
670 682 [255]
671 683 $ cd ../a
672 684 $ rm .hg/hgrc
673 685
674 686 prechangegroup hook can prevent incoming changes
675 687
676 688 $ cd ../b
677 689 $ hg -q tip
678 690 3:07f3376c1e65
679 691 $ cat > .hg/hgrc <<EOF
680 692 > [hooks]
681 693 > prechangegroup.forbid = sh -c "printenv.py --line prechangegroup.forbid 1"
682 694 > EOF
683 695 $ hg pull ../a
684 696 pulling from ../a
685 697 searching for changes
686 698 prechangegroup.forbid hook: HG_HOOKNAME=prechangegroup.forbid
687 699 HG_HOOKTYPE=prechangegroup
688 700 HG_SOURCE=pull
689 701 HG_TXNID=TXN:$ID$
702 HG_TXNNAME=pull
703 file:/*/$TESTTMP/a (glob)
690 704 HG_URL=file:$TESTTMP/a
691 705
692 706 abort: prechangegroup.forbid hook exited with status 1
693 707 [255]
694 708
695 709 pretxnchangegroup hook can see incoming changes, can roll back txn,
696 710 incoming changes no longer there after
697 711
698 712 $ cat > .hg/hgrc <<EOF
699 713 > [hooks]
700 714 > pretxnchangegroup.forbid0 = hg tip -q
701 715 > pretxnchangegroup.forbid1 = sh -c "printenv.py --line pretxnchangegroup.forbid 1"
702 716 > EOF
703 717 $ hg pull ../a
704 718 pulling from ../a
705 719 searching for changes
706 720 adding changesets
707 721 adding manifests
708 722 adding file changes
709 723 added 1 changesets with 1 changes to 1 files
710 724 4:539e4b31b6dc
711 725 pretxnchangegroup.forbid hook: HG_HOOKNAME=pretxnchangegroup.forbid1
712 726 HG_HOOKTYPE=pretxnchangegroup
713 727 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
714 728 HG_NODE_LAST=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
715 729 HG_PENDING=$TESTTMP/b
716 730 HG_SOURCE=pull
717 731 HG_TXNID=TXN:$ID$
732 HG_TXNNAME=pull
733 file:/*/$TESTTMP/a (glob)
718 734 HG_URL=file:$TESTTMP/a
719 735
720 736 transaction abort!
721 737 rollback completed
722 738 abort: pretxnchangegroup.forbid1 hook exited with status 1
723 739 [255]
724 740 $ hg -q tip
725 741 3:07f3376c1e65
726 742
727 743 outgoing hooks can see env vars
728 744
729 745 $ rm .hg/hgrc
730 746 $ cat > ../a/.hg/hgrc <<EOF
731 747 > [hooks]
732 748 > preoutgoing = sh -c "printenv.py --line preoutgoing"
733 749 > outgoing = sh -c "printenv.py --line outgoing"
734 750 > EOF
735 751 $ hg pull ../a
736 752 pulling from ../a
737 753 searching for changes
738 754 preoutgoing hook: HG_HOOKNAME=preoutgoing
739 755 HG_HOOKTYPE=preoutgoing
740 756 HG_SOURCE=pull
741 757
742 758 outgoing hook: HG_HOOKNAME=outgoing
743 759 HG_HOOKTYPE=outgoing
744 760 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
745 761 HG_SOURCE=pull
746 762
747 763 adding changesets
748 764 adding manifests
749 765 adding file changes
750 766 added 1 changesets with 1 changes to 1 files
751 767 adding remote bookmark quux
752 768 new changesets 539e4b31b6dc
753 769 (run 'hg update' to get a working copy)
754 770 $ hg rollback
755 771 repository tip rolled back to revision 3 (undo pull)
756 772
757 773 preoutgoing hook can prevent outgoing changes
758 774
759 775 $ cat >> ../a/.hg/hgrc <<EOF
760 776 > preoutgoing.forbid = sh -c "printenv.py --line preoutgoing.forbid 1"
761 777 > EOF
762 778 $ hg pull ../a
763 779 pulling from ../a
764 780 searching for changes
765 781 preoutgoing hook: HG_HOOKNAME=preoutgoing
766 782 HG_HOOKTYPE=preoutgoing
767 783 HG_SOURCE=pull
768 784
769 785 preoutgoing.forbid hook: HG_HOOKNAME=preoutgoing.forbid
770 786 HG_HOOKTYPE=preoutgoing
771 787 HG_SOURCE=pull
772 788
773 789 abort: preoutgoing.forbid hook exited with status 1
774 790 [255]
775 791
776 792 outgoing hooks work for local clones
777 793
778 794 $ cd ..
779 795 $ cat > a/.hg/hgrc <<EOF
780 796 > [hooks]
781 797 > preoutgoing = sh -c "printenv.py --line preoutgoing"
782 798 > outgoing = sh -c "printenv.py --line outgoing"
783 799 > EOF
784 800 $ hg clone a c
785 801 preoutgoing hook: HG_HOOKNAME=preoutgoing
786 802 HG_HOOKTYPE=preoutgoing
787 803 HG_SOURCE=clone
788 804
789 805 outgoing hook: HG_HOOKNAME=outgoing
790 806 HG_HOOKTYPE=outgoing
791 807 HG_NODE=0000000000000000000000000000000000000000
792 808 HG_SOURCE=clone
793 809
794 810 updating to branch default
795 811 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
796 812 $ rm -rf c
797 813
798 814 preoutgoing hook can prevent outgoing changes for local clones
799 815
800 816 $ cat >> a/.hg/hgrc <<EOF
801 817 > preoutgoing.forbid = sh -c "printenv.py --line preoutgoing.forbid 1"
802 818 > EOF
803 819 $ hg clone a zzz
804 820 preoutgoing hook: HG_HOOKNAME=preoutgoing
805 821 HG_HOOKTYPE=preoutgoing
806 822 HG_SOURCE=clone
807 823
808 824 preoutgoing.forbid hook: HG_HOOKNAME=preoutgoing.forbid
809 825 HG_HOOKTYPE=preoutgoing
810 826 HG_SOURCE=clone
811 827
812 828 abort: preoutgoing.forbid hook exited with status 1
813 829 [255]
814 830
815 831 $ cd "$TESTTMP/b"
816 832
817 833 $ cat > hooktests.py <<EOF
818 834 > from __future__ import print_function
819 835 > from mercurial import (
820 836 > error,
821 837 > pycompat,
822 838 > )
823 839 >
824 840 > uncallable = 0
825 841 >
826 842 > def printargs(ui, args):
827 843 > a = list(pycompat.byteskwargs(args).items())
828 844 > a.sort()
829 845 > ui.write(b'hook args:\n')
830 846 > for k, v in a:
831 847 > ui.write(b' %s %s\n' % (k, v))
832 848 >
833 849 > def passhook(ui, repo, **args):
834 850 > printargs(ui, args)
835 851 >
836 852 > def failhook(ui, repo, **args):
837 853 > printargs(ui, args)
838 854 > return True
839 855 >
840 856 > class LocalException(Exception):
841 857 > pass
842 858 >
843 859 > def raisehook(**args):
844 860 > raise LocalException('exception from hook')
845 861 >
846 862 > def aborthook(**args):
847 863 > raise error.Abort(b'raise abort from hook')
848 864 >
849 865 > def brokenhook(**args):
850 866 > return 1 + {}
851 867 >
852 868 > def verbosehook(ui, **args):
853 869 > ui.note(b'verbose output from hook\n')
854 870 >
855 871 > def printtags(ui, repo, **args):
856 872 > ui.write(b'[%s]\n' % b', '.join(sorted(repo.tags())))
857 873 >
858 874 > class container(object):
859 875 > unreachable = 1
860 876 > EOF
861 877
862 878 $ cat > syntaxerror.py << NO_CHECK_EOF
863 879 > (foo
864 880 > NO_CHECK_EOF
865 881
866 882 test python hooks
867 883
868 884 #if windows
869 885 $ PYTHONPATH="$TESTTMP/b;$PYTHONPATH"
870 886 #else
871 887 $ PYTHONPATH="$TESTTMP/b:$PYTHONPATH"
872 888 #endif
873 889 $ export PYTHONPATH
874 890
875 891 $ echo '[hooks]' > ../a/.hg/hgrc
876 892 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
877 893 $ hg pull ../a 2>&1 | grep 'raised an exception'
878 894 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
879 895
880 896 $ echo '[hooks]' > ../a/.hg/hgrc
881 897 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
882 898 $ hg pull ../a 2>&1 | grep 'raised an exception'
883 899 error: preoutgoing.raise hook raised an exception: exception from hook
884 900
885 901 $ echo '[hooks]' > ../a/.hg/hgrc
886 902 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
887 903 $ hg pull ../a
888 904 pulling from ../a
889 905 searching for changes
890 906 error: preoutgoing.abort hook failed: raise abort from hook
891 907 abort: raise abort from hook
892 908 [255]
893 909
894 910 $ echo '[hooks]' > ../a/.hg/hgrc
895 911 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
896 912 $ hg pull ../a
897 913 pulling from ../a
898 914 searching for changes
899 915 hook args:
900 916 hooktype preoutgoing
901 917 source pull
902 918 abort: preoutgoing.fail hook failed
903 919 [255]
904 920
905 921 $ echo '[hooks]' > ../a/.hg/hgrc
906 922 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
907 923 $ hg pull ../a
908 924 pulling from ../a
909 925 searching for changes
910 926 abort: preoutgoing.uncallable hook is invalid: "hooktests.uncallable" is not callable
911 927 [255]
912 928
913 929 $ echo '[hooks]' > ../a/.hg/hgrc
914 930 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
915 931 $ hg pull ../a
916 932 pulling from ../a
917 933 searching for changes
918 934 abort: preoutgoing.nohook hook is invalid: "hooktests.nohook" is not defined
919 935 [255]
920 936
921 937 $ echo '[hooks]' > ../a/.hg/hgrc
922 938 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
923 939 $ hg pull ../a
924 940 pulling from ../a
925 941 searching for changes
926 942 abort: preoutgoing.nomodule hook is invalid: "nomodule" not in a module
927 943 [255]
928 944
929 945 $ echo '[hooks]' > ../a/.hg/hgrc
930 946 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
931 947 $ hg pull ../a
932 948 pulling from ../a
933 949 searching for changes
934 950 abort: preoutgoing.badmodule hook is invalid: import of "nomodule" failed
935 951 (run with --traceback for stack trace)
936 952 [255]
937 953
938 954 $ echo '[hooks]' > ../a/.hg/hgrc
939 955 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
940 956 $ hg pull ../a
941 957 pulling from ../a
942 958 searching for changes
943 959 abort: preoutgoing.unreachable hook is invalid: import of "hooktests.container" failed
944 960 (run with --traceback for stack trace)
945 961 [255]
946 962
947 963 $ echo '[hooks]' > ../a/.hg/hgrc
948 964 $ echo 'preoutgoing.syntaxerror = python:syntaxerror.syntaxerror' >> ../a/.hg/hgrc
949 965 $ hg pull ../a
950 966 pulling from ../a
951 967 searching for changes
952 968 abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
953 969 (run with --traceback for stack trace)
954 970 [255]
955 971
956 972 The second egrep is to filter out lines like ' ^', which are slightly
957 973 different between Python 2.6 and Python 2.7.
958 974 $ hg pull ../a --traceback 2>&1 | egrep -v '^( +File| [_a-zA-Z*(])' | egrep -v '^( )+(\^)?$'
959 975 pulling from ../a
960 976 searching for changes
961 977 exception from first failed import attempt:
962 978 Traceback (most recent call last):
963 979 SyntaxError: * (glob)
964 980 exception from second failed import attempt:
965 981 Traceback (most recent call last):
966 982 ImportError: No module named hgext_syntaxerror
967 983 Traceback (most recent call last):
968 984 HookLoadError: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
969 985 abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
970 986
971 987 $ echo '[hooks]' > ../a/.hg/hgrc
972 988 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
973 989 $ hg pull ../a
974 990 pulling from ../a
975 991 searching for changes
976 992 hook args:
977 993 hooktype preoutgoing
978 994 source pull
979 995 adding changesets
980 996 adding manifests
981 997 adding file changes
982 998 added 1 changesets with 1 changes to 1 files
983 999 adding remote bookmark quux
984 1000 new changesets 539e4b31b6dc
985 1001 (run 'hg update' to get a working copy)
986 1002
987 1003 post- python hooks that fail to *run* don't cause an abort
988 1004 $ rm ../a/.hg/hgrc
989 1005 $ echo '[hooks]' > .hg/hgrc
990 1006 $ echo 'post-pull.broken = python:hooktests.brokenhook' >> .hg/hgrc
991 1007 $ hg pull ../a
992 1008 pulling from ../a
993 1009 searching for changes
994 1010 no changes found
995 1011 error: post-pull.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
996 1012 (run with --traceback for stack trace)
997 1013
998 1014 but post- python hooks that fail to *load* do
999 1015 $ echo '[hooks]' > .hg/hgrc
1000 1016 $ echo 'post-pull.nomodule = python:nomodule' >> .hg/hgrc
1001 1017 $ hg pull ../a
1002 1018 pulling from ../a
1003 1019 searching for changes
1004 1020 no changes found
1005 1021 abort: post-pull.nomodule hook is invalid: "nomodule" not in a module
1006 1022 [255]
1007 1023
1008 1024 $ echo '[hooks]' > .hg/hgrc
1009 1025 $ echo 'post-pull.badmodule = python:nomodule.nowhere' >> .hg/hgrc
1010 1026 $ hg pull ../a
1011 1027 pulling from ../a
1012 1028 searching for changes
1013 1029 no changes found
1014 1030 abort: post-pull.badmodule hook is invalid: import of "nomodule" failed
1015 1031 (run with --traceback for stack trace)
1016 1032 [255]
1017 1033
1018 1034 $ echo '[hooks]' > .hg/hgrc
1019 1035 $ echo 'post-pull.nohook = python:hooktests.nohook' >> .hg/hgrc
1020 1036 $ hg pull ../a
1021 1037 pulling from ../a
1022 1038 searching for changes
1023 1039 no changes found
1024 1040 abort: post-pull.nohook hook is invalid: "hooktests.nohook" is not defined
1025 1041 [255]
1026 1042
1027 1043 make sure --traceback works
1028 1044
1029 1045 $ echo '[hooks]' > .hg/hgrc
1030 1046 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
1031 1047
1032 1048 $ echo aa > a
1033 1049 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
1034 1050 Traceback (most recent call last):
1035 1051
1036 1052 $ cd ..
1037 1053 $ hg init c
1038 1054 $ cd c
1039 1055
1040 1056 $ cat > hookext.py <<EOF
1041 1057 > def autohook(ui, **args):
1042 1058 > ui.write(b'Automatically installed hook\n')
1043 1059 >
1044 1060 > def reposetup(ui, repo):
1045 1061 > repo.ui.setconfig(b"hooks", b"commit.auto", autohook)
1046 1062 > EOF
1047 1063 $ echo '[extensions]' >> .hg/hgrc
1048 1064 $ echo 'hookext = hookext.py' >> .hg/hgrc
1049 1065
1050 1066 $ touch foo
1051 1067 $ hg add foo
1052 1068 $ hg ci -d '0 0' -m 'add foo'
1053 1069 Automatically installed hook
1054 1070 $ echo >> foo
1055 1071 $ hg ci --debug -d '0 0' -m 'change foo'
1056 1072 committing files:
1057 1073 foo
1058 1074 committing manifest
1059 1075 committing changelog
1060 1076 updating the branch cache
1061 1077 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
1062 1078 calling hook commit.auto: hgext_hookext.autohook
1063 1079 Automatically installed hook
1064 1080
1065 1081 $ hg showconfig hooks
1066 1082 hooks.commit.auto=<function autohook at *> (glob)
1067 1083
1068 1084 test python hook configured with python:[file]:[hook] syntax
1069 1085
1070 1086 $ cd ..
1071 1087 $ mkdir d
1072 1088 $ cd d
1073 1089 $ hg init repo
1074 1090 $ mkdir hooks
1075 1091
1076 1092 $ cd hooks
1077 1093 $ cat > testhooks.py <<EOF
1078 1094 > def testhook(ui, **args):
1079 1095 > ui.write(b'hook works\n')
1080 1096 > EOF
1081 1097 $ echo '[hooks]' > ../repo/.hg/hgrc
1082 1098 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
1083 1099
1084 1100 $ cd ../repo
1085 1101 $ hg commit -d '0 0'
1086 1102 hook works
1087 1103 nothing changed
1088 1104 [1]
1089 1105
1090 1106 $ echo '[hooks]' > .hg/hgrc
1091 1107 $ echo "update.ne = python:`pwd`/nonexistent.py:testhook" >> .hg/hgrc
1092 1108 $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc
1093 1109
1094 1110 $ hg up null
1095 1111 loading update.ne hook failed:
1096 1112 abort: $ENOENT$: '$TESTTMP/d/repo/nonexistent.py'
1097 1113 [255]
1098 1114
1099 1115 $ hg id
1100 1116 loading pre-identify.npmd hook failed:
1101 1117 abort: No module named repo!
1102 1118 [255]
1103 1119
1104 1120 $ cd ../../b
1105 1121
1106 1122 make sure --traceback works on hook import failure
1107 1123
1108 1124 $ cat > importfail.py <<EOF
1109 1125 > import somebogusmodule
1110 1126 > # dereference something in the module to force demandimport to load it
1111 1127 > somebogusmodule.whatever
1112 1128 > EOF
1113 1129
1114 1130 $ echo '[hooks]' > .hg/hgrc
1115 1131 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
1116 1132
1117 1133 $ echo a >> a
1118 1134 $ hg --traceback commit -ma 2>&1 | egrep -v '^( +File| [a-zA-Z(])'
1119 1135 exception from first failed import attempt:
1120 1136 Traceback (most recent call last):
1121 1137 ImportError: No module named somebogusmodule
1122 1138 exception from second failed import attempt:
1123 1139 Traceback (most recent call last):
1124 1140 ImportError: No module named hgext_importfail
1125 1141 Traceback (most recent call last):
1126 1142 HookLoadError: precommit.importfail hook is invalid: import of "importfail" failed
1127 1143 abort: precommit.importfail hook is invalid: import of "importfail" failed
1128 1144
1129 1145 Issue1827: Hooks Update & Commit not completely post operation
1130 1146
1131 1147 commit and update hooks should run after command completion. The largefiles
1132 1148 use demonstrates a recursive wlock, showing the hook doesn't run until the
1133 1149 final release (and dirstate flush).
1134 1150
1135 1151 $ echo '[hooks]' > .hg/hgrc
1136 1152 $ echo 'commit = hg id' >> .hg/hgrc
1137 1153 $ echo 'update = hg id' >> .hg/hgrc
1138 1154 $ echo bb > a
1139 1155 $ hg ci -ma
1140 1156 223eafe2750c tip
1141 1157 $ hg up 0 --config extensions.largefiles=
1142 1158 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
1143 1159 cb9a9f314b8b
1144 1160 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1145 1161
1146 1162 make sure --verbose (and --quiet/--debug etc.) are propagated to the local ui
1147 1163 that is passed to pre/post hooks
1148 1164
1149 1165 $ echo '[hooks]' > .hg/hgrc
1150 1166 $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc
1151 1167 $ hg id
1152 1168 cb9a9f314b8b
1153 1169 $ hg id --verbose
1154 1170 calling hook pre-identify: hooktests.verbosehook
1155 1171 verbose output from hook
1156 1172 cb9a9f314b8b
1157 1173
1158 1174 Ensure hooks can be prioritized
1159 1175
1160 1176 $ echo '[hooks]' > .hg/hgrc
1161 1177 $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc
1162 1178 $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc
1163 1179 $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc
1164 1180 $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc
1165 1181 $ hg id --verbose
1166 1182 calling hook pre-identify.b: hooktests.verbosehook
1167 1183 verbose output from hook
1168 1184 calling hook pre-identify.a: hooktests.verbosehook
1169 1185 verbose output from hook
1170 1186 calling hook pre-identify.c: hooktests.verbosehook
1171 1187 verbose output from hook
1172 1188 cb9a9f314b8b
1173 1189
1174 1190 new tags must be visible in pretxncommit (issue3210)
1175 1191
1176 1192 $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc
1177 1193 $ hg tag -f foo
1178 1194 [a, foo, tip]
1179 1195
1180 1196 post-init hooks must not crash (issue4983)
1181 1197 This also creates the `to` repo for the next test block.
1182 1198
1183 1199 $ cd ..
1184 1200 $ cat << EOF >> hgrc-with-post-init-hook
1185 1201 > [hooks]
1186 1202 > post-init = sh -c "printenv.py --line post-init"
1187 1203 > EOF
1188 1204 $ HGRCPATH=hgrc-with-post-init-hook hg init to
1189 1205 post-init hook: HG_ARGS=init to
1190 1206 HG_HOOKNAME=post-init
1191 1207 HG_HOOKTYPE=post-init
1192 1208 HG_OPTS={'insecure': None, 'remotecmd': '', 'ssh': ''}
1193 1209 HG_PATS=['to']
1194 1210 HG_RESULT=0
1195 1211
1196 1212
1197 1213 new commits must be visible in pretxnchangegroup (issue3428)
1198 1214
1199 1215 $ echo '[hooks]' >> to/.hg/hgrc
1200 1216 $ echo 'prechangegroup = hg --traceback tip' >> to/.hg/hgrc
1201 1217 $ echo 'pretxnchangegroup = hg --traceback tip' >> to/.hg/hgrc
1202 1218 $ echo a >> to/a
1203 1219 $ hg --cwd to ci -Ama
1204 1220 adding a
1205 1221 $ hg clone to from
1206 1222 updating to branch default
1207 1223 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1208 1224 $ echo aa >> from/a
1209 1225 $ hg --cwd from ci -mb
1210 1226 $ hg --cwd from push
1211 1227 pushing to $TESTTMP/to
1212 1228 searching for changes
1213 1229 changeset: 0:cb9a9f314b8b
1214 1230 tag: tip
1215 1231 user: test
1216 1232 date: Thu Jan 01 00:00:00 1970 +0000
1217 1233 summary: a
1218 1234
1219 1235 adding changesets
1220 1236 adding manifests
1221 1237 adding file changes
1222 1238 added 1 changesets with 1 changes to 1 files
1223 1239 changeset: 1:9836a07b9b9d
1224 1240 tag: tip
1225 1241 user: test
1226 1242 date: Thu Jan 01 00:00:00 1970 +0000
1227 1243 summary: b
1228 1244
1229 1245
1230 1246 pretxnclose hook failure should abort the transaction
1231 1247
1232 1248 $ hg init txnfailure
1233 1249 $ cd txnfailure
1234 1250 $ touch a && hg commit -Aqm a
1235 1251 $ cat >> .hg/hgrc <<EOF
1236 1252 > [hooks]
1237 1253 > pretxnclose.error = exit 1
1238 1254 > EOF
1239 1255 $ hg strip -r 0 --config extensions.strip=
1240 1256 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1241 1257 saved backup bundle to * (glob)
1242 1258 transaction abort!
1243 1259 rollback completed
1244 1260 strip failed, backup bundle stored in * (glob)
1245 1261 abort: pretxnclose.error hook exited with status 1
1246 1262 [255]
1247 1263 $ hg recover
1248 1264 no interrupted transaction available
1249 1265 [1]
1250 1266 $ cd ..
1251 1267
1252 1268 check whether HG_PENDING makes pending changes only in related
1253 1269 repositories visible to an external hook.
1254 1270
1255 1271 (emulate a transaction running concurrently by copied
1256 1272 .hg/store/00changelog.i.a in subsequent test)
1257 1273
1258 1274 $ cat > $TESTTMP/savepending.sh <<EOF
1259 1275 > cp .hg/store/00changelog.i.a .hg/store/00changelog.i.a.saved
1260 1276 > exit 1 # to avoid adding new revision for subsequent tests
1261 1277 > EOF
1262 1278 $ cd a
1263 1279 $ hg tip -q
1264 1280 4:539e4b31b6dc
1265 1281 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" commit -m "invisible"
1266 1282 transaction abort!
1267 1283 rollback completed
1268 1284 abort: pretxnclose hook exited with status 1
1269 1285 [255]
1270 1286 $ cp .hg/store/00changelog.i.a.saved .hg/store/00changelog.i.a
1271 1287
1272 1288 (check (in)visibility of new changeset while transaction running in
1273 1289 repo)
1274 1290
1275 1291 $ cat > $TESTTMP/checkpending.sh <<EOF
1276 1292 > echo '@a'
1277 1293 > hg -R "$TESTTMP/a" tip -q
1278 1294 > echo '@a/nested'
1279 1295 > hg -R "$TESTTMP/a/nested" tip -q
1280 1296 > exit 1 # to avoid adding new revision for subsequent tests
1281 1297 > EOF
1282 1298 $ hg init nested
1283 1299 $ cd nested
1284 1300 $ echo a > a
1285 1301 $ hg add a
1286 1302 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" commit -m '#0'
1287 1303 @a
1288 1304 4:539e4b31b6dc
1289 1305 @a/nested
1290 1306 0:bf5e395ced2c
1291 1307 transaction abort!
1292 1308 rollback completed
1293 1309 abort: pretxnclose hook exited with status 1
1294 1310 [255]
1295 1311
1296 1312 Hook from untrusted hgrc are reported as failure
1297 1313 ================================================
1298 1314
1299 1315 $ cat << EOF > $TESTTMP/untrusted.py
1300 1316 > from mercurial import scmutil, util
1301 1317 > def uisetup(ui):
1302 1318 > class untrustedui(ui.__class__):
1303 1319 > def _trusted(self, fp, f):
1304 1320 > if util.normpath(fp.name).endswith(b'untrusted/.hg/hgrc'):
1305 1321 > return False
1306 1322 > return super(untrustedui, self)._trusted(fp, f)
1307 1323 > ui.__class__ = untrustedui
1308 1324 > EOF
1309 1325 $ cat << EOF >> $HGRCPATH
1310 1326 > [extensions]
1311 1327 > untrusted=$TESTTMP/untrusted.py
1312 1328 > EOF
1313 1329 $ hg init untrusted
1314 1330 $ cd untrusted
1315 1331
1316 1332 Non-blocking hook
1317 1333 -----------------
1318 1334
1319 1335 $ cat << EOF >> .hg/hgrc
1320 1336 > [hooks]
1321 1337 > txnclose.testing=echo txnclose hook called
1322 1338 > EOF
1323 1339 $ touch a && hg commit -Aqm a
1324 1340 warning: untrusted hook txnclose.testing not executed
1325 1341 $ hg log
1326 1342 changeset: 0:3903775176ed
1327 1343 tag: tip
1328 1344 user: test
1329 1345 date: Thu Jan 01 00:00:00 1970 +0000
1330 1346 summary: a
1331 1347
1332 1348
1333 1349 Non-blocking hook
1334 1350 -----------------
1335 1351
1336 1352 $ cat << EOF >> .hg/hgrc
1337 1353 > [hooks]
1338 1354 > pretxnclose.testing=echo pre-txnclose hook called
1339 1355 > EOF
1340 1356 $ touch b && hg commit -Aqm a
1341 1357 transaction abort!
1342 1358 rollback completed
1343 1359 abort: untrusted hook pretxnclose.testing not executed
1344 1360 (see 'hg help config.trusted')
1345 1361 [255]
1346 1362 $ hg log
1347 1363 changeset: 0:3903775176ed
1348 1364 tag: tip
1349 1365 user: test
1350 1366 date: Thu Jan 01 00:00:00 1970 +0000
1351 1367 summary: a
1352 1368
@@ -1,408 +1,410 b''
1 1 #require serve
2 2
3 3 This test is a duplicate of 'test-http.t', feel free to factor out
4 4 parts that are not bundle1/bundle2 specific.
5 5
6 6 $ cat << EOF >> $HGRCPATH
7 7 > [devel]
8 8 > # This test is dedicated to interaction through old bundle
9 9 > legacy.exchange = bundle1
10 10 > EOF
11 11
12 12 $ hg init test
13 13 $ cd test
14 14 $ echo foo>foo
15 15 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
16 16 $ echo foo>foo.d/foo
17 17 $ echo bar>foo.d/bAr.hg.d/BaR
18 18 $ echo bar>foo.d/baR.d.hg/bAR
19 19 $ hg commit -A -m 1
20 20 adding foo
21 21 adding foo.d/bAr.hg.d/BaR
22 22 adding foo.d/baR.d.hg/bAR
23 23 adding foo.d/foo
24 24 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
25 25 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
26 26
27 27 Test server address cannot be reused
28 28
29 29 $ hg serve -p $HGPORT1 2>&1
30 30 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
31 31 [255]
32 32
33 33 $ cd ..
34 34 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
35 35
36 36 clone via stream
37 37
38 38 #if no-reposimplestore
39 39 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
40 40 streaming all changes
41 41 6 files to transfer, 606 bytes of data
42 42 transferred * bytes in * seconds (*/sec) (glob)
43 43 searching for changes
44 44 no changes found
45 45 updating to branch default
46 46 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 47 $ hg verify -R copy
48 48 checking changesets
49 49 checking manifests
50 50 crosschecking files in changesets and manifests
51 51 checking files
52 52 checked 1 changesets with 4 changes to 4 files
53 53 #endif
54 54
55 55 try to clone via stream, should use pull instead
56 56
57 57 $ hg clone --stream http://localhost:$HGPORT1/ copy2
58 58 warning: stream clone requested but server has them disabled
59 59 requesting all changes
60 60 adding changesets
61 61 adding manifests
62 62 adding file changes
63 63 added 1 changesets with 4 changes to 4 files
64 64 new changesets 8b6053c928fe
65 65 updating to branch default
66 66 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 67
68 68 try to clone via stream but missing requirements, so should use pull instead
69 69
70 70 $ cat > $TESTTMP/removesupportedformat.py << EOF
71 71 > from mercurial import localrepo
72 72 > def extsetup(ui):
73 73 > localrepo.localrepository.supportedformats.remove(b'generaldelta')
74 74 > EOF
75 75
76 76 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
77 77 warning: stream clone requested but client is missing requirements: generaldelta
78 78 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
79 79 requesting all changes
80 80 adding changesets
81 81 adding manifests
82 82 adding file changes
83 83 added 1 changesets with 4 changes to 4 files
84 84 new changesets 8b6053c928fe
85 85 updating to branch default
86 86 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 87
88 88 clone via pull
89 89
90 90 $ hg clone http://localhost:$HGPORT1/ copy-pull
91 91 requesting all changes
92 92 adding changesets
93 93 adding manifests
94 94 adding file changes
95 95 added 1 changesets with 4 changes to 4 files
96 96 new changesets 8b6053c928fe
97 97 updating to branch default
98 98 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 99 $ hg verify -R copy-pull
100 100 checking changesets
101 101 checking manifests
102 102 crosschecking files in changesets and manifests
103 103 checking files
104 104 checked 1 changesets with 4 changes to 4 files
105 105 $ cd test
106 106 $ echo bar > bar
107 107 $ hg commit -A -d '1 0' -m 2
108 108 adding bar
109 109 $ cd ..
110 110
111 111 clone over http with --update
112 112
113 113 $ hg clone http://localhost:$HGPORT1/ updated --update 0
114 114 requesting all changes
115 115 adding changesets
116 116 adding manifests
117 117 adding file changes
118 118 added 2 changesets with 5 changes to 5 files
119 119 new changesets 8b6053c928fe:5fed3813f7f5
120 120 updating to branch default
121 121 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 122 $ hg log -r . -R updated
123 123 changeset: 0:8b6053c928fe
124 124 user: test
125 125 date: Thu Jan 01 00:00:00 1970 +0000
126 126 summary: 1
127 127
128 128 $ rm -rf updated
129 129
130 130 incoming via HTTP
131 131
132 132 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
133 133 adding changesets
134 134 adding manifests
135 135 adding file changes
136 136 added 1 changesets with 4 changes to 4 files
137 137 new changesets 8b6053c928fe
138 138 updating to branch default
139 139 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
140 140 $ cd partial
141 141 $ touch LOCAL
142 142 $ hg ci -qAm LOCAL
143 143 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
144 144 comparing with http://localhost:$HGPORT1/
145 145 searching for changes
146 146 2
147 147 $ cd ..
148 148
149 149 pull
150 150
151 151 $ cd copy-pull
152 152 $ cat >> .hg/hgrc <<EOF
153 153 > [hooks]
154 154 > changegroup = sh -c "printenv.py --line changegroup"
155 155 > EOF
156 156 $ hg pull
157 157 pulling from http://localhost:$HGPORT1/
158 158 searching for changes
159 159 adding changesets
160 160 adding manifests
161 161 adding file changes
162 162 added 1 changesets with 1 changes to 1 files
163 163 new changesets 5fed3813f7f5
164 164 changegroup hook: HG_HOOKNAME=changegroup
165 165 HG_HOOKTYPE=changegroup
166 166 HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
167 167 HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
168 168 HG_SOURCE=pull
169 169 HG_TXNID=TXN:$ID$
170 HG_TXNNAME=pull
171 http://localhost:$HGPORT1/
170 172 HG_URL=http://localhost:$HGPORT1/
171 173
172 174 (run 'hg update' to get a working copy)
173 175 $ cd ..
174 176
175 177 clone from invalid URL
176 178
177 179 $ hg clone http://localhost:$HGPORT/bad
178 180 abort: HTTP Error 404: Not Found
179 181 [255]
180 182
181 183 test http authentication
182 184 + use the same server to test server side streaming preference
183 185
184 186 $ cd test
185 187
186 188 $ hg serve --config extensions.x=$TESTDIR/httpserverauth.py -p $HGPORT2 -d \
187 189 > --pid-file=pid --config server.preferuncompressed=True \
188 190 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
189 191 $ cat pid >> $DAEMON_PIDS
190 192
191 193 $ cat << EOF > get_pass.py
192 194 > import getpass
193 195 > def newgetpass(arg):
194 196 > return "pass"
195 197 > getpass.getpass = newgetpass
196 198 > EOF
197 199
198 200 $ hg id http://localhost:$HGPORT2/
199 201 abort: http authorization required for http://localhost:$HGPORT2/
200 202 [255]
201 203 $ hg id http://localhost:$HGPORT2/
202 204 abort: http authorization required for http://localhost:$HGPORT2/
203 205 [255]
204 206 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
205 207 http authorization required for http://localhost:$HGPORT2/
206 208 realm: mercurial
207 209 user: user
208 210 password: 5fed3813f7f5
209 211 $ hg id http://user:pass@localhost:$HGPORT2/
210 212 5fed3813f7f5
211 213 $ echo '[auth]' >> .hg/hgrc
212 214 $ echo 'l.schemes=http' >> .hg/hgrc
213 215 $ echo 'l.prefix=lo' >> .hg/hgrc
214 216 $ echo 'l.username=user' >> .hg/hgrc
215 217 $ echo 'l.password=pass' >> .hg/hgrc
216 218 $ hg id http://localhost:$HGPORT2/
217 219 5fed3813f7f5
218 220 $ hg id http://localhost:$HGPORT2/
219 221 5fed3813f7f5
220 222 $ hg id http://user@localhost:$HGPORT2/
221 223 5fed3813f7f5
222 224
223 225 #if no-reposimplestore
224 226 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
225 227 streaming all changes
226 228 7 files to transfer, 916 bytes of data
227 229 transferred * bytes in * seconds (*/sec) (glob)
228 230 searching for changes
229 231 no changes found
230 232 updating to branch default
231 233 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 234 #endif
233 235
234 236 --pull should override server's preferuncompressed
235 237
236 238 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
237 239 requesting all changes
238 240 adding changesets
239 241 adding manifests
240 242 adding file changes
241 243 added 2 changesets with 5 changes to 5 files
242 244 new changesets 8b6053c928fe:5fed3813f7f5
243 245 updating to branch default
244 246 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
245 247
246 248 $ hg id http://user2@localhost:$HGPORT2/
247 249 abort: http authorization required for http://localhost:$HGPORT2/
248 250 [255]
249 251 $ hg id http://user:pass2@localhost:$HGPORT2/
250 252 abort: HTTP Error 403: no
251 253 [255]
252 254
253 255 $ hg -R dest-pull tag -r tip top
254 256 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/
255 257 pushing to http://user:***@localhost:$HGPORT2/
256 258 searching for changes
257 259 remote: adding changesets
258 260 remote: adding manifests
259 261 remote: adding file changes
260 262 remote: added 1 changesets with 1 changes to 1 files
261 263 $ hg rollback -q
262 264
263 265 $ sed 's/.*] "/"/' < ../access.log
264 266 "GET /?cmd=capabilities HTTP/1.1" 401 -
265 267 "GET /?cmd=capabilities HTTP/1.1" 401 -
266 268 "GET /?cmd=capabilities HTTP/1.1" 401 -
267 269 "GET /?cmd=capabilities HTTP/1.1" 200 -
268 270 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
269 271 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
270 272 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
271 273 "GET /?cmd=capabilities HTTP/1.1" 401 -
272 274 "GET /?cmd=capabilities HTTP/1.1" 200 -
273 275 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
274 276 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
275 277 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
276 278 "GET /?cmd=capabilities HTTP/1.1" 401 -
277 279 "GET /?cmd=capabilities HTTP/1.1" 200 -
278 280 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
279 281 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
280 282 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
281 283 "GET /?cmd=capabilities HTTP/1.1" 401 -
282 284 "GET /?cmd=capabilities HTTP/1.1" 200 -
283 285 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
284 286 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
285 287 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
286 288 "GET /?cmd=capabilities HTTP/1.1" 401 -
287 289 "GET /?cmd=capabilities HTTP/1.1" 200 -
288 290 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
289 291 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
290 292 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
291 293 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
292 294 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
293 295 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
294 296 "GET /?cmd=stream_out HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
295 297 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
296 298 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
297 299 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
298 300 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
299 301 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
300 302 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
301 303 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
302 304 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
303 305 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
304 306 "GET /?cmd=capabilities HTTP/1.1" 401 -
305 307 "GET /?cmd=capabilities HTTP/1.1" 401 -
306 308 "GET /?cmd=capabilities HTTP/1.1" 403 -
307 309 "GET /?cmd=capabilities HTTP/1.1" 401 -
308 310 "GET /?cmd=capabilities HTTP/1.1" 200 -
309 311 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
310 312 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
311 313 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
312 314 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
313 315 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
314 316 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+5eb5abfefeea63c80dd7553bcc3783f37e0c5524* (glob)
315 317 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
316 318
317 319 $ cd ..
318 320
319 321 clone of serve with repo in root and unserved subrepo (issue2970)
320 322
321 323 $ hg --cwd test init sub
322 324 $ echo empty > test/sub/empty
323 325 $ hg --cwd test/sub add empty
324 326 $ hg --cwd test/sub commit -qm 'add empty'
325 327 $ hg --cwd test/sub tag -r 0 something
326 328 $ echo sub = sub > test/.hgsub
327 329 $ hg --cwd test add .hgsub
328 330 $ hg --cwd test commit -qm 'add subrepo'
329 331 $ hg clone http://localhost:$HGPORT noslash-clone
330 332 requesting all changes
331 333 adding changesets
332 334 adding manifests
333 335 adding file changes
334 336 added 3 changesets with 7 changes to 7 files
335 337 new changesets 8b6053c928fe:56f9bc90cce6
336 338 updating to branch default
337 339 cloning subrepo sub from http://localhost:$HGPORT/sub
338 340 abort: HTTP Error 404: Not Found
339 341 [255]
340 342 $ hg clone http://localhost:$HGPORT/ slash-clone
341 343 requesting all changes
342 344 adding changesets
343 345 adding manifests
344 346 adding file changes
345 347 added 3 changesets with 7 changes to 7 files
346 348 new changesets 8b6053c928fe:56f9bc90cce6
347 349 updating to branch default
348 350 cloning subrepo sub from http://localhost:$HGPORT/sub
349 351 abort: HTTP Error 404: Not Found
350 352 [255]
351 353
352 354 check error log
353 355
354 356 $ cat error.log
355 357
356 358 Check error reporting while pulling/cloning
357 359
358 360 $ $RUNTESTDIR/killdaemons.py
359 361 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
360 362 $ cat hg3.pid >> $DAEMON_PIDS
361 363 $ hg clone http://localhost:$HGPORT/ abort-clone
362 364 requesting all changes
363 365 abort: remote error:
364 366 this is an exercise
365 367 [255]
366 368 $ cat error.log
367 369
368 370 disable pull-based clones
369 371
370 372 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
371 373 $ cat hg4.pid >> $DAEMON_PIDS
372 374 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
373 375 requesting all changes
374 376 abort: remote error:
375 377 server has pull-based clones disabled
376 378 [255]
377 379
378 380 #if no-reposimplestore
379 381 ... but keep stream clones working
380 382
381 383 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
382 384 streaming all changes
383 385 * files to transfer, * of data (glob)
384 386 transferred * in * seconds (* KB/sec) (glob)
385 387 searching for changes
386 388 no changes found
387 389 #endif
388 390
389 391 ... and also keep partial clones and pulls working
390 392 $ hg clone http://localhost:$HGPORT1 --rev 0 test-partial-clone
391 393 adding changesets
392 394 adding manifests
393 395 adding file changes
394 396 added 1 changesets with 4 changes to 4 files
395 397 new changesets 8b6053c928fe
396 398 updating to branch default
397 399 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
398 400 $ hg pull -R test-partial-clone
399 401 pulling from http://localhost:$HGPORT1/
400 402 searching for changes
401 403 adding changesets
402 404 adding manifests
403 405 adding file changes
404 406 added 2 changesets with 3 changes to 3 files
405 407 new changesets 5fed3813f7f5:56f9bc90cce6
406 408 (run 'hg update' to get a working copy)
407 409
408 410 $ cat error.log
@@ -1,581 +1,583 b''
1 1 #require serve
2 2
3 3 $ hg init test
4 4 $ cd test
5 5 $ echo foo>foo
6 6 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
7 7 $ echo foo>foo.d/foo
8 8 $ echo bar>foo.d/bAr.hg.d/BaR
9 9 $ echo bar>foo.d/baR.d.hg/bAR
10 10 $ hg commit -A -m 1
11 11 adding foo
12 12 adding foo.d/bAr.hg.d/BaR
13 13 adding foo.d/baR.d.hg/bAR
14 14 adding foo.d/foo
15 15 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
16 16 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
17 17
18 18 Test server address cannot be reused
19 19
20 20 $ hg serve -p $HGPORT1 2>&1
21 21 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
22 22 [255]
23 23
24 24 $ cd ..
25 25 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
26 26
27 27 clone via stream
28 28
29 29 #if no-reposimplestore
30 30 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
31 31 streaming all changes
32 32 9 files to transfer, 715 bytes of data
33 33 transferred * bytes in * seconds (*/sec) (glob)
34 34 updating to branch default
35 35 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 36 $ hg verify -R copy
37 37 checking changesets
38 38 checking manifests
39 39 crosschecking files in changesets and manifests
40 40 checking files
41 41 checked 1 changesets with 4 changes to 4 files
42 42 #endif
43 43
44 44 try to clone via stream, should use pull instead
45 45
46 46 $ hg clone --stream http://localhost:$HGPORT1/ copy2
47 47 warning: stream clone requested but server has them disabled
48 48 requesting all changes
49 49 adding changesets
50 50 adding manifests
51 51 adding file changes
52 52 added 1 changesets with 4 changes to 4 files
53 53 new changesets 8b6053c928fe
54 54 updating to branch default
55 55 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 56
57 57 try to clone via stream but missing requirements, so should use pull instead
58 58
59 59 $ cat > $TESTTMP/removesupportedformat.py << EOF
60 60 > from mercurial import localrepo
61 61 > def extsetup(ui):
62 62 > localrepo.localrepository.supportedformats.remove(b'generaldelta')
63 63 > EOF
64 64
65 65 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
66 66 warning: stream clone requested but client is missing requirements: generaldelta
67 67 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
68 68 requesting all changes
69 69 adding changesets
70 70 adding manifests
71 71 adding file changes
72 72 added 1 changesets with 4 changes to 4 files
73 73 new changesets 8b6053c928fe
74 74 updating to branch default
75 75 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 76
77 77 clone via pull
78 78
79 79 $ hg clone http://localhost:$HGPORT1/ copy-pull
80 80 requesting all changes
81 81 adding changesets
82 82 adding manifests
83 83 adding file changes
84 84 added 1 changesets with 4 changes to 4 files
85 85 new changesets 8b6053c928fe
86 86 updating to branch default
87 87 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 88 $ hg verify -R copy-pull
89 89 checking changesets
90 90 checking manifests
91 91 crosschecking files in changesets and manifests
92 92 checking files
93 93 checked 1 changesets with 4 changes to 4 files
94 94 $ cd test
95 95 $ echo bar > bar
96 96 $ hg commit -A -d '1 0' -m 2
97 97 adding bar
98 98 $ cd ..
99 99
100 100 clone over http with --update
101 101
102 102 $ hg clone http://localhost:$HGPORT1/ updated --update 0
103 103 requesting all changes
104 104 adding changesets
105 105 adding manifests
106 106 adding file changes
107 107 added 2 changesets with 5 changes to 5 files
108 108 new changesets 8b6053c928fe:5fed3813f7f5
109 109 updating to branch default
110 110 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 111 $ hg log -r . -R updated
112 112 changeset: 0:8b6053c928fe
113 113 user: test
114 114 date: Thu Jan 01 00:00:00 1970 +0000
115 115 summary: 1
116 116
117 117 $ rm -rf updated
118 118
119 119 incoming via HTTP
120 120
121 121 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
122 122 adding changesets
123 123 adding manifests
124 124 adding file changes
125 125 added 1 changesets with 4 changes to 4 files
126 126 new changesets 8b6053c928fe
127 127 updating to branch default
128 128 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
129 129 $ cd partial
130 130 $ touch LOCAL
131 131 $ hg ci -qAm LOCAL
132 132 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
133 133 comparing with http://localhost:$HGPORT1/
134 134 searching for changes
135 135 2
136 136 $ cd ..
137 137
138 138 pull
139 139
140 140 $ cd copy-pull
141 141 $ cat >> .hg/hgrc <<EOF
142 142 > [hooks]
143 143 > changegroup = sh -c "printenv.py --line changegroup"
144 144 > EOF
145 145 $ hg pull
146 146 pulling from http://localhost:$HGPORT1/
147 147 searching for changes
148 148 adding changesets
149 149 adding manifests
150 150 adding file changes
151 151 added 1 changesets with 1 changes to 1 files
152 152 new changesets 5fed3813f7f5
153 153 changegroup hook: HG_HOOKNAME=changegroup
154 154 HG_HOOKTYPE=changegroup
155 155 HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
156 156 HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
157 157 HG_SOURCE=pull
158 158 HG_TXNID=TXN:$ID$
159 HG_TXNNAME=pull
160 http://localhost:$HGPORT1/
159 161 HG_URL=http://localhost:$HGPORT1/
160 162
161 163 (run 'hg update' to get a working copy)
162 164 $ cd ..
163 165
164 166 clone from invalid URL
165 167
166 168 $ hg clone http://localhost:$HGPORT/bad
167 169 abort: HTTP Error 404: Not Found
168 170 [255]
169 171
170 172 test http authentication
171 173 + use the same server to test server side streaming preference
172 174
173 175 $ cd test
174 176
175 177 $ hg serve --config extensions.x=$TESTDIR/httpserverauth.py -p $HGPORT2 -d \
176 178 > --pid-file=pid --config server.preferuncompressed=True -E ../errors2.log \
177 179 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
178 180 $ cat pid >> $DAEMON_PIDS
179 181
180 182 $ cat << EOF > get_pass.py
181 183 > import getpass
182 184 > def newgetpass(arg):
183 185 > return "pass"
184 186 > getpass.getpass = newgetpass
185 187 > EOF
186 188
187 189 $ hg id http://localhost:$HGPORT2/
188 190 abort: http authorization required for http://localhost:$HGPORT2/
189 191 [255]
190 192 $ hg id http://localhost:$HGPORT2/
191 193 abort: http authorization required for http://localhost:$HGPORT2/
192 194 [255]
193 195 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
194 196 http authorization required for http://localhost:$HGPORT2/
195 197 realm: mercurial
196 198 user: user
197 199 password: 5fed3813f7f5
198 200 $ hg id http://user:pass@localhost:$HGPORT2/
199 201 5fed3813f7f5
200 202 $ echo '[auth]' >> .hg/hgrc
201 203 $ echo 'l.schemes=http' >> .hg/hgrc
202 204 $ echo 'l.prefix=lo' >> .hg/hgrc
203 205 $ echo 'l.username=user' >> .hg/hgrc
204 206 $ echo 'l.password=pass' >> .hg/hgrc
205 207 $ hg id http://localhost:$HGPORT2/
206 208 5fed3813f7f5
207 209 $ hg id http://localhost:$HGPORT2/
208 210 5fed3813f7f5
209 211 $ hg id http://user@localhost:$HGPORT2/
210 212 5fed3813f7f5
211 213
212 214 $ cat > use_digests.py << EOF
213 215 > from mercurial import (
214 216 > exthelper,
215 217 > url,
216 218 > )
217 219 >
218 220 > eh = exthelper.exthelper()
219 221 > uisetup = eh.finaluisetup
220 222 >
221 223 > @eh.wrapfunction(url, 'opener')
222 224 > def urlopener(orig, *args, **kwargs):
223 225 > opener = orig(*args, **kwargs)
224 226 > opener.addheaders.append((r'X-HgTest-AuthType', r'Digest'))
225 227 > return opener
226 228 > EOF
227 229
228 230 $ hg id http://localhost:$HGPORT2/ --config extensions.x=use_digests.py
229 231 5fed3813f7f5
230 232
231 233 #if no-reposimplestore
232 234 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
233 235 streaming all changes
234 236 10 files to transfer, 1.01 KB of data
235 237 transferred * KB in * seconds (*/sec) (glob)
236 238 updating to branch default
237 239 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
238 240 #endif
239 241
240 242 --pull should override server's preferuncompressed
241 243 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
242 244 requesting all changes
243 245 adding changesets
244 246 adding manifests
245 247 adding file changes
246 248 added 2 changesets with 5 changes to 5 files
247 249 new changesets 8b6053c928fe:5fed3813f7f5
248 250 updating to branch default
249 251 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
250 252
251 253 $ hg id http://user2@localhost:$HGPORT2/
252 254 abort: http authorization required for http://localhost:$HGPORT2/
253 255 [255]
254 256 $ hg id http://user:pass2@localhost:$HGPORT2/
255 257 abort: HTTP Error 403: no
256 258 [255]
257 259
258 260 $ hg -R dest-pull tag -r tip top
259 261 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/
260 262 pushing to http://user:***@localhost:$HGPORT2/
261 263 searching for changes
262 264 remote: adding changesets
263 265 remote: adding manifests
264 266 remote: adding file changes
265 267 remote: added 1 changesets with 1 changes to 1 files
266 268 $ hg rollback -q
267 269 $ hg -R dest-pull push http://user:pass@localhost:$HGPORT2/ --debug --config devel.debug.peer-request=yes
268 270 pushing to http://user:***@localhost:$HGPORT2/
269 271 using http://localhost:$HGPORT2/
270 272 http auth: user user, password ****
271 273 sending capabilities command
272 274 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=capabilities
273 275 http auth: user user, password ****
274 276 devel-peer-request: finished in *.???? seconds (200) (glob)
275 277 query 1; heads
276 278 devel-peer-request: batched-content
277 279 devel-peer-request: - heads (0 arguments)
278 280 devel-peer-request: - known (1 arguments)
279 281 sending batch command
280 282 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=batch
281 283 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
282 284 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
283 285 devel-peer-request: 68 bytes of commands arguments in headers
284 286 devel-peer-request: finished in *.???? seconds (200) (glob)
285 287 searching for changes
286 288 all remote heads known locally
287 289 preparing listkeys for "phases"
288 290 sending listkeys command
289 291 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
290 292 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
291 293 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
292 294 devel-peer-request: 16 bytes of commands arguments in headers
293 295 devel-peer-request: finished in *.???? seconds (200) (glob)
294 296 received listkey for "phases": 58 bytes
295 297 checking for updated bookmarks
296 298 preparing listkeys for "bookmarks"
297 299 sending listkeys command
298 300 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
299 301 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
300 302 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
301 303 devel-peer-request: 19 bytes of commands arguments in headers
302 304 devel-peer-request: finished in *.???? seconds (200) (glob)
303 305 received listkey for "bookmarks": 0 bytes
304 306 sending branchmap command
305 307 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=branchmap
306 308 devel-peer-request: Vary X-HgProto-1
307 309 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
308 310 devel-peer-request: finished in *.???? seconds (200) (glob)
309 311 preparing listkeys for "bookmarks"
310 312 sending listkeys command
311 313 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
312 314 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
313 315 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
314 316 devel-peer-request: 19 bytes of commands arguments in headers
315 317 devel-peer-request: finished in *.???? seconds (200) (glob)
316 318 received listkey for "bookmarks": 0 bytes
317 319 1 changesets found
318 320 list of changesets:
319 321 7f4e523d01f2cc3765ac8934da3d14db775ff872
320 322 bundle2-output-bundle: "HG20", 5 parts total
321 323 bundle2-output-part: "replycaps" 205 bytes payload
322 324 bundle2-output-part: "check:phases" 24 bytes payload
323 325 bundle2-output-part: "check:heads" streamed payload
324 326 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
325 327 bundle2-output-part: "phase-heads" 24 bytes payload
326 328 sending unbundle command
327 329 sending 1013 bytes
328 330 devel-peer-request: POST http://localhost:$HGPORT2/?cmd=unbundle
329 331 devel-peer-request: Content-length 1013
330 332 devel-peer-request: Content-type application/mercurial-0.1
331 333 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
332 334 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
333 335 devel-peer-request: 16 bytes of commands arguments in headers
334 336 devel-peer-request: 1013 bytes of data
335 337 devel-peer-request: finished in *.???? seconds (200) (glob)
336 338 bundle2-input-bundle: no-transaction
337 339 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
338 340 bundle2-input-part: "output" (advisory) (params: 0 advisory) supported
339 341 bundle2-input-part: total payload size 100
340 342 remote: adding changesets
341 343 remote: adding manifests
342 344 remote: adding file changes
343 345 remote: added 1 changesets with 1 changes to 1 files
344 346 bundle2-input-part: "output" (advisory) supported
345 347 bundle2-input-bundle: 2 parts total
346 348 preparing listkeys for "phases"
347 349 sending listkeys command
348 350 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
349 351 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
350 352 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
351 353 devel-peer-request: 16 bytes of commands arguments in headers
352 354 devel-peer-request: finished in *.???? seconds (200) (glob)
353 355 received listkey for "phases": 15 bytes
354 356 $ hg rollback -q
355 357
356 358 $ sed 's/.*] "/"/' < ../access.log
357 359 "GET /?cmd=capabilities HTTP/1.1" 401 -
358 360 "GET /?cmd=capabilities HTTP/1.1" 401 -
359 361 "GET /?cmd=capabilities HTTP/1.1" 401 -
360 362 "GET /?cmd=capabilities HTTP/1.1" 200 -
361 363 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
362 364 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
363 365 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
364 366 "GET /?cmd=capabilities HTTP/1.1" 401 -
365 367 "GET /?cmd=capabilities HTTP/1.1" 200 -
366 368 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
367 369 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
368 370 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
369 371 "GET /?cmd=capabilities HTTP/1.1" 401 -
370 372 "GET /?cmd=capabilities HTTP/1.1" 200 -
371 373 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
372 374 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
373 375 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
374 376 "GET /?cmd=capabilities HTTP/1.1" 401 -
375 377 "GET /?cmd=capabilities HTTP/1.1" 200 -
376 378 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
377 379 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
378 380 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
379 381 "GET /?cmd=capabilities HTTP/1.1" 401 -
380 382 "GET /?cmd=capabilities HTTP/1.1" 200 -
381 383 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
382 384 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
383 385 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
384 386 "GET /?cmd=capabilities HTTP/1.1" 401 - x-hgtest-authtype:Digest
385 387 "GET /?cmd=capabilities HTTP/1.1" 200 - x-hgtest-authtype:Digest
386 388 "GET /?cmd=lookup HTTP/1.1" 401 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest
387 389 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest
388 390 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest
389 391 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest
390 392 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest
391 393 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull x-hgtest-authtype:Digest
392 394 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
393 395 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
394 396 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
395 397 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=0&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&stream=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (no-reposimplestore !)
396 398 "GET /?cmd=capabilities HTTP/1.1" 401 - (no-reposimplestore !)
397 399 "GET /?cmd=capabilities HTTP/1.1" 200 - (no-reposimplestore !)
398 400 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
399 401 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
400 402 "GET /?cmd=capabilities HTTP/1.1" 401 -
401 403 "GET /?cmd=capabilities HTTP/1.1" 401 -
402 404 "GET /?cmd=capabilities HTTP/1.1" 403 -
403 405 "GET /?cmd=capabilities HTTP/1.1" 401 -
404 406 "GET /?cmd=capabilities HTTP/1.1" 200 -
405 407 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
406 408 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
407 409 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
408 410 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
409 411 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
410 412 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365* (glob)
411 413 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
412 414 "GET /?cmd=capabilities HTTP/1.1" 401 -
413 415 "GET /?cmd=capabilities HTTP/1.1" 200 -
414 416 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
415 417 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
416 418 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
417 419 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
418 420 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
419 421 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
420 422 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull
421 423
422 424 $ cd ..
423 425
424 426 clone of serve with repo in root and unserved subrepo (issue2970)
425 427
426 428 $ hg --cwd test init sub
427 429 $ echo empty > test/sub/empty
428 430 $ hg --cwd test/sub add empty
429 431 $ hg --cwd test/sub commit -qm 'add empty'
430 432 $ hg --cwd test/sub tag -r 0 something
431 433 $ echo sub = sub > test/.hgsub
432 434 $ hg --cwd test add .hgsub
433 435 $ hg --cwd test commit -qm 'add subrepo'
434 436 $ hg clone http://localhost:$HGPORT noslash-clone
435 437 requesting all changes
436 438 adding changesets
437 439 adding manifests
438 440 adding file changes
439 441 added 3 changesets with 7 changes to 7 files
440 442 new changesets 8b6053c928fe:56f9bc90cce6
441 443 updating to branch default
442 444 cloning subrepo sub from http://localhost:$HGPORT/sub
443 445 abort: HTTP Error 404: Not Found
444 446 [255]
445 447 $ hg clone http://localhost:$HGPORT/ slash-clone
446 448 requesting all changes
447 449 adding changesets
448 450 adding manifests
449 451 adding file changes
450 452 added 3 changesets with 7 changes to 7 files
451 453 new changesets 8b6053c928fe:56f9bc90cce6
452 454 updating to branch default
453 455 cloning subrepo sub from http://localhost:$HGPORT/sub
454 456 abort: HTTP Error 404: Not Found
455 457 [255]
456 458
457 459 check error log
458 460
459 461 $ cat error.log
460 462
461 463 $ cat errors2.log
462 464
463 465 check abort error reporting while pulling/cloning
464 466
465 467 $ $RUNTESTDIR/killdaemons.py
466 468 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
467 469 $ cat hg3.pid >> $DAEMON_PIDS
468 470 $ hg clone http://localhost:$HGPORT/ abort-clone
469 471 requesting all changes
470 472 remote: abort: this is an exercise
471 473 abort: pull failed on remote
472 474 [255]
473 475 $ cat error.log
474 476
475 477 disable pull-based clones
476 478
477 479 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
478 480 $ cat hg4.pid >> $DAEMON_PIDS
479 481 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
480 482 requesting all changes
481 483 remote: abort: server has pull-based clones disabled
482 484 abort: pull failed on remote
483 485 (remove --pull if specified or upgrade Mercurial)
484 486 [255]
485 487
486 488 #if no-reposimplestore
487 489 ... but keep stream clones working
488 490
489 491 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
490 492 streaming all changes
491 493 * files to transfer, * of data (glob)
492 494 transferred * in * seconds (*/sec) (glob)
493 495 $ cat error.log
494 496 #endif
495 497
496 498 ... and also keep partial clones and pulls working
497 499 $ hg clone http://localhost:$HGPORT1 --rev 0 test/partial/clone
498 500 adding changesets
499 501 adding manifests
500 502 adding file changes
501 503 added 1 changesets with 4 changes to 4 files
502 504 new changesets 8b6053c928fe
503 505 updating to branch default
504 506 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
505 507 $ hg pull -R test/partial/clone
506 508 pulling from http://localhost:$HGPORT1/
507 509 searching for changes
508 510 adding changesets
509 511 adding manifests
510 512 adding file changes
511 513 added 2 changesets with 3 changes to 3 files
512 514 new changesets 5fed3813f7f5:56f9bc90cce6
513 515 (run 'hg update' to get a working copy)
514 516
515 517 $ hg clone -U -r 0 test/partial/clone test/another/clone
516 518 adding changesets
517 519 adding manifests
518 520 adding file changes
519 521 added 1 changesets with 4 changes to 4 files
520 522 new changesets 8b6053c928fe
521 523
522 524 corrupt cookies file should yield a warning
523 525
524 526 $ cat > $TESTTMP/cookies.txt << EOF
525 527 > bad format
526 528 > EOF
527 529
528 530 $ hg --config auth.cookiefile=$TESTTMP/cookies.txt id http://localhost:$HGPORT/
529 531 (error loading cookie file $TESTTMP/cookies.txt: '*/cookies.txt' does not look like a Netscape format cookies file; continuing without cookies) (glob)
530 532 56f9bc90cce6
531 533
532 534 $ killdaemons.py
533 535
534 536 Create dummy authentication handler that looks for cookies. It doesn't do anything
535 537 useful. It just raises an HTTP 500 with details about the Cookie request header.
536 538 We raise HTTP 500 because its message is printed in the abort message.
537 539
538 540 $ cat > cookieauth.py << EOF
539 541 > from mercurial import util
540 542 > from mercurial.hgweb import common
541 543 > def perform_authentication(hgweb, req, op):
542 544 > cookie = req.headers.get(b'Cookie')
543 545 > if not cookie:
544 546 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'no-cookie')
545 547 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, b'Cookie: %s' % cookie)
546 548 > def extsetup(ui):
547 549 > common.permhooks.insert(0, perform_authentication)
548 550 > EOF
549 551
550 552 $ hg serve --config extensions.cookieauth=cookieauth.py -R test -p $HGPORT -d --pid-file=pid
551 553 $ cat pid > $DAEMON_PIDS
552 554
553 555 Request without cookie sent should fail due to lack of cookie
554 556
555 557 $ hg id http://localhost:$HGPORT
556 558 abort: HTTP Error 500: no-cookie
557 559 [255]
558 560
559 561 Populate a cookies file
560 562
561 563 $ cat > cookies.txt << EOF
562 564 > # HTTP Cookie File
563 565 > # Expiration is 2030-01-01 at midnight
564 566 > .example.com TRUE / FALSE 1893456000 hgkey examplevalue
565 567 > EOF
566 568
567 569 Should not send a cookie for another domain
568 570
569 571 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
570 572 abort: HTTP Error 500: no-cookie
571 573 [255]
572 574
573 575 Add a cookie entry for our test server and verify it is sent
574 576
575 577 $ cat >> cookies.txt << EOF
576 578 > localhost.local FALSE / FALSE 1893456000 hgkey localhostvalue
577 579 > EOF
578 580
579 581 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
580 582 abort: HTTP Error 500: Cookie: hgkey=localhostvalue
581 583 [255]
@@ -1,673 +1,675 b''
1 1 #require serve ssl
2 2
3 3 Proper https client requires the built-in ssl from Python 2.6.
4 4
5 5 Make server certificates:
6 6
7 7 $ CERTSDIR="$TESTDIR/sslcerts"
8 8 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub.pem" >> server.pem
9 9 $ PRIV=`pwd`/server.pem
10 10 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-not-yet.pem" > server-not-yet.pem
11 11 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-expired.pem" > server-expired.pem
12 12
13 13 $ hg init test
14 14 $ cd test
15 15 $ echo foo>foo
16 16 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
17 17 $ echo foo>foo.d/foo
18 18 $ echo bar>foo.d/bAr.hg.d/BaR
19 19 $ echo bar>foo.d/baR.d.hg/bAR
20 20 $ hg commit -A -m 1
21 21 adding foo
22 22 adding foo.d/bAr.hg.d/BaR
23 23 adding foo.d/baR.d.hg/bAR
24 24 adding foo.d/foo
25 25 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV
26 26 $ cat ../hg0.pid >> $DAEMON_PIDS
27 27
28 28 cacert not found
29 29
30 30 $ hg in --config web.cacerts=no-such.pem https://localhost:$HGPORT/
31 31 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
32 32 abort: could not find web.cacerts: no-such.pem
33 33 [255]
34 34
35 35 Test server address cannot be reused
36 36
37 37 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
38 38 abort: cannot start server at 'localhost:$HGPORT': $EADDRINUSE$
39 39 [255]
40 40
41 41 $ cd ..
42 42
43 43 Our test cert is not signed by a trusted CA. It should fail to verify if
44 44 we are able to load CA certs.
45 45
46 46 #if sslcontext defaultcacerts no-defaultcacertsloaded
47 47 $ hg clone https://localhost:$HGPORT/ copy-pull
48 48 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
49 49 abort: error: *certificate verify failed* (glob)
50 50 [255]
51 51 #endif
52 52
53 53 #if no-sslcontext defaultcacerts
54 54 $ hg clone https://localhost:$HGPORT/ copy-pull
55 55 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
56 56 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
57 57 abort: error: *certificate verify failed* (glob)
58 58 [255]
59 59 #endif
60 60
61 61 #if no-sslcontext windows
62 62 $ hg clone https://localhost:$HGPORT/ copy-pull
63 63 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
64 64 (unable to load Windows CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
65 65 abort: error: *certificate verify failed* (glob)
66 66 [255]
67 67 #endif
68 68
69 69 #if no-sslcontext osx
70 70 $ hg clone https://localhost:$HGPORT/ copy-pull
71 71 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
72 72 (unable to load CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
73 73 abort: localhost certificate error: no certificate received
74 74 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
75 75 [255]
76 76 #endif
77 77
78 78 #if defaultcacertsloaded
79 79 $ hg clone https://localhost:$HGPORT/ copy-pull
80 80 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
81 81 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
82 82 (the full certificate chain may not be available locally; see "hg help debugssl") (windows !)
83 83 abort: error: *certificate verify failed* (glob)
84 84 [255]
85 85 #endif
86 86
87 87 #if no-defaultcacerts
88 88 $ hg clone https://localhost:$HGPORT/ copy-pull
89 89 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
90 90 (unable to load * certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
91 91 abort: localhost certificate error: no certificate received
92 92 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
93 93 [255]
94 94 #endif
95 95
96 96 Specifying a per-host certificate file that doesn't exist will abort. The full
97 97 C:/path/to/msysroot will print on Windows.
98 98
99 99 $ hg --config hostsecurity.localhost:verifycertsfile=/does/not/exist clone https://localhost:$HGPORT/
100 100 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
101 101 abort: path specified by hostsecurity.localhost:verifycertsfile does not exist: */does/not/exist (glob)
102 102 [255]
103 103
104 104 A malformed per-host certificate file will raise an error
105 105
106 106 $ echo baddata > badca.pem
107 107 #if sslcontext
108 108 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
109 109 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
110 110 abort: error loading CA file badca.pem: * (glob)
111 111 (file is empty or malformed?)
112 112 [255]
113 113 #else
114 114 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
115 115 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
116 116 abort: error: * (glob)
117 117 [255]
118 118 #endif
119 119
120 120 A per-host certificate mismatching the server will fail verification
121 121
122 122 (modern ssl is able to discern whether the loaded cert is a CA cert)
123 123 #if sslcontext
124 124 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
125 125 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
126 126 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
127 127 (the full certificate chain may not be available locally; see "hg help debugssl") (windows !)
128 128 abort: error: *certificate verify failed* (glob)
129 129 [255]
130 130 #else
131 131 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
132 132 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
133 133 abort: error: *certificate verify failed* (glob)
134 134 [255]
135 135 #endif
136 136
137 137 A per-host certificate matching the server's cert will be accepted
138 138
139 139 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" clone -U https://localhost:$HGPORT/ perhostgood1
140 140 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
141 141 requesting all changes
142 142 adding changesets
143 143 adding manifests
144 144 adding file changes
145 145 added 1 changesets with 4 changes to 4 files
146 146 new changesets 8b6053c928fe
147 147
148 148 A per-host certificate with multiple certs and one matching will be accepted
149 149
150 150 $ cat "$CERTSDIR/client-cert.pem" "$CERTSDIR/pub.pem" > perhost.pem
151 151 $ hg --config hostsecurity.localhost:verifycertsfile=perhost.pem clone -U https://localhost:$HGPORT/ perhostgood2
152 152 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
153 153 requesting all changes
154 154 adding changesets
155 155 adding manifests
156 156 adding file changes
157 157 added 1 changesets with 4 changes to 4 files
158 158 new changesets 8b6053c928fe
159 159
160 160 Defining both per-host certificate and a fingerprint will print a warning
161 161
162 162 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 clone -U https://localhost:$HGPORT/ caandfingerwarning
163 163 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
164 164 (hostsecurity.localhost:verifycertsfile ignored when host fingerprints defined; using host fingerprints for verification)
165 165 requesting all changes
166 166 adding changesets
167 167 adding manifests
168 168 adding file changes
169 169 added 1 changesets with 4 changes to 4 files
170 170 new changesets 8b6053c928fe
171 171
172 172 $ DISABLECACERTS="--config devel.disableloaddefaultcerts=true"
173 173
174 174 Inability to verify peer certificate will result in abort
175 175
176 176 $ hg clone https://localhost:$HGPORT/ copy-pull $DISABLECACERTS
177 177 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
178 178 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
179 179 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
180 180 [255]
181 181
182 182 $ hg clone --insecure https://localhost:$HGPORT/ copy-pull
183 183 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
184 184 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
185 185 requesting all changes
186 186 adding changesets
187 187 adding manifests
188 188 adding file changes
189 189 added 1 changesets with 4 changes to 4 files
190 190 new changesets 8b6053c928fe
191 191 updating to branch default
192 192 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
193 193 $ hg verify -R copy-pull
194 194 checking changesets
195 195 checking manifests
196 196 crosschecking files in changesets and manifests
197 197 checking files
198 198 checked 1 changesets with 4 changes to 4 files
199 199 $ cd test
200 200 $ echo bar > bar
201 201 $ hg commit -A -d '1 0' -m 2
202 202 adding bar
203 203 $ cd ..
204 204
205 205 pull without cacert
206 206
207 207 $ cd copy-pull
208 208 $ cat >> .hg/hgrc <<EOF
209 209 > [hooks]
210 210 > changegroup = sh -c "printenv.py --line changegroup"
211 211 > EOF
212 212 $ hg pull $DISABLECACERTS
213 213 pulling from https://localhost:$HGPORT/
214 214 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
215 215 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
216 216 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
217 217 [255]
218 218
219 219 $ hg pull --insecure
220 220 pulling from https://localhost:$HGPORT/
221 221 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
222 222 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
223 223 searching for changes
224 224 adding changesets
225 225 adding manifests
226 226 adding file changes
227 227 added 1 changesets with 1 changes to 1 files
228 228 new changesets 5fed3813f7f5
229 229 changegroup hook: HG_HOOKNAME=changegroup
230 230 HG_HOOKTYPE=changegroup
231 231 HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
232 232 HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d
233 233 HG_SOURCE=pull
234 234 HG_TXNID=TXN:$ID$
235 HG_TXNNAME=pull
236 https://localhost:$HGPORT/
235 237 HG_URL=https://localhost:$HGPORT/
236 238
237 239 (run 'hg update' to get a working copy)
238 240 $ cd ..
239 241
240 242 cacert configured in local repo
241 243
242 244 $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu
243 245 $ echo "[web]" >> copy-pull/.hg/hgrc
244 246 $ echo "cacerts=$CERTSDIR/pub.pem" >> copy-pull/.hg/hgrc
245 247 $ hg -R copy-pull pull
246 248 pulling from https://localhost:$HGPORT/
247 249 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
248 250 searching for changes
249 251 no changes found
250 252 $ mv copy-pull/.hg/hgrc.bu copy-pull/.hg/hgrc
251 253
252 254 cacert configured globally, also testing expansion of environment
253 255 variables in the filename
254 256
255 257 $ echo "[web]" >> $HGRCPATH
256 258 $ echo 'cacerts=$P/pub.pem' >> $HGRCPATH
257 259 $ P="$CERTSDIR" hg -R copy-pull pull
258 260 pulling from https://localhost:$HGPORT/
259 261 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
260 262 searching for changes
261 263 no changes found
262 264 $ P="$CERTSDIR" hg -R copy-pull pull --insecure
263 265 pulling from https://localhost:$HGPORT/
264 266 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
265 267 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
266 268 searching for changes
267 269 no changes found
268 270
269 271 empty cacert file
270 272
271 273 $ touch emptycafile
272 274
273 275 #if sslcontext
274 276 $ hg --config web.cacerts=emptycafile -R copy-pull pull
275 277 pulling from https://localhost:$HGPORT/
276 278 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
277 279 abort: error loading CA file emptycafile: * (glob)
278 280 (file is empty or malformed?)
279 281 [255]
280 282 #else
281 283 $ hg --config web.cacerts=emptycafile -R copy-pull pull
282 284 pulling from https://localhost:$HGPORT/
283 285 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
284 286 abort: error: * (glob)
285 287 [255]
286 288 #endif
287 289
288 290 cacert mismatch
289 291
290 292 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
291 293 > https://$LOCALIP:$HGPORT/
292 294 pulling from https://*:$HGPORT/ (glob)
293 295 warning: connecting to $LOCALIP using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
294 296 abort: $LOCALIP certificate error: certificate is for localhost (glob)
295 297 (set hostsecurity.$LOCALIP:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
296 298 [255]
297 299 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
298 300 > https://$LOCALIP:$HGPORT/ --insecure
299 301 pulling from https://*:$HGPORT/ (glob)
300 302 warning: connecting to $LOCALIP using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
301 303 warning: connection security to $LOCALIP is disabled per current settings; communication is susceptible to eavesdropping and tampering (glob)
302 304 searching for changes
303 305 no changes found
304 306 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem"
305 307 pulling from https://localhost:$HGPORT/
306 308 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
307 309 (the full certificate chain may not be available locally; see "hg help debugssl") (windows !)
308 310 abort: error: *certificate verify failed* (glob)
309 311 [255]
310 312 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem" \
311 313 > --insecure
312 314 pulling from https://localhost:$HGPORT/
313 315 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
314 316 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
315 317 searching for changes
316 318 no changes found
317 319
318 320 Test server cert which isn't valid yet
319 321
320 322 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem
321 323 $ cat hg1.pid >> $DAEMON_PIDS
322 324 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-not-yet.pem" \
323 325 > https://localhost:$HGPORT1/
324 326 pulling from https://localhost:$HGPORT1/
325 327 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
326 328 (the full certificate chain may not be available locally; see "hg help debugssl") (windows !)
327 329 abort: error: *certificate verify failed* (glob)
328 330 [255]
329 331
330 332 Test server cert which no longer is valid
331 333
332 334 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
333 335 $ cat hg2.pid >> $DAEMON_PIDS
334 336 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-expired.pem" \
335 337 > https://localhost:$HGPORT2/
336 338 pulling from https://localhost:$HGPORT2/
337 339 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
338 340 (the full certificate chain may not be available locally; see "hg help debugssl") (windows !)
339 341 abort: error: *certificate verify failed* (glob)
340 342 [255]
341 343
342 344 Disabling the TLS 1.0 warning works
343 345 $ hg -R copy-pull id https://localhost:$HGPORT/ \
344 346 > --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 \
345 347 > --config hostsecurity.disabletls10warning=true
346 348 5fed3813f7f5
347 349
348 350 Error message for setting ciphers is different depending on SSLContext support
349 351
350 352 #if no-sslcontext
351 353 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=invalid -R copy-pull id https://localhost:$HGPORT/
352 354 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
353 355 abort: *No cipher can be selected. (glob)
354 356 [255]
355 357
356 358 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=HIGH -R copy-pull id https://localhost:$HGPORT/
357 359 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
358 360 5fed3813f7f5
359 361 #endif
360 362
361 363 #if sslcontext
362 364 Setting ciphers to an invalid value aborts
363 365 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=invalid -R copy-pull id https://localhost:$HGPORT/
364 366 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
365 367 abort: could not set ciphers: No cipher can be selected.
366 368 (change cipher string (invalid) in config)
367 369 [255]
368 370
369 371 $ P="$CERTSDIR" hg --config hostsecurity.localhost:ciphers=invalid -R copy-pull id https://localhost:$HGPORT/
370 372 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
371 373 abort: could not set ciphers: No cipher can be selected.
372 374 (change cipher string (invalid) in config)
373 375 [255]
374 376
375 377 Changing the cipher string works
376 378
377 379 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=HIGH -R copy-pull id https://localhost:$HGPORT/
378 380 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
379 381 5fed3813f7f5
380 382 #endif
381 383
382 384 Fingerprints
383 385
384 386 - works without cacerts (hostfingerprints)
385 387 $ hg -R copy-pull id https://localhost:$HGPORT/ --insecure --config hostfingerprints.localhost=ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
386 388 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
387 389 (SHA-1 fingerprint for localhost found in legacy [hostfingerprints] section; if you trust this fingerprint, remove the old SHA-1 fingerprint from [hostfingerprints] and add the following entry to the new [hostsecurity] section: localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
388 390 5fed3813f7f5
389 391
390 392 - works without cacerts (hostsecurity)
391 393 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
392 394 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
393 395 5fed3813f7f5
394 396
395 397 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e
396 398 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
397 399 5fed3813f7f5
398 400
399 401 - multiple fingerprints specified and first matches
400 402 $ hg --config 'hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
401 403 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
402 404 (SHA-1 fingerprint for localhost found in legacy [hostfingerprints] section; if you trust this fingerprint, remove the old SHA-1 fingerprint from [hostfingerprints] and add the following entry to the new [hostsecurity] section: localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
403 405 5fed3813f7f5
404 406
405 407 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
406 408 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
407 409 5fed3813f7f5
408 410
409 411 - multiple fingerprints specified and last matches
410 412 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/ --insecure
411 413 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
412 414 (SHA-1 fingerprint for localhost found in legacy [hostfingerprints] section; if you trust this fingerprint, remove the old SHA-1 fingerprint from [hostfingerprints] and add the following entry to the new [hostsecurity] section: localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
413 415 5fed3813f7f5
414 416
415 417 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/
416 418 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
417 419 5fed3813f7f5
418 420
419 421 - multiple fingerprints specified and none match
420 422
421 423 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
422 424 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
423 425 abort: certificate for localhost has unexpected fingerprint ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
424 426 (check hostfingerprint configuration)
425 427 [255]
426 428
427 429 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
428 430 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
429 431 abort: certificate for localhost has unexpected fingerprint sha1:ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
430 432 (check hostsecurity configuration)
431 433 [255]
432 434
433 435 - fails when cert doesn't match hostname (port is ignored)
434 436 $ hg -R copy-pull id https://localhost:$HGPORT1/ --config hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
435 437 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
436 438 abort: certificate for localhost has unexpected fingerprint f4:2f:5a:0c:3e:52:5b:db:e7:24:a8:32:1d:18:97:6d:69:b5:87:84
437 439 (check hostfingerprint configuration)
438 440 [255]
439 441
440 442
441 443 - ignores that certificate doesn't match hostname
442 444 $ hg -R copy-pull id https://$LOCALIP:$HGPORT/ --config hostfingerprints.$LOCALIP=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
443 445 warning: connecting to $LOCALIP using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
444 446 (SHA-1 fingerprint for $LOCALIP found in legacy [hostfingerprints] section; if you trust this fingerprint, remove the old SHA-1 fingerprint from [hostfingerprints] and add the following entry to the new [hostsecurity] section: $LOCALIP:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
445 447 5fed3813f7f5
446 448
447 449 Ports used by next test. Kill servers.
448 450
449 451 $ killdaemons.py hg0.pid
450 452 $ killdaemons.py hg1.pid
451 453 $ killdaemons.py hg2.pid
452 454
453 455 #if sslcontext tls1.2
454 456 Start servers running supported TLS versions
455 457
456 458 $ cd test
457 459 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
458 460 > --config devel.serverexactprotocol=tls1.0
459 461 $ cat ../hg0.pid >> $DAEMON_PIDS
460 462 $ hg serve -p $HGPORT1 -d --pid-file=../hg1.pid --certificate=$PRIV \
461 463 > --config devel.serverexactprotocol=tls1.1
462 464 $ cat ../hg1.pid >> $DAEMON_PIDS
463 465 $ hg serve -p $HGPORT2 -d --pid-file=../hg2.pid --certificate=$PRIV \
464 466 > --config devel.serverexactprotocol=tls1.2
465 467 $ cat ../hg2.pid >> $DAEMON_PIDS
466 468 $ cd ..
467 469
468 470 Clients talking same TLS versions work
469 471
470 472 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.0 id https://localhost:$HGPORT/
471 473 5fed3813f7f5
472 474 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT1/
473 475 5fed3813f7f5
474 476 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT2/
475 477 5fed3813f7f5
476 478
477 479 Clients requiring newer TLS version than what server supports fail
478 480
479 481 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
480 482 (could not negotiate a common security protocol (tls1.1+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
481 483 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
482 484 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
483 485 abort: error: *unsupported protocol* (glob)
484 486 [255]
485 487
486 488 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT/
487 489 (could not negotiate a common security protocol (tls1.1+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
488 490 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
489 491 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
490 492 abort: error: *unsupported protocol* (glob)
491 493 [255]
492 494 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT/
493 495 (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
494 496 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
495 497 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
496 498 abort: error: *unsupported protocol* (glob)
497 499 [255]
498 500 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT1/
499 501 (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
500 502 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
501 503 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
502 504 abort: error: *unsupported protocol* (glob)
503 505 [255]
504 506
505 507 --insecure will allow TLS 1.0 connections and override configs
506 508
507 509 $ hg --config hostsecurity.minimumprotocol=tls1.2 id --insecure https://localhost:$HGPORT1/
508 510 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
509 511 5fed3813f7f5
510 512
511 513 The per-host config option overrides the default
512 514
513 515 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
514 516 > --config hostsecurity.minimumprotocol=tls1.2 \
515 517 > --config hostsecurity.localhost:minimumprotocol=tls1.0
516 518 5fed3813f7f5
517 519
518 520 The per-host config option by itself works
519 521
520 522 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
521 523 > --config hostsecurity.localhost:minimumprotocol=tls1.2
522 524 (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
523 525 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
524 526 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
525 527 abort: error: *unsupported protocol* (glob)
526 528 [255]
527 529
528 530 .hg/hgrc file [hostsecurity] settings are applied to remote ui instances (issue5305)
529 531
530 532 $ cat >> copy-pull/.hg/hgrc << EOF
531 533 > [hostsecurity]
532 534 > localhost:minimumprotocol=tls1.2
533 535 > EOF
534 536 $ P="$CERTSDIR" hg -R copy-pull id https://localhost:$HGPORT/
535 537 (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
536 538 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
537 539 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
538 540 abort: error: *unsupported protocol* (glob)
539 541 [255]
540 542
541 543 $ killdaemons.py hg0.pid
542 544 $ killdaemons.py hg1.pid
543 545 $ killdaemons.py hg2.pid
544 546 #endif
545 547
546 548 Prepare for connecting through proxy
547 549
548 550 $ hg serve -R test -p $HGPORT -d --pid-file=hg0.pid --certificate=$PRIV
549 551 $ cat hg0.pid >> $DAEMON_PIDS
550 552 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
551 553 $ cat hg2.pid >> $DAEMON_PIDS
552 554 tinyproxy.py doesn't fully detach, so killing it may result in extra output
553 555 from the shell. So don't kill it.
554 556 $ tinyproxy.py $HGPORT1 localhost >proxy.log </dev/null 2>&1 &
555 557 $ while [ ! -f proxy.pid ]; do sleep 0; done
556 558 $ cat proxy.pid >> $DAEMON_PIDS
557 559
558 560 $ echo "[http_proxy]" >> copy-pull/.hg/hgrc
559 561 $ echo "always=True" >> copy-pull/.hg/hgrc
560 562 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
561 563 $ echo "localhost =" >> copy-pull/.hg/hgrc
562 564
563 565 Test unvalidated https through proxy
564 566
565 567 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure
566 568 pulling from https://localhost:$HGPORT/
567 569 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
568 570 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
569 571 searching for changes
570 572 no changes found
571 573
572 574 Test https with cacert and fingerprint through proxy
573 575
574 576 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
575 577 > --config web.cacerts="$CERTSDIR/pub.pem"
576 578 pulling from https://localhost:$HGPORT/
577 579 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
578 580 searching for changes
579 581 no changes found
580 582 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://localhost:$HGPORT/ --config hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 --trace
581 583 pulling from https://*:$HGPORT/ (glob)
582 584 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
583 585 (SHA-1 fingerprint for localhost found in legacy [hostfingerprints] section; if you trust this fingerprint, remove the old SHA-1 fingerprint from [hostfingerprints] and add the following entry to the new [hostsecurity] section: localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
584 586 searching for changes
585 587 no changes found
586 588
587 589 Test https with cert problems through proxy
588 590
589 591 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
590 592 > --config web.cacerts="$CERTSDIR/pub-other.pem"
591 593 pulling from https://localhost:$HGPORT/
592 594 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
593 595 (the full certificate chain may not be available locally; see "hg help debugssl") (windows !)
594 596 abort: error: *certificate verify failed* (glob)
595 597 [255]
596 598 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
597 599 > --config web.cacerts="$CERTSDIR/pub-expired.pem" https://localhost:$HGPORT2/
598 600 pulling from https://localhost:$HGPORT2/
599 601 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
600 602 (the full certificate chain may not be available locally; see "hg help debugssl") (windows !)
601 603 abort: error: *certificate verify failed* (glob)
602 604 [255]
603 605
604 606
605 607 $ killdaemons.py hg0.pid
606 608
607 609 #if sslcontext
608 610
609 611 $ cd test
610 612
611 613 Missing certificate file(s) are detected
612 614
613 615 $ hg serve -p $HGPORT --certificate=/missing/certificate \
614 616 > --config devel.servercafile=$PRIV --config devel.serverrequirecert=true
615 617 abort: referenced certificate file (*/missing/certificate) does not exist (glob)
616 618 [255]
617 619
618 620 $ hg serve -p $HGPORT --certificate=$PRIV \
619 621 > --config devel.servercafile=/missing/cafile --config devel.serverrequirecert=true
620 622 abort: referenced certificate file (*/missing/cafile) does not exist (glob)
621 623 [255]
622 624
623 625 Start hgweb that requires client certificates:
624 626
625 627 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
626 628 > --config devel.servercafile=$PRIV --config devel.serverrequirecert=true
627 629 $ cat ../hg0.pid >> $DAEMON_PIDS
628 630 $ cd ..
629 631
630 632 without client certificate:
631 633
632 634 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
633 635 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
634 636 abort: error: *handshake failure* (glob)
635 637 [255]
636 638
637 639 with client certificate:
638 640
639 641 $ cat << EOT >> $HGRCPATH
640 642 > [auth]
641 643 > l.prefix = localhost
642 644 > l.cert = $CERTSDIR/client-cert.pem
643 645 > l.key = $CERTSDIR/client-key.pem
644 646 > EOT
645 647
646 648 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
647 649 > --config auth.l.key="$CERTSDIR/client-key-decrypted.pem"
648 650 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
649 651 5fed3813f7f5
650 652
651 653 $ printf '1234\n' | env P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
652 654 > --config ui.interactive=True --config ui.nontty=True
653 655 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
654 656 passphrase for */client-key.pem: 5fed3813f7f5 (glob)
655 657
656 658 $ env P="$CERTSDIR" hg id https://localhost:$HGPORT/
657 659 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
658 660 abort: error: * (glob)
659 661 [255]
660 662
661 663 Missing certficate and key files result in error
662 664
663 665 $ hg id https://localhost:$HGPORT/ --config auth.l.cert=/missing/cert
664 666 abort: certificate file (*/missing/cert) does not exist; cannot connect to localhost (glob)
665 667 (restore missing file or fix references in Mercurial config)
666 668 [255]
667 669
668 670 $ hg id https://localhost:$HGPORT/ --config auth.l.key=/missing/key
669 671 abort: certificate file (*/missing/key) does not exist; cannot connect to localhost (glob)
670 672 (restore missing file or fix references in Mercurial config)
671 673 [255]
672 674
673 675 #endif
@@ -1,540 +1,555 b''
1 1 #require no-chg
2 2
3 3 #testcases bundle1 bundle2
4 4
5 5 #if bundle1
6 6 $ cat << EOF >> $HGRCPATH
7 7 > [devel]
8 8 > # This test is dedicated to interaction through old bundle
9 9 > legacy.exchange = bundle1
10 10 > EOF
11 11 #endif
12 12
13 13 $ hg init test
14 14 $ cd test
15 15 $ echo a > a
16 16 $ hg ci -Ama
17 17 adding a
18 18 $ cd ..
19 19 $ hg clone test test2
20 20 updating to branch default
21 21 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 22 $ cd test2
23 23 $ echo a >> a
24 24 $ hg ci -mb
25 25 $ req() {
26 26 > hg $1 serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
27 27 > cat hg.pid >> $DAEMON_PIDS
28 28 > hg --cwd ../test2 push http://localhost:$HGPORT/
29 29 > exitstatus=$?
30 30 > killdaemons.py
31 31 > echo % serve errors
32 32 > cat errors.log
33 33 > return $exitstatus
34 34 > }
35 35 $ cd ../test
36 36
37 37 expect ssl error
38 38
39 39 $ req
40 40 pushing to http://localhost:$HGPORT/
41 41 searching for changes
42 42 abort: HTTP Error 403: ssl required
43 43 % serve errors
44 44 [255]
45 45
46 46 expect authorization error
47 47
48 48 $ echo '[web]' > .hg/hgrc
49 49 $ echo 'push_ssl = false' >> .hg/hgrc
50 50 $ req
51 51 pushing to http://localhost:$HGPORT/
52 52 searching for changes
53 53 abort: authorization failed
54 54 % serve errors
55 55 [255]
56 56
57 57 expect authorization error: must have authorized user
58 58
59 59 $ echo 'allow_push = unperson' >> .hg/hgrc
60 60 $ req
61 61 pushing to http://localhost:$HGPORT/
62 62 searching for changes
63 63 abort: authorization failed
64 64 % serve errors
65 65 [255]
66 66
67 67 expect success
68 68
69 69 $ cat > $TESTTMP/hook.sh <<'EOF'
70 70 > echo "phase-move: $HG_NODE: $HG_OLDPHASE -> $HG_PHASE"
71 71 > EOF
72 72
73 73 #if bundle1
74 74 $ cat >> .hg/hgrc <<EOF
75 75 > allow_push = *
76 76 > [hooks]
77 77 > changegroup = sh -c "printenv.py --line changegroup 0"
78 78 > pushkey = sh -c "printenv.py --line pushkey 0"
79 79 > txnclose-phase.test = sh $TESTTMP/hook.sh
80 80 > EOF
81 81 $ req "--debug --config extensions.blackbox="
82 82 listening at http://*:$HGPORT/ (bound to $LOCALIP:$HGPORT) (glob) (?)
83 83 pushing to http://localhost:$HGPORT/
84 84 searching for changes
85 85 remote: redirecting incoming bundle to */hg-unbundle-* (glob)
86 86 remote: adding changesets
87 87 remote: add changeset ba677d0156c1
88 88 remote: adding manifests
89 89 remote: adding file changes
90 90 remote: adding a revisions
91 91 remote: added 1 changesets with 1 changes to 1 files
92 92 remote: updating the branch cache
93 93 remote: running hook txnclose-phase.test: sh $TESTTMP/hook.sh
94 94 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
95 95 remote: running hook txnclose-phase.test: sh $TESTTMP/hook.sh
96 96 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
97 97 remote: running hook changegroup: sh -c "printenv.py --line changegroup 0"
98 98 remote: changegroup hook: HG_HOOKNAME=changegroup
99 99 remote: HG_HOOKTYPE=changegroup
100 100 remote: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872
101 101 remote: HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872
102 102 remote: HG_SOURCE=serve
103 103 remote: HG_TXNID=TXN:$ID$
104 remote: HG_TXNNAME=serve
105 remote: remote:http:$LOCALIP: (glob)
104 106 remote: HG_URL=remote:http:$LOCALIP: (glob)
105 107 remote:
106 108 % serve errors
107 109 $ hg rollback
108 110 repository tip rolled back to revision 0 (undo serve)
109 111 $ req "--debug --config server.streamunbundle=True --config extensions.blackbox="
110 112 listening at http://*:$HGPORT/ (bound to $LOCALIP:$HGPORT) (glob) (?)
111 113 pushing to http://localhost:$HGPORT/
112 114 searching for changes
113 115 remote: adding changesets
114 116 remote: add changeset ba677d0156c1
115 117 remote: adding manifests
116 118 remote: adding file changes
117 119 remote: adding a revisions
118 120 remote: added 1 changesets with 1 changes to 1 files
119 121 remote: updating the branch cache
120 122 remote: running hook txnclose-phase.test: sh $TESTTMP/hook.sh
121 123 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
122 124 remote: running hook txnclose-phase.test: sh $TESTTMP/hook.sh
123 125 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
124 126 remote: running hook changegroup: sh -c "printenv.py --line changegroup 0"
125 127 remote: changegroup hook: HG_HOOKNAME=changegroup
126 128 remote: HG_HOOKTYPE=changegroup
127 129 remote: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872
128 130 remote: HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872
129 131 remote: HG_SOURCE=serve
130 132 remote: HG_TXNID=TXN:$ID$
133 remote: HG_TXNNAME=serve
134 remote: remote:http:$LOCALIP: (glob)
131 135 remote: HG_URL=remote:http:$LOCALIP: (glob)
132 136 remote:
133 137 % serve errors
134 138 $ hg rollback
135 139 repository tip rolled back to revision 0 (undo serve)
136 140 #endif
137 141
138 142 #if bundle2
139 143 $ cat >> .hg/hgrc <<EOF
140 144 > allow_push = *
141 145 > [hooks]
142 146 > changegroup = sh -c "printenv.py --line changegroup 0"
143 147 > pushkey = sh -c "printenv.py --line pushkey 0"
144 148 > txnclose-phase.test = sh $TESTTMP/hook.sh
145 149 > EOF
146 150 $ req
147 151 pushing to http://localhost:$HGPORT/
148 152 searching for changes
149 153 remote: adding changesets
150 154 remote: adding manifests
151 155 remote: adding file changes
152 156 remote: added 1 changesets with 1 changes to 1 files
153 157 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
154 158 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
155 159 remote: changegroup hook: HG_BUNDLE2=1
156 160 remote: HG_HOOKNAME=changegroup
157 161 remote: HG_HOOKTYPE=changegroup
158 162 remote: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872
159 163 remote: HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872
160 164 remote: HG_SOURCE=serve
161 165 remote: HG_TXNID=TXN:$ID$
166 remote: HG_TXNNAME=serve
162 167 remote: HG_URL=remote:http:$LOCALIP: (glob)
163 168 remote:
164 169 % serve errors
165 170 $ hg rollback
166 171 repository tip rolled back to revision 0 (undo serve)
167 172 #endif
168 173
169 174 expect success, server lacks the httpheader capability
170 175
171 176 $ CAP=httpheader
172 177 $ . "$TESTDIR/notcapable"
173 178 $ req
174 179 pushing to http://localhost:$HGPORT/
175 180 searching for changes
176 181 remote: adding changesets
177 182 remote: adding manifests
178 183 remote: adding file changes
179 184 remote: added 1 changesets with 1 changes to 1 files
180 185 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
181 186 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
182 187 remote: changegroup hook: HG_HOOKNAME=changegroup (no-bundle2 !)
183 188 remote: changegroup hook: HG_BUNDLE2=1 (bundle2 !)
184 189 remote: HG_HOOKNAME=changegroup (bundle2 !)
185 190 remote: HG_HOOKTYPE=changegroup
186 191 remote: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872
187 192 remote: HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872
188 193 remote: HG_SOURCE=serve
189 194 remote: HG_TXNID=TXN:$ID$
195 remote: HG_TXNNAME=serve
196 remote: remote:http:$LOCALIP: (glob) (no-bundle2 !)
190 197 remote: HG_URL=remote:http:$LOCALIP: (glob)
191 198 remote:
192 199 % serve errors
193 200 $ hg rollback
194 201 repository tip rolled back to revision 0 (undo serve)
195 202
196 203 expect success, server lacks the unbundlehash capability
197 204
198 205 $ CAP=unbundlehash
199 206 $ . "$TESTDIR/notcapable"
200 207 $ req
201 208 pushing to http://localhost:$HGPORT/
202 209 searching for changes
203 210 remote: adding changesets
204 211 remote: adding manifests
205 212 remote: adding file changes
206 213 remote: added 1 changesets with 1 changes to 1 files
207 214 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
208 215 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
209 216 remote: changegroup hook: HG_HOOKNAME=changegroup (no-bundle2 !)
210 217 remote: changegroup hook: HG_BUNDLE2=1 (bundle2 !)
211 218 remote: HG_HOOKNAME=changegroup (bundle2 !)
212 219 remote: HG_HOOKTYPE=changegroup
213 220 remote: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872
214 221 remote: HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872
215 222 remote: HG_SOURCE=serve
216 223 remote: HG_TXNID=TXN:$ID$
224 remote: HG_TXNNAME=serve
225 remote: remote:http:$LOCALIP: (glob) (no-bundle2 !)
217 226 remote: HG_URL=remote:http:$LOCALIP: (glob)
218 227 remote:
219 228 % serve errors
220 229 $ hg rollback
221 230 repository tip rolled back to revision 0 (undo serve)
222 231
223 232 expect success, pre-d1b16a746db6 server supports the unbundle capability, but
224 233 has no parameter
225 234
226 235 $ cat <<EOF > notcapable-unbundleparam.py
227 236 > from mercurial import extensions, httppeer
228 237 > def capable(orig, self, name):
229 238 > if name == 'unbundle':
230 239 > return True
231 240 > return orig(self, name)
232 241 > def uisetup(ui):
233 242 > extensions.wrapfunction(httppeer.httppeer, 'capable', capable)
234 243 > EOF
235 244 $ cp $HGRCPATH $HGRCPATH.orig
236 245 $ cat <<EOF >> $HGRCPATH
237 246 > [extensions]
238 247 > notcapable-unbundleparam = `pwd`/notcapable-unbundleparam.py
239 248 > EOF
240 249 $ req
241 250 pushing to http://localhost:$HGPORT/
242 251 searching for changes
243 252 remote: adding changesets
244 253 remote: adding manifests
245 254 remote: adding file changes
246 255 remote: added 1 changesets with 1 changes to 1 files
247 256 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
248 257 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
249 258 remote: changegroup hook: * (glob)
250 259 remote: HG_HOOKNAME=changegroup (bundle2 !)
251 260 remote: HG_HOOKTYPE=changegroup
252 261 remote: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872
253 262 remote: HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872
254 263 remote: HG_SOURCE=serve
255 264 remote: HG_TXNID=TXN:$ID$
265 remote: HG_TXNNAME=serve
266 remote: remote:http:$LOCALIP: (glob) (no-bundle2 !)
256 267 remote: HG_URL=remote:http:$LOCALIP: (glob)
257 268 remote:
258 269 % serve errors
259 270 $ hg rollback
260 271 repository tip rolled back to revision 0 (undo serve)
261 272 $ mv $HGRCPATH.orig $HGRCPATH
262 273
263 274 Test pushing to a publishing repository with a failing prepushkey hook
264 275
265 276 $ cat > .hg/hgrc <<EOF
266 277 > [web]
267 278 > push_ssl = false
268 279 > allow_push = *
269 280 > [hooks]
270 281 > prepushkey = sh -c "printenv.py --line prepushkey 1"
271 282 > [devel]
272 283 > legacy.exchange=phases
273 284 > EOF
274 285
275 286 #if bundle1
276 287 Bundle1 works because a) phases are updated as part of changegroup application
277 288 and b) client checks phases after the "unbundle" command. Since it sees no
278 289 phase changes are necessary, it doesn't send the "pushkey" command and the
279 290 prepushkey hook never has to fire.
280 291
281 292 $ req
282 293 pushing to http://localhost:$HGPORT/
283 294 searching for changes
284 295 remote: adding changesets
285 296 remote: adding manifests
286 297 remote: adding file changes
287 298 remote: added 1 changesets with 1 changes to 1 files
288 299 % serve errors
289 300
290 301 #endif
291 302
292 303 #if bundle2
293 304 Bundle2 sends a "pushkey" bundle2 part. This runs as part of the transaction
294 305 and fails the entire push.
295 306 $ req
296 307 pushing to http://localhost:$HGPORT/
297 308 searching for changes
298 309 remote: adding changesets
299 310 remote: adding manifests
300 311 remote: adding file changes
301 312 remote: added 1 changesets with 1 changes to 1 files
302 313 remote: prepushkey hook: HG_BUNDLE2=1
303 314 remote: HG_HOOKNAME=prepushkey
304 315 remote: HG_HOOKTYPE=prepushkey
305 316 remote: HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872
306 317 remote: HG_NAMESPACE=phases
307 318 remote: HG_NEW=0
308 319 remote: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872
309 320 remote: HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872
310 321 remote: HG_OLD=1
311 322 remote: HG_PENDING=$TESTTMP/test
312 323 remote: HG_PHASES_MOVED=1
313 324 remote: HG_SOURCE=serve
314 325 remote: HG_TXNID=TXN:$ID$
326 remote: HG_TXNNAME=serve
315 327 remote: HG_URL=remote:http:$LOCALIP: (glob)
316 328 remote:
317 329 remote: pushkey-abort: prepushkey hook exited with status 1
318 330 remote: transaction abort!
319 331 remote: rollback completed
320 332 abort: updating ba677d0156c1 to public failed
321 333 % serve errors
322 334 [255]
323 335
324 336 #endif
325 337
326 338 Now remove the failing prepushkey hook.
327 339
328 340 $ cat >> .hg/hgrc <<EOF
329 341 > [hooks]
330 342 > prepushkey = sh -c "printenv.py --line prepushkey 0"
331 343 > EOF
332 344
333 345 We don't need to test bundle1 because it succeeded above.
334 346
335 347 #if bundle2
336 348 $ req
337 349 pushing to http://localhost:$HGPORT/
338 350 searching for changes
339 351 remote: adding changesets
340 352 remote: adding manifests
341 353 remote: adding file changes
342 354 remote: added 1 changesets with 1 changes to 1 files
343 355 remote: prepushkey hook: HG_BUNDLE2=1
344 356 remote: HG_HOOKNAME=prepushkey
345 357 remote: HG_HOOKTYPE=prepushkey
346 358 remote: HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872
347 359 remote: HG_NAMESPACE=phases
348 360 remote: HG_NEW=0
349 361 remote: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872
350 362 remote: HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872
351 363 remote: HG_OLD=1
352 364 remote: HG_PENDING=$TESTTMP/test
353 365 remote: HG_PHASES_MOVED=1
354 366 remote: HG_SOURCE=serve
355 367 remote: HG_TXNID=TXN:$ID$
368 remote: HG_TXNNAME=serve
356 369 remote: HG_URL=remote:http:$LOCALIP: (glob)
357 370 remote:
358 371 % serve errors
359 372 #endif
360 373
361 374 $ hg --config extensions.strip= strip -r 1:
362 375 saved backup bundle to $TESTTMP/test/.hg/strip-backup/ba677d0156c1-eea704d7-backup.hg
363 376
364 377 Now do a variant of the above, except on a non-publishing repository
365 378
366 379 $ cat >> .hg/hgrc <<EOF
367 380 > [phases]
368 381 > publish = false
369 382 > [hooks]
370 383 > prepushkey = sh -c "printenv.py --line prepushkey 1"
371 384 > EOF
372 385
373 386 #if bundle1
374 387 $ req
375 388 pushing to http://localhost:$HGPORT/
376 389 searching for changes
377 390 remote: adding changesets
378 391 remote: adding manifests
379 392 remote: adding file changes
380 393 remote: added 1 changesets with 1 changes to 1 files
381 394 remote: prepushkey hook: HG_HOOKNAME=prepushkey
382 395 remote: HG_HOOKTYPE=prepushkey
383 396 remote: HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872
384 397 remote: HG_NAMESPACE=phases
385 398 remote: HG_NEW=0
386 399 remote: HG_OLD=1
387 400 remote:
388 401 remote: pushkey-abort: prepushkey hook exited with status 1
389 402 updating ba677d0156c1 to public failed!
390 403 % serve errors
391 404 #endif
392 405
393 406 #if bundle2
394 407 $ req
395 408 pushing to http://localhost:$HGPORT/
396 409 searching for changes
397 410 remote: adding changesets
398 411 remote: adding manifests
399 412 remote: adding file changes
400 413 remote: added 1 changesets with 1 changes to 1 files
401 414 remote: prepushkey hook: HG_BUNDLE2=1
402 415 remote: HG_HOOKNAME=prepushkey
403 416 remote: HG_HOOKTYPE=prepushkey
404 417 remote: HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872
405 418 remote: HG_NAMESPACE=phases
406 419 remote: HG_NEW=0
407 420 remote: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872
408 421 remote: HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872
409 422 remote: HG_OLD=1
410 423 remote: HG_PENDING=$TESTTMP/test
411 424 remote: HG_PHASES_MOVED=1
412 425 remote: HG_SOURCE=serve
413 426 remote: HG_TXNID=TXN:$ID$
427 remote: HG_TXNNAME=serve
414 428 remote: HG_URL=remote:http:$LOCALIP: (glob)
415 429 remote:
416 430 remote: pushkey-abort: prepushkey hook exited with status 1
417 431 remote: transaction abort!
418 432 remote: rollback completed
419 433 abort: updating ba677d0156c1 to public failed
420 434 % serve errors
421 435 [255]
422 436 #endif
423 437
424 438 Make phases updates work
425 439
426 440 $ cat >> .hg/hgrc <<EOF
427 441 > [hooks]
428 442 > prepushkey = sh -c "printenv.py --line prepushkey 0"
429 443 > EOF
430 444
431 445 #if bundle1
432 446 $ req
433 447 pushing to http://localhost:$HGPORT/
434 448 searching for changes
435 449 no changes found
436 450 remote: prepushkey hook: HG_HOOKNAME=prepushkey
437 451 remote: HG_HOOKTYPE=prepushkey
438 452 remote: HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872
439 453 remote: HG_NAMESPACE=phases
440 454 remote: HG_NEW=0
441 455 remote: HG_OLD=1
442 456 remote:
443 457 % serve errors
444 458 [1]
445 459 #endif
446 460
447 461 #if bundle2
448 462 $ req
449 463 pushing to http://localhost:$HGPORT/
450 464 searching for changes
451 465 remote: adding changesets
452 466 remote: adding manifests
453 467 remote: adding file changes
454 468 remote: added 1 changesets with 1 changes to 1 files
455 469 remote: prepushkey hook: HG_BUNDLE2=1
456 470 remote: HG_HOOKNAME=prepushkey
457 471 remote: HG_HOOKTYPE=prepushkey
458 472 remote: HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872
459 473 remote: HG_NAMESPACE=phases
460 474 remote: HG_NEW=0
461 475 remote: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872
462 476 remote: HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872
463 477 remote: HG_OLD=1
464 478 remote: HG_PENDING=$TESTTMP/test
465 479 remote: HG_PHASES_MOVED=1
466 480 remote: HG_SOURCE=serve
467 481 remote: HG_TXNID=TXN:$ID$
482 remote: HG_TXNNAME=serve
468 483 remote: HG_URL=remote:http:$LOCALIP: (glob)
469 484 remote:
470 485 % serve errors
471 486 #endif
472 487
473 488 $ hg --config extensions.strip= strip -r 1:
474 489 saved backup bundle to $TESTTMP/test/.hg/strip-backup/ba677d0156c1-eea704d7-backup.hg
475 490
476 491 #if bundle2
477 492
478 493 $ cat > .hg/hgrc <<EOF
479 494 > [web]
480 495 > push_ssl = false
481 496 > allow_push = *
482 497 > [experimental]
483 498 > httppostargs=true
484 499 > EOF
485 500 $ req
486 501 pushing to http://localhost:$HGPORT/
487 502 searching for changes
488 503 remote: adding changesets
489 504 remote: adding manifests
490 505 remote: adding file changes
491 506 remote: added 1 changesets with 1 changes to 1 files
492 507 % serve errors
493 508
494 509 #endif
495 510
496 511 $ cd ..
497 512
498 513 Pushing via hgwebdir works
499 514
500 515 $ hg init hgwebdir
501 516 $ cd hgwebdir
502 517 $ echo 0 > a
503 518 $ hg -q commit -A -m initial
504 519 $ cd ..
505 520
506 521 $ cat > web.conf << EOF
507 522 > [paths]
508 523 > / = *
509 524 > [web]
510 525 > push_ssl = false
511 526 > allow_push = *
512 527 > EOF
513 528
514 529 $ hg serve --web-conf web.conf -p $HGPORT -d --pid-file hg.pid
515 530 $ cat hg.pid >> $DAEMON_PIDS
516 531
517 532 $ hg clone http://localhost:$HGPORT/hgwebdir hgwebdir-local
518 533 requesting all changes
519 534 adding changesets
520 535 adding manifests
521 536 adding file changes
522 537 added 1 changesets with 1 changes to 1 files
523 538 new changesets 98a3f8f02ba7
524 539 updating to branch default
525 540 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
526 541 $ cd hgwebdir-local
527 542 $ echo commit > a
528 543 $ hg commit -m 'local commit'
529 544
530 545 $ hg push
531 546 pushing to http://localhost:$HGPORT/hgwebdir
532 547 searching for changes
533 548 remote: adding changesets
534 549 remote: adding manifests
535 550 remote: adding file changes
536 551 remote: added 1 changesets with 1 changes to 1 files
537 552
538 553 $ killdaemons.py
539 554
540 555 $ cd ..
@@ -1,603 +1,609 b''
1 1 This test is a duplicate of 'test-http.t' feel free to factor out
2 2 parts that are not bundle1/bundle2 specific.
3 3
4 4 #testcases sshv1 sshv2
5 5
6 6 #if sshv2
7 7 $ cat >> $HGRCPATH << EOF
8 8 > [experimental]
9 9 > sshpeer.advertise-v2 = true
10 10 > sshserver.support-v2 = true
11 11 > EOF
12 12 #endif
13 13
14 14 $ cat << EOF >> $HGRCPATH
15 15 > [devel]
16 16 > # This test is dedicated to interaction through old bundle
17 17 > legacy.exchange = bundle1
18 18 > EOF
19 19
20 20
21 21 This test tries to exercise the ssh functionality with a dummy script
22 22
23 23 creating 'remote' repo
24 24
25 25 $ hg init remote
26 26 $ cd remote
27 27 $ echo this > foo
28 28 $ echo this > fooO
29 29 $ hg ci -A -m "init" foo fooO
30 30
31 31 insert a closed branch (issue4428)
32 32
33 33 $ hg up null
34 34 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
35 35 $ hg branch closed
36 36 marked working directory as branch closed
37 37 (branches are permanent and global, did you want a bookmark?)
38 38 $ hg ci -mc0
39 39 $ hg ci --close-branch -mc1
40 40 $ hg up -q default
41 41
42 42 configure for serving
43 43
44 44 $ cat <<EOF > .hg/hgrc
45 45 > [server]
46 46 > uncompressed = True
47 47 >
48 48 > [hooks]
49 49 > changegroup = sh -c "printenv.py --line changegroup-in-remote 0 ../dummylog"
50 50 > EOF
51 51 $ cd $TESTTMP
52 52
53 53 repo not found error
54 54
55 55 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
56 56 remote: abort: repository nonexistent not found!
57 57 abort: no suitable response from remote hg!
58 58 [255]
59 59
60 60 non-existent absolute path
61 61
62 62 #if no-msys
63 63 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local
64 64 remote: abort: repository /$TESTTMP/nonexistent not found!
65 65 abort: no suitable response from remote hg!
66 66 [255]
67 67 #endif
68 68
69 69 clone remote via stream
70 70
71 71 #if no-reposimplestore
72 72
73 73 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --stream ssh://user@dummy/remote local-stream
74 74 streaming all changes
75 75 4 files to transfer, 602 bytes of data
76 76 transferred 602 bytes in * seconds (*) (glob)
77 77 searching for changes
78 78 no changes found
79 79 updating to branch default
80 80 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 81 $ cd local-stream
82 82 $ hg verify
83 83 checking changesets
84 84 checking manifests
85 85 crosschecking files in changesets and manifests
86 86 checking files
87 87 checked 3 changesets with 2 changes to 2 files
88 88 $ hg branches
89 89 default 0:1160648e36ce
90 90 $ cd $TESTTMP
91 91
92 92 clone bookmarks via stream
93 93
94 94 $ hg -R local-stream book mybook
95 95 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --stream ssh://user@dummy/local-stream stream2
96 96 streaming all changes
97 97 4 files to transfer, 602 bytes of data
98 98 transferred 602 bytes in * seconds (*) (glob)
99 99 searching for changes
100 100 no changes found
101 101 updating to branch default
102 102 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
103 103 $ cd stream2
104 104 $ hg book
105 105 mybook 0:1160648e36ce
106 106 $ cd $TESTTMP
107 107 $ rm -rf local-stream stream2
108 108
109 109 #endif
110 110
111 111 clone remote via pull
112 112
113 113 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
114 114 requesting all changes
115 115 adding changesets
116 116 adding manifests
117 117 adding file changes
118 118 added 3 changesets with 2 changes to 2 files
119 119 new changesets 1160648e36ce:ad076bfb429d
120 120 updating to branch default
121 121 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 122
123 123 verify
124 124
125 125 $ cd local
126 126 $ hg verify
127 127 checking changesets
128 128 checking manifests
129 129 crosschecking files in changesets and manifests
130 130 checking files
131 131 checked 3 changesets with 2 changes to 2 files
132 132 $ cat >> .hg/hgrc <<EOF
133 133 > [hooks]
134 134 > changegroup = sh -c "printenv.py --line changegroup-in-local 0 ../dummylog"
135 135 > EOF
136 136
137 137 empty default pull
138 138
139 139 $ hg paths
140 140 default = ssh://user@dummy/remote
141 141 $ hg pull -e "\"$PYTHON\" \"$TESTDIR/dummyssh\""
142 142 pulling from ssh://user@dummy/remote
143 143 searching for changes
144 144 no changes found
145 145
146 146 pull from wrong ssh URL
147 147
148 148 $ hg pull -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist
149 149 pulling from ssh://user@dummy/doesnotexist
150 150 remote: abort: repository doesnotexist not found!
151 151 abort: no suitable response from remote hg!
152 152 [255]
153 153
154 154 local change
155 155
156 156 $ echo bleah > foo
157 157 $ hg ci -m "add"
158 158
159 159 updating rc
160 160
161 161 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
162 162 $ echo "[ui]" >> .hg/hgrc
163 163 $ echo "ssh = \"$PYTHON\" \"$TESTDIR/dummyssh\"" >> .hg/hgrc
164 164
165 165 find outgoing
166 166
167 167 $ hg out ssh://user@dummy/remote
168 168 comparing with ssh://user@dummy/remote
169 169 searching for changes
170 170 changeset: 3:a28a9d1a809c
171 171 tag: tip
172 172 parent: 0:1160648e36ce
173 173 user: test
174 174 date: Thu Jan 01 00:00:00 1970 +0000
175 175 summary: add
176 176
177 177
178 178 find incoming on the remote side
179 179
180 180 $ hg incoming -R ../remote -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
181 181 comparing with ssh://user@dummy/local
182 182 searching for changes
183 183 changeset: 3:a28a9d1a809c
184 184 tag: tip
185 185 parent: 0:1160648e36ce
186 186 user: test
187 187 date: Thu Jan 01 00:00:00 1970 +0000
188 188 summary: add
189 189
190 190
191 191 find incoming on the remote side (using absolute path)
192 192
193 193 $ hg incoming -R ../remote -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
194 194 comparing with ssh://user@dummy/$TESTTMP/local
195 195 searching for changes
196 196 changeset: 3:a28a9d1a809c
197 197 tag: tip
198 198 parent: 0:1160648e36ce
199 199 user: test
200 200 date: Thu Jan 01 00:00:00 1970 +0000
201 201 summary: add
202 202
203 203
204 204 push
205 205
206 206 $ hg push
207 207 pushing to ssh://user@dummy/remote
208 208 searching for changes
209 209 remote: adding changesets
210 210 remote: adding manifests
211 211 remote: adding file changes
212 212 remote: added 1 changesets with 1 changes to 1 files
213 213 $ cd $TESTTMP/remote
214 214
215 215 check remote tip
216 216
217 217 $ hg tip
218 218 changeset: 3:a28a9d1a809c
219 219 tag: tip
220 220 parent: 0:1160648e36ce
221 221 user: test
222 222 date: Thu Jan 01 00:00:00 1970 +0000
223 223 summary: add
224 224
225 225 $ hg verify
226 226 checking changesets
227 227 checking manifests
228 228 crosschecking files in changesets and manifests
229 229 checking files
230 230 checked 4 changesets with 3 changes to 2 files
231 231 $ hg cat -r tip foo
232 232 bleah
233 233 $ echo z > z
234 234 $ hg ci -A -m z z
235 235 created new head
236 236
237 237 test pushkeys and bookmarks
238 238
239 239 $ cd $TESTTMP/local
240 240 $ hg debugpushkey --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
241 241 bookmarks
242 242 namespaces
243 243 phases
244 244 $ hg book foo -r 0
245 245 $ hg out -B
246 246 comparing with ssh://user@dummy/remote
247 247 searching for changed bookmarks
248 248 foo 1160648e36ce
249 249 $ hg push -B foo
250 250 pushing to ssh://user@dummy/remote
251 251 searching for changes
252 252 no changes found
253 253 exporting bookmark foo
254 254 [1]
255 255 $ hg debugpushkey --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
256 256 foo 1160648e36cec0054048a7edc4110c6f84fde594
257 257 $ hg book -f foo
258 258 $ hg push --traceback
259 259 pushing to ssh://user@dummy/remote
260 260 searching for changes
261 261 no changes found
262 262 updating bookmark foo
263 263 [1]
264 264 $ hg book -d foo
265 265 $ hg in -B
266 266 comparing with ssh://user@dummy/remote
267 267 searching for changed bookmarks
268 268 foo a28a9d1a809c
269 269 $ hg book -f -r 0 foo
270 270 $ hg pull -B foo
271 271 pulling from ssh://user@dummy/remote
272 272 no changes found
273 273 updating bookmark foo
274 274 $ hg book -d foo
275 275 $ hg push -B foo
276 276 pushing to ssh://user@dummy/remote
277 277 searching for changes
278 278 no changes found
279 279 deleting remote bookmark foo
280 280 [1]
281 281
282 282 a bad, evil hook that prints to stdout
283 283
284 284 $ cat <<EOF > $TESTTMP/badhook
285 285 > import sys
286 286 > sys.stdout.write("KABOOM\n")
287 287 > EOF
288 288
289 289 $ echo '[hooks]' >> ../remote/.hg/hgrc
290 290 $ echo "changegroup.stdout = \"$PYTHON\" $TESTTMP/badhook" >> ../remote/.hg/hgrc
291 291 $ echo r > r
292 292 $ hg ci -A -m z r
293 293
294 294 push should succeed even though it has an unexpected response
295 295
296 296 $ hg push
297 297 pushing to ssh://user@dummy/remote
298 298 searching for changes
299 299 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
300 300 remote: adding changesets
301 301 remote: adding manifests
302 302 remote: adding file changes
303 303 remote: added 1 changesets with 1 changes to 1 files
304 304 remote: KABOOM
305 305 $ hg -R ../remote heads
306 306 changeset: 5:1383141674ec
307 307 tag: tip
308 308 parent: 3:a28a9d1a809c
309 309 user: test
310 310 date: Thu Jan 01 00:00:00 1970 +0000
311 311 summary: z
312 312
313 313 changeset: 4:6c0482d977a3
314 314 parent: 0:1160648e36ce
315 315 user: test
316 316 date: Thu Jan 01 00:00:00 1970 +0000
317 317 summary: z
318 318
319 319
320 320 clone bookmarks
321 321
322 322 $ hg -R ../remote bookmark test
323 323 $ hg -R ../remote bookmarks
324 324 * test 4:6c0482d977a3
325 325 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
326 326 requesting all changes
327 327 adding changesets
328 328 adding manifests
329 329 adding file changes
330 330 added 6 changesets with 5 changes to 4 files (+1 heads)
331 331 new changesets 1160648e36ce:1383141674ec
332 332 updating to branch default
333 333 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
334 334 $ hg -R local-bookmarks bookmarks
335 335 test 4:6c0482d977a3
336 336
337 337 passwords in ssh urls are not supported
338 338 (we use a glob here because different Python versions give different
339 339 results here)
340 340
341 341 $ hg push ssh://user:erroneouspwd@dummy/remote
342 342 pushing to ssh://user:*@dummy/remote (glob)
343 343 abort: password in URL not supported!
344 344 [255]
345 345
346 346 $ cd $TESTTMP
347 347
348 348 hide outer repo
349 349 $ hg init
350 350
351 351 Test remote paths with spaces (issue2983):
352 352
353 353 $ hg init --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
354 354 $ touch "$TESTTMP/a repo/test"
355 355 $ hg -R 'a repo' commit -A -m "test"
356 356 adding test
357 357 $ hg -R 'a repo' tag tag
358 358 $ hg id --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
359 359 73649e48688a
360 360
361 361 $ hg id --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo#noNoNO"
362 362 abort: unknown revision 'noNoNO'!
363 363 [255]
364 364
365 365 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
366 366
367 367 $ hg clone --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
368 368 destination directory: a repo
369 369 abort: destination 'a repo' is not empty
370 370 [255]
371 371
372 372 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
373 373 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
374 374 parameters:
375 375
376 376 $ cat > ssh.sh << EOF
377 377 > userhost="\$1"
378 378 > SSH_ORIGINAL_COMMAND="\$2"
379 379 > export SSH_ORIGINAL_COMMAND
380 380 > PYTHONPATH="$PYTHONPATH"
381 381 > export PYTHONPATH
382 382 > "$PYTHON" "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
383 383 > EOF
384 384
385 385 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
386 386 73649e48688a
387 387
388 388 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
389 389 remote: Illegal repository "$TESTTMP/a'repo"
390 390 abort: no suitable response from remote hg!
391 391 [255]
392 392
393 393 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
394 394 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
395 395 abort: no suitable response from remote hg!
396 396 [255]
397 397
398 398 $ SSH_ORIGINAL_COMMAND="'hg' serve -R 'a'repo' --stdio" "$PYTHON" "$TESTDIR/../contrib/hg-ssh"
399 399 Illegal command "'hg' serve -R 'a'repo' --stdio": No closing quotation
400 400 [255]
401 401
402 402 Test hg-ssh in read-only mode:
403 403
404 404 $ cat > ssh.sh << EOF
405 405 > userhost="\$1"
406 406 > SSH_ORIGINAL_COMMAND="\$2"
407 407 > export SSH_ORIGINAL_COMMAND
408 408 > PYTHONPATH="$PYTHONPATH"
409 409 > export PYTHONPATH
410 410 > "$PYTHON" "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
411 411 > EOF
412 412
413 413 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
414 414 requesting all changes
415 415 adding changesets
416 416 adding manifests
417 417 adding file changes
418 418 added 6 changesets with 5 changes to 4 files (+1 heads)
419 419 new changesets 1160648e36ce:1383141674ec
420 420 updating to branch default
421 421 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
422 422
423 423 $ cd read-only-local
424 424 $ echo "baz" > bar
425 425 $ hg ci -A -m "unpushable commit" bar
426 426 $ hg push --ssh "sh ../ssh.sh"
427 427 pushing to ssh://user@dummy/*/remote (glob)
428 428 searching for changes
429 429 remote: Permission denied
430 430 remote: abort: pretxnopen.hg-ssh hook failed
431 431 remote: Permission denied
432 432 remote: pushkey-abort: prepushkey.hg-ssh hook failed
433 433 updating 6c0482d977a3 to public failed!
434 434 [1]
435 435
436 436 $ cd $TESTTMP
437 437
438 438 stderr from remote commands should be printed before stdout from local code (issue4336)
439 439
440 440 $ hg clone remote stderr-ordering
441 441 updating to branch default
442 442 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
443 443 $ cd stderr-ordering
444 444 $ cat >> localwrite.py << EOF
445 445 > from mercurial import exchange, extensions
446 446 >
447 447 > def wrappedpush(orig, repo, *args, **kwargs):
448 448 > res = orig(repo, *args, **kwargs)
449 449 > repo.ui.write(b'local stdout\n')
450 450 > return res
451 451 >
452 452 > def extsetup(ui):
453 453 > extensions.wrapfunction(exchange, b'push', wrappedpush)
454 454 > EOF
455 455
456 456 $ cat >> .hg/hgrc << EOF
457 457 > [paths]
458 458 > default-push = ssh://user@dummy/remote
459 459 > [ui]
460 460 > ssh = "$PYTHON" "$TESTDIR/dummyssh"
461 461 > [extensions]
462 462 > localwrite = localwrite.py
463 463 > EOF
464 464
465 465 $ echo localwrite > foo
466 466 $ hg commit -m 'testing localwrite'
467 467 $ hg push
468 468 pushing to ssh://user@dummy/remote
469 469 searching for changes
470 470 remote: adding changesets
471 471 remote: adding manifests
472 472 remote: adding file changes
473 473 remote: added 1 changesets with 1 changes to 1 files
474 474 remote: KABOOM
475 475 local stdout
476 476
477 477 debug output
478 478
479 479 $ hg pull --debug ssh://user@dummy/remote
480 480 pulling from ssh://user@dummy/remote
481 481 running .* ".*/dummyssh" ['"]user@dummy['"] ('|")hg -R remote serve --stdio('|") (re)
482 482 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
483 483 sending hello command
484 484 sending between command
485 485 remote: 440 (sshv1 !)
486 486 protocol upgraded to exp-ssh-v2-0003 (sshv2 !)
487 487 remote: capabilities: batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset getbundle known lookup protocaps pushkey streamreqs=generaldelta,revlogv1,sparserevlog unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
488 488 remote: 1 (sshv1 !)
489 489 sending protocaps command
490 490 preparing listkeys for "bookmarks"
491 491 sending listkeys command
492 492 received listkey for "bookmarks": 45 bytes
493 493 query 1; heads
494 494 sending batch command
495 495 searching for changes
496 496 all remote heads known locally
497 497 no changes found
498 498 preparing listkeys for "phases"
499 499 sending listkeys command
500 500 received listkey for "phases": 15 bytes
501 501 checking for updated bookmarks
502 502
503 503 $ cd $TESTTMP
504 504
505 505 $ cat dummylog
506 506 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
507 507 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio (no-msys !)
508 508 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
509 509 Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio (no-reposimplestore !)
510 510 Got arguments 1:user@dummy 2:hg -R remote serve --stdio (no-reposimplestore !)
511 511 Got arguments 1:user@dummy 2:hg -R remote serve --stdio (no-reposimplestore !)
512 512 Got arguments 1:user@dummy 2:hg -R doesnotexist serve --stdio
513 513 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
514 514 Got arguments 1:user@dummy 2:hg -R local serve --stdio
515 515 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
516 516 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
517 517 changegroup-in-remote hook: HG_HOOKNAME=changegroup
518 518 HG_HOOKTYPE=changegroup
519 519 HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60
520 520 HG_NODE_LAST=a28a9d1a809cab7d4e2fde4bee738a9ede948b60
521 521 HG_SOURCE=serve
522 522 HG_TXNID=TXN:$ID$
523 HG_TXNNAME=serve
524 remote:ssh:$LOCALIP
523 525 HG_URL=remote:ssh:$LOCALIP
524 526
525 527 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
526 528 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
527 529 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
528 530 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
529 531 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
530 532 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
531 533 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
532 534 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
533 535 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
534 536 changegroup-in-remote hook: HG_HOOKNAME=changegroup
535 537 HG_HOOKTYPE=changegroup
536 538 HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6
537 539 HG_NODE_LAST=1383141674ec756a6056f6a9097618482fe0f4a6
538 540 HG_SOURCE=serve
539 541 HG_TXNID=TXN:$ID$
542 HG_TXNNAME=serve
543 remote:ssh:$LOCALIP
540 544 HG_URL=remote:ssh:$LOCALIP
541 545
542 546 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
543 547 Got arguments 1:user@dummy 2:hg init 'a repo'
544 548 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
545 549 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
546 550 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
547 551 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
548 552 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
549 553 changegroup-in-remote hook: HG_HOOKNAME=changegroup
550 554 HG_HOOKTYPE=changegroup
551 555 HG_NODE=65c38f4125f9602c8db4af56530cc221d93b8ef8
552 556 HG_NODE_LAST=65c38f4125f9602c8db4af56530cc221d93b8ef8
553 557 HG_SOURCE=serve
554 558 HG_TXNID=TXN:$ID$
559 HG_TXNNAME=serve
560 remote:ssh:$LOCALIP
555 561 HG_URL=remote:ssh:$LOCALIP
556 562
557 563 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
558 564
559 565 remote hook failure is attributed to remote
560 566
561 567 $ cat > $TESTTMP/failhook << EOF
562 568 > def hook(ui, repo, **kwargs):
563 569 > ui.write(b'hook failure!\n')
564 570 > ui.flush()
565 571 > return 1
566 572 > EOF
567 573
568 574 $ echo "pretxnchangegroup.fail = python:$TESTTMP/failhook:hook" >> remote/.hg/hgrc
569 575
570 576 $ hg -q --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" clone ssh://user@dummy/remote hookout
571 577 $ cd hookout
572 578 $ touch hookfailure
573 579 $ hg -q commit -A -m 'remote hook failure'
574 580 $ hg --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" push
575 581 pushing to ssh://user@dummy/remote
576 582 searching for changes
577 583 remote: adding changesets
578 584 remote: adding manifests
579 585 remote: adding file changes
580 586 remote: added 1 changesets with 1 changes to 1 files
581 587 remote: hook failure!
582 588 remote: transaction abort!
583 589 remote: rollback completed
584 590 remote: abort: pretxnchangegroup.fail hook failed
585 591 [1]
586 592
587 593 abort during pull is properly reported as such
588 594
589 595 $ echo morefoo >> ../remote/foo
590 596 $ hg -R ../remote commit --message "more foo to be pulled"
591 597 $ cat >> ../remote/.hg/hgrc << EOF
592 598 > [extensions]
593 599 > crash = ${TESTDIR}/crashgetbundler.py
594 600 > EOF
595 601 $ hg --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" pull
596 602 pulling from ssh://user@dummy/remote
597 603 searching for changes
598 604 adding changesets
599 605 remote: abort: this is an exercise
600 606 transaction abort!
601 607 rollback completed
602 608 abort: stream ended unexpectedly (got 0 bytes, expected 4)
603 609 [255]
@@ -1,698 +1,702 b''
1 1 #testcases sshv1 sshv2
2 2
3 3 #if sshv2
4 4 $ cat >> $HGRCPATH << EOF
5 5 > [experimental]
6 6 > sshpeer.advertise-v2 = true
7 7 > sshserver.support-v2 = true
8 8 > EOF
9 9 #endif
10 10
11 11 This test tries to exercise the ssh functionality with a dummy script
12 12
13 13 creating 'remote' repo
14 14
15 15 $ hg init remote
16 16 $ cd remote
17 17 $ echo this > foo
18 18 $ echo this > fooO
19 19 $ hg ci -A -m "init" foo fooO
20 20
21 21 insert a closed branch (issue4428)
22 22
23 23 $ hg up null
24 24 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
25 25 $ hg branch closed
26 26 marked working directory as branch closed
27 27 (branches are permanent and global, did you want a bookmark?)
28 28 $ hg ci -mc0
29 29 $ hg ci --close-branch -mc1
30 30 $ hg up -q default
31 31
32 32 configure for serving
33 33
34 34 $ cat <<EOF > .hg/hgrc
35 35 > [server]
36 36 > uncompressed = True
37 37 >
38 38 > [hooks]
39 39 > changegroup = sh -c "printenv.py --line changegroup-in-remote 0 ../dummylog"
40 40 > EOF
41 41 $ cd $TESTTMP
42 42
43 43 repo not found error
44 44
45 45 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
46 46 remote: abort: repository nonexistent not found!
47 47 abort: no suitable response from remote hg!
48 48 [255]
49 49
50 50 non-existent absolute path
51 51
52 52 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/`pwd`/nonexistent local
53 53 remote: abort: repository $TESTTMP/nonexistent not found!
54 54 abort: no suitable response from remote hg!
55 55 [255]
56 56
57 57 clone remote via stream
58 58
59 59 #if no-reposimplestore
60 60
61 61 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --stream ssh://user@dummy/remote local-stream
62 62 streaming all changes
63 63 8 files to transfer, 827 bytes of data
64 64 transferred 827 bytes in * seconds (*) (glob)
65 65 updating to branch default
66 66 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 67 $ cd local-stream
68 68 $ hg verify
69 69 checking changesets
70 70 checking manifests
71 71 crosschecking files in changesets and manifests
72 72 checking files
73 73 checked 3 changesets with 2 changes to 2 files
74 74 $ hg branches
75 75 default 0:1160648e36ce
76 76 $ cd $TESTTMP
77 77
78 78 clone bookmarks via stream
79 79
80 80 $ hg -R local-stream book mybook
81 81 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --stream ssh://user@dummy/local-stream stream2
82 82 streaming all changes
83 83 9 files to transfer, 870 bytes of data
84 84 transferred 870 bytes in * seconds (*) (glob)
85 85 updating to branch default
86 86 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 87 $ cd stream2
88 88 $ hg book
89 89 mybook 0:1160648e36ce
90 90 $ cd $TESTTMP
91 91 $ rm -rf local-stream stream2
92 92
93 93 #endif
94 94
95 95 clone remote via pull
96 96
97 97 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
98 98 requesting all changes
99 99 adding changesets
100 100 adding manifests
101 101 adding file changes
102 102 added 3 changesets with 2 changes to 2 files
103 103 new changesets 1160648e36ce:ad076bfb429d
104 104 updating to branch default
105 105 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 106
107 107 verify
108 108
109 109 $ cd local
110 110 $ hg verify
111 111 checking changesets
112 112 checking manifests
113 113 crosschecking files in changesets and manifests
114 114 checking files
115 115 checked 3 changesets with 2 changes to 2 files
116 116 $ cat >> .hg/hgrc <<EOF
117 117 > [hooks]
118 118 > changegroup = sh -c "printenv.py changegroup-in-local 0 ../dummylog"
119 119 > EOF
120 120
121 121 empty default pull
122 122
123 123 $ hg paths
124 124 default = ssh://user@dummy/remote
125 125 $ hg pull -e "\"$PYTHON\" \"$TESTDIR/dummyssh\""
126 126 pulling from ssh://user@dummy/remote
127 127 searching for changes
128 128 no changes found
129 129
130 130 pull from wrong ssh URL
131 131
132 132 $ hg pull -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist
133 133 pulling from ssh://user@dummy/doesnotexist
134 134 remote: abort: repository doesnotexist not found!
135 135 abort: no suitable response from remote hg!
136 136 [255]
137 137
138 138 local change
139 139
140 140 $ echo bleah > foo
141 141 $ hg ci -m "add"
142 142
143 143 updating rc
144 144
145 145 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
146 146 $ echo "[ui]" >> .hg/hgrc
147 147 $ echo "ssh = \"$PYTHON\" \"$TESTDIR/dummyssh\"" >> .hg/hgrc
148 148
149 149 find outgoing
150 150
151 151 $ hg out ssh://user@dummy/remote
152 152 comparing with ssh://user@dummy/remote
153 153 searching for changes
154 154 changeset: 3:a28a9d1a809c
155 155 tag: tip
156 156 parent: 0:1160648e36ce
157 157 user: test
158 158 date: Thu Jan 01 00:00:00 1970 +0000
159 159 summary: add
160 160
161 161
162 162 find incoming on the remote side
163 163
164 164 $ hg incoming -R ../remote -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
165 165 comparing with ssh://user@dummy/local
166 166 searching for changes
167 167 changeset: 3:a28a9d1a809c
168 168 tag: tip
169 169 parent: 0:1160648e36ce
170 170 user: test
171 171 date: Thu Jan 01 00:00:00 1970 +0000
172 172 summary: add
173 173
174 174
175 175 find incoming on the remote side (using absolute path)
176 176
177 177 $ hg incoming -R ../remote -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
178 178 comparing with ssh://user@dummy/$TESTTMP/local
179 179 searching for changes
180 180 changeset: 3:a28a9d1a809c
181 181 tag: tip
182 182 parent: 0:1160648e36ce
183 183 user: test
184 184 date: Thu Jan 01 00:00:00 1970 +0000
185 185 summary: add
186 186
187 187
188 188 push
189 189
190 190 $ hg push
191 191 pushing to ssh://user@dummy/remote
192 192 searching for changes
193 193 remote: adding changesets
194 194 remote: adding manifests
195 195 remote: adding file changes
196 196 remote: added 1 changesets with 1 changes to 1 files
197 197 $ cd $TESTTMP/remote
198 198
199 199 check remote tip
200 200
201 201 $ hg tip
202 202 changeset: 3:a28a9d1a809c
203 203 tag: tip
204 204 parent: 0:1160648e36ce
205 205 user: test
206 206 date: Thu Jan 01 00:00:00 1970 +0000
207 207 summary: add
208 208
209 209 $ hg verify
210 210 checking changesets
211 211 checking manifests
212 212 crosschecking files in changesets and manifests
213 213 checking files
214 214 checked 4 changesets with 3 changes to 2 files
215 215 $ hg cat -r tip foo
216 216 bleah
217 217 $ echo z > z
218 218 $ hg ci -A -m z z
219 219 created new head
220 220
221 221 test pushkeys and bookmarks
222 222
223 223 $ cd $TESTTMP/local
224 224 $ hg debugpushkey --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
225 225 bookmarks
226 226 namespaces
227 227 phases
228 228 $ hg book foo -r 0
229 229 $ hg out -B --config paths.default=bogus://invalid --config paths.default:pushurl=`hg paths default`
230 230 comparing with ssh://user@dummy/remote
231 231 searching for changed bookmarks
232 232 foo 1160648e36ce
233 233 $ hg push -B foo
234 234 pushing to ssh://user@dummy/remote
235 235 searching for changes
236 236 no changes found
237 237 exporting bookmark foo
238 238 [1]
239 239 $ hg debugpushkey --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
240 240 foo 1160648e36cec0054048a7edc4110c6f84fde594
241 241 $ hg book -f foo
242 242 $ hg push --traceback
243 243 pushing to ssh://user@dummy/remote
244 244 searching for changes
245 245 no changes found
246 246 updating bookmark foo
247 247 [1]
248 248 $ hg book -d foo
249 249 $ hg in -B
250 250 comparing with ssh://user@dummy/remote
251 251 searching for changed bookmarks
252 252 foo a28a9d1a809c
253 253 $ hg book -f -r 0 foo
254 254 $ hg pull -B foo
255 255 pulling from ssh://user@dummy/remote
256 256 no changes found
257 257 updating bookmark foo
258 258 $ hg book -d foo
259 259 $ hg push -B foo
260 260 pushing to ssh://user@dummy/remote
261 261 searching for changes
262 262 no changes found
263 263 deleting remote bookmark foo
264 264 [1]
265 265
266 266 a bad, evil hook that prints to stdout
267 267
268 268 $ cat <<EOF > $TESTTMP/badhook
269 269 > import sys
270 270 > sys.stdout.write("KABOOM\n")
271 271 > sys.stdout.flush()
272 272 > EOF
273 273
274 274 $ cat <<EOF > $TESTTMP/badpyhook.py
275 275 > import sys
276 276 > def hook(ui, repo, hooktype, **kwargs):
277 277 > sys.stdout.write("KABOOM IN PROCESS\n")
278 278 > sys.stdout.flush()
279 279 > EOF
280 280
281 281 $ cat <<EOF >> ../remote/.hg/hgrc
282 282 > [hooks]
283 283 > changegroup.stdout = "$PYTHON" $TESTTMP/badhook
284 284 > changegroup.pystdout = python:$TESTTMP/badpyhook.py:hook
285 285 > EOF
286 286 $ echo r > r
287 287 $ hg ci -A -m z r
288 288
289 289 push should succeed even though it has an unexpected response
290 290
291 291 $ hg push
292 292 pushing to ssh://user@dummy/remote
293 293 searching for changes
294 294 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
295 295 remote: adding changesets
296 296 remote: adding manifests
297 297 remote: adding file changes
298 298 remote: added 1 changesets with 1 changes to 1 files
299 299 remote: KABOOM
300 300 remote: KABOOM IN PROCESS
301 301 $ hg -R ../remote heads
302 302 changeset: 5:1383141674ec
303 303 tag: tip
304 304 parent: 3:a28a9d1a809c
305 305 user: test
306 306 date: Thu Jan 01 00:00:00 1970 +0000
307 307 summary: z
308 308
309 309 changeset: 4:6c0482d977a3
310 310 parent: 0:1160648e36ce
311 311 user: test
312 312 date: Thu Jan 01 00:00:00 1970 +0000
313 313 summary: z
314 314
315 315
316 316 #if chg
317 317
318 318 try again with remote chg, which should succeed as well
319 319
320 320 $ hg rollback -R ../remote
321 321 repository tip rolled back to revision 4 (undo serve)
322 322
323 323 $ hg push --config ui.remotecmd=chg
324 324 pushing to ssh://user@dummy/remote
325 325 searching for changes
326 326 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
327 327 remote: adding changesets
328 328 remote: adding manifests
329 329 remote: adding file changes
330 330 remote: added 1 changesets with 1 changes to 1 files
331 331 remote: KABOOM
332 332 remote: KABOOM IN PROCESS
333 333
334 334 #endif
335 335
336 336 clone bookmarks
337 337
338 338 $ hg -R ../remote bookmark test
339 339 $ hg -R ../remote bookmarks
340 340 * test 4:6c0482d977a3
341 341 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
342 342 requesting all changes
343 343 adding changesets
344 344 adding manifests
345 345 adding file changes
346 346 added 6 changesets with 5 changes to 4 files (+1 heads)
347 347 new changesets 1160648e36ce:1383141674ec
348 348 updating to branch default
349 349 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
350 350 $ hg -R local-bookmarks bookmarks
351 351 test 4:6c0482d977a3
352 352
353 353 passwords in ssh urls are not supported
354 354 (we use a glob here because different Python versions give different
355 355 results here)
356 356
357 357 $ hg push ssh://user:erroneouspwd@dummy/remote
358 358 pushing to ssh://user:*@dummy/remote (glob)
359 359 abort: password in URL not supported!
360 360 [255]
361 361
362 362 $ cd $TESTTMP
363 363
364 364 hide outer repo
365 365 $ hg init
366 366
367 367 Test remote paths with spaces (issue2983):
368 368
369 369 $ hg init --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
370 370 $ touch "$TESTTMP/a repo/test"
371 371 $ hg -R 'a repo' commit -A -m "test"
372 372 adding test
373 373 $ hg -R 'a repo' tag tag
374 374 $ hg id --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
375 375 73649e48688a
376 376
377 377 $ hg id --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo#noNoNO"
378 378 abort: unknown revision 'noNoNO'!
379 379 [255]
380 380
381 381 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
382 382
383 383 $ hg clone --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
384 384 destination directory: a repo
385 385 abort: destination 'a repo' is not empty
386 386 [255]
387 387
388 388 Make sure hg is really paranoid in serve --stdio mode. It used to be
389 389 possible to get a debugger REPL by specifying a repo named --debugger.
390 390 $ hg -R --debugger serve --stdio
391 391 abort: potentially unsafe serve --stdio invocation: ['-R', '--debugger', 'serve', '--stdio']
392 392 [255]
393 393 $ hg -R --config=ui.debugger=yes serve --stdio
394 394 abort: potentially unsafe serve --stdio invocation: ['-R', '--config=ui.debugger=yes', 'serve', '--stdio']
395 395 [255]
396 396 Abbreviations of 'serve' also don't work, to avoid shenanigans.
397 397 $ hg -R narf serv --stdio
398 398 abort: potentially unsafe serve --stdio invocation: ['-R', 'narf', 'serv', '--stdio']
399 399 [255]
400 400
401 401 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
402 402 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
403 403 parameters:
404 404
405 405 $ cat > ssh.sh << EOF
406 406 > userhost="\$1"
407 407 > SSH_ORIGINAL_COMMAND="\$2"
408 408 > export SSH_ORIGINAL_COMMAND
409 409 > PYTHONPATH="$PYTHONPATH"
410 410 > export PYTHONPATH
411 411 > "$PYTHON" "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
412 412 > EOF
413 413
414 414 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
415 415 73649e48688a
416 416
417 417 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
418 418 remote: Illegal repository "$TESTTMP/a'repo"
419 419 abort: no suitable response from remote hg!
420 420 [255]
421 421
422 422 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
423 423 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
424 424 abort: no suitable response from remote hg!
425 425 [255]
426 426
427 427 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a'repo' serve --stdio" "$PYTHON" "$TESTDIR/../contrib/hg-ssh"
428 428 Illegal command "'hg' -R 'a'repo' serve --stdio": No closing quotation
429 429 [255]
430 430
431 431 Test hg-ssh in read-only mode:
432 432
433 433 $ cat > ssh.sh << EOF
434 434 > userhost="\$1"
435 435 > SSH_ORIGINAL_COMMAND="\$2"
436 436 > export SSH_ORIGINAL_COMMAND
437 437 > PYTHONPATH="$PYTHONPATH"
438 438 > export PYTHONPATH
439 439 > "$PYTHON" "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
440 440 > EOF
441 441
442 442 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
443 443 requesting all changes
444 444 adding changesets
445 445 adding manifests
446 446 adding file changes
447 447 added 6 changesets with 5 changes to 4 files (+1 heads)
448 448 new changesets 1160648e36ce:1383141674ec
449 449 updating to branch default
450 450 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
451 451
452 452 $ cd read-only-local
453 453 $ echo "baz" > bar
454 454 $ hg ci -A -m "unpushable commit" bar
455 455 $ hg push --ssh "sh ../ssh.sh"
456 456 pushing to ssh://user@dummy/*/remote (glob)
457 457 searching for changes
458 458 remote: Permission denied
459 459 remote: pretxnopen.hg-ssh hook failed
460 460 abort: push failed on remote
461 461 [255]
462 462
463 463 $ cd $TESTTMP
464 464
465 465 stderr from remote commands should be printed before stdout from local code (issue4336)
466 466
467 467 $ hg clone remote stderr-ordering
468 468 updating to branch default
469 469 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
470 470 $ cd stderr-ordering
471 471 $ cat >> localwrite.py << EOF
472 472 > from mercurial import exchange, extensions
473 473 >
474 474 > def wrappedpush(orig, repo, *args, **kwargs):
475 475 > res = orig(repo, *args, **kwargs)
476 476 > repo.ui.write(b'local stdout\n')
477 477 > repo.ui.flush()
478 478 > return res
479 479 >
480 480 > def extsetup(ui):
481 481 > extensions.wrapfunction(exchange, b'push', wrappedpush)
482 482 > EOF
483 483
484 484 $ cat >> .hg/hgrc << EOF
485 485 > [paths]
486 486 > default-push = ssh://user@dummy/remote
487 487 > [ui]
488 488 > ssh = "$PYTHON" "$TESTDIR/dummyssh"
489 489 > [extensions]
490 490 > localwrite = localwrite.py
491 491 > EOF
492 492
493 493 $ echo localwrite > foo
494 494 $ hg commit -m 'testing localwrite'
495 495 $ hg push
496 496 pushing to ssh://user@dummy/remote
497 497 searching for changes
498 498 remote: adding changesets
499 499 remote: adding manifests
500 500 remote: adding file changes
501 501 remote: added 1 changesets with 1 changes to 1 files
502 502 remote: KABOOM
503 503 remote: KABOOM IN PROCESS
504 504 local stdout
505 505
506 506 debug output
507 507
508 508 $ hg pull --debug ssh://user@dummy/remote --config devel.debug.peer-request=yes
509 509 pulling from ssh://user@dummy/remote
510 510 running .* ".*/dummyssh" ['"]user@dummy['"] ('|")hg -R remote serve --stdio('|") (re)
511 511 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
512 512 devel-peer-request: hello+between
513 513 devel-peer-request: pairs: 81 bytes
514 514 sending hello command
515 515 sending between command
516 516 remote: 440 (sshv1 !)
517 517 protocol upgraded to exp-ssh-v2-0003 (sshv2 !)
518 518 remote: capabilities: batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset getbundle known lookup protocaps pushkey streamreqs=generaldelta,revlogv1,sparserevlog unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
519 519 remote: 1 (sshv1 !)
520 520 devel-peer-request: protocaps
521 521 devel-peer-request: caps: * bytes (glob)
522 522 sending protocaps command
523 523 query 1; heads
524 524 devel-peer-request: batched-content
525 525 devel-peer-request: - heads (0 arguments)
526 526 devel-peer-request: - known (1 arguments)
527 527 devel-peer-request: batch
528 528 devel-peer-request: cmds: 141 bytes
529 529 sending batch command
530 530 searching for changes
531 531 all remote heads known locally
532 532 no changes found
533 533 devel-peer-request: getbundle
534 534 devel-peer-request: bookmarks: 1 bytes
535 535 devel-peer-request: bundlecaps: 266 bytes
536 536 devel-peer-request: cg: 1 bytes
537 537 devel-peer-request: common: 122 bytes
538 538 devel-peer-request: heads: 122 bytes
539 539 devel-peer-request: listkeys: 9 bytes
540 540 devel-peer-request: phases: 1 bytes
541 541 sending getbundle command
542 542 bundle2-input-bundle: with-transaction
543 543 bundle2-input-part: "bookmarks" supported
544 544 bundle2-input-part: total payload size 26
545 545 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
546 546 bundle2-input-part: total payload size 45
547 547 bundle2-input-part: "phase-heads" supported
548 548 bundle2-input-part: total payload size 72
549 549 bundle2-input-bundle: 2 parts total
550 550 checking for updated bookmarks
551 551
552 552 $ cd $TESTTMP
553 553
554 554 $ cat dummylog
555 555 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
556 556 Got arguments 1:user@dummy 2:hg -R $TESTTMP/nonexistent serve --stdio
557 557 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
558 558 Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio (no-reposimplestore !)
559 559 Got arguments 1:user@dummy 2:hg -R remote serve --stdio (no-reposimplestore !)
560 560 Got arguments 1:user@dummy 2:hg -R remote serve --stdio (no-reposimplestore !)
561 561 Got arguments 1:user@dummy 2:hg -R doesnotexist serve --stdio
562 562 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
563 563 Got arguments 1:user@dummy 2:hg -R local serve --stdio
564 564 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
565 565 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
566 566 changegroup-in-remote hook: HG_BUNDLE2=1
567 567 HG_HOOKNAME=changegroup
568 568 HG_HOOKTYPE=changegroup
569 569 HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60
570 570 HG_NODE_LAST=a28a9d1a809cab7d4e2fde4bee738a9ede948b60
571 571 HG_SOURCE=serve
572 572 HG_TXNID=TXN:$ID$
573 HG_TXNNAME=serve
573 574 HG_URL=remote:ssh:$LOCALIP
574 575
575 576 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
576 577 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
577 578 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
578 579 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
579 580 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
580 581 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
581 582 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
582 583 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
583 584 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
584 585 changegroup-in-remote hook: HG_BUNDLE2=1
585 586 HG_HOOKNAME=changegroup
586 587 HG_HOOKTYPE=changegroup
587 588 HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6
588 589 HG_NODE_LAST=1383141674ec756a6056f6a9097618482fe0f4a6
589 590 HG_SOURCE=serve
590 591 HG_TXNID=TXN:$ID$
592 HG_TXNNAME=serve
591 593 HG_URL=remote:ssh:$LOCALIP
592 594
593 595 Got arguments 1:user@dummy 2:chg -R remote serve --stdio (chg !)
594 596 changegroup-in-remote hook: HG_BUNDLE2=1 (chg !)
595 597 HG_HOOKNAME=changegroup (chg !)
596 598 HG_HOOKTYPE=changegroup (chg !)
597 599 HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 (chg !)
598 600 HG_NODE_LAST=1383141674ec756a6056f6a9097618482fe0f4a6 (chg !)
599 601 HG_SOURCE=serve (chg !)
600 602 HG_TXNID=TXN:$ID$ (chg !)
603 HG_TXNNAME=serve (chg !)
601 604 HG_URL=remote:ssh:$LOCALIP (chg !)
602 605 (chg !)
603 606 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
604 607 Got arguments 1:user@dummy 2:hg init 'a repo'
605 608 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
606 609 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
607 610 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
608 611 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
609 612 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
610 613 changegroup-in-remote hook: HG_BUNDLE2=1
611 614 HG_HOOKNAME=changegroup
612 615 HG_HOOKTYPE=changegroup
613 616 HG_NODE=65c38f4125f9602c8db4af56530cc221d93b8ef8
614 617 HG_NODE_LAST=65c38f4125f9602c8db4af56530cc221d93b8ef8
615 618 HG_SOURCE=serve
616 619 HG_TXNID=TXN:$ID$
620 HG_TXNNAME=serve
617 621 HG_URL=remote:ssh:$LOCALIP
618 622
619 623 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
620 624
621 625
622 626 remote hook failure is attributed to remote
623 627
624 628 $ cat > $TESTTMP/failhook << EOF
625 629 > def hook(ui, repo, **kwargs):
626 630 > ui.write(b'hook failure!\n')
627 631 > ui.flush()
628 632 > return 1
629 633 > EOF
630 634
631 635 $ echo "pretxnchangegroup.fail = python:$TESTTMP/failhook:hook" >> remote/.hg/hgrc
632 636
633 637 $ hg -q --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" clone ssh://user@dummy/remote hookout
634 638 $ cd hookout
635 639 $ touch hookfailure
636 640 $ hg -q commit -A -m 'remote hook failure'
637 641 $ hg --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" push
638 642 pushing to ssh://user@dummy/remote
639 643 searching for changes
640 644 remote: adding changesets
641 645 remote: adding manifests
642 646 remote: adding file changes
643 647 remote: added 1 changesets with 1 changes to 1 files
644 648 remote: hook failure!
645 649 remote: transaction abort!
646 650 remote: rollback completed
647 651 remote: pretxnchangegroup.fail hook failed
648 652 abort: push failed on remote
649 653 [255]
650 654
651 655 abort during pull is properly reported as such
652 656
653 657 $ echo morefoo >> ../remote/foo
654 658 $ hg -R ../remote commit --message "more foo to be pulled"
655 659 $ cat >> ../remote/.hg/hgrc << EOF
656 660 > [extensions]
657 661 > crash = ${TESTDIR}/crashgetbundler.py
658 662 > EOF
659 663 $ hg --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" pull
660 664 pulling from ssh://user@dummy/remote
661 665 searching for changes
662 666 remote: abort: this is an exercise
663 667 abort: pull failed on remote
664 668 [255]
665 669
666 670 abort with no error hint when there is a ssh problem when pulling
667 671
668 672 $ hg pull ssh://brokenrepository -e "\"$PYTHON\" \"$TESTDIR/dummyssh\""
669 673 pulling from ssh://brokenrepository/
670 674 abort: no suitable response from remote hg!
671 675 [255]
672 676
673 677 abort with configured error hint when there is a ssh problem when pulling
674 678
675 679 $ hg pull ssh://brokenrepository -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" \
676 680 > --config ui.ssherrorhint="Please see http://company/internalwiki/ssh.html"
677 681 pulling from ssh://brokenrepository/
678 682 abort: no suitable response from remote hg!
679 683 (Please see http://company/internalwiki/ssh.html)
680 684 [255]
681 685
682 686 test that custom environment is passed down to ssh executable
683 687 $ cat >>dumpenv <<EOF
684 688 > #! /bin/sh
685 689 > echo \$VAR >&2
686 690 > EOF
687 691 $ chmod +x dumpenv
688 692 $ hg pull ssh://something --config ui.ssh="sh dumpenv"
689 693 pulling from ssh://something/
690 694 remote:
691 695 abort: no suitable response from remote hg!
692 696 [255]
693 697 $ hg pull ssh://something --config ui.ssh="sh dumpenv" --config sshenv.VAR=17
694 698 pulling from ssh://something/
695 699 remote: 17
696 700 abort: no suitable response from remote hg!
697 701 [255]
698 702
@@ -1,292 +1,294 b''
1 1 #require no-reposimplestore
2 2
3 3 $ hg clone http://localhost:$HGPORT/ copy
4 4 abort: * (glob)
5 5 [255]
6 6 $ test -d copy
7 7 [1]
8 8
9 9 This server doesn't do range requests so it's basically only good for
10 10 one pull
11 11
12 12 $ "$PYTHON" "$TESTDIR/dumbhttp.py" -p $HGPORT --pid dumb.pid \
13 13 > --logfile server.log
14 14 $ cat dumb.pid >> $DAEMON_PIDS
15 15 $ hg init remote
16 16 $ cd remote
17 17 $ echo foo > bar
18 18 $ echo c2 > '.dotfile with spaces'
19 19 $ hg add
20 20 adding .dotfile with spaces
21 21 adding bar
22 22 $ hg commit -m"test"
23 23 $ hg tip
24 24 changeset: 0:02770d679fb8
25 25 tag: tip
26 26 user: test
27 27 date: Thu Jan 01 00:00:00 1970 +0000
28 28 summary: test
29 29
30 30 $ cd ..
31 31 $ hg clone static-http://localhost:$HGPORT/remote local
32 32 requesting all changes
33 33 adding changesets
34 34 adding manifests
35 35 adding file changes
36 36 added 1 changesets with 2 changes to 2 files
37 37 new changesets 02770d679fb8
38 38 updating to branch default
39 39 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
40 40 $ cd local
41 41 $ hg verify
42 42 checking changesets
43 43 checking manifests
44 44 crosschecking files in changesets and manifests
45 45 checking files
46 46 checked 1 changesets with 2 changes to 2 files
47 47 $ cat bar
48 48 foo
49 49 $ cd ../remote
50 50 $ echo baz > quux
51 51 $ hg commit -A -mtest2
52 52 adding quux
53 53
54 54 check for HTTP opener failures when cachefile does not exist
55 55
56 56 $ rm .hg/cache/*
57 57 $ cd ../local
58 58 $ cat >> .hg/hgrc <<EOF
59 59 > [hooks]
60 60 > changegroup = sh -c "printenv.py --line changegroup"
61 61 > EOF
62 62 $ hg pull
63 63 pulling from static-http://localhost:$HGPORT/remote
64 64 searching for changes
65 65 adding changesets
66 66 adding manifests
67 67 adding file changes
68 68 added 1 changesets with 1 changes to 1 files
69 69 new changesets 4ac2e3648604
70 70 changegroup hook: HG_HOOKNAME=changegroup
71 71 HG_HOOKTYPE=changegroup
72 72 HG_NODE=4ac2e3648604439c580c69b09ec9d93a88d93432
73 73 HG_NODE_LAST=4ac2e3648604439c580c69b09ec9d93a88d93432
74 74 HG_SOURCE=pull
75 75 HG_TXNID=TXN:$ID$
76 HG_TXNNAME=pull
77 http://localhost:$HGPORT/remote
76 78 HG_URL=http://localhost:$HGPORT/remote
77 79
78 80 (run 'hg update' to get a working copy)
79 81
80 82 trying to push
81 83
82 84 $ hg update
83 85 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 86 $ echo more foo >> bar
85 87 $ hg commit -m"test"
86 88 $ hg push
87 89 pushing to static-http://localhost:$HGPORT/remote
88 90 abort: destination does not support push
89 91 [255]
90 92
91 93 trying clone -r
92 94
93 95 $ cd ..
94 96 $ hg clone -r doesnotexist static-http://localhost:$HGPORT/remote local0
95 97 abort: unknown revision 'doesnotexist'!
96 98 [255]
97 99 $ hg clone -r 0 static-http://localhost:$HGPORT/remote local0
98 100 adding changesets
99 101 adding manifests
100 102 adding file changes
101 103 added 1 changesets with 2 changes to 2 files
102 104 new changesets 02770d679fb8
103 105 updating to branch default
104 106 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 107
106 108 test with "/" URI (issue747) and subrepo
107 109
108 110 $ hg init
109 111 $ hg init sub
110 112 $ touch sub/test
111 113 $ hg -R sub commit -A -m "test"
112 114 adding test
113 115 $ hg -R sub tag not-empty
114 116 $ echo sub=sub > .hgsub
115 117 $ echo a > a
116 118 $ hg add a .hgsub
117 119 $ hg -q ci -ma
118 120 $ hg clone static-http://localhost:$HGPORT/ local2
119 121 requesting all changes
120 122 adding changesets
121 123 adding manifests
122 124 adding file changes
123 125 added 1 changesets with 3 changes to 3 files
124 126 new changesets a9ebfbe8e587
125 127 updating to branch default
126 128 cloning subrepo sub from static-http://localhost:$HGPORT/sub
127 129 requesting all changes
128 130 adding changesets
129 131 adding manifests
130 132 adding file changes
131 133 added 2 changesets with 2 changes to 2 files
132 134 new changesets be090ea66256:322ea90975df
133 135 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
134 136 $ cd local2
135 137 $ hg verify
136 138 checking changesets
137 139 checking manifests
138 140 crosschecking files in changesets and manifests
139 141 checking files
140 142 checked 1 changesets with 3 changes to 3 files
141 143 checking subrepo links
142 144 $ cat a
143 145 a
144 146 $ hg paths
145 147 default = static-http://localhost:$HGPORT/
146 148
147 149 test with empty repo (issue965)
148 150
149 151 $ cd ..
150 152 $ hg init remotempty
151 153 $ hg clone static-http://localhost:$HGPORT/remotempty local3
152 154 no changes found
153 155 updating to branch default
154 156 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
155 157 $ cd local3
156 158 $ hg verify
157 159 checking changesets
158 160 checking manifests
159 161 crosschecking files in changesets and manifests
160 162 checking files
161 163 checked 0 changesets with 0 changes to 0 files
162 164 $ hg paths
163 165 default = static-http://localhost:$HGPORT/remotempty
164 166
165 167 test with non-repo
166 168
167 169 $ cd ..
168 170 $ mkdir notarepo
169 171 $ hg clone static-http://localhost:$HGPORT/notarepo local3
170 172 abort: 'http://localhost:$HGPORT/notarepo' does not appear to be an hg repository!
171 173 [255]
172 174
173 175 Clone with tags and branches works
174 176
175 177 $ hg init remote-with-names
176 178 $ cd remote-with-names
177 179 $ echo 0 > foo
178 180 $ hg -q commit -A -m initial
179 181 $ echo 1 > foo
180 182 $ hg commit -m 'commit 1'
181 183 $ hg -q up 0
182 184 $ hg branch mybranch
183 185 marked working directory as branch mybranch
184 186 (branches are permanent and global, did you want a bookmark?)
185 187 $ echo 2 > foo
186 188 $ hg commit -m 'commit 2 (mybranch)'
187 189 $ hg tag -r 1 'default-tag'
188 190 $ hg tag -r 2 'branch-tag'
189 191
190 192 $ cd ..
191 193
192 194 $ hg clone static-http://localhost:$HGPORT/remote-with-names local-with-names
193 195 requesting all changes
194 196 adding changesets
195 197 adding manifests
196 198 adding file changes
197 199 added 5 changesets with 5 changes to 2 files (+1 heads)
198 200 new changesets 68986213bd44:0c325bd2b5a7
199 201 updating to branch default
200 202 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 203
202 204 Clone a specific branch works
203 205
204 206 $ hg clone -r mybranch static-http://localhost:$HGPORT/remote-with-names local-with-names-branch
205 207 adding changesets
206 208 adding manifests
207 209 adding file changes
208 210 added 4 changesets with 4 changes to 2 files
209 211 new changesets 68986213bd44:0c325bd2b5a7
210 212 updating to branch mybranch
211 213 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
212 214
213 215 Clone a specific tag works
214 216
215 217 $ hg clone -r default-tag static-http://localhost:$HGPORT/remote-with-names local-with-names-tag
216 218 adding changesets
217 219 adding manifests
218 220 adding file changes
219 221 added 2 changesets with 2 changes to 1 files
220 222 new changesets 68986213bd44:4ee3fcef1c80
221 223 updating to branch default
222 224 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 225
224 226 $ killdaemons.py
225 227
226 228 List of files accessed over HTTP:
227 229
228 230 $ cat server.log | sed -n -e 's|.*GET \(/[^ ]*\).*|\1|p' | sort -u
229 231 /.hg/bookmarks
230 232 /.hg/bookmarks.current
231 233 /.hg/cache/hgtagsfnodes1
232 234 /.hg/cache/rbc-names-v1
233 235 /.hg/cache/rbc-revs-v1
234 236 /.hg/requires
235 237 /.hg/store/00changelog.i
236 238 /.hg/store/00manifest.i
237 239 /.hg/store/data/%7E2ehgsub.i (no-py37 !)
238 240 /.hg/store/data/%7E2ehgsubstate.i (no-py37 !)
239 241 /.hg/store/data/a.i
240 242 /.hg/store/data/~2ehgsub.i (py37 !)
241 243 /.hg/store/data/~2ehgsubstate.i (py37 !)
242 244 /notarepo/.hg/00changelog.i
243 245 /notarepo/.hg/requires
244 246 /remote-with-names/.hg/bookmarks
245 247 /remote-with-names/.hg/bookmarks.current
246 248 /remote-with-names/.hg/cache/branch2-served
247 249 /remote-with-names/.hg/cache/hgtagsfnodes1
248 250 /remote-with-names/.hg/cache/rbc-names-v1
249 251 /remote-with-names/.hg/cache/rbc-revs-v1
250 252 /remote-with-names/.hg/cache/tags2-served
251 253 /remote-with-names/.hg/localtags
252 254 /remote-with-names/.hg/requires
253 255 /remote-with-names/.hg/store/00changelog.i
254 256 /remote-with-names/.hg/store/00manifest.i
255 257 /remote-with-names/.hg/store/data/%7E2ehgtags.i (no-py37 !)
256 258 /remote-with-names/.hg/store/data/foo.i
257 259 /remote-with-names/.hg/store/data/~2ehgtags.i (py37 !)
258 260 /remote/.hg/bookmarks
259 261 /remote/.hg/bookmarks.current
260 262 /remote/.hg/cache/branch2-base
261 263 /remote/.hg/cache/branch2-immutable
262 264 /remote/.hg/cache/branch2-served
263 265 /remote/.hg/cache/hgtagsfnodes1
264 266 /remote/.hg/cache/rbc-names-v1
265 267 /remote/.hg/cache/rbc-revs-v1
266 268 /remote/.hg/cache/tags2-served
267 269 /remote/.hg/localtags
268 270 /remote/.hg/requires
269 271 /remote/.hg/store/00changelog.i
270 272 /remote/.hg/store/00manifest.i
271 273 /remote/.hg/store/data/%7E2edotfile%20with%20spaces.i (no-py37 !)
272 274 /remote/.hg/store/data/%7E2ehgtags.i (no-py37 !)
273 275 /remote/.hg/store/data/bar.i
274 276 /remote/.hg/store/data/quux.i
275 277 /remote/.hg/store/data/~2edotfile%20with%20spaces.i (py37 !)
276 278 /remote/.hg/store/data/~2ehgtags.i (py37 !)
277 279 /remotempty/.hg/bookmarks
278 280 /remotempty/.hg/bookmarks.current
279 281 /remotempty/.hg/requires
280 282 /remotempty/.hg/store/00changelog.i
281 283 /remotempty/.hg/store/00manifest.i
282 284 /sub/.hg/bookmarks
283 285 /sub/.hg/bookmarks.current
284 286 /sub/.hg/cache/hgtagsfnodes1
285 287 /sub/.hg/cache/rbc-names-v1
286 288 /sub/.hg/cache/rbc-revs-v1
287 289 /sub/.hg/requires
288 290 /sub/.hg/store/00changelog.i
289 291 /sub/.hg/store/00manifest.i
290 292 /sub/.hg/store/data/%7E2ehgtags.i (no-py37 !)
291 293 /sub/.hg/store/data/test.i
292 294 /sub/.hg/store/data/~2ehgtags.i (py37 !)
General Comments 0
You need to be logged in to leave comments. Login now