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