##// END OF EJS Templates
clone: don't %-escape the default destination (issue3145)
Matt Mackall -
r17844:b32e55e6 stable
parent child Browse files
Show More
@@ -1,610 +1,610 b''
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, httppeer, sshpeer, statichttprepo, bookmarks
12 import localrepo, bundlerepo, httppeer, sshpeer, statichttprepo, bookmarks
13 import lock, util, extensions, error, node, scmutil, phases
13 import lock, util, extensions, error, node, scmutil, phases
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, other, branches, revs):
23 def addbranchrevs(lrepo, other, branches, revs):
24 peer = other.peer() # a courtesy to callers using a localrepo for other
24 peer = other.peer() # a courtesy to callers using a localrepo for other
25 hashbranch, branches = branches
25 hashbranch, branches = branches
26 if not hashbranch and not branches:
26 if not hashbranch and not branches:
27 return revs or None, revs and revs[0] or None
27 return revs or None, revs and revs[0] or None
28 revs = revs and list(revs) or []
28 revs = revs and list(revs) or []
29 if not peer.capable('branchmap'):
29 if not peer.capable('branchmap'):
30 if branches:
30 if branches:
31 raise util.Abort(_("remote branch lookup not supported"))
31 raise util.Abort(_("remote branch lookup not supported"))
32 revs.append(hashbranch)
32 revs.append(hashbranch)
33 return revs, revs[0]
33 return revs, revs[0]
34 branchmap = peer.branchmap()
34 branchmap = peer.branchmap()
35
35
36 def primary(branch):
36 def primary(branch):
37 if branch == '.':
37 if branch == '.':
38 if not lrepo:
38 if not lrepo:
39 raise util.Abort(_("dirstate branch not accessible"))
39 raise util.Abort(_("dirstate branch not accessible"))
40 branch = lrepo.dirstate.branch()
40 branch = lrepo.dirstate.branch()
41 if branch in branchmap:
41 if branch in branchmap:
42 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
42 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
43 return True
43 return True
44 else:
44 else:
45 return False
45 return False
46
46
47 for branch in branches:
47 for branch in branches:
48 if not primary(branch):
48 if not primary(branch):
49 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
49 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
50 if hashbranch:
50 if hashbranch:
51 if not primary(hashbranch):
51 if not primary(hashbranch):
52 revs.append(hashbranch)
52 revs.append(hashbranch)
53 return revs, revs[0]
53 return revs, revs[0]
54
54
55 def parseurl(path, branches=None):
55 def parseurl(path, branches=None):
56 '''parse url#branch, returning (url, (branch, branches))'''
56 '''parse url#branch, returning (url, (branch, branches))'''
57
57
58 u = util.url(path)
58 u = util.url(path)
59 branch = None
59 branch = None
60 if u.fragment:
60 if u.fragment:
61 branch = u.fragment
61 branch = u.fragment
62 u.fragment = None
62 u.fragment = None
63 return str(u), (branch, branches or [])
63 return str(u), (branch, branches or [])
64
64
65 schemes = {
65 schemes = {
66 'bundle': bundlerepo,
66 'bundle': bundlerepo,
67 'file': _local,
67 'file': _local,
68 'http': httppeer,
68 'http': httppeer,
69 'https': httppeer,
69 'https': httppeer,
70 'ssh': sshpeer,
70 'ssh': sshpeer,
71 'static-http': statichttprepo,
71 'static-http': statichttprepo,
72 }
72 }
73
73
74 def _peerlookup(path):
74 def _peerlookup(path):
75 u = util.url(path)
75 u = util.url(path)
76 scheme = u.scheme or 'file'
76 scheme = u.scheme or 'file'
77 thing = schemes.get(scheme) or schemes['file']
77 thing = schemes.get(scheme) or schemes['file']
78 try:
78 try:
79 return thing(path)
79 return thing(path)
80 except TypeError:
80 except TypeError:
81 return thing
81 return thing
82
82
83 def islocal(repo):
83 def islocal(repo):
84 '''return true if repo or path is local'''
84 '''return true if repo or path is local'''
85 if isinstance(repo, str):
85 if isinstance(repo, str):
86 try:
86 try:
87 return _peerlookup(repo).islocal(repo)
87 return _peerlookup(repo).islocal(repo)
88 except AttributeError:
88 except AttributeError:
89 return False
89 return False
90 return repo.local()
90 return repo.local()
91
91
92 def _peerorrepo(ui, path, create=False):
92 def _peerorrepo(ui, path, create=False):
93 """return a repository object for the specified path"""
93 """return a repository object for the specified path"""
94 obj = _peerlookup(path).instance(ui, path, create)
94 obj = _peerlookup(path).instance(ui, path, create)
95 ui = getattr(obj, "ui", ui)
95 ui = getattr(obj, "ui", ui)
96 for name, module in extensions.extensions():
96 for name, module in extensions.extensions():
97 hook = getattr(module, 'reposetup', None)
97 hook = getattr(module, 'reposetup', None)
98 if hook:
98 if hook:
99 hook(ui, obj)
99 hook(ui, obj)
100 return obj
100 return obj
101
101
102 def repository(ui, path='', create=False):
102 def repository(ui, path='', create=False):
103 """return a repository object for the specified path"""
103 """return a repository object for the specified path"""
104 peer = _peerorrepo(ui, path, create)
104 peer = _peerorrepo(ui, path, create)
105 repo = peer.local()
105 repo = peer.local()
106 if not repo:
106 if not repo:
107 raise util.Abort(_("repository '%s' is not local") %
107 raise util.Abort(_("repository '%s' is not local") %
108 (path or peer.url()))
108 (path or peer.url()))
109 return repo
109 return repo
110
110
111 def peer(uiorrepo, opts, path, create=False):
111 def peer(uiorrepo, opts, path, create=False):
112 '''return a repository peer for the specified path'''
112 '''return a repository peer for the specified path'''
113 rui = remoteui(uiorrepo, opts)
113 rui = remoteui(uiorrepo, opts)
114 return _peerorrepo(rui, path, create).peer()
114 return _peerorrepo(rui, path, create).peer()
115
115
116 def defaultdest(source):
116 def defaultdest(source):
117 '''return default destination of clone if none is given'''
117 '''return default destination of clone if none is given'''
118 return os.path.basename(os.path.normpath(source))
118 return os.path.basename(os.path.normpath(util.url(source).path))
119
119
120 def share(ui, source, dest=None, update=True):
120 def share(ui, source, dest=None, update=True):
121 '''create a shared repository'''
121 '''create a shared repository'''
122
122
123 if not islocal(source):
123 if not islocal(source):
124 raise util.Abort(_('can only share local repositories'))
124 raise util.Abort(_('can only share local repositories'))
125
125
126 if not dest:
126 if not dest:
127 dest = defaultdest(source)
127 dest = defaultdest(source)
128 else:
128 else:
129 dest = ui.expandpath(dest)
129 dest = ui.expandpath(dest)
130
130
131 if isinstance(source, str):
131 if isinstance(source, str):
132 origsource = ui.expandpath(source)
132 origsource = ui.expandpath(source)
133 source, branches = parseurl(origsource)
133 source, branches = parseurl(origsource)
134 srcrepo = repository(ui, source)
134 srcrepo = repository(ui, source)
135 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
135 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
136 else:
136 else:
137 srcrepo = source.local()
137 srcrepo = source.local()
138 origsource = source = srcrepo.url()
138 origsource = source = srcrepo.url()
139 checkout = None
139 checkout = None
140
140
141 sharedpath = srcrepo.sharedpath # if our source is already sharing
141 sharedpath = srcrepo.sharedpath # if our source is already sharing
142
142
143 root = os.path.realpath(dest)
143 root = os.path.realpath(dest)
144 roothg = os.path.join(root, '.hg')
144 roothg = os.path.join(root, '.hg')
145
145
146 if os.path.exists(roothg):
146 if os.path.exists(roothg):
147 raise util.Abort(_('destination already exists'))
147 raise util.Abort(_('destination already exists'))
148
148
149 if not os.path.isdir(root):
149 if not os.path.isdir(root):
150 os.mkdir(root)
150 os.mkdir(root)
151 util.makedir(roothg, notindexed=True)
151 util.makedir(roothg, notindexed=True)
152
152
153 requirements = ''
153 requirements = ''
154 try:
154 try:
155 requirements = srcrepo.opener.read('requires')
155 requirements = srcrepo.opener.read('requires')
156 except IOError, inst:
156 except IOError, inst:
157 if inst.errno != errno.ENOENT:
157 if inst.errno != errno.ENOENT:
158 raise
158 raise
159
159
160 requirements += 'shared\n'
160 requirements += 'shared\n'
161 util.writefile(os.path.join(roothg, 'requires'), requirements)
161 util.writefile(os.path.join(roothg, 'requires'), requirements)
162 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
162 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
163
163
164 r = repository(ui, root)
164 r = repository(ui, root)
165
165
166 default = srcrepo.ui.config('paths', 'default')
166 default = srcrepo.ui.config('paths', 'default')
167 if default:
167 if default:
168 fp = r.opener("hgrc", "w", text=True)
168 fp = r.opener("hgrc", "w", text=True)
169 fp.write("[paths]\n")
169 fp.write("[paths]\n")
170 fp.write("default = %s\n" % default)
170 fp.write("default = %s\n" % default)
171 fp.close()
171 fp.close()
172
172
173 if update:
173 if update:
174 r.ui.status(_("updating working directory\n"))
174 r.ui.status(_("updating working directory\n"))
175 if update is not True:
175 if update is not True:
176 checkout = update
176 checkout = update
177 for test in (checkout, 'default', 'tip'):
177 for test in (checkout, 'default', 'tip'):
178 if test is None:
178 if test is None:
179 continue
179 continue
180 try:
180 try:
181 uprev = r.lookup(test)
181 uprev = r.lookup(test)
182 break
182 break
183 except error.RepoLookupError:
183 except error.RepoLookupError:
184 continue
184 continue
185 _update(r, uprev)
185 _update(r, uprev)
186
186
187 def copystore(ui, srcrepo, destpath):
187 def copystore(ui, srcrepo, destpath):
188 '''copy files from store of srcrepo in destpath
188 '''copy files from store of srcrepo in destpath
189
189
190 returns destlock
190 returns destlock
191 '''
191 '''
192 destlock = None
192 destlock = None
193 try:
193 try:
194 hardlink = None
194 hardlink = None
195 num = 0
195 num = 0
196 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
196 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
197 for f in srcrepo.store.copylist():
197 for f in srcrepo.store.copylist():
198 if srcpublishing and f.endswith('phaseroots'):
198 if srcpublishing and f.endswith('phaseroots'):
199 continue
199 continue
200 src = os.path.join(srcrepo.sharedpath, f)
200 src = os.path.join(srcrepo.sharedpath, f)
201 dst = os.path.join(destpath, f)
201 dst = os.path.join(destpath, f)
202 dstbase = os.path.dirname(dst)
202 dstbase = os.path.dirname(dst)
203 if dstbase and not os.path.exists(dstbase):
203 if dstbase and not os.path.exists(dstbase):
204 os.mkdir(dstbase)
204 os.mkdir(dstbase)
205 if os.path.exists(src):
205 if os.path.exists(src):
206 if dst.endswith('data'):
206 if dst.endswith('data'):
207 # lock to avoid premature writing to the target
207 # lock to avoid premature writing to the target
208 destlock = lock.lock(os.path.join(dstbase, "lock"))
208 destlock = lock.lock(os.path.join(dstbase, "lock"))
209 hardlink, n = util.copyfiles(src, dst, hardlink)
209 hardlink, n = util.copyfiles(src, dst, hardlink)
210 num += n
210 num += n
211 if hardlink:
211 if hardlink:
212 ui.debug("linked %d files\n" % num)
212 ui.debug("linked %d files\n" % num)
213 else:
213 else:
214 ui.debug("copied %d files\n" % num)
214 ui.debug("copied %d files\n" % num)
215 return destlock
215 return destlock
216 except: # re-raises
216 except: # re-raises
217 release(destlock)
217 release(destlock)
218 raise
218 raise
219
219
220 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
220 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
221 update=True, stream=False, branch=None):
221 update=True, stream=False, branch=None):
222 """Make a copy of an existing repository.
222 """Make a copy of an existing repository.
223
223
224 Create a copy of an existing repository in a new directory. The
224 Create a copy of an existing repository in a new directory. The
225 source and destination are URLs, as passed to the repository
225 source and destination are URLs, as passed to the repository
226 function. Returns a pair of repository peers, the source and
226 function. Returns a pair of repository peers, the source and
227 newly created destination.
227 newly created destination.
228
228
229 The location of the source is added to the new repository's
229 The location of the source is added to the new repository's
230 .hg/hgrc file, as the default to be used for future pulls and
230 .hg/hgrc file, as the default to be used for future pulls and
231 pushes.
231 pushes.
232
232
233 If an exception is raised, the partly cloned/updated destination
233 If an exception is raised, the partly cloned/updated destination
234 repository will be deleted.
234 repository will be deleted.
235
235
236 Arguments:
236 Arguments:
237
237
238 source: repository object or URL
238 source: repository object or URL
239
239
240 dest: URL of destination repository to create (defaults to base
240 dest: URL of destination repository to create (defaults to base
241 name of source repository)
241 name of source repository)
242
242
243 pull: always pull from source repository, even in local case
243 pull: always pull from source repository, even in local case
244
244
245 stream: stream raw data uncompressed from repository (fast over
245 stream: stream raw data uncompressed from repository (fast over
246 LAN, slow over WAN)
246 LAN, slow over WAN)
247
247
248 rev: revision to clone up to (implies pull=True)
248 rev: revision to clone up to (implies pull=True)
249
249
250 update: update working directory after clone completes, if
250 update: update working directory after clone completes, if
251 destination is local repository (True means update to default rev,
251 destination is local repository (True means update to default rev,
252 anything else is treated as a revision)
252 anything else is treated as a revision)
253
253
254 branch: branches to clone
254 branch: branches to clone
255 """
255 """
256
256
257 if isinstance(source, str):
257 if isinstance(source, str):
258 origsource = ui.expandpath(source)
258 origsource = ui.expandpath(source)
259 source, branch = parseurl(origsource, branch)
259 source, branch = parseurl(origsource, branch)
260 srcpeer = peer(ui, peeropts, source)
260 srcpeer = peer(ui, peeropts, source)
261 else:
261 else:
262 srcpeer = source.peer() # in case we were called with a localrepo
262 srcpeer = source.peer() # in case we were called with a localrepo
263 branch = (None, branch or [])
263 branch = (None, branch or [])
264 origsource = source = srcpeer.url()
264 origsource = source = srcpeer.url()
265 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
265 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
266
266
267 if dest is None:
267 if dest is None:
268 dest = defaultdest(source)
268 dest = defaultdest(source)
269 ui.status(_("destination directory: %s\n") % dest)
269 ui.status(_("destination directory: %s\n") % dest)
270 else:
270 else:
271 dest = ui.expandpath(dest)
271 dest = ui.expandpath(dest)
272
272
273 dest = util.urllocalpath(dest)
273 dest = util.urllocalpath(dest)
274 source = util.urllocalpath(source)
274 source = util.urllocalpath(source)
275
275
276 if not dest:
276 if not dest:
277 raise util.Abort(_("empty destination path is not valid"))
277 raise util.Abort(_("empty destination path is not valid"))
278 if os.path.exists(dest):
278 if os.path.exists(dest):
279 if not os.path.isdir(dest):
279 if not os.path.isdir(dest):
280 raise util.Abort(_("destination '%s' already exists") % dest)
280 raise util.Abort(_("destination '%s' already exists") % dest)
281 elif os.listdir(dest):
281 elif os.listdir(dest):
282 raise util.Abort(_("destination '%s' is not empty") % dest)
282 raise util.Abort(_("destination '%s' is not empty") % dest)
283
283
284 class DirCleanup(object):
284 class DirCleanup(object):
285 def __init__(self, dir_):
285 def __init__(self, dir_):
286 self.rmtree = shutil.rmtree
286 self.rmtree = shutil.rmtree
287 self.dir_ = dir_
287 self.dir_ = dir_
288 def close(self):
288 def close(self):
289 self.dir_ = None
289 self.dir_ = None
290 def cleanup(self):
290 def cleanup(self):
291 if self.dir_:
291 if self.dir_:
292 self.rmtree(self.dir_, True)
292 self.rmtree(self.dir_, True)
293
293
294 srclock = destlock = dircleanup = None
294 srclock = destlock = dircleanup = None
295 srcrepo = srcpeer.local()
295 srcrepo = srcpeer.local()
296 try:
296 try:
297 abspath = origsource
297 abspath = origsource
298 if islocal(origsource):
298 if islocal(origsource):
299 abspath = os.path.abspath(util.urllocalpath(origsource))
299 abspath = os.path.abspath(util.urllocalpath(origsource))
300
300
301 if islocal(dest):
301 if islocal(dest):
302 dircleanup = DirCleanup(dest)
302 dircleanup = DirCleanup(dest)
303
303
304 copy = False
304 copy = False
305 if (srcrepo and srcrepo.cancopy() and islocal(dest)
305 if (srcrepo and srcrepo.cancopy() and islocal(dest)
306 and not phases.hassecret(srcrepo)):
306 and not phases.hassecret(srcrepo)):
307 copy = not pull and not rev
307 copy = not pull and not rev
308
308
309 if copy:
309 if copy:
310 try:
310 try:
311 # we use a lock here because if we race with commit, we
311 # we use a lock here because if we race with commit, we
312 # can end up with extra data in the cloned revlogs that's
312 # can end up with extra data in the cloned revlogs that's
313 # not pointed to by changesets, thus causing verify to
313 # not pointed to by changesets, thus causing verify to
314 # fail
314 # fail
315 srclock = srcrepo.lock(wait=False)
315 srclock = srcrepo.lock(wait=False)
316 except error.LockError:
316 except error.LockError:
317 copy = False
317 copy = False
318
318
319 if copy:
319 if copy:
320 srcrepo.hook('preoutgoing', throw=True, source='clone')
320 srcrepo.hook('preoutgoing', throw=True, source='clone')
321 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
321 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
322 if not os.path.exists(dest):
322 if not os.path.exists(dest):
323 os.mkdir(dest)
323 os.mkdir(dest)
324 else:
324 else:
325 # only clean up directories we create ourselves
325 # only clean up directories we create ourselves
326 dircleanup.dir_ = hgdir
326 dircleanup.dir_ = hgdir
327 try:
327 try:
328 destpath = hgdir
328 destpath = hgdir
329 util.makedir(destpath, notindexed=True)
329 util.makedir(destpath, notindexed=True)
330 except OSError, inst:
330 except OSError, inst:
331 if inst.errno == errno.EEXIST:
331 if inst.errno == errno.EEXIST:
332 dircleanup.close()
332 dircleanup.close()
333 raise util.Abort(_("destination '%s' already exists")
333 raise util.Abort(_("destination '%s' already exists")
334 % dest)
334 % dest)
335 raise
335 raise
336
336
337 destlock = copystore(ui, srcrepo, destpath)
337 destlock = copystore(ui, srcrepo, destpath)
338
338
339 # Recomputing branch cache might be slow on big repos,
339 # Recomputing branch cache might be slow on big repos,
340 # so just copy it
340 # so just copy it
341 dstcachedir = os.path.join(destpath, 'cache')
341 dstcachedir = os.path.join(destpath, 'cache')
342 srcbranchcache = srcrepo.sjoin('cache/branchheads')
342 srcbranchcache = srcrepo.sjoin('cache/branchheads')
343 dstbranchcache = os.path.join(dstcachedir, 'branchheads')
343 dstbranchcache = os.path.join(dstcachedir, 'branchheads')
344 if os.path.exists(srcbranchcache):
344 if os.path.exists(srcbranchcache):
345 if not os.path.exists(dstcachedir):
345 if not os.path.exists(dstcachedir):
346 os.mkdir(dstcachedir)
346 os.mkdir(dstcachedir)
347 util.copyfile(srcbranchcache, dstbranchcache)
347 util.copyfile(srcbranchcache, dstbranchcache)
348
348
349 # we need to re-init the repo after manually copying the data
349 # we need to re-init the repo after manually copying the data
350 # into it
350 # into it
351 destpeer = peer(ui, peeropts, dest)
351 destpeer = peer(ui, peeropts, dest)
352 srcrepo.hook('outgoing', source='clone',
352 srcrepo.hook('outgoing', source='clone',
353 node=node.hex(node.nullid))
353 node=node.hex(node.nullid))
354 else:
354 else:
355 try:
355 try:
356 destpeer = peer(ui, peeropts, dest, create=True)
356 destpeer = peer(ui, peeropts, dest, create=True)
357 except OSError, inst:
357 except OSError, inst:
358 if inst.errno == errno.EEXIST:
358 if inst.errno == errno.EEXIST:
359 dircleanup.close()
359 dircleanup.close()
360 raise util.Abort(_("destination '%s' already exists")
360 raise util.Abort(_("destination '%s' already exists")
361 % dest)
361 % dest)
362 raise
362 raise
363
363
364 revs = None
364 revs = None
365 if rev:
365 if rev:
366 if not srcpeer.capable('lookup'):
366 if not srcpeer.capable('lookup'):
367 raise util.Abort(_("src repository does not support "
367 raise util.Abort(_("src repository does not support "
368 "revision lookup and so doesn't "
368 "revision lookup and so doesn't "
369 "support clone by revision"))
369 "support clone by revision"))
370 revs = [srcpeer.lookup(r) for r in rev]
370 revs = [srcpeer.lookup(r) for r in rev]
371 checkout = revs[0]
371 checkout = revs[0]
372 if destpeer.local():
372 if destpeer.local():
373 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
373 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
374 elif srcrepo:
374 elif srcrepo:
375 srcrepo.push(destpeer, revs=revs)
375 srcrepo.push(destpeer, revs=revs)
376 else:
376 else:
377 raise util.Abort(_("clone from remote to remote not supported"))
377 raise util.Abort(_("clone from remote to remote not supported"))
378
378
379 if dircleanup:
379 if dircleanup:
380 dircleanup.close()
380 dircleanup.close()
381
381
382 # clone all bookmarks except divergent ones
382 # clone all bookmarks except divergent ones
383 destrepo = destpeer.local()
383 destrepo = destpeer.local()
384 if destrepo and srcpeer.capable("pushkey"):
384 if destrepo and srcpeer.capable("pushkey"):
385 rb = srcpeer.listkeys('bookmarks')
385 rb = srcpeer.listkeys('bookmarks')
386 for k, n in rb.iteritems():
386 for k, n in rb.iteritems():
387 try:
387 try:
388 m = destrepo.lookup(n)
388 m = destrepo.lookup(n)
389 destrepo._bookmarks[k] = m
389 destrepo._bookmarks[k] = m
390 except error.RepoLookupError:
390 except error.RepoLookupError:
391 pass
391 pass
392 if rb:
392 if rb:
393 bookmarks.write(destrepo)
393 bookmarks.write(destrepo)
394 elif srcrepo and destpeer.capable("pushkey"):
394 elif srcrepo and destpeer.capable("pushkey"):
395 for k, n in srcrepo._bookmarks.iteritems():
395 for k, n in srcrepo._bookmarks.iteritems():
396 destpeer.pushkey('bookmarks', k, '', hex(n))
396 destpeer.pushkey('bookmarks', k, '', hex(n))
397
397
398 if destrepo:
398 if destrepo:
399 fp = destrepo.opener("hgrc", "w", text=True)
399 fp = destrepo.opener("hgrc", "w", text=True)
400 fp.write("[paths]\n")
400 fp.write("[paths]\n")
401 u = util.url(abspath)
401 u = util.url(abspath)
402 u.passwd = None
402 u.passwd = None
403 defaulturl = str(u)
403 defaulturl = str(u)
404 fp.write("default = %s\n" % defaulturl)
404 fp.write("default = %s\n" % defaulturl)
405 fp.close()
405 fp.close()
406
406
407 destrepo.ui.setconfig('paths', 'default', defaulturl)
407 destrepo.ui.setconfig('paths', 'default', defaulturl)
408
408
409 if update:
409 if update:
410 if update is not True:
410 if update is not True:
411 checkout = srcpeer.lookup(update)
411 checkout = srcpeer.lookup(update)
412 for test in (checkout, '@', 'default', 'tip'):
412 for test in (checkout, '@', 'default', 'tip'):
413 if test is None:
413 if test is None:
414 continue
414 continue
415 try:
415 try:
416 uprev = destrepo.lookup(test)
416 uprev = destrepo.lookup(test)
417 break
417 break
418 except error.RepoLookupError:
418 except error.RepoLookupError:
419 continue
419 continue
420 bn = destrepo[uprev].branch()
420 bn = destrepo[uprev].branch()
421 destrepo.ui.status(_("updating to branch %s\n") % bn)
421 destrepo.ui.status(_("updating to branch %s\n") % bn)
422 _update(destrepo, uprev)
422 _update(destrepo, uprev)
423 if update in destrepo._bookmarks:
423 if update in destrepo._bookmarks:
424 bookmarks.setcurrent(destrepo, update)
424 bookmarks.setcurrent(destrepo, update)
425
425
426 return srcpeer, destpeer
426 return srcpeer, destpeer
427 finally:
427 finally:
428 release(srclock, destlock)
428 release(srclock, destlock)
429 if dircleanup is not None:
429 if dircleanup is not None:
430 dircleanup.cleanup()
430 dircleanup.cleanup()
431 if srcpeer is not None:
431 if srcpeer is not None:
432 srcpeer.close()
432 srcpeer.close()
433
433
434 def _showstats(repo, stats):
434 def _showstats(repo, stats):
435 repo.ui.status(_("%d files updated, %d files merged, "
435 repo.ui.status(_("%d files updated, %d files merged, "
436 "%d files removed, %d files unresolved\n") % stats)
436 "%d files removed, %d files unresolved\n") % stats)
437
437
438 def update(repo, node):
438 def update(repo, node):
439 """update the working directory to node, merging linear changes"""
439 """update the working directory to node, merging linear changes"""
440 stats = mergemod.update(repo, node, False, False, None)
440 stats = mergemod.update(repo, node, False, False, None)
441 _showstats(repo, stats)
441 _showstats(repo, stats)
442 if stats[3]:
442 if stats[3]:
443 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
443 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
444 return stats[3] > 0
444 return stats[3] > 0
445
445
446 # naming conflict in clone()
446 # naming conflict in clone()
447 _update = update
447 _update = update
448
448
449 def clean(repo, node, show_stats=True):
449 def clean(repo, node, show_stats=True):
450 """forcibly switch the working directory to node, clobbering changes"""
450 """forcibly switch the working directory to node, clobbering changes"""
451 stats = mergemod.update(repo, node, False, True, None)
451 stats = mergemod.update(repo, node, False, True, None)
452 if show_stats:
452 if show_stats:
453 _showstats(repo, stats)
453 _showstats(repo, stats)
454 return stats[3] > 0
454 return stats[3] > 0
455
455
456 def merge(repo, node, force=None, remind=True):
456 def merge(repo, node, force=None, remind=True):
457 """Branch merge with node, resolving changes. Return true if any
457 """Branch merge with node, resolving changes. Return true if any
458 unresolved conflicts."""
458 unresolved conflicts."""
459 stats = mergemod.update(repo, node, True, force, False)
459 stats = mergemod.update(repo, node, True, force, False)
460 _showstats(repo, stats)
460 _showstats(repo, stats)
461 if stats[3]:
461 if stats[3]:
462 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
462 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
463 "or 'hg update -C .' to abandon\n"))
463 "or 'hg update -C .' to abandon\n"))
464 elif remind:
464 elif remind:
465 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
465 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
466 return stats[3] > 0
466 return stats[3] > 0
467
467
468 def _incoming(displaychlist, subreporecurse, ui, repo, source,
468 def _incoming(displaychlist, subreporecurse, ui, repo, source,
469 opts, buffered=False):
469 opts, buffered=False):
470 """
470 """
471 Helper for incoming / gincoming.
471 Helper for incoming / gincoming.
472 displaychlist gets called with
472 displaychlist gets called with
473 (remoterepo, incomingchangesetlist, displayer) parameters,
473 (remoterepo, incomingchangesetlist, displayer) parameters,
474 and is supposed to contain only code that can't be unified.
474 and is supposed to contain only code that can't be unified.
475 """
475 """
476 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
476 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
477 other = peer(repo, opts, source)
477 other = peer(repo, opts, source)
478 ui.status(_('comparing with %s\n') % util.hidepassword(source))
478 ui.status(_('comparing with %s\n') % util.hidepassword(source))
479 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
479 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
480
480
481 if revs:
481 if revs:
482 revs = [other.lookup(rev) for rev in revs]
482 revs = [other.lookup(rev) for rev in revs]
483 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
483 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
484 revs, opts["bundle"], opts["force"])
484 revs, opts["bundle"], opts["force"])
485 try:
485 try:
486 if not chlist:
486 if not chlist:
487 ui.status(_("no changes found\n"))
487 ui.status(_("no changes found\n"))
488 return subreporecurse()
488 return subreporecurse()
489
489
490 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
490 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
491
491
492 # XXX once graphlog extension makes it into core,
492 # XXX once graphlog extension makes it into core,
493 # should be replaced by a if graph/else
493 # should be replaced by a if graph/else
494 displaychlist(other, chlist, displayer)
494 displaychlist(other, chlist, displayer)
495
495
496 displayer.close()
496 displayer.close()
497 finally:
497 finally:
498 cleanupfn()
498 cleanupfn()
499 subreporecurse()
499 subreporecurse()
500 return 0 # exit code is zero since we found incoming changes
500 return 0 # exit code is zero since we found incoming changes
501
501
502 def incoming(ui, repo, source, opts):
502 def incoming(ui, repo, source, opts):
503 def subreporecurse():
503 def subreporecurse():
504 ret = 1
504 ret = 1
505 if opts.get('subrepos'):
505 if opts.get('subrepos'):
506 ctx = repo[None]
506 ctx = repo[None]
507 for subpath in sorted(ctx.substate):
507 for subpath in sorted(ctx.substate):
508 sub = ctx.sub(subpath)
508 sub = ctx.sub(subpath)
509 ret = min(ret, sub.incoming(ui, source, opts))
509 ret = min(ret, sub.incoming(ui, source, opts))
510 return ret
510 return ret
511
511
512 def display(other, chlist, displayer):
512 def display(other, chlist, displayer):
513 limit = cmdutil.loglimit(opts)
513 limit = cmdutil.loglimit(opts)
514 if opts.get('newest_first'):
514 if opts.get('newest_first'):
515 chlist.reverse()
515 chlist.reverse()
516 count = 0
516 count = 0
517 for n in chlist:
517 for n in chlist:
518 if limit is not None and count >= limit:
518 if limit is not None and count >= limit:
519 break
519 break
520 parents = [p for p in other.changelog.parents(n) if p != nullid]
520 parents = [p for p in other.changelog.parents(n) if p != nullid]
521 if opts.get('no_merges') and len(parents) == 2:
521 if opts.get('no_merges') and len(parents) == 2:
522 continue
522 continue
523 count += 1
523 count += 1
524 displayer.show(other[n])
524 displayer.show(other[n])
525 return _incoming(display, subreporecurse, ui, repo, source, opts)
525 return _incoming(display, subreporecurse, ui, repo, source, opts)
526
526
527 def _outgoing(ui, repo, dest, opts):
527 def _outgoing(ui, repo, dest, opts):
528 dest = ui.expandpath(dest or 'default-push', dest or 'default')
528 dest = ui.expandpath(dest or 'default-push', dest or 'default')
529 dest, branches = parseurl(dest, opts.get('branch'))
529 dest, branches = parseurl(dest, opts.get('branch'))
530 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
530 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
531 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
531 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
532 if revs:
532 if revs:
533 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
533 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
534
534
535 other = peer(repo, opts, dest)
535 other = peer(repo, opts, dest)
536 outgoing = discovery.findcommonoutgoing(repo, other, revs,
536 outgoing = discovery.findcommonoutgoing(repo, other, revs,
537 force=opts.get('force'))
537 force=opts.get('force'))
538 o = outgoing.missing
538 o = outgoing.missing
539 if not o:
539 if not o:
540 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
540 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
541 return None
541 return None
542 return o
542 return o
543
543
544 def outgoing(ui, repo, dest, opts):
544 def outgoing(ui, repo, dest, opts):
545 def recurse():
545 def recurse():
546 ret = 1
546 ret = 1
547 if opts.get('subrepos'):
547 if opts.get('subrepos'):
548 ctx = repo[None]
548 ctx = repo[None]
549 for subpath in sorted(ctx.substate):
549 for subpath in sorted(ctx.substate):
550 sub = ctx.sub(subpath)
550 sub = ctx.sub(subpath)
551 ret = min(ret, sub.outgoing(ui, dest, opts))
551 ret = min(ret, sub.outgoing(ui, dest, opts))
552 return ret
552 return ret
553
553
554 limit = cmdutil.loglimit(opts)
554 limit = cmdutil.loglimit(opts)
555 o = _outgoing(ui, repo, dest, opts)
555 o = _outgoing(ui, repo, dest, opts)
556 if o is None:
556 if o is None:
557 return recurse()
557 return recurse()
558
558
559 if opts.get('newest_first'):
559 if opts.get('newest_first'):
560 o.reverse()
560 o.reverse()
561 displayer = cmdutil.show_changeset(ui, repo, opts)
561 displayer = cmdutil.show_changeset(ui, repo, opts)
562 count = 0
562 count = 0
563 for n in o:
563 for n in o:
564 if limit is not None and count >= limit:
564 if limit is not None and count >= limit:
565 break
565 break
566 parents = [p for p in repo.changelog.parents(n) if p != nullid]
566 parents = [p for p in repo.changelog.parents(n) if p != nullid]
567 if opts.get('no_merges') and len(parents) == 2:
567 if opts.get('no_merges') and len(parents) == 2:
568 continue
568 continue
569 count += 1
569 count += 1
570 displayer.show(repo[n])
570 displayer.show(repo[n])
571 displayer.close()
571 displayer.close()
572 recurse()
572 recurse()
573 return 0 # exit code is zero since we found outgoing changes
573 return 0 # exit code is zero since we found outgoing changes
574
574
575 def revert(repo, node, choose):
575 def revert(repo, node, choose):
576 """revert changes to revision in node without updating dirstate"""
576 """revert changes to revision in node without updating dirstate"""
577 return mergemod.update(repo, node, False, True, choose)[3] > 0
577 return mergemod.update(repo, node, False, True, choose)[3] > 0
578
578
579 def verify(repo):
579 def verify(repo):
580 """verify the consistency of a repository"""
580 """verify the consistency of a repository"""
581 return verifymod.verify(repo)
581 return verifymod.verify(repo)
582
582
583 def remoteui(src, opts):
583 def remoteui(src, opts):
584 'build a remote ui from ui or repo and opts'
584 'build a remote ui from ui or repo and opts'
585 if util.safehasattr(src, 'baseui'): # looks like a repository
585 if util.safehasattr(src, 'baseui'): # looks like a repository
586 dst = src.baseui.copy() # drop repo-specific config
586 dst = src.baseui.copy() # drop repo-specific config
587 src = src.ui # copy target options from repo
587 src = src.ui # copy target options from repo
588 else: # assume it's a global ui object
588 else: # assume it's a global ui object
589 dst = src.copy() # keep all global options
589 dst = src.copy() # keep all global options
590
590
591 # copy ssh-specific options
591 # copy ssh-specific options
592 for o in 'ssh', 'remotecmd':
592 for o in 'ssh', 'remotecmd':
593 v = opts.get(o) or src.config('ui', o)
593 v = opts.get(o) or src.config('ui', o)
594 if v:
594 if v:
595 dst.setconfig("ui", o, v)
595 dst.setconfig("ui", o, v)
596
596
597 # copy bundle-specific options
597 # copy bundle-specific options
598 r = src.config('bundle', 'mainreporoot')
598 r = src.config('bundle', 'mainreporoot')
599 if r:
599 if r:
600 dst.setconfig('bundle', 'mainreporoot', r)
600 dst.setconfig('bundle', 'mainreporoot', r)
601
601
602 # copy selected local settings to the remote ui
602 # copy selected local settings to the remote ui
603 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
603 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
604 for key, val in src.configitems(sect):
604 for key, val in src.configitems(sect):
605 dst.setconfig(sect, key, val)
605 dst.setconfig(sect, key, val)
606 v = src.config('web', 'cacerts')
606 v = src.config('web', 'cacerts')
607 if v:
607 if v:
608 dst.setconfig('web', 'cacerts', util.expandpath(v))
608 dst.setconfig('web', 'cacerts', util.expandpath(v))
609
609
610 return dst
610 return dst
@@ -1,376 +1,384 b''
1
1
2
2
3 This test tries to exercise the ssh functionality with a dummy script
3 This test tries to exercise the ssh functionality with a dummy script
4
4
5 creating 'remote' repo
5 creating 'remote' repo
6
6
7 $ hg init remote
7 $ hg init remote
8 $ cd remote
8 $ cd remote
9 $ echo this > foo
9 $ echo this > foo
10 $ echo this > fooO
10 $ echo this > fooO
11 $ hg ci -A -m "init" foo fooO
11 $ hg ci -A -m "init" foo fooO
12 $ cat <<EOF > .hg/hgrc
12 $ cat <<EOF > .hg/hgrc
13 > [server]
13 > [server]
14 > uncompressed = True
14 > uncompressed = True
15 >
15 >
16 > [hooks]
16 > [hooks]
17 > changegroup = python "$TESTDIR/printenv.py" changegroup-in-remote 0 ../dummylog
17 > changegroup = python "$TESTDIR/printenv.py" changegroup-in-remote 0 ../dummylog
18 > EOF
18 > EOF
19 $ cd ..
19 $ cd ..
20
20
21 repo not found error
21 repo not found error
22
22
23 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
23 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
24 remote: abort: there is no Mercurial repository here (.hg not found)!
24 remote: abort: there is no Mercurial repository here (.hg not found)!
25 abort: no suitable response from remote hg!
25 abort: no suitable response from remote hg!
26 [255]
26 [255]
27
27
28 non-existent absolute path
28 non-existent absolute path
29
29
30 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local
30 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local
31 remote: abort: there is no Mercurial repository here (.hg not found)!
31 remote: abort: there is no Mercurial repository here (.hg not found)!
32 abort: no suitable response from remote hg!
32 abort: no suitable response from remote hg!
33 [255]
33 [255]
34
34
35 clone remote via stream
35 clone remote via stream
36
36
37 $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/remote local-stream
37 $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/remote local-stream
38 streaming all changes
38 streaming all changes
39 4 files to transfer, 392 bytes of data
39 4 files to transfer, 392 bytes of data
40 transferred 392 bytes in * seconds (*/sec) (glob)
40 transferred 392 bytes in * seconds (*/sec) (glob)
41 updating to branch default
41 updating to branch default
42 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 $ cd local-stream
43 $ cd local-stream
44 $ hg verify
44 $ hg verify
45 checking changesets
45 checking changesets
46 checking manifests
46 checking manifests
47 crosschecking files in changesets and manifests
47 crosschecking files in changesets and manifests
48 checking files
48 checking files
49 2 files, 1 changesets, 2 total revisions
49 2 files, 1 changesets, 2 total revisions
50 $ cd ..
50 $ cd ..
51
51
52 clone remote via pull
52 clone remote via pull
53
53
54 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
54 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
55 requesting all changes
55 requesting all changes
56 adding changesets
56 adding changesets
57 adding manifests
57 adding manifests
58 adding file changes
58 adding file changes
59 added 1 changesets with 2 changes to 2 files
59 added 1 changesets with 2 changes to 2 files
60 updating to branch default
60 updating to branch default
61 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
62
62
63 verify
63 verify
64
64
65 $ cd local
65 $ cd local
66 $ hg verify
66 $ hg verify
67 checking changesets
67 checking changesets
68 checking manifests
68 checking manifests
69 crosschecking files in changesets and manifests
69 crosschecking files in changesets and manifests
70 checking files
70 checking files
71 2 files, 1 changesets, 2 total revisions
71 2 files, 1 changesets, 2 total revisions
72 $ echo '[hooks]' >> .hg/hgrc
72 $ echo '[hooks]' >> .hg/hgrc
73 $ echo "changegroup = python \"$TESTDIR/printenv.py\" changegroup-in-local 0 ../dummylog" >> .hg/hgrc
73 $ echo "changegroup = python \"$TESTDIR/printenv.py\" changegroup-in-local 0 ../dummylog" >> .hg/hgrc
74
74
75 empty default pull
75 empty default pull
76
76
77 $ hg paths
77 $ hg paths
78 default = ssh://user@dummy/remote
78 default = ssh://user@dummy/remote
79 $ hg pull -e "python \"$TESTDIR/dummyssh\""
79 $ hg pull -e "python \"$TESTDIR/dummyssh\""
80 pulling from ssh://user@dummy/remote
80 pulling from ssh://user@dummy/remote
81 searching for changes
81 searching for changes
82 no changes found
82 no changes found
83
83
84 local change
84 local change
85
85
86 $ echo bleah > foo
86 $ echo bleah > foo
87 $ hg ci -m "add"
87 $ hg ci -m "add"
88
88
89 updating rc
89 updating rc
90
90
91 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
91 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
92 $ echo "[ui]" >> .hg/hgrc
92 $ echo "[ui]" >> .hg/hgrc
93 $ echo "ssh = python \"$TESTDIR/dummyssh\"" >> .hg/hgrc
93 $ echo "ssh = python \"$TESTDIR/dummyssh\"" >> .hg/hgrc
94
94
95 find outgoing
95 find outgoing
96
96
97 $ hg out ssh://user@dummy/remote
97 $ hg out ssh://user@dummy/remote
98 comparing with ssh://user@dummy/remote
98 comparing with ssh://user@dummy/remote
99 searching for changes
99 searching for changes
100 changeset: 1:a28a9d1a809c
100 changeset: 1:a28a9d1a809c
101 tag: tip
101 tag: tip
102 user: test
102 user: test
103 date: Thu Jan 01 00:00:00 1970 +0000
103 date: Thu Jan 01 00:00:00 1970 +0000
104 summary: add
104 summary: add
105
105
106
106
107 find incoming on the remote side
107 find incoming on the remote side
108
108
109 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
109 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
110 comparing with ssh://user@dummy/local
110 comparing with ssh://user@dummy/local
111 searching for changes
111 searching for changes
112 changeset: 1:a28a9d1a809c
112 changeset: 1:a28a9d1a809c
113 tag: tip
113 tag: tip
114 user: test
114 user: test
115 date: Thu Jan 01 00:00:00 1970 +0000
115 date: Thu Jan 01 00:00:00 1970 +0000
116 summary: add
116 summary: add
117
117
118
118
119 find incoming on the remote side (using absolute path)
119 find incoming on the remote side (using absolute path)
120
120
121 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
121 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
122 comparing with ssh://user@dummy/$TESTTMP/local
122 comparing with ssh://user@dummy/$TESTTMP/local
123 searching for changes
123 searching for changes
124 changeset: 1:a28a9d1a809c
124 changeset: 1:a28a9d1a809c
125 tag: tip
125 tag: tip
126 user: test
126 user: test
127 date: Thu Jan 01 00:00:00 1970 +0000
127 date: Thu Jan 01 00:00:00 1970 +0000
128 summary: add
128 summary: add
129
129
130
130
131 push
131 push
132
132
133 $ hg push
133 $ hg push
134 pushing to ssh://user@dummy/remote
134 pushing to ssh://user@dummy/remote
135 searching for changes
135 searching for changes
136 remote: adding changesets
136 remote: adding changesets
137 remote: adding manifests
137 remote: adding manifests
138 remote: adding file changes
138 remote: adding file changes
139 remote: added 1 changesets with 1 changes to 1 files
139 remote: added 1 changesets with 1 changes to 1 files
140 $ cd ../remote
140 $ cd ../remote
141
141
142 check remote tip
142 check remote tip
143
143
144 $ hg tip
144 $ hg tip
145 changeset: 1:a28a9d1a809c
145 changeset: 1:a28a9d1a809c
146 tag: tip
146 tag: tip
147 user: test
147 user: test
148 date: Thu Jan 01 00:00:00 1970 +0000
148 date: Thu Jan 01 00:00:00 1970 +0000
149 summary: add
149 summary: add
150
150
151 $ hg verify
151 $ hg verify
152 checking changesets
152 checking changesets
153 checking manifests
153 checking manifests
154 crosschecking files in changesets and manifests
154 crosschecking files in changesets and manifests
155 checking files
155 checking files
156 2 files, 2 changesets, 3 total revisions
156 2 files, 2 changesets, 3 total revisions
157 $ hg cat -r tip foo
157 $ hg cat -r tip foo
158 bleah
158 bleah
159 $ echo z > z
159 $ echo z > z
160 $ hg ci -A -m z z
160 $ hg ci -A -m z z
161 created new head
161 created new head
162
162
163 test pushkeys and bookmarks
163 test pushkeys and bookmarks
164
164
165 $ cd ../local
165 $ cd ../local
166 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
166 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
167 bookmarks
167 bookmarks
168 phases
168 phases
169 namespaces
169 namespaces
170 $ hg book foo -r 0
170 $ hg book foo -r 0
171 $ hg out -B
171 $ hg out -B
172 comparing with ssh://user@dummy/remote
172 comparing with ssh://user@dummy/remote
173 searching for changed bookmarks
173 searching for changed bookmarks
174 foo 1160648e36ce
174 foo 1160648e36ce
175 $ hg push -B foo
175 $ hg push -B foo
176 pushing to ssh://user@dummy/remote
176 pushing to ssh://user@dummy/remote
177 searching for changes
177 searching for changes
178 no changes found
178 no changes found
179 exporting bookmark foo
179 exporting bookmark foo
180 [1]
180 [1]
181 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
181 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
182 foo 1160648e36cec0054048a7edc4110c6f84fde594
182 foo 1160648e36cec0054048a7edc4110c6f84fde594
183 $ hg book -f foo
183 $ hg book -f foo
184 $ hg push --traceback
184 $ hg push --traceback
185 pushing to ssh://user@dummy/remote
185 pushing to ssh://user@dummy/remote
186 searching for changes
186 searching for changes
187 no changes found
187 no changes found
188 updating bookmark foo
188 updating bookmark foo
189 [1]
189 [1]
190 $ hg book -d foo
190 $ hg book -d foo
191 $ hg in -B
191 $ hg in -B
192 comparing with ssh://user@dummy/remote
192 comparing with ssh://user@dummy/remote
193 searching for changed bookmarks
193 searching for changed bookmarks
194 foo a28a9d1a809c
194 foo a28a9d1a809c
195 $ hg book -f -r 0 foo
195 $ hg book -f -r 0 foo
196 $ hg pull -B foo
196 $ hg pull -B foo
197 pulling from ssh://user@dummy/remote
197 pulling from ssh://user@dummy/remote
198 no changes found
198 no changes found
199 updating bookmark foo
199 updating bookmark foo
200 importing bookmark foo
200 importing bookmark foo
201 $ hg book -d foo
201 $ hg book -d foo
202 $ hg push -B foo
202 $ hg push -B foo
203 pushing to ssh://user@dummy/remote
203 pushing to ssh://user@dummy/remote
204 searching for changes
204 searching for changes
205 no changes found
205 no changes found
206 deleting remote bookmark foo
206 deleting remote bookmark foo
207 [1]
207 [1]
208
208
209 a bad, evil hook that prints to stdout
209 a bad, evil hook that prints to stdout
210
210
211 $ cat <<EOF > $TESTTMP/badhook
211 $ cat <<EOF > $TESTTMP/badhook
212 > import sys
212 > import sys
213 > sys.stdout.write("KABOOM\n")
213 > sys.stdout.write("KABOOM\n")
214 > EOF
214 > EOF
215
215
216 $ echo '[hooks]' >> ../remote/.hg/hgrc
216 $ echo '[hooks]' >> ../remote/.hg/hgrc
217 $ echo "changegroup.stdout = python $TESTTMP/badhook" >> ../remote/.hg/hgrc
217 $ echo "changegroup.stdout = python $TESTTMP/badhook" >> ../remote/.hg/hgrc
218 $ echo r > r
218 $ echo r > r
219 $ hg ci -A -m z r
219 $ hg ci -A -m z r
220
220
221 push should succeed even though it has an unexpected response
221 push should succeed even though it has an unexpected response
222
222
223 $ hg push
223 $ hg push
224 pushing to ssh://user@dummy/remote
224 pushing to ssh://user@dummy/remote
225 searching for changes
225 searching for changes
226 note: unsynced remote changes!
226 note: unsynced remote changes!
227 remote: adding changesets
227 remote: adding changesets
228 remote: adding manifests
228 remote: adding manifests
229 remote: adding file changes
229 remote: adding file changes
230 remote: added 1 changesets with 1 changes to 1 files
230 remote: added 1 changesets with 1 changes to 1 files
231 remote: KABOOM
231 remote: KABOOM
232 $ hg -R ../remote heads
232 $ hg -R ../remote heads
233 changeset: 3:1383141674ec
233 changeset: 3:1383141674ec
234 tag: tip
234 tag: tip
235 parent: 1:a28a9d1a809c
235 parent: 1:a28a9d1a809c
236 user: test
236 user: test
237 date: Thu Jan 01 00:00:00 1970 +0000
237 date: Thu Jan 01 00:00:00 1970 +0000
238 summary: z
238 summary: z
239
239
240 changeset: 2:6c0482d977a3
240 changeset: 2:6c0482d977a3
241 parent: 0:1160648e36ce
241 parent: 0:1160648e36ce
242 user: test
242 user: test
243 date: Thu Jan 01 00:00:00 1970 +0000
243 date: Thu Jan 01 00:00:00 1970 +0000
244 summary: z
244 summary: z
245
245
246
246
247 clone bookmarks
247 clone bookmarks
248
248
249 $ hg -R ../remote bookmark test
249 $ hg -R ../remote bookmark test
250 $ hg -R ../remote bookmarks
250 $ hg -R ../remote bookmarks
251 * test 2:6c0482d977a3
251 * test 2:6c0482d977a3
252 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
252 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
253 requesting all changes
253 requesting all changes
254 adding changesets
254 adding changesets
255 adding manifests
255 adding manifests
256 adding file changes
256 adding file changes
257 added 4 changesets with 5 changes to 4 files (+1 heads)
257 added 4 changesets with 5 changes to 4 files (+1 heads)
258 updating to branch default
258 updating to branch default
259 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
259 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
260 $ hg -R local-bookmarks bookmarks
260 $ hg -R local-bookmarks bookmarks
261 test 2:6c0482d977a3
261 test 2:6c0482d977a3
262
262
263 passwords in ssh urls are not supported
263 passwords in ssh urls are not supported
264 (we use a glob here because different Python versions give different
264 (we use a glob here because different Python versions give different
265 results here)
265 results here)
266
266
267 $ hg push ssh://user:erroneouspwd@dummy/remote
267 $ hg push ssh://user:erroneouspwd@dummy/remote
268 pushing to ssh://user:*@dummy/remote (glob)
268 pushing to ssh://user:*@dummy/remote (glob)
269 abort: password in URL not supported!
269 abort: password in URL not supported!
270 [255]
270 [255]
271
271
272 $ cd ..
272 $ cd ..
273
273
274 hide outer repo
274 hide outer repo
275 $ hg init
275 $ hg init
276
276
277 Test remote paths with spaces (issue2983):
277 Test remote paths with spaces (issue2983):
278
278
279 $ hg init --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
279 $ hg init --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
280 $ touch "$TESTTMP/a repo/test"
280 $ touch "$TESTTMP/a repo/test"
281 $ hg -R 'a repo' commit -A -m "test"
281 $ hg -R 'a repo' commit -A -m "test"
282 adding test
282 adding test
283 $ hg -R 'a repo' tag tag
283 $ hg -R 'a repo' tag tag
284 $ hg id --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
284 $ hg id --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
285 73649e48688a
285 73649e48688a
286
286
287 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
288
289 $ hg clone --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
290 destination directory: a repo
291 abort: destination 'a repo' is not empty
292 [255]
293
287 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
294 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
288 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
295 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
289 parameters:
296 parameters:
290
297
291 $ cat > ssh.sh << EOF
298 $ cat > ssh.sh << EOF
292 > userhost="\$1"
299 > userhost="\$1"
293 > SSH_ORIGINAL_COMMAND="\$2"
300 > SSH_ORIGINAL_COMMAND="\$2"
294 > export SSH_ORIGINAL_COMMAND
301 > export SSH_ORIGINAL_COMMAND
295 > PYTHONPATH="$PYTHONPATH"
302 > PYTHONPATH="$PYTHONPATH"
296 > export PYTHONPATH
303 > export PYTHONPATH
297 > python "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
304 > python "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
298 > EOF
305 > EOF
299
306
300 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
307 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
301 73649e48688a
308 73649e48688a
302
309
303 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
310 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
304 remote: Illegal repository "$TESTTMP/a'repo" (glob)
311 remote: Illegal repository "$TESTTMP/a'repo" (glob)
305 abort: no suitable response from remote hg!
312 abort: no suitable response from remote hg!
306 [255]
313 [255]
307
314
308 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
315 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
309 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
316 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
310 abort: no suitable response from remote hg!
317 abort: no suitable response from remote hg!
311 [255]
318 [255]
312
319
313 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a'repo' serve --stdio" python "$TESTDIR/../contrib/hg-ssh"
320 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a'repo' serve --stdio" python "$TESTDIR/../contrib/hg-ssh"
314 Illegal command "'hg' -R 'a'repo' serve --stdio": No closing quotation
321 Illegal command "'hg' -R 'a'repo' serve --stdio": No closing quotation
315 [255]
322 [255]
316
323
317 Test hg-ssh in read-only mode:
324 Test hg-ssh in read-only mode:
318
325
319 $ cat > ssh.sh << EOF
326 $ cat > ssh.sh << EOF
320 > userhost="\$1"
327 > userhost="\$1"
321 > SSH_ORIGINAL_COMMAND="\$2"
328 > SSH_ORIGINAL_COMMAND="\$2"
322 > export SSH_ORIGINAL_COMMAND
329 > export SSH_ORIGINAL_COMMAND
323 > PYTHONPATH="$PYTHONPATH"
330 > PYTHONPATH="$PYTHONPATH"
324 > export PYTHONPATH
331 > export PYTHONPATH
325 > python "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
332 > python "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
326 > EOF
333 > EOF
327
334
328 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
335 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
329 requesting all changes
336 requesting all changes
330 adding changesets
337 adding changesets
331 adding manifests
338 adding manifests
332 adding file changes
339 adding file changes
333 added 4 changesets with 5 changes to 4 files (+1 heads)
340 added 4 changesets with 5 changes to 4 files (+1 heads)
334 updating to branch default
341 updating to branch default
335 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
342 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
336
343
337 $ cd read-only-local
344 $ cd read-only-local
338 $ echo "baz" > bar
345 $ echo "baz" > bar
339 $ hg ci -A -m "unpushable commit" bar
346 $ hg ci -A -m "unpushable commit" bar
340 $ hg push --ssh "sh ../ssh.sh"
347 $ hg push --ssh "sh ../ssh.sh"
341 pushing to ssh://user@dummy/*/remote (glob)
348 pushing to ssh://user@dummy/*/remote (glob)
342 searching for changes
349 searching for changes
343 remote: Permission denied
350 remote: Permission denied
344 remote: abort: prechangegroup.hg-ssh hook failed
351 remote: abort: prechangegroup.hg-ssh hook failed
345 remote: Permission denied
352 remote: Permission denied
346 remote: abort: prepushkey.hg-ssh hook failed
353 remote: abort: prepushkey.hg-ssh hook failed
347 abort: unexpected response: empty string
354 abort: unexpected response: empty string
348 [255]
355 [255]
349
356
350 $ cd ..
357 $ cd ..
351
358
352 $ cat dummylog
359 $ cat dummylog
353 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
360 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
354 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
361 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
355 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
362 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
356 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
363 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
357 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
364 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
358 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
365 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
359 Got arguments 1:user@dummy 2:hg -R local serve --stdio
366 Got arguments 1:user@dummy 2:hg -R local serve --stdio
360 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
367 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
361 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
368 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
362 changegroup-in-remote hook: HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_URL=remote:ssh:127.0.0.1
369 changegroup-in-remote hook: HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_URL=remote:ssh:127.0.0.1
363 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
370 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
364 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
371 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
365 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
372 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
366 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
373 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
367 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
374 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
368 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
375 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
369 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
376 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
370 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
377 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
371 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
378 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
372 changegroup-in-remote hook: HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_URL=remote:ssh:127.0.0.1
379 changegroup-in-remote hook: HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_URL=remote:ssh:127.0.0.1
373 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
380 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
374 Got arguments 1:user@dummy 2:hg init 'a repo'
381 Got arguments 1:user@dummy 2:hg init 'a repo'
375 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
382 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
376 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
383 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
384 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
General Comments 0
You need to be logged in to leave comments. Login now