##// END OF EJS Templates
clone: get all bookmarks before updating
Arne Babenhauserheide -
r15590:dbdb8aa7 default
parent child Browse files
Show More
@@ -1,581 +1,581
1 1 # hg.py - repository classes for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 from i18n import _
10 10 from lock import release
11 11 from node import hex, nullid
12 12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
13 13 import lock, util, extensions, error, node
14 14 import cmdutil, discovery
15 15 import merge as mergemod
16 16 import verify as verifymod
17 17 import errno, os, shutil
18 18
19 19 def _local(path):
20 20 path = util.expandpath(util.urllocalpath(path))
21 21 return (os.path.isfile(path) and bundlerepo or localrepo)
22 22
23 23 def addbranchrevs(lrepo, repo, branches, revs):
24 24 hashbranch, branches = branches
25 25 if not hashbranch and not branches:
26 26 return revs or None, revs and revs[0] or None
27 27 revs = revs and list(revs) or []
28 28 if not repo.capable('branchmap'):
29 29 if branches:
30 30 raise util.Abort(_("remote branch lookup not supported"))
31 31 revs.append(hashbranch)
32 32 return revs, revs[0]
33 33 branchmap = repo.branchmap()
34 34
35 35 def primary(branch):
36 36 if branch == '.':
37 37 if not lrepo or not lrepo.local():
38 38 raise util.Abort(_("dirstate branch not accessible"))
39 39 branch = lrepo.dirstate.branch()
40 40 if branch in branchmap:
41 41 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
42 42 return True
43 43 else:
44 44 return False
45 45
46 46 for branch in branches:
47 47 if not primary(branch):
48 48 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
49 49 if hashbranch:
50 50 if not primary(hashbranch):
51 51 revs.append(hashbranch)
52 52 return revs, revs[0]
53 53
54 54 def parseurl(path, branches=None):
55 55 '''parse url#branch, returning (url, (branch, branches))'''
56 56
57 57 u = util.url(path)
58 58 branch = None
59 59 if u.fragment:
60 60 branch = u.fragment
61 61 u.fragment = None
62 62 return str(u), (branch, branches or [])
63 63
64 64 schemes = {
65 65 'bundle': bundlerepo,
66 66 'file': _local,
67 67 'http': httprepo,
68 68 'https': httprepo,
69 69 'ssh': sshrepo,
70 70 'static-http': statichttprepo,
71 71 }
72 72
73 73 def _peerlookup(path):
74 74 u = util.url(path)
75 75 scheme = u.scheme or 'file'
76 76 thing = schemes.get(scheme) or schemes['file']
77 77 try:
78 78 return thing(path)
79 79 except TypeError:
80 80 return thing
81 81
82 82 def islocal(repo):
83 83 '''return true if repo or path is local'''
84 84 if isinstance(repo, str):
85 85 try:
86 86 return _peerlookup(repo).islocal(repo)
87 87 except AttributeError:
88 88 return False
89 89 return repo.local()
90 90
91 91 def repository(ui, path='', create=False):
92 92 """return a repository object for the specified path"""
93 93 repo = _peerlookup(path).instance(ui, path, create)
94 94 ui = getattr(repo, "ui", ui)
95 95 for name, module in extensions.extensions():
96 96 hook = getattr(module, 'reposetup', None)
97 97 if hook:
98 98 hook(ui, repo)
99 99 return repo
100 100
101 101 def peer(uiorrepo, opts, path, create=False):
102 102 '''return a repository peer for the specified path'''
103 103 rui = remoteui(uiorrepo, opts)
104 104 return repository(rui, path, create)
105 105
106 106 def defaultdest(source):
107 107 '''return default destination of clone if none is given'''
108 108 return os.path.basename(os.path.normpath(source))
109 109
110 110 def share(ui, source, dest=None, update=True):
111 111 '''create a shared repository'''
112 112
113 113 if not islocal(source):
114 114 raise util.Abort(_('can only share local repositories'))
115 115
116 116 if not dest:
117 117 dest = defaultdest(source)
118 118 else:
119 119 dest = ui.expandpath(dest)
120 120
121 121 if isinstance(source, str):
122 122 origsource = ui.expandpath(source)
123 123 source, branches = parseurl(origsource)
124 124 srcrepo = repository(ui, source)
125 125 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
126 126 else:
127 127 srcrepo = source
128 128 origsource = source = srcrepo.url()
129 129 checkout = None
130 130
131 131 sharedpath = srcrepo.sharedpath # if our source is already sharing
132 132
133 133 root = os.path.realpath(dest)
134 134 roothg = os.path.join(root, '.hg')
135 135
136 136 if os.path.exists(roothg):
137 137 raise util.Abort(_('destination already exists'))
138 138
139 139 if not os.path.isdir(root):
140 140 os.mkdir(root)
141 141 util.makedir(roothg, notindexed=True)
142 142
143 143 requirements = ''
144 144 try:
145 145 requirements = srcrepo.opener.read('requires')
146 146 except IOError, inst:
147 147 if inst.errno != errno.ENOENT:
148 148 raise
149 149
150 150 requirements += 'shared\n'
151 151 util.writefile(os.path.join(roothg, 'requires'), requirements)
152 152 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
153 153
154 154 r = repository(ui, root)
155 155
156 156 default = srcrepo.ui.config('paths', 'default')
157 157 if default:
158 158 fp = r.opener("hgrc", "w", text=True)
159 159 fp.write("[paths]\n")
160 160 fp.write("default = %s\n" % default)
161 161 fp.close()
162 162
163 163 if update:
164 164 r.ui.status(_("updating working directory\n"))
165 165 if update is not True:
166 166 checkout = update
167 167 for test in (checkout, 'default', 'tip'):
168 168 if test is None:
169 169 continue
170 170 try:
171 171 uprev = r.lookup(test)
172 172 break
173 173 except error.RepoLookupError:
174 174 continue
175 175 _update(r, uprev)
176 176
177 177 def copystore(ui, srcrepo, destpath):
178 178 '''copy files from store of srcrepo in destpath
179 179
180 180 returns destlock
181 181 '''
182 182 destlock = None
183 183 try:
184 184 hardlink = None
185 185 num = 0
186 186 for f in srcrepo.store.copylist():
187 187 src = os.path.join(srcrepo.sharedpath, f)
188 188 dst = os.path.join(destpath, f)
189 189 dstbase = os.path.dirname(dst)
190 190 if dstbase and not os.path.exists(dstbase):
191 191 os.mkdir(dstbase)
192 192 if os.path.exists(src):
193 193 if dst.endswith('data'):
194 194 # lock to avoid premature writing to the target
195 195 destlock = lock.lock(os.path.join(dstbase, "lock"))
196 196 hardlink, n = util.copyfiles(src, dst, hardlink)
197 197 num += n
198 198 if hardlink:
199 199 ui.debug("linked %d files\n" % num)
200 200 else:
201 201 ui.debug("copied %d files\n" % num)
202 202 return destlock
203 203 except:
204 204 release(destlock)
205 205 raise
206 206
207 207 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
208 208 update=True, stream=False, branch=None):
209 209 """Make a copy of an existing repository.
210 210
211 211 Create a copy of an existing repository in a new directory. The
212 212 source and destination are URLs, as passed to the repository
213 213 function. Returns a pair of repository objects, the source and
214 214 newly created destination.
215 215
216 216 The location of the source is added to the new repository's
217 217 .hg/hgrc file, as the default to be used for future pulls and
218 218 pushes.
219 219
220 220 If an exception is raised, the partly cloned/updated destination
221 221 repository will be deleted.
222 222
223 223 Arguments:
224 224
225 225 source: repository object or URL
226 226
227 227 dest: URL of destination repository to create (defaults to base
228 228 name of source repository)
229 229
230 230 pull: always pull from source repository, even in local case
231 231
232 232 stream: stream raw data uncompressed from repository (fast over
233 233 LAN, slow over WAN)
234 234
235 235 rev: revision to clone up to (implies pull=True)
236 236
237 237 update: update working directory after clone completes, if
238 238 destination is local repository (True means update to default rev,
239 239 anything else is treated as a revision)
240 240
241 241 branch: branches to clone
242 242 """
243 243
244 244 if isinstance(source, str):
245 245 origsource = ui.expandpath(source)
246 246 source, branch = parseurl(origsource, branch)
247 247 srcrepo = repository(remoteui(ui, peeropts), source)
248 248 else:
249 249 srcrepo = source
250 250 branch = (None, branch or [])
251 251 origsource = source = srcrepo.url()
252 252 rev, checkout = addbranchrevs(srcrepo, srcrepo, branch, rev)
253 253
254 254 if dest is None:
255 255 dest = defaultdest(source)
256 256 ui.status(_("destination directory: %s\n") % dest)
257 257 else:
258 258 dest = ui.expandpath(dest)
259 259
260 260 dest = util.urllocalpath(dest)
261 261 source = util.urllocalpath(source)
262 262
263 263 if os.path.exists(dest):
264 264 if not os.path.isdir(dest):
265 265 raise util.Abort(_("destination '%s' already exists") % dest)
266 266 elif os.listdir(dest):
267 267 raise util.Abort(_("destination '%s' is not empty") % dest)
268 268
269 269 class DirCleanup(object):
270 270 def __init__(self, dir_):
271 271 self.rmtree = shutil.rmtree
272 272 self.dir_ = dir_
273 273 def close(self):
274 274 self.dir_ = None
275 275 def cleanup(self):
276 276 if self.dir_:
277 277 self.rmtree(self.dir_, True)
278 278
279 279 srclock = destlock = dircleanup = None
280 280 try:
281 281 abspath = origsource
282 282 if islocal(origsource):
283 283 abspath = os.path.abspath(util.urllocalpath(origsource))
284 284
285 285 if islocal(dest):
286 286 dircleanup = DirCleanup(dest)
287 287
288 288 copy = False
289 289 if srcrepo.cancopy() and islocal(dest):
290 290 copy = not pull and not rev
291 291
292 292 if copy:
293 293 try:
294 294 # we use a lock here because if we race with commit, we
295 295 # can end up with extra data in the cloned revlogs that's
296 296 # not pointed to by changesets, thus causing verify to
297 297 # fail
298 298 srclock = srcrepo.lock(wait=False)
299 299 except error.LockError:
300 300 copy = False
301 301
302 302 if copy:
303 303 srcrepo.hook('preoutgoing', throw=True, source='clone')
304 304 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
305 305 if not os.path.exists(dest):
306 306 os.mkdir(dest)
307 307 else:
308 308 # only clean up directories we create ourselves
309 309 dircleanup.dir_ = hgdir
310 310 try:
311 311 destpath = hgdir
312 312 util.makedir(destpath, notindexed=True)
313 313 except OSError, inst:
314 314 if inst.errno == errno.EEXIST:
315 315 dircleanup.close()
316 316 raise util.Abort(_("destination '%s' already exists")
317 317 % dest)
318 318 raise
319 319
320 320 destlock = copystore(ui, srcrepo, destpath)
321 321
322 322 # we need to re-init the repo after manually copying the data
323 323 # into it
324 324 destrepo = repository(remoteui(ui, peeropts), dest)
325 325 srcrepo.hook('outgoing', source='clone',
326 326 node=node.hex(node.nullid))
327 327 else:
328 328 try:
329 329 destrepo = repository(remoteui(ui, peeropts), dest,
330 330 create=True)
331 331 except OSError, inst:
332 332 if inst.errno == errno.EEXIST:
333 333 dircleanup.close()
334 334 raise util.Abort(_("destination '%s' already exists")
335 335 % dest)
336 336 raise
337 337
338 338 revs = None
339 339 if rev:
340 340 if not srcrepo.capable('lookup'):
341 341 raise util.Abort(_("src repository does not support "
342 342 "revision lookup and so doesn't "
343 343 "support clone by revision"))
344 344 revs = [srcrepo.lookup(r) for r in rev]
345 345 checkout = revs[0]
346 346 if destrepo.local():
347 347 destrepo.clone(srcrepo, heads=revs, stream=stream)
348 348 elif srcrepo.local():
349 349 srcrepo.push(destrepo, revs=revs)
350 350 else:
351 351 raise util.Abort(_("clone from remote to remote not supported"))
352 352
353 353 if dircleanup:
354 354 dircleanup.close()
355 355
356 # clone all bookmarks
357 if destrepo.local() and srcrepo.capable("pushkey"):
358 rb = srcrepo.listkeys('bookmarks')
359 for k, n in rb.iteritems():
360 try:
361 m = destrepo.lookup(n)
362 destrepo._bookmarks[k] = m
363 except error.RepoLookupError:
364 pass
365 if rb:
366 bookmarks.write(destrepo)
367 elif srcrepo.local() and destrepo.capable("pushkey"):
368 for k, n in srcrepo._bookmarks.iteritems():
369 destrepo.pushkey('bookmarks', k, '', hex(n))
370
356 371 if destrepo.local():
357 372 fp = destrepo.opener("hgrc", "w", text=True)
358 373 fp.write("[paths]\n")
359 374 u = util.url(abspath)
360 375 u.passwd = None
361 376 defaulturl = str(u)
362 377 fp.write("default = %s\n" % defaulturl)
363 378 fp.close()
364 379
365 380 destrepo.ui.setconfig('paths', 'default', defaulturl)
366 381
367 382 if update:
368 383 if update is not True:
369 384 checkout = update
370 385 if srcrepo.local():
371 386 checkout = srcrepo.lookup(update)
372 387 for test in (checkout, 'default', 'tip'):
373 388 if test is None:
374 389 continue
375 390 try:
376 391 uprev = destrepo.lookup(test)
377 392 break
378 393 except error.RepoLookupError:
379 394 continue
380 395 bn = destrepo[uprev].branch()
381 396 destrepo.ui.status(_("updating to branch %s\n") % bn)
382 397 _update(destrepo, uprev)
383 398
384 # clone all bookmarks
385 if destrepo.local() and srcrepo.capable("pushkey"):
386 rb = srcrepo.listkeys('bookmarks')
387 for k, n in rb.iteritems():
388 try:
389 m = destrepo.lookup(n)
390 destrepo._bookmarks[k] = m
391 except error.RepoLookupError:
392 pass
393 if rb:
394 bookmarks.write(destrepo)
395 elif srcrepo.local() and destrepo.capable("pushkey"):
396 for k, n in srcrepo._bookmarks.iteritems():
397 destrepo.pushkey('bookmarks', k, '', hex(n))
398
399 399 return srcrepo, destrepo
400 400 finally:
401 401 release(srclock, destlock)
402 402 if dircleanup is not None:
403 403 dircleanup.cleanup()
404 404
405 405 def _showstats(repo, stats):
406 406 repo.ui.status(_("%d files updated, %d files merged, "
407 407 "%d files removed, %d files unresolved\n") % stats)
408 408
409 409 def update(repo, node):
410 410 """update the working directory to node, merging linear changes"""
411 411 stats = mergemod.update(repo, node, False, False, None)
412 412 _showstats(repo, stats)
413 413 if stats[3]:
414 414 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
415 415 return stats[3] > 0
416 416
417 417 # naming conflict in clone()
418 418 _update = update
419 419
420 420 def clean(repo, node, show_stats=True):
421 421 """forcibly switch the working directory to node, clobbering changes"""
422 422 stats = mergemod.update(repo, node, False, True, None)
423 423 if show_stats:
424 424 _showstats(repo, stats)
425 425 return stats[3] > 0
426 426
427 427 def merge(repo, node, force=None, remind=True):
428 428 """Branch merge with node, resolving changes. Return true if any
429 429 unresolved conflicts."""
430 430 stats = mergemod.update(repo, node, True, force, False)
431 431 _showstats(repo, stats)
432 432 if stats[3]:
433 433 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
434 434 "or 'hg update -C .' to abandon\n"))
435 435 elif remind:
436 436 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
437 437 return stats[3] > 0
438 438
439 439 def _incoming(displaychlist, subreporecurse, ui, repo, source,
440 440 opts, buffered=False):
441 441 """
442 442 Helper for incoming / gincoming.
443 443 displaychlist gets called with
444 444 (remoterepo, incomingchangesetlist, displayer) parameters,
445 445 and is supposed to contain only code that can't be unified.
446 446 """
447 447 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
448 448 other = peer(repo, opts, source)
449 449 ui.status(_('comparing with %s\n') % util.hidepassword(source))
450 450 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
451 451
452 452 if revs:
453 453 revs = [other.lookup(rev) for rev in revs]
454 454 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
455 455 revs, opts["bundle"], opts["force"])
456 456 try:
457 457 if not chlist:
458 458 ui.status(_("no changes found\n"))
459 459 return subreporecurse()
460 460
461 461 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
462 462
463 463 # XXX once graphlog extension makes it into core,
464 464 # should be replaced by a if graph/else
465 465 displaychlist(other, chlist, displayer)
466 466
467 467 displayer.close()
468 468 finally:
469 469 cleanupfn()
470 470 subreporecurse()
471 471 return 0 # exit code is zero since we found incoming changes
472 472
473 473 def incoming(ui, repo, source, opts):
474 474 def subreporecurse():
475 475 ret = 1
476 476 if opts.get('subrepos'):
477 477 ctx = repo[None]
478 478 for subpath in sorted(ctx.substate):
479 479 sub = ctx.sub(subpath)
480 480 ret = min(ret, sub.incoming(ui, source, opts))
481 481 return ret
482 482
483 483 def display(other, chlist, displayer):
484 484 limit = cmdutil.loglimit(opts)
485 485 if opts.get('newest_first'):
486 486 chlist.reverse()
487 487 count = 0
488 488 for n in chlist:
489 489 if limit is not None and count >= limit:
490 490 break
491 491 parents = [p for p in other.changelog.parents(n) if p != nullid]
492 492 if opts.get('no_merges') and len(parents) == 2:
493 493 continue
494 494 count += 1
495 495 displayer.show(other[n])
496 496 return _incoming(display, subreporecurse, ui, repo, source, opts)
497 497
498 498 def _outgoing(ui, repo, dest, opts):
499 499 dest = ui.expandpath(dest or 'default-push', dest or 'default')
500 500 dest, branches = parseurl(dest, opts.get('branch'))
501 501 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
502 502 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
503 503 if revs:
504 504 revs = [repo.lookup(rev) for rev in revs]
505 505
506 506 other = peer(repo, opts, dest)
507 507 common, outheads = discovery.findcommonoutgoing(repo, other, revs,
508 508 force=opts.get('force'))
509 509 o = repo.changelog.findmissing(common, outheads)
510 510 if not o:
511 511 ui.status(_("no changes found\n"))
512 512 return None
513 513 return o
514 514
515 515 def outgoing(ui, repo, dest, opts):
516 516 def recurse():
517 517 ret = 1
518 518 if opts.get('subrepos'):
519 519 ctx = repo[None]
520 520 for subpath in sorted(ctx.substate):
521 521 sub = ctx.sub(subpath)
522 522 ret = min(ret, sub.outgoing(ui, dest, opts))
523 523 return ret
524 524
525 525 limit = cmdutil.loglimit(opts)
526 526 o = _outgoing(ui, repo, dest, opts)
527 527 if o is None:
528 528 return recurse()
529 529
530 530 if opts.get('newest_first'):
531 531 o.reverse()
532 532 displayer = cmdutil.show_changeset(ui, repo, opts)
533 533 count = 0
534 534 for n in o:
535 535 if limit is not None and count >= limit:
536 536 break
537 537 parents = [p for p in repo.changelog.parents(n) if p != nullid]
538 538 if opts.get('no_merges') and len(parents) == 2:
539 539 continue
540 540 count += 1
541 541 displayer.show(repo[n])
542 542 displayer.close()
543 543 recurse()
544 544 return 0 # exit code is zero since we found outgoing changes
545 545
546 546 def revert(repo, node, choose):
547 547 """revert changes to revision in node without updating dirstate"""
548 548 return mergemod.update(repo, node, False, True, choose)[3] > 0
549 549
550 550 def verify(repo):
551 551 """verify the consistency of a repository"""
552 552 return verifymod.verify(repo)
553 553
554 554 def remoteui(src, opts):
555 555 'build a remote ui from ui or repo and opts'
556 556 if util.safehasattr(src, 'baseui'): # looks like a repository
557 557 dst = src.baseui.copy() # drop repo-specific config
558 558 src = src.ui # copy target options from repo
559 559 else: # assume it's a global ui object
560 560 dst = src.copy() # keep all global options
561 561
562 562 # copy ssh-specific options
563 563 for o in 'ssh', 'remotecmd':
564 564 v = opts.get(o) or src.config('ui', o)
565 565 if v:
566 566 dst.setconfig("ui", o, v)
567 567
568 568 # copy bundle-specific options
569 569 r = src.config('bundle', 'mainreporoot')
570 570 if r:
571 571 dst.setconfig('bundle', 'mainreporoot', r)
572 572
573 573 # copy selected local settings to the remote ui
574 574 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
575 575 for key, val in src.configitems(sect):
576 576 dst.setconfig(sect, key, val)
577 577 v = src.config('web', 'cacerts')
578 578 if v:
579 579 dst.setconfig('web', 'cacerts', util.expandpath(v))
580 580
581 581 return dst
@@ -1,275 +1,275
1 1 Proper https client requires the built-in ssl from Python 2.6.
2 2
3 3 $ "$TESTDIR/hghave" serve ssl || exit 80
4 4
5 5 Certificates created with:
6 6 printf '.\n.\n.\n.\n.\nlocalhost\nhg@localhost\n' | \
7 7 openssl req -newkey rsa:512 -keyout priv.pem -nodes -x509 -days 9000 -out pub.pem
8 8 Can be dumped with:
9 9 openssl x509 -in pub.pem -text
10 10
11 11 $ cat << EOT > priv.pem
12 12 > -----BEGIN PRIVATE KEY-----
13 13 > MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEApjCWeYGrIa/Vo7LH
14 14 > aRF8ou0tbgHKE33Use/whCnKEUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8
15 15 > j/xgSwIDAQABAkBxHC6+Qlf0VJXGlb6NL16yEVVTQxqDS6hA9zqu6TZjrr0YMfzc
16 16 > EGNIiZGt7HCBL0zO+cPDg/LeCZc6HQhf0KrhAiEAzlJq4hWWzvguWFIJWSoBeBUG
17 17 > MF1ACazQO7PYE8M0qfECIQDONHHP0SKZzz/ZwBZcAveC5K61f/v9hONFwbeYulzR
18 18 > +wIgc9SvbtgB/5Yzpp//4ZAEnR7oh5SClCvyB+KSx52K3nECICbhQphhoXmI10wy
19 19 > aMTellaq0bpNMHFDziqH9RsqAHhjAiEAgYGxfzkftt5IUUn/iFK89aaIpyrpuaAh
20 20 > HY8gUVkVRVs=
21 21 > -----END PRIVATE KEY-----
22 22 > EOT
23 23
24 24 $ cat << EOT > pub.pem
25 25 > -----BEGIN CERTIFICATE-----
26 26 > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNV
27 27 > BAMMCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEw
28 28 > MTAxNDIwMzAxNFoXDTM1MDYwNTIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0
29 29 > MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANL
30 30 > ADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnKEUm34rDaXQd4lxxX
31 31 > 6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA+amm
32 32 > r24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQw
33 33 > DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAFArvQFiAZJgQczRsbYlG1xl
34 34 > t+truk37w5B3m3Ick1ntRcQrqs+hf0CO1q6Squ144geYaQ8CDirSR92fICELI1c=
35 35 > -----END CERTIFICATE-----
36 36 > EOT
37 37 $ cat priv.pem pub.pem >> server.pem
38 38 $ PRIV=`pwd`/server.pem
39 39
40 40 $ cat << EOT > pub-other.pem
41 41 > -----BEGIN CERTIFICATE-----
42 42 > MIIBqzCCAVWgAwIBAgIJALwZS731c/ORMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNV
43 43 > BAMMCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEw
44 44 > MTAxNDIwNDUxNloXDTM1MDYwNTIwNDUxNlowMTESMBAGA1UEAwwJbG9jYWxob3N0
45 45 > MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANL
46 46 > ADBIAkEAsxsapLbHrqqUKuQBxdpK4G3m2LjtyrTSdpzzzFlecxd5yhNP6AyWrufo
47 47 > K4VMGo2xlu9xOo88nDSUNSKPuD09MwIDAQABo1AwTjAdBgNVHQ4EFgQUoIB1iMhN
48 48 > y868rpQ2qk9dHnU6ebswHwYDVR0jBBgwFoAUoIB1iMhNy868rpQ2qk9dHnU6ebsw
49 49 > DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJ544f125CsE7J2t55PdFaF6
50 50 > bBlNBb91FCywBgSjhBjf+GG3TNPwrPdc3yqeq+hzJiuInqbOBv9abmMyq8Wsoig=
51 51 > -----END CERTIFICATE-----
52 52 > EOT
53 53
54 54 pub.pem patched with other notBefore / notAfter:
55 55
56 56 $ cat << EOT > pub-not-yet.pem
57 57 > -----BEGIN CERTIFICATE-----
58 58 > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNVBAMMCWxvY2Fs
59 59 > aG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTM1MDYwNTIwMzAxNFoXDTM1MDYw
60 60 > NTIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhv
61 61 > c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnK
62 62 > EUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA
63 63 > +ammr24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQwDAYDVR0T
64 64 > BAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJXV41gWnkgC7jcpPpFRSUSZaxyzrXmD1CIqQf0WgVDb
65 65 > /12E0vR2DuZitgzUYtBaofM81aTtc0a2/YsrmqePGm0=
66 66 > -----END CERTIFICATE-----
67 67 > EOT
68 68 $ cat priv.pem pub-not-yet.pem > server-not-yet.pem
69 69
70 70 $ cat << EOT > pub-expired.pem
71 71 > -----BEGIN CERTIFICATE-----
72 72 > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNVBAMMCWxvY2Fs
73 73 > aG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEwMTAxNDIwMzAxNFoXDTEwMTAx
74 74 > NDIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhv
75 75 > c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnK
76 76 > EUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA
77 77 > +ammr24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQwDAYDVR0T
78 78 > BAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJfk57DTRf2nUbYaMSlVAARxMNbFGOjQhAUtY400GhKt
79 79 > 2uiKCNGKXVXD3AHWe13yHc5KttzbHQStE5Nm/DlWBWQ=
80 80 > -----END CERTIFICATE-----
81 81 > EOT
82 82 $ cat priv.pem pub-expired.pem > server-expired.pem
83 83
84 84 $ hg init test
85 85 $ cd test
86 86 $ echo foo>foo
87 87 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
88 88 $ echo foo>foo.d/foo
89 89 $ echo bar>foo.d/bAr.hg.d/BaR
90 90 $ echo bar>foo.d/baR.d.hg/bAR
91 91 $ hg commit -A -m 1
92 92 adding foo
93 93 adding foo.d/bAr.hg.d/BaR
94 94 adding foo.d/baR.d.hg/bAR
95 95 adding foo.d/foo
96 96 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV
97 97 $ cat ../hg0.pid >> $DAEMON_PIDS
98 98
99 99 cacert not found
100 100
101 101 $ hg in --config web.cacerts=no-such.pem https://localhost:$HGPORT/
102 102 abort: could not find web.cacerts: no-such.pem
103 103 [255]
104 104
105 105 Test server address cannot be reused
106 106
107 107 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
108 108 abort: cannot start server at ':$HGPORT': Address already in use
109 109 [255]
110 110 $ cd ..
111 111
112 112 clone via pull
113 113
114 114 $ hg clone https://localhost:$HGPORT/ copy-pull
115 115 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
116 116 requesting all changes
117 117 adding changesets
118 118 adding manifests
119 119 adding file changes
120 120 added 1 changesets with 4 changes to 4 files
121 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
121 122 updating to branch default
122 123 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
123 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
124 124 $ hg verify -R copy-pull
125 125 checking changesets
126 126 checking manifests
127 127 crosschecking files in changesets and manifests
128 128 checking files
129 129 4 files, 1 changesets, 4 total revisions
130 130 $ cd test
131 131 $ echo bar > bar
132 132 $ hg commit -A -d '1 0' -m 2
133 133 adding bar
134 134 $ cd ..
135 135
136 136 pull without cacert
137 137
138 138 $ cd copy-pull
139 139 $ echo '[hooks]' >> .hg/hgrc
140 140 $ echo "changegroup = python '$TESTDIR'/printenv.py changegroup" >> .hg/hgrc
141 141 $ hg pull
142 142 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
143 143 pulling from https://localhost:$HGPORT/
144 144 searching for changes
145 145 adding changesets
146 146 adding manifests
147 147 adding file changes
148 148 added 1 changesets with 1 changes to 1 files
149 149 changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=https://localhost:$HGPORT/
150 150 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
151 151 (run 'hg update' to get a working copy)
152 152 $ cd ..
153 153
154 154 cacert configured in local repo
155 155
156 156 $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu
157 157 $ echo "[web]" >> copy-pull/.hg/hgrc
158 158 $ echo "cacerts=`pwd`/pub.pem" >> copy-pull/.hg/hgrc
159 159 $ hg -R copy-pull pull --traceback
160 160 pulling from https://localhost:$HGPORT/
161 161 searching for changes
162 162 no changes found
163 163 $ mv copy-pull/.hg/hgrc.bu copy-pull/.hg/hgrc
164 164
165 165 cacert configured globally, also testing expansion of environment
166 166 variables in the filename
167 167
168 168 $ echo "[web]" >> $HGRCPATH
169 169 $ echo 'cacerts=$P/pub.pem' >> $HGRCPATH
170 170 $ P=`pwd` hg -R copy-pull pull
171 171 pulling from https://localhost:$HGPORT/
172 172 searching for changes
173 173 no changes found
174 174 $ P=`pwd` hg -R copy-pull pull --insecure
175 175 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
176 176 pulling from https://localhost:$HGPORT/
177 177 searching for changes
178 178 no changes found
179 179
180 180 cacert mismatch
181 181
182 182 $ hg -R copy-pull pull --config web.cacerts=pub.pem https://127.0.0.1:$HGPORT/
183 183 abort: 127.0.0.1 certificate error: certificate is for localhost (use --insecure to connect insecurely)
184 184 [255]
185 185 $ hg -R copy-pull pull --config web.cacerts=pub.pem https://127.0.0.1:$HGPORT/ --insecure
186 186 warning: 127.0.0.1 certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
187 187 pulling from https://127.0.0.1:$HGPORT/
188 188 searching for changes
189 189 no changes found
190 190 $ hg -R copy-pull pull --config web.cacerts=pub-other.pem
191 191 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
192 192 [255]
193 193 $ hg -R copy-pull pull --config web.cacerts=pub-other.pem --insecure
194 194 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
195 195 pulling from https://localhost:$HGPORT/
196 196 searching for changes
197 197 no changes found
198 198
199 199 Test server cert which isn't valid yet
200 200
201 201 $ hg -R test serve -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem
202 202 $ cat hg1.pid >> $DAEMON_PIDS
203 203 $ hg -R copy-pull pull --config web.cacerts=pub-not-yet.pem https://localhost:$HGPORT1/
204 204 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
205 205 [255]
206 206
207 207 Test server cert which no longer is valid
208 208
209 209 $ hg -R test serve -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
210 210 $ cat hg2.pid >> $DAEMON_PIDS
211 211 $ hg -R copy-pull pull --config web.cacerts=pub-expired.pem https://localhost:$HGPORT2/
212 212 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
213 213 [255]
214 214
215 215 Fingerprints
216 216
217 217 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
218 218 $ echo "localhost = 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca" >> copy-pull/.hg/hgrc
219 219 $ echo "127.0.0.1 = 914f1aff87249c09b6859b88b1906d30756491ca" >> copy-pull/.hg/hgrc
220 220
221 221 - works without cacerts
222 222 $ hg -R copy-pull id https://localhost:$HGPORT/ --config web.cacerts=
223 223 5fed3813f7f5
224 224
225 225 - fails when cert doesn't match hostname (port is ignored)
226 226 $ hg -R copy-pull id https://localhost:$HGPORT1/
227 227 abort: invalid certificate for localhost with fingerprint 28:ff:71:bf:65:31:14:23:ad:62:92:b4:0e:31:99:18:fc:83:e3:9b
228 228 [255]
229 229
230 230 - ignores that certificate doesn't match hostname
231 231 $ hg -R copy-pull id https://127.0.0.1:$HGPORT/
232 232 5fed3813f7f5
233 233
234 234 Prepare for connecting through proxy
235 235
236 236 $ kill `cat hg1.pid`
237 237 $ sleep 1
238 238
239 239 $ ("$TESTDIR/tinyproxy.py" $HGPORT1 localhost >proxy.log 2>&1 </dev/null &
240 240 $ echo $! > proxy.pid)
241 241 $ cat proxy.pid >> $DAEMON_PIDS
242 242 $ sleep 2
243 243
244 244 $ echo "[http_proxy]" >> copy-pull/.hg/hgrc
245 245 $ echo "always=True" >> copy-pull/.hg/hgrc
246 246 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
247 247 $ echo "localhost =" >> copy-pull/.hg/hgrc
248 248
249 249 Test unvalidated https through proxy
250 250
251 251 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure --traceback
252 252 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
253 253 pulling from https://localhost:$HGPORT/
254 254 searching for changes
255 255 no changes found
256 256
257 257 Test https with cacert and fingerprint through proxy
258 258
259 259 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub.pem
260 260 pulling from https://localhost:$HGPORT/
261 261 searching for changes
262 262 no changes found
263 263 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://127.0.0.1:$HGPORT/
264 264 pulling from https://127.0.0.1:$HGPORT/
265 265 searching for changes
266 266 no changes found
267 267
268 268 Test https with cert problems through proxy
269 269
270 270 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub-other.pem
271 271 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
272 272 [255]
273 273 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub-expired.pem https://localhost:$HGPORT2/
274 274 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
275 275 [255]
General Comments 0
You need to be logged in to leave comments. Login now