##// END OF EJS Templates
ssh: quote parameters using shellquote (SEC)...
Jun Wu -
r33715:00a75672 stable
parent child Browse files
Show More
@@ -1,653 +1,656
1 # posix.py - Posix utility function implementations for Mercurial
1 # posix.py - Posix utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import fcntl
11 import fcntl
12 import getpass
12 import getpass
13 import grp
13 import grp
14 import os
14 import os
15 import pwd
15 import pwd
16 import re
16 import re
17 import select
17 import select
18 import stat
18 import stat
19 import sys
19 import sys
20 import tempfile
20 import tempfile
21 import unicodedata
21 import unicodedata
22
22
23 from .i18n import _
23 from .i18n import _
24 from . import (
24 from . import (
25 encoding,
25 encoding,
26 error,
26 error,
27 pycompat,
27 pycompat,
28 )
28 )
29
29
30 posixfile = open
30 posixfile = open
31 normpath = os.path.normpath
31 normpath = os.path.normpath
32 samestat = os.path.samestat
32 samestat = os.path.samestat
33 try:
33 try:
34 oslink = os.link
34 oslink = os.link
35 except AttributeError:
35 except AttributeError:
36 # Some platforms build Python without os.link on systems that are
36 # Some platforms build Python without os.link on systems that are
37 # vaguely unix-like but don't have hardlink support. For those
37 # vaguely unix-like but don't have hardlink support. For those
38 # poor souls, just say we tried and that it failed so we fall back
38 # poor souls, just say we tried and that it failed so we fall back
39 # to copies.
39 # to copies.
40 def oslink(src, dst):
40 def oslink(src, dst):
41 raise OSError(errno.EINVAL,
41 raise OSError(errno.EINVAL,
42 'hardlinks not supported: %s to %s' % (src, dst))
42 'hardlinks not supported: %s to %s' % (src, dst))
43 unlink = os.unlink
43 unlink = os.unlink
44 rename = os.rename
44 rename = os.rename
45 removedirs = os.removedirs
45 removedirs = os.removedirs
46 expandglobs = False
46 expandglobs = False
47
47
48 umask = os.umask(0)
48 umask = os.umask(0)
49 os.umask(umask)
49 os.umask(umask)
50
50
51 def split(p):
51 def split(p):
52 '''Same as posixpath.split, but faster
52 '''Same as posixpath.split, but faster
53
53
54 >>> import posixpath
54 >>> import posixpath
55 >>> for f in ['/absolute/path/to/file',
55 >>> for f in ['/absolute/path/to/file',
56 ... 'relative/path/to/file',
56 ... 'relative/path/to/file',
57 ... 'file_alone',
57 ... 'file_alone',
58 ... 'path/to/directory/',
58 ... 'path/to/directory/',
59 ... '/multiple/path//separators',
59 ... '/multiple/path//separators',
60 ... '/file_at_root',
60 ... '/file_at_root',
61 ... '///multiple_leading_separators_at_root',
61 ... '///multiple_leading_separators_at_root',
62 ... '']:
62 ... '']:
63 ... assert split(f) == posixpath.split(f), f
63 ... assert split(f) == posixpath.split(f), f
64 '''
64 '''
65 ht = p.rsplit('/', 1)
65 ht = p.rsplit('/', 1)
66 if len(ht) == 1:
66 if len(ht) == 1:
67 return '', p
67 return '', p
68 nh = ht[0].rstrip('/')
68 nh = ht[0].rstrip('/')
69 if nh:
69 if nh:
70 return nh, ht[1]
70 return nh, ht[1]
71 return ht[0] + '/', ht[1]
71 return ht[0] + '/', ht[1]
72
72
73 def openhardlinks():
73 def openhardlinks():
74 '''return true if it is safe to hold open file handles to hardlinks'''
74 '''return true if it is safe to hold open file handles to hardlinks'''
75 return True
75 return True
76
76
77 def nlinks(name):
77 def nlinks(name):
78 '''return number of hardlinks for the given file'''
78 '''return number of hardlinks for the given file'''
79 return os.lstat(name).st_nlink
79 return os.lstat(name).st_nlink
80
80
81 def parsepatchoutput(output_line):
81 def parsepatchoutput(output_line):
82 """parses the output produced by patch and returns the filename"""
82 """parses the output produced by patch and returns the filename"""
83 pf = output_line[14:]
83 pf = output_line[14:]
84 if pycompat.sysplatform == 'OpenVMS':
84 if pycompat.sysplatform == 'OpenVMS':
85 if pf[0] == '`':
85 if pf[0] == '`':
86 pf = pf[1:-1] # Remove the quotes
86 pf = pf[1:-1] # Remove the quotes
87 else:
87 else:
88 if pf.startswith("'") and pf.endswith("'") and " " in pf:
88 if pf.startswith("'") and pf.endswith("'") and " " in pf:
89 pf = pf[1:-1] # Remove the quotes
89 pf = pf[1:-1] # Remove the quotes
90 return pf
90 return pf
91
91
92 def sshargs(sshcmd, host, user, port):
92 def sshargs(sshcmd, host, user, port):
93 '''Build argument list for ssh'''
93 '''Build argument list for ssh'''
94 args = user and ("%s@%s" % (user, host)) or host
94 args = user and ("%s@%s" % (user, host)) or host
95 if '-' in args[:2]:
95 if '-' in args[:1]:
96 raise error.Abort(
96 raise error.Abort(
97 _('illegal ssh hostname or username starting with -: %s') % args)
97 _('illegal ssh hostname or username starting with -: %s') % args)
98 return port and ("%s -p %s" % (args, port)) or args
98 args = shellquote(args)
99 if port:
100 args = '-p %s %s' % (shellquote(port), args)
101 return args
99
102
100 def isexec(f):
103 def isexec(f):
101 """check whether a file is executable"""
104 """check whether a file is executable"""
102 return (os.lstat(f).st_mode & 0o100 != 0)
105 return (os.lstat(f).st_mode & 0o100 != 0)
103
106
104 def setflags(f, l, x):
107 def setflags(f, l, x):
105 s = os.lstat(f).st_mode
108 s = os.lstat(f).st_mode
106 if l:
109 if l:
107 if not stat.S_ISLNK(s):
110 if not stat.S_ISLNK(s):
108 # switch file to link
111 # switch file to link
109 fp = open(f)
112 fp = open(f)
110 data = fp.read()
113 data = fp.read()
111 fp.close()
114 fp.close()
112 unlink(f)
115 unlink(f)
113 try:
116 try:
114 os.symlink(data, f)
117 os.symlink(data, f)
115 except OSError:
118 except OSError:
116 # failed to make a link, rewrite file
119 # failed to make a link, rewrite file
117 fp = open(f, "w")
120 fp = open(f, "w")
118 fp.write(data)
121 fp.write(data)
119 fp.close()
122 fp.close()
120 # no chmod needed at this point
123 # no chmod needed at this point
121 return
124 return
122 if stat.S_ISLNK(s):
125 if stat.S_ISLNK(s):
123 # switch link to file
126 # switch link to file
124 data = os.readlink(f)
127 data = os.readlink(f)
125 unlink(f)
128 unlink(f)
126 fp = open(f, "w")
129 fp = open(f, "w")
127 fp.write(data)
130 fp.write(data)
128 fp.close()
131 fp.close()
129 s = 0o666 & ~umask # avoid restatting for chmod
132 s = 0o666 & ~umask # avoid restatting for chmod
130
133
131 sx = s & 0o100
134 sx = s & 0o100
132 if x and not sx:
135 if x and not sx:
133 # Turn on +x for every +r bit when making a file executable
136 # Turn on +x for every +r bit when making a file executable
134 # and obey umask.
137 # and obey umask.
135 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
138 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
136 elif not x and sx:
139 elif not x and sx:
137 # Turn off all +x bits
140 # Turn off all +x bits
138 os.chmod(f, s & 0o666)
141 os.chmod(f, s & 0o666)
139
142
140 def copymode(src, dst, mode=None):
143 def copymode(src, dst, mode=None):
141 '''Copy the file mode from the file at path src to dst.
144 '''Copy the file mode from the file at path src to dst.
142 If src doesn't exist, we're using mode instead. If mode is None, we're
145 If src doesn't exist, we're using mode instead. If mode is None, we're
143 using umask.'''
146 using umask.'''
144 try:
147 try:
145 st_mode = os.lstat(src).st_mode & 0o777
148 st_mode = os.lstat(src).st_mode & 0o777
146 except OSError as inst:
149 except OSError as inst:
147 if inst.errno != errno.ENOENT:
150 if inst.errno != errno.ENOENT:
148 raise
151 raise
149 st_mode = mode
152 st_mode = mode
150 if st_mode is None:
153 if st_mode is None:
151 st_mode = ~umask
154 st_mode = ~umask
152 st_mode &= 0o666
155 st_mode &= 0o666
153 os.chmod(dst, st_mode)
156 os.chmod(dst, st_mode)
154
157
155 def checkexec(path):
158 def checkexec(path):
156 """
159 """
157 Check whether the given path is on a filesystem with UNIX-like exec flags
160 Check whether the given path is on a filesystem with UNIX-like exec flags
158
161
159 Requires a directory (like /foo/.hg)
162 Requires a directory (like /foo/.hg)
160 """
163 """
161
164
162 # VFAT on some Linux versions can flip mode but it doesn't persist
165 # VFAT on some Linux versions can flip mode but it doesn't persist
163 # a FS remount. Frequently we can detect it if files are created
166 # a FS remount. Frequently we can detect it if files are created
164 # with exec bit on.
167 # with exec bit on.
165
168
166 try:
169 try:
167 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
170 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
168 cachedir = os.path.join(path, '.hg', 'cache')
171 cachedir = os.path.join(path, '.hg', 'cache')
169 if os.path.isdir(cachedir):
172 if os.path.isdir(cachedir):
170 checkisexec = os.path.join(cachedir, 'checkisexec')
173 checkisexec = os.path.join(cachedir, 'checkisexec')
171 checknoexec = os.path.join(cachedir, 'checknoexec')
174 checknoexec = os.path.join(cachedir, 'checknoexec')
172
175
173 try:
176 try:
174 m = os.stat(checkisexec).st_mode
177 m = os.stat(checkisexec).st_mode
175 except OSError as e:
178 except OSError as e:
176 if e.errno != errno.ENOENT:
179 if e.errno != errno.ENOENT:
177 raise
180 raise
178 # checkisexec does not exist - fall through ...
181 # checkisexec does not exist - fall through ...
179 else:
182 else:
180 # checkisexec exists, check if it actually is exec
183 # checkisexec exists, check if it actually is exec
181 if m & EXECFLAGS != 0:
184 if m & EXECFLAGS != 0:
182 # ensure checkisexec exists, check it isn't exec
185 # ensure checkisexec exists, check it isn't exec
183 try:
186 try:
184 m = os.stat(checknoexec).st_mode
187 m = os.stat(checknoexec).st_mode
185 except OSError as e:
188 except OSError as e:
186 if e.errno != errno.ENOENT:
189 if e.errno != errno.ENOENT:
187 raise
190 raise
188 open(checknoexec, 'w').close() # might fail
191 open(checknoexec, 'w').close() # might fail
189 m = os.stat(checknoexec).st_mode
192 m = os.stat(checknoexec).st_mode
190 if m & EXECFLAGS == 0:
193 if m & EXECFLAGS == 0:
191 # check-exec is exec and check-no-exec is not exec
194 # check-exec is exec and check-no-exec is not exec
192 return True
195 return True
193 # checknoexec exists but is exec - delete it
196 # checknoexec exists but is exec - delete it
194 unlink(checknoexec)
197 unlink(checknoexec)
195 # checkisexec exists but is not exec - delete it
198 # checkisexec exists but is not exec - delete it
196 unlink(checkisexec)
199 unlink(checkisexec)
197
200
198 # check using one file, leave it as checkisexec
201 # check using one file, leave it as checkisexec
199 checkdir = cachedir
202 checkdir = cachedir
200 else:
203 else:
201 # check directly in path and don't leave checkisexec behind
204 # check directly in path and don't leave checkisexec behind
202 checkdir = path
205 checkdir = path
203 checkisexec = None
206 checkisexec = None
204 fh, fn = tempfile.mkstemp(dir=checkdir, prefix='hg-checkexec-')
207 fh, fn = tempfile.mkstemp(dir=checkdir, prefix='hg-checkexec-')
205 try:
208 try:
206 os.close(fh)
209 os.close(fh)
207 m = os.stat(fn).st_mode
210 m = os.stat(fn).st_mode
208 if m & EXECFLAGS == 0:
211 if m & EXECFLAGS == 0:
209 os.chmod(fn, m & 0o777 | EXECFLAGS)
212 os.chmod(fn, m & 0o777 | EXECFLAGS)
210 if os.stat(fn).st_mode & EXECFLAGS != 0:
213 if os.stat(fn).st_mode & EXECFLAGS != 0:
211 if checkisexec is not None:
214 if checkisexec is not None:
212 os.rename(fn, checkisexec)
215 os.rename(fn, checkisexec)
213 fn = None
216 fn = None
214 return True
217 return True
215 finally:
218 finally:
216 if fn is not None:
219 if fn is not None:
217 unlink(fn)
220 unlink(fn)
218 except (IOError, OSError):
221 except (IOError, OSError):
219 # we don't care, the user probably won't be able to commit anyway
222 # we don't care, the user probably won't be able to commit anyway
220 return False
223 return False
221
224
222 def checklink(path):
225 def checklink(path):
223 """check whether the given path is on a symlink-capable filesystem"""
226 """check whether the given path is on a symlink-capable filesystem"""
224 # mktemp is not racy because symlink creation will fail if the
227 # mktemp is not racy because symlink creation will fail if the
225 # file already exists
228 # file already exists
226 while True:
229 while True:
227 cachedir = os.path.join(path, '.hg', 'cache')
230 cachedir = os.path.join(path, '.hg', 'cache')
228 checklink = os.path.join(cachedir, 'checklink')
231 checklink = os.path.join(cachedir, 'checklink')
229 # try fast path, read only
232 # try fast path, read only
230 if os.path.islink(checklink):
233 if os.path.islink(checklink):
231 return True
234 return True
232 if os.path.isdir(cachedir):
235 if os.path.isdir(cachedir):
233 checkdir = cachedir
236 checkdir = cachedir
234 else:
237 else:
235 checkdir = path
238 checkdir = path
236 cachedir = None
239 cachedir = None
237 fscheckdir = pycompat.fsdecode(checkdir)
240 fscheckdir = pycompat.fsdecode(checkdir)
238 name = tempfile.mktemp(dir=fscheckdir,
241 name = tempfile.mktemp(dir=fscheckdir,
239 prefix=r'checklink-')
242 prefix=r'checklink-')
240 name = pycompat.fsencode(name)
243 name = pycompat.fsencode(name)
241 try:
244 try:
242 fd = None
245 fd = None
243 if cachedir is None:
246 if cachedir is None:
244 fd = tempfile.NamedTemporaryFile(dir=fscheckdir,
247 fd = tempfile.NamedTemporaryFile(dir=fscheckdir,
245 prefix=r'hg-checklink-')
248 prefix=r'hg-checklink-')
246 target = pycompat.fsencode(os.path.basename(fd.name))
249 target = pycompat.fsencode(os.path.basename(fd.name))
247 else:
250 else:
248 # create a fixed file to link to; doesn't matter if it
251 # create a fixed file to link to; doesn't matter if it
249 # already exists.
252 # already exists.
250 target = 'checklink-target'
253 target = 'checklink-target'
251 open(os.path.join(cachedir, target), 'w').close()
254 open(os.path.join(cachedir, target), 'w').close()
252 try:
255 try:
253 os.symlink(target, name)
256 os.symlink(target, name)
254 if cachedir is None:
257 if cachedir is None:
255 unlink(name)
258 unlink(name)
256 else:
259 else:
257 try:
260 try:
258 os.rename(name, checklink)
261 os.rename(name, checklink)
259 except OSError:
262 except OSError:
260 unlink(name)
263 unlink(name)
261 return True
264 return True
262 except OSError as inst:
265 except OSError as inst:
263 # link creation might race, try again
266 # link creation might race, try again
264 if inst[0] == errno.EEXIST:
267 if inst[0] == errno.EEXIST:
265 continue
268 continue
266 raise
269 raise
267 finally:
270 finally:
268 if fd is not None:
271 if fd is not None:
269 fd.close()
272 fd.close()
270 except AttributeError:
273 except AttributeError:
271 return False
274 return False
272 except OSError as inst:
275 except OSError as inst:
273 # sshfs might report failure while successfully creating the link
276 # sshfs might report failure while successfully creating the link
274 if inst[0] == errno.EIO and os.path.exists(name):
277 if inst[0] == errno.EIO and os.path.exists(name):
275 unlink(name)
278 unlink(name)
276 return False
279 return False
277
280
278 def checkosfilename(path):
281 def checkosfilename(path):
279 '''Check that the base-relative path is a valid filename on this platform.
282 '''Check that the base-relative path is a valid filename on this platform.
280 Returns None if the path is ok, or a UI string describing the problem.'''
283 Returns None if the path is ok, or a UI string describing the problem.'''
281 pass # on posix platforms, every path is ok
284 pass # on posix platforms, every path is ok
282
285
283 def setbinary(fd):
286 def setbinary(fd):
284 pass
287 pass
285
288
286 def pconvert(path):
289 def pconvert(path):
287 return path
290 return path
288
291
289 def localpath(path):
292 def localpath(path):
290 return path
293 return path
291
294
292 def samefile(fpath1, fpath2):
295 def samefile(fpath1, fpath2):
293 """Returns whether path1 and path2 refer to the same file. This is only
296 """Returns whether path1 and path2 refer to the same file. This is only
294 guaranteed to work for files, not directories."""
297 guaranteed to work for files, not directories."""
295 return os.path.samefile(fpath1, fpath2)
298 return os.path.samefile(fpath1, fpath2)
296
299
297 def samedevice(fpath1, fpath2):
300 def samedevice(fpath1, fpath2):
298 """Returns whether fpath1 and fpath2 are on the same device. This is only
301 """Returns whether fpath1 and fpath2 are on the same device. This is only
299 guaranteed to work for files, not directories."""
302 guaranteed to work for files, not directories."""
300 st1 = os.lstat(fpath1)
303 st1 = os.lstat(fpath1)
301 st2 = os.lstat(fpath2)
304 st2 = os.lstat(fpath2)
302 return st1.st_dev == st2.st_dev
305 return st1.st_dev == st2.st_dev
303
306
304 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
307 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
305 def normcase(path):
308 def normcase(path):
306 return path.lower()
309 return path.lower()
307
310
308 # what normcase does to ASCII strings
311 # what normcase does to ASCII strings
309 normcasespec = encoding.normcasespecs.lower
312 normcasespec = encoding.normcasespecs.lower
310 # fallback normcase function for non-ASCII strings
313 # fallback normcase function for non-ASCII strings
311 normcasefallback = normcase
314 normcasefallback = normcase
312
315
313 if pycompat.sysplatform == 'darwin':
316 if pycompat.sysplatform == 'darwin':
314
317
315 def normcase(path):
318 def normcase(path):
316 '''
319 '''
317 Normalize a filename for OS X-compatible comparison:
320 Normalize a filename for OS X-compatible comparison:
318 - escape-encode invalid characters
321 - escape-encode invalid characters
319 - decompose to NFD
322 - decompose to NFD
320 - lowercase
323 - lowercase
321 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
324 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
322
325
323 >>> normcase('UPPER')
326 >>> normcase('UPPER')
324 'upper'
327 'upper'
325 >>> normcase('Caf\xc3\xa9')
328 >>> normcase('Caf\xc3\xa9')
326 'cafe\\xcc\\x81'
329 'cafe\\xcc\\x81'
327 >>> normcase('\xc3\x89')
330 >>> normcase('\xc3\x89')
328 'e\\xcc\\x81'
331 'e\\xcc\\x81'
329 >>> normcase('\xb8\xca\xc3\xca\xbe\xc8.JPG') # issue3918
332 >>> normcase('\xb8\xca\xc3\xca\xbe\xc8.JPG') # issue3918
330 '%b8%ca%c3\\xca\\xbe%c8.jpg'
333 '%b8%ca%c3\\xca\\xbe%c8.jpg'
331 '''
334 '''
332
335
333 try:
336 try:
334 return encoding.asciilower(path) # exception for non-ASCII
337 return encoding.asciilower(path) # exception for non-ASCII
335 except UnicodeDecodeError:
338 except UnicodeDecodeError:
336 return normcasefallback(path)
339 return normcasefallback(path)
337
340
338 normcasespec = encoding.normcasespecs.lower
341 normcasespec = encoding.normcasespecs.lower
339
342
340 def normcasefallback(path):
343 def normcasefallback(path):
341 try:
344 try:
342 u = path.decode('utf-8')
345 u = path.decode('utf-8')
343 except UnicodeDecodeError:
346 except UnicodeDecodeError:
344 # OS X percent-encodes any bytes that aren't valid utf-8
347 # OS X percent-encodes any bytes that aren't valid utf-8
345 s = ''
348 s = ''
346 pos = 0
349 pos = 0
347 l = len(path)
350 l = len(path)
348 while pos < l:
351 while pos < l:
349 try:
352 try:
350 c = encoding.getutf8char(path, pos)
353 c = encoding.getutf8char(path, pos)
351 pos += len(c)
354 pos += len(c)
352 except ValueError:
355 except ValueError:
353 c = '%%%02X' % ord(path[pos])
356 c = '%%%02X' % ord(path[pos])
354 pos += 1
357 pos += 1
355 s += c
358 s += c
356
359
357 u = s.decode('utf-8')
360 u = s.decode('utf-8')
358
361
359 # Decompose then lowercase (HFS+ technote specifies lower)
362 # Decompose then lowercase (HFS+ technote specifies lower)
360 enc = unicodedata.normalize('NFD', u).lower().encode('utf-8')
363 enc = unicodedata.normalize('NFD', u).lower().encode('utf-8')
361 # drop HFS+ ignored characters
364 # drop HFS+ ignored characters
362 return encoding.hfsignoreclean(enc)
365 return encoding.hfsignoreclean(enc)
363
366
364 if pycompat.sysplatform == 'cygwin':
367 if pycompat.sysplatform == 'cygwin':
365 # workaround for cygwin, in which mount point part of path is
368 # workaround for cygwin, in which mount point part of path is
366 # treated as case sensitive, even though underlying NTFS is case
369 # treated as case sensitive, even though underlying NTFS is case
367 # insensitive.
370 # insensitive.
368
371
369 # default mount points
372 # default mount points
370 cygwinmountpoints = sorted([
373 cygwinmountpoints = sorted([
371 "/usr/bin",
374 "/usr/bin",
372 "/usr/lib",
375 "/usr/lib",
373 "/cygdrive",
376 "/cygdrive",
374 ], reverse=True)
377 ], reverse=True)
375
378
376 # use upper-ing as normcase as same as NTFS workaround
379 # use upper-ing as normcase as same as NTFS workaround
377 def normcase(path):
380 def normcase(path):
378 pathlen = len(path)
381 pathlen = len(path)
379 if (pathlen == 0) or (path[0] != pycompat.ossep):
382 if (pathlen == 0) or (path[0] != pycompat.ossep):
380 # treat as relative
383 # treat as relative
381 return encoding.upper(path)
384 return encoding.upper(path)
382
385
383 # to preserve case of mountpoint part
386 # to preserve case of mountpoint part
384 for mp in cygwinmountpoints:
387 for mp in cygwinmountpoints:
385 if not path.startswith(mp):
388 if not path.startswith(mp):
386 continue
389 continue
387
390
388 mplen = len(mp)
391 mplen = len(mp)
389 if mplen == pathlen: # mount point itself
392 if mplen == pathlen: # mount point itself
390 return mp
393 return mp
391 if path[mplen] == pycompat.ossep:
394 if path[mplen] == pycompat.ossep:
392 return mp + encoding.upper(path[mplen:])
395 return mp + encoding.upper(path[mplen:])
393
396
394 return encoding.upper(path)
397 return encoding.upper(path)
395
398
396 normcasespec = encoding.normcasespecs.other
399 normcasespec = encoding.normcasespecs.other
397 normcasefallback = normcase
400 normcasefallback = normcase
398
401
399 # Cygwin translates native ACLs to POSIX permissions,
402 # Cygwin translates native ACLs to POSIX permissions,
400 # but these translations are not supported by native
403 # but these translations are not supported by native
401 # tools, so the exec bit tends to be set erroneously.
404 # tools, so the exec bit tends to be set erroneously.
402 # Therefore, disable executable bit access on Cygwin.
405 # Therefore, disable executable bit access on Cygwin.
403 def checkexec(path):
406 def checkexec(path):
404 return False
407 return False
405
408
406 # Similarly, Cygwin's symlink emulation is likely to create
409 # Similarly, Cygwin's symlink emulation is likely to create
407 # problems when Mercurial is used from both Cygwin and native
410 # problems when Mercurial is used from both Cygwin and native
408 # Windows, with other native tools, or on shared volumes
411 # Windows, with other native tools, or on shared volumes
409 def checklink(path):
412 def checklink(path):
410 return False
413 return False
411
414
412 _needsshellquote = None
415 _needsshellquote = None
413 def shellquote(s):
416 def shellquote(s):
414 if pycompat.sysplatform == 'OpenVMS':
417 if pycompat.sysplatform == 'OpenVMS':
415 return '"%s"' % s
418 return '"%s"' % s
416 global _needsshellquote
419 global _needsshellquote
417 if _needsshellquote is None:
420 if _needsshellquote is None:
418 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
421 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
419 if s and not _needsshellquote(s):
422 if s and not _needsshellquote(s):
420 # "s" shouldn't have to be quoted
423 # "s" shouldn't have to be quoted
421 return s
424 return s
422 else:
425 else:
423 return "'%s'" % s.replace("'", "'\\''")
426 return "'%s'" % s.replace("'", "'\\''")
424
427
425 def quotecommand(cmd):
428 def quotecommand(cmd):
426 return cmd
429 return cmd
427
430
428 def popen(command, mode='r'):
431 def popen(command, mode='r'):
429 return os.popen(command, mode)
432 return os.popen(command, mode)
430
433
431 def testpid(pid):
434 def testpid(pid):
432 '''return False if pid dead, True if running or not sure'''
435 '''return False if pid dead, True if running or not sure'''
433 if pycompat.sysplatform == 'OpenVMS':
436 if pycompat.sysplatform == 'OpenVMS':
434 return True
437 return True
435 try:
438 try:
436 os.kill(pid, 0)
439 os.kill(pid, 0)
437 return True
440 return True
438 except OSError as inst:
441 except OSError as inst:
439 return inst.errno != errno.ESRCH
442 return inst.errno != errno.ESRCH
440
443
441 def explainexit(code):
444 def explainexit(code):
442 """return a 2-tuple (desc, code) describing a subprocess status
445 """return a 2-tuple (desc, code) describing a subprocess status
443 (codes from kill are negative - not os.system/wait encoding)"""
446 (codes from kill are negative - not os.system/wait encoding)"""
444 if code >= 0:
447 if code >= 0:
445 return _("exited with status %d") % code, code
448 return _("exited with status %d") % code, code
446 return _("killed by signal %d") % -code, -code
449 return _("killed by signal %d") % -code, -code
447
450
448 def isowner(st):
451 def isowner(st):
449 """Return True if the stat object st is from the current user."""
452 """Return True if the stat object st is from the current user."""
450 return st.st_uid == os.getuid()
453 return st.st_uid == os.getuid()
451
454
452 def findexe(command):
455 def findexe(command):
453 '''Find executable for command searching like which does.
456 '''Find executable for command searching like which does.
454 If command is a basename then PATH is searched for command.
457 If command is a basename then PATH is searched for command.
455 PATH isn't searched if command is an absolute or relative path.
458 PATH isn't searched if command is an absolute or relative path.
456 If command isn't found None is returned.'''
459 If command isn't found None is returned.'''
457 if pycompat.sysplatform == 'OpenVMS':
460 if pycompat.sysplatform == 'OpenVMS':
458 return command
461 return command
459
462
460 def findexisting(executable):
463 def findexisting(executable):
461 'Will return executable if existing file'
464 'Will return executable if existing file'
462 if os.path.isfile(executable) and os.access(executable, os.X_OK):
465 if os.path.isfile(executable) and os.access(executable, os.X_OK):
463 return executable
466 return executable
464 return None
467 return None
465
468
466 if pycompat.ossep in command:
469 if pycompat.ossep in command:
467 return findexisting(command)
470 return findexisting(command)
468
471
469 if pycompat.sysplatform == 'plan9':
472 if pycompat.sysplatform == 'plan9':
470 return findexisting(os.path.join('/bin', command))
473 return findexisting(os.path.join('/bin', command))
471
474
472 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
475 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
473 executable = findexisting(os.path.join(path, command))
476 executable = findexisting(os.path.join(path, command))
474 if executable is not None:
477 if executable is not None:
475 return executable
478 return executable
476 return None
479 return None
477
480
478 def setsignalhandler():
481 def setsignalhandler():
479 pass
482 pass
480
483
481 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
484 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
482
485
483 def statfiles(files):
486 def statfiles(files):
484 '''Stat each file in files. Yield each stat, or None if a file does not
487 '''Stat each file in files. Yield each stat, or None if a file does not
485 exist or has a type we don't care about.'''
488 exist or has a type we don't care about.'''
486 lstat = os.lstat
489 lstat = os.lstat
487 getkind = stat.S_IFMT
490 getkind = stat.S_IFMT
488 for nf in files:
491 for nf in files:
489 try:
492 try:
490 st = lstat(nf)
493 st = lstat(nf)
491 if getkind(st.st_mode) not in _wantedkinds:
494 if getkind(st.st_mode) not in _wantedkinds:
492 st = None
495 st = None
493 except OSError as err:
496 except OSError as err:
494 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
497 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
495 raise
498 raise
496 st = None
499 st = None
497 yield st
500 yield st
498
501
499 def getuser():
502 def getuser():
500 '''return name of current user'''
503 '''return name of current user'''
501 return getpass.getuser()
504 return getpass.getuser()
502
505
503 def username(uid=None):
506 def username(uid=None):
504 """Return the name of the user with the given uid.
507 """Return the name of the user with the given uid.
505
508
506 If uid is None, return the name of the current user."""
509 If uid is None, return the name of the current user."""
507
510
508 if uid is None:
511 if uid is None:
509 uid = os.getuid()
512 uid = os.getuid()
510 try:
513 try:
511 return pwd.getpwuid(uid)[0]
514 return pwd.getpwuid(uid)[0]
512 except KeyError:
515 except KeyError:
513 return str(uid)
516 return str(uid)
514
517
515 def groupname(gid=None):
518 def groupname(gid=None):
516 """Return the name of the group with the given gid.
519 """Return the name of the group with the given gid.
517
520
518 If gid is None, return the name of the current group."""
521 If gid is None, return the name of the current group."""
519
522
520 if gid is None:
523 if gid is None:
521 gid = os.getgid()
524 gid = os.getgid()
522 try:
525 try:
523 return grp.getgrgid(gid)[0]
526 return grp.getgrgid(gid)[0]
524 except KeyError:
527 except KeyError:
525 return str(gid)
528 return str(gid)
526
529
527 def groupmembers(name):
530 def groupmembers(name):
528 """Return the list of members of the group with the given
531 """Return the list of members of the group with the given
529 name, KeyError if the group does not exist.
532 name, KeyError if the group does not exist.
530 """
533 """
531 return list(grp.getgrnam(name).gr_mem)
534 return list(grp.getgrnam(name).gr_mem)
532
535
533 def spawndetached(args):
536 def spawndetached(args):
534 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
537 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
535 args[0], args)
538 args[0], args)
536
539
537 def gethgcmd():
540 def gethgcmd():
538 return sys.argv[:1]
541 return sys.argv[:1]
539
542
540 def makedir(path, notindexed):
543 def makedir(path, notindexed):
541 os.mkdir(path)
544 os.mkdir(path)
542
545
543 def lookupreg(key, name=None, scope=None):
546 def lookupreg(key, name=None, scope=None):
544 return None
547 return None
545
548
546 def hidewindow():
549 def hidewindow():
547 """Hide current shell window.
550 """Hide current shell window.
548
551
549 Used to hide the window opened when starting asynchronous
552 Used to hide the window opened when starting asynchronous
550 child process under Windows, unneeded on other systems.
553 child process under Windows, unneeded on other systems.
551 """
554 """
552 pass
555 pass
553
556
554 class cachestat(object):
557 class cachestat(object):
555 def __init__(self, path):
558 def __init__(self, path):
556 self.stat = os.stat(path)
559 self.stat = os.stat(path)
557
560
558 def cacheable(self):
561 def cacheable(self):
559 return bool(self.stat.st_ino)
562 return bool(self.stat.st_ino)
560
563
561 __hash__ = object.__hash__
564 __hash__ = object.__hash__
562
565
563 def __eq__(self, other):
566 def __eq__(self, other):
564 try:
567 try:
565 # Only dev, ino, size, mtime and atime are likely to change. Out
568 # Only dev, ino, size, mtime and atime are likely to change. Out
566 # of these, we shouldn't compare atime but should compare the
569 # of these, we shouldn't compare atime but should compare the
567 # rest. However, one of the other fields changing indicates
570 # rest. However, one of the other fields changing indicates
568 # something fishy going on, so return False if anything but atime
571 # something fishy going on, so return False if anything but atime
569 # changes.
572 # changes.
570 return (self.stat.st_mode == other.stat.st_mode and
573 return (self.stat.st_mode == other.stat.st_mode and
571 self.stat.st_ino == other.stat.st_ino and
574 self.stat.st_ino == other.stat.st_ino and
572 self.stat.st_dev == other.stat.st_dev and
575 self.stat.st_dev == other.stat.st_dev and
573 self.stat.st_nlink == other.stat.st_nlink and
576 self.stat.st_nlink == other.stat.st_nlink and
574 self.stat.st_uid == other.stat.st_uid and
577 self.stat.st_uid == other.stat.st_uid and
575 self.stat.st_gid == other.stat.st_gid and
578 self.stat.st_gid == other.stat.st_gid and
576 self.stat.st_size == other.stat.st_size and
579 self.stat.st_size == other.stat.st_size and
577 self.stat.st_mtime == other.stat.st_mtime and
580 self.stat.st_mtime == other.stat.st_mtime and
578 self.stat.st_ctime == other.stat.st_ctime)
581 self.stat.st_ctime == other.stat.st_ctime)
579 except AttributeError:
582 except AttributeError:
580 return False
583 return False
581
584
582 def __ne__(self, other):
585 def __ne__(self, other):
583 return not self == other
586 return not self == other
584
587
585 def executablepath():
588 def executablepath():
586 return None # available on Windows only
589 return None # available on Windows only
587
590
588 def statislink(st):
591 def statislink(st):
589 '''check whether a stat result is a symlink'''
592 '''check whether a stat result is a symlink'''
590 return st and stat.S_ISLNK(st.st_mode)
593 return st and stat.S_ISLNK(st.st_mode)
591
594
592 def statisexec(st):
595 def statisexec(st):
593 '''check whether a stat result is an executable file'''
596 '''check whether a stat result is an executable file'''
594 return st and (st.st_mode & 0o100 != 0)
597 return st and (st.st_mode & 0o100 != 0)
595
598
596 def poll(fds):
599 def poll(fds):
597 """block until something happens on any file descriptor
600 """block until something happens on any file descriptor
598
601
599 This is a generic helper that will check for any activity
602 This is a generic helper that will check for any activity
600 (read, write. exception) and return the list of touched files.
603 (read, write. exception) and return the list of touched files.
601
604
602 In unsupported cases, it will raise a NotImplementedError"""
605 In unsupported cases, it will raise a NotImplementedError"""
603 try:
606 try:
604 while True:
607 while True:
605 try:
608 try:
606 res = select.select(fds, fds, fds)
609 res = select.select(fds, fds, fds)
607 break
610 break
608 except select.error as inst:
611 except select.error as inst:
609 if inst.args[0] == errno.EINTR:
612 if inst.args[0] == errno.EINTR:
610 continue
613 continue
611 raise
614 raise
612 except ValueError: # out of range file descriptor
615 except ValueError: # out of range file descriptor
613 raise NotImplementedError()
616 raise NotImplementedError()
614 return sorted(list(set(sum(res, []))))
617 return sorted(list(set(sum(res, []))))
615
618
616 def readpipe(pipe):
619 def readpipe(pipe):
617 """Read all available data from a pipe."""
620 """Read all available data from a pipe."""
618 # We can't fstat() a pipe because Linux will always report 0.
621 # We can't fstat() a pipe because Linux will always report 0.
619 # So, we set the pipe to non-blocking mode and read everything
622 # So, we set the pipe to non-blocking mode and read everything
620 # that's available.
623 # that's available.
621 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
624 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
622 flags |= os.O_NONBLOCK
625 flags |= os.O_NONBLOCK
623 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
626 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
624
627
625 try:
628 try:
626 chunks = []
629 chunks = []
627 while True:
630 while True:
628 try:
631 try:
629 s = pipe.read()
632 s = pipe.read()
630 if not s:
633 if not s:
631 break
634 break
632 chunks.append(s)
635 chunks.append(s)
633 except IOError:
636 except IOError:
634 break
637 break
635
638
636 return ''.join(chunks)
639 return ''.join(chunks)
637 finally:
640 finally:
638 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
641 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
639
642
640 def bindunixsocket(sock, path):
643 def bindunixsocket(sock, path):
641 """Bind the UNIX domain socket to the specified path"""
644 """Bind the UNIX domain socket to the specified path"""
642 # use relative path instead of full path at bind() if possible, since
645 # use relative path instead of full path at bind() if possible, since
643 # AF_UNIX path has very small length limit (107 chars) on common
646 # AF_UNIX path has very small length limit (107 chars) on common
644 # platforms (see sys/un.h)
647 # platforms (see sys/un.h)
645 dirname, basename = os.path.split(path)
648 dirname, basename = os.path.split(path)
646 bakwdfd = None
649 bakwdfd = None
647 if dirname:
650 if dirname:
648 bakwdfd = os.open('.', os.O_DIRECTORY)
651 bakwdfd = os.open('.', os.O_DIRECTORY)
649 os.chdir(dirname)
652 os.chdir(dirname)
650 sock.bind(basename)
653 sock.bind(basename)
651 if bakwdfd:
654 if bakwdfd:
652 os.fchdir(bakwdfd)
655 os.fchdir(bakwdfd)
653 os.close(bakwdfd)
656 os.close(bakwdfd)
@@ -1,369 +1,366
1 # sshpeer.py - ssh repository proxy class for mercurial
1 # sshpeer.py - ssh repository proxy class for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13 from . import (
13 from . import (
14 error,
14 error,
15 util,
15 util,
16 wireproto,
16 wireproto,
17 )
17 )
18
18
19 class remotelock(object):
19 class remotelock(object):
20 def __init__(self, repo):
20 def __init__(self, repo):
21 self.repo = repo
21 self.repo = repo
22 def release(self):
22 def release(self):
23 self.repo.unlock()
23 self.repo.unlock()
24 self.repo = None
24 self.repo = None
25 def __enter__(self):
25 def __enter__(self):
26 return self
26 return self
27 def __exit__(self, exc_type, exc_val, exc_tb):
27 def __exit__(self, exc_type, exc_val, exc_tb):
28 if self.repo:
28 if self.repo:
29 self.release()
29 self.release()
30 def __del__(self):
30 def __del__(self):
31 if self.repo:
31 if self.repo:
32 self.release()
32 self.release()
33
33
34 def _serverquote(s):
34 def _serverquote(s):
35 if not s:
35 if not s:
36 return s
36 return s
37 '''quote a string for the remote shell ... which we assume is sh'''
37 '''quote a string for the remote shell ... which we assume is sh'''
38 if re.match('[a-zA-Z0-9@%_+=:,./-]*$', s):
38 if re.match('[a-zA-Z0-9@%_+=:,./-]*$', s):
39 return s
39 return s
40 return "'%s'" % s.replace("'", "'\\''")
40 return "'%s'" % s.replace("'", "'\\''")
41
41
42 def _forwardoutput(ui, pipe):
42 def _forwardoutput(ui, pipe):
43 """display all data currently available on pipe as remote output.
43 """display all data currently available on pipe as remote output.
44
44
45 This is non blocking."""
45 This is non blocking."""
46 s = util.readpipe(pipe)
46 s = util.readpipe(pipe)
47 if s:
47 if s:
48 for l in s.splitlines():
48 for l in s.splitlines():
49 ui.status(_("remote: "), l, '\n')
49 ui.status(_("remote: "), l, '\n')
50
50
51 class doublepipe(object):
51 class doublepipe(object):
52 """Operate a side-channel pipe in addition of a main one
52 """Operate a side-channel pipe in addition of a main one
53
53
54 The side-channel pipe contains server output to be forwarded to the user
54 The side-channel pipe contains server output to be forwarded to the user
55 input. The double pipe will behave as the "main" pipe, but will ensure the
55 input. The double pipe will behave as the "main" pipe, but will ensure the
56 content of the "side" pipe is properly processed while we wait for blocking
56 content of the "side" pipe is properly processed while we wait for blocking
57 call on the "main" pipe.
57 call on the "main" pipe.
58
58
59 If large amounts of data are read from "main", the forward will cease after
59 If large amounts of data are read from "main", the forward will cease after
60 the first bytes start to appear. This simplifies the implementation
60 the first bytes start to appear. This simplifies the implementation
61 without affecting actual output of sshpeer too much as we rarely issue
61 without affecting actual output of sshpeer too much as we rarely issue
62 large read for data not yet emitted by the server.
62 large read for data not yet emitted by the server.
63
63
64 The main pipe is expected to be a 'bufferedinputpipe' from the util module
64 The main pipe is expected to be a 'bufferedinputpipe' from the util module
65 that handle all the os specific bits. This class lives in this module
65 that handle all the os specific bits. This class lives in this module
66 because it focus on behavior specific to the ssh protocol."""
66 because it focus on behavior specific to the ssh protocol."""
67
67
68 def __init__(self, ui, main, side):
68 def __init__(self, ui, main, side):
69 self._ui = ui
69 self._ui = ui
70 self._main = main
70 self._main = main
71 self._side = side
71 self._side = side
72
72
73 def _wait(self):
73 def _wait(self):
74 """wait until some data are available on main or side
74 """wait until some data are available on main or side
75
75
76 return a pair of boolean (ismainready, issideready)
76 return a pair of boolean (ismainready, issideready)
77
77
78 (This will only wait for data if the setup is supported by `util.poll`)
78 (This will only wait for data if the setup is supported by `util.poll`)
79 """
79 """
80 if getattr(self._main, 'hasbuffer', False): # getattr for classic pipe
80 if getattr(self._main, 'hasbuffer', False): # getattr for classic pipe
81 return (True, True) # main has data, assume side is worth poking at.
81 return (True, True) # main has data, assume side is worth poking at.
82 fds = [self._main.fileno(), self._side.fileno()]
82 fds = [self._main.fileno(), self._side.fileno()]
83 try:
83 try:
84 act = util.poll(fds)
84 act = util.poll(fds)
85 except NotImplementedError:
85 except NotImplementedError:
86 # non supported yet case, assume all have data.
86 # non supported yet case, assume all have data.
87 act = fds
87 act = fds
88 return (self._main.fileno() in act, self._side.fileno() in act)
88 return (self._main.fileno() in act, self._side.fileno() in act)
89
89
90 def write(self, data):
90 def write(self, data):
91 return self._call('write', data)
91 return self._call('write', data)
92
92
93 def read(self, size):
93 def read(self, size):
94 r = self._call('read', size)
94 r = self._call('read', size)
95 if size != 0 and not r:
95 if size != 0 and not r:
96 # We've observed a condition that indicates the
96 # We've observed a condition that indicates the
97 # stdout closed unexpectedly. Check stderr one
97 # stdout closed unexpectedly. Check stderr one
98 # more time and snag anything that's there before
98 # more time and snag anything that's there before
99 # letting anyone know the main part of the pipe
99 # letting anyone know the main part of the pipe
100 # closed prematurely.
100 # closed prematurely.
101 _forwardoutput(self._ui, self._side)
101 _forwardoutput(self._ui, self._side)
102 return r
102 return r
103
103
104 def readline(self):
104 def readline(self):
105 return self._call('readline')
105 return self._call('readline')
106
106
107 def _call(self, methname, data=None):
107 def _call(self, methname, data=None):
108 """call <methname> on "main", forward output of "side" while blocking
108 """call <methname> on "main", forward output of "side" while blocking
109 """
109 """
110 # data can be '' or 0
110 # data can be '' or 0
111 if (data is not None and not data) or self._main.closed:
111 if (data is not None and not data) or self._main.closed:
112 _forwardoutput(self._ui, self._side)
112 _forwardoutput(self._ui, self._side)
113 return ''
113 return ''
114 while True:
114 while True:
115 mainready, sideready = self._wait()
115 mainready, sideready = self._wait()
116 if sideready:
116 if sideready:
117 _forwardoutput(self._ui, self._side)
117 _forwardoutput(self._ui, self._side)
118 if mainready:
118 if mainready:
119 meth = getattr(self._main, methname)
119 meth = getattr(self._main, methname)
120 if data is None:
120 if data is None:
121 return meth()
121 return meth()
122 else:
122 else:
123 return meth(data)
123 return meth(data)
124
124
125 def close(self):
125 def close(self):
126 return self._main.close()
126 return self._main.close()
127
127
128 def flush(self):
128 def flush(self):
129 return self._main.flush()
129 return self._main.flush()
130
130
131 class sshpeer(wireproto.wirepeer):
131 class sshpeer(wireproto.wirepeer):
132 def __init__(self, ui, path, create=False):
132 def __init__(self, ui, path, create=False):
133 self._url = path
133 self._url = path
134 self.ui = ui
134 self.ui = ui
135 self.pipeo = self.pipei = self.pipee = None
135 self.pipeo = self.pipei = self.pipee = None
136
136
137 u = util.url(path, parsequery=False, parsefragment=False)
137 u = util.url(path, parsequery=False, parsefragment=False)
138 if u.scheme != 'ssh' or not u.host or u.path is None:
138 if u.scheme != 'ssh' or not u.host or u.path is None:
139 self._abort(error.RepoError(_("couldn't parse location %s") % path))
139 self._abort(error.RepoError(_("couldn't parse location %s") % path))
140
140
141 util.checksafessh(path)
141 util.checksafessh(path)
142
142
143 self.user = u.user
143 self.user = u.user
144 if u.passwd is not None:
144 if u.passwd is not None:
145 self._abort(error.RepoError(_("password in URL not supported")))
145 self._abort(error.RepoError(_("password in URL not supported")))
146 self.host = u.host
146 self.host = u.host
147 self.port = u.port
147 self.port = u.port
148 self.path = u.path or "."
148 self.path = u.path or "."
149
149
150 sshcmd = self.ui.config("ui", "ssh", "ssh")
150 sshcmd = self.ui.config("ui", "ssh", "ssh")
151 remotecmd = self.ui.config("ui", "remotecmd", "hg")
151 remotecmd = self.ui.config("ui", "remotecmd", "hg")
152
152
153 args = util.sshargs(sshcmd,
153 args = util.sshargs(sshcmd, self.host, self.user, self.port)
154 _serverquote(self.host),
155 _serverquote(self.user),
156 _serverquote(self.port))
157
154
158 if create:
155 if create:
159 cmd = '%s %s %s' % (sshcmd, args,
156 cmd = '%s %s %s' % (sshcmd, args,
160 util.shellquote("%s init %s" %
157 util.shellquote("%s init %s" %
161 (_serverquote(remotecmd), _serverquote(self.path))))
158 (_serverquote(remotecmd), _serverquote(self.path))))
162 ui.debug('running %s\n' % cmd)
159 ui.debug('running %s\n' % cmd)
163 res = ui.system(cmd, blockedtag='sshpeer')
160 res = ui.system(cmd, blockedtag='sshpeer')
164 if res != 0:
161 if res != 0:
165 self._abort(error.RepoError(_("could not create remote repo")))
162 self._abort(error.RepoError(_("could not create remote repo")))
166
163
167 self._validaterepo(sshcmd, args, remotecmd)
164 self._validaterepo(sshcmd, args, remotecmd)
168
165
169 def url(self):
166 def url(self):
170 return self._url
167 return self._url
171
168
172 def _validaterepo(self, sshcmd, args, remotecmd):
169 def _validaterepo(self, sshcmd, args, remotecmd):
173 # cleanup up previous run
170 # cleanup up previous run
174 self.cleanup()
171 self.cleanup()
175
172
176 cmd = '%s %s %s' % (sshcmd, args,
173 cmd = '%s %s %s' % (sshcmd, args,
177 util.shellquote("%s -R %s serve --stdio" %
174 util.shellquote("%s -R %s serve --stdio" %
178 (_serverquote(remotecmd), _serverquote(self.path))))
175 (_serverquote(remotecmd), _serverquote(self.path))))
179 self.ui.debug('running %s\n' % cmd)
176 self.ui.debug('running %s\n' % cmd)
180 cmd = util.quotecommand(cmd)
177 cmd = util.quotecommand(cmd)
181
178
182 # while self.subprocess isn't used, having it allows the subprocess to
179 # while self.subprocess isn't used, having it allows the subprocess to
183 # to clean up correctly later
180 # to clean up correctly later
184 #
181 #
185 # no buffer allow the use of 'select'
182 # no buffer allow the use of 'select'
186 # feel free to remove buffering and select usage when we ultimately
183 # feel free to remove buffering and select usage when we ultimately
187 # move to threading.
184 # move to threading.
188 sub = util.popen4(cmd, bufsize=0)
185 sub = util.popen4(cmd, bufsize=0)
189 self.pipeo, self.pipei, self.pipee, self.subprocess = sub
186 self.pipeo, self.pipei, self.pipee, self.subprocess = sub
190
187
191 self.pipei = util.bufferedinputpipe(self.pipei)
188 self.pipei = util.bufferedinputpipe(self.pipei)
192 self.pipei = doublepipe(self.ui, self.pipei, self.pipee)
189 self.pipei = doublepipe(self.ui, self.pipei, self.pipee)
193 self.pipeo = doublepipe(self.ui, self.pipeo, self.pipee)
190 self.pipeo = doublepipe(self.ui, self.pipeo, self.pipee)
194
191
195 # skip any noise generated by remote shell
192 # skip any noise generated by remote shell
196 self._callstream("hello")
193 self._callstream("hello")
197 r = self._callstream("between", pairs=("%s-%s" % ("0"*40, "0"*40)))
194 r = self._callstream("between", pairs=("%s-%s" % ("0"*40, "0"*40)))
198 lines = ["", "dummy"]
195 lines = ["", "dummy"]
199 max_noise = 500
196 max_noise = 500
200 while lines[-1] and max_noise:
197 while lines[-1] and max_noise:
201 l = r.readline()
198 l = r.readline()
202 self.readerr()
199 self.readerr()
203 if lines[-1] == "1\n" and l == "\n":
200 if lines[-1] == "1\n" and l == "\n":
204 break
201 break
205 if l:
202 if l:
206 self.ui.debug("remote: ", l)
203 self.ui.debug("remote: ", l)
207 lines.append(l)
204 lines.append(l)
208 max_noise -= 1
205 max_noise -= 1
209 else:
206 else:
210 self._abort(error.RepoError(_('no suitable response from '
207 self._abort(error.RepoError(_('no suitable response from '
211 'remote hg')))
208 'remote hg')))
212
209
213 self._caps = set()
210 self._caps = set()
214 for l in reversed(lines):
211 for l in reversed(lines):
215 if l.startswith("capabilities:"):
212 if l.startswith("capabilities:"):
216 self._caps.update(l[:-1].split(":")[1].split())
213 self._caps.update(l[:-1].split(":")[1].split())
217 break
214 break
218
215
219 def _capabilities(self):
216 def _capabilities(self):
220 return self._caps
217 return self._caps
221
218
222 def readerr(self):
219 def readerr(self):
223 _forwardoutput(self.ui, self.pipee)
220 _forwardoutput(self.ui, self.pipee)
224
221
225 def _abort(self, exception):
222 def _abort(self, exception):
226 self.cleanup()
223 self.cleanup()
227 raise exception
224 raise exception
228
225
229 def cleanup(self):
226 def cleanup(self):
230 if self.pipeo is None:
227 if self.pipeo is None:
231 return
228 return
232 self.pipeo.close()
229 self.pipeo.close()
233 self.pipei.close()
230 self.pipei.close()
234 try:
231 try:
235 # read the error descriptor until EOF
232 # read the error descriptor until EOF
236 for l in self.pipee:
233 for l in self.pipee:
237 self.ui.status(_("remote: "), l)
234 self.ui.status(_("remote: "), l)
238 except (IOError, ValueError):
235 except (IOError, ValueError):
239 pass
236 pass
240 self.pipee.close()
237 self.pipee.close()
241
238
242 __del__ = cleanup
239 __del__ = cleanup
243
240
244 def _submitbatch(self, req):
241 def _submitbatch(self, req):
245 rsp = self._callstream("batch", cmds=wireproto.encodebatchcmds(req))
242 rsp = self._callstream("batch", cmds=wireproto.encodebatchcmds(req))
246 available = self._getamount()
243 available = self._getamount()
247 # TODO this response parsing is probably suboptimal for large
244 # TODO this response parsing is probably suboptimal for large
248 # batches with large responses.
245 # batches with large responses.
249 toread = min(available, 1024)
246 toread = min(available, 1024)
250 work = rsp.read(toread)
247 work = rsp.read(toread)
251 available -= toread
248 available -= toread
252 chunk = work
249 chunk = work
253 while chunk:
250 while chunk:
254 while ';' in work:
251 while ';' in work:
255 one, work = work.split(';', 1)
252 one, work = work.split(';', 1)
256 yield wireproto.unescapearg(one)
253 yield wireproto.unescapearg(one)
257 toread = min(available, 1024)
254 toread = min(available, 1024)
258 chunk = rsp.read(toread)
255 chunk = rsp.read(toread)
259 available -= toread
256 available -= toread
260 work += chunk
257 work += chunk
261 yield wireproto.unescapearg(work)
258 yield wireproto.unescapearg(work)
262
259
263 def _callstream(self, cmd, **args):
260 def _callstream(self, cmd, **args):
264 self.ui.debug("sending %s command\n" % cmd)
261 self.ui.debug("sending %s command\n" % cmd)
265 self.pipeo.write("%s\n" % cmd)
262 self.pipeo.write("%s\n" % cmd)
266 _func, names = wireproto.commands[cmd]
263 _func, names = wireproto.commands[cmd]
267 keys = names.split()
264 keys = names.split()
268 wireargs = {}
265 wireargs = {}
269 for k in keys:
266 for k in keys:
270 if k == '*':
267 if k == '*':
271 wireargs['*'] = args
268 wireargs['*'] = args
272 break
269 break
273 else:
270 else:
274 wireargs[k] = args[k]
271 wireargs[k] = args[k]
275 del args[k]
272 del args[k]
276 for k, v in sorted(wireargs.iteritems()):
273 for k, v in sorted(wireargs.iteritems()):
277 self.pipeo.write("%s %d\n" % (k, len(v)))
274 self.pipeo.write("%s %d\n" % (k, len(v)))
278 if isinstance(v, dict):
275 if isinstance(v, dict):
279 for dk, dv in v.iteritems():
276 for dk, dv in v.iteritems():
280 self.pipeo.write("%s %d\n" % (dk, len(dv)))
277 self.pipeo.write("%s %d\n" % (dk, len(dv)))
281 self.pipeo.write(dv)
278 self.pipeo.write(dv)
282 else:
279 else:
283 self.pipeo.write(v)
280 self.pipeo.write(v)
284 self.pipeo.flush()
281 self.pipeo.flush()
285
282
286 return self.pipei
283 return self.pipei
287
284
288 def _callcompressable(self, cmd, **args):
285 def _callcompressable(self, cmd, **args):
289 return self._callstream(cmd, **args)
286 return self._callstream(cmd, **args)
290
287
291 def _call(self, cmd, **args):
288 def _call(self, cmd, **args):
292 self._callstream(cmd, **args)
289 self._callstream(cmd, **args)
293 return self._recv()
290 return self._recv()
294
291
295 def _callpush(self, cmd, fp, **args):
292 def _callpush(self, cmd, fp, **args):
296 r = self._call(cmd, **args)
293 r = self._call(cmd, **args)
297 if r:
294 if r:
298 return '', r
295 return '', r
299 for d in iter(lambda: fp.read(4096), ''):
296 for d in iter(lambda: fp.read(4096), ''):
300 self._send(d)
297 self._send(d)
301 self._send("", flush=True)
298 self._send("", flush=True)
302 r = self._recv()
299 r = self._recv()
303 if r:
300 if r:
304 return '', r
301 return '', r
305 return self._recv(), ''
302 return self._recv(), ''
306
303
307 def _calltwowaystream(self, cmd, fp, **args):
304 def _calltwowaystream(self, cmd, fp, **args):
308 r = self._call(cmd, **args)
305 r = self._call(cmd, **args)
309 if r:
306 if r:
310 # XXX needs to be made better
307 # XXX needs to be made better
311 raise error.Abort(_('unexpected remote reply: %s') % r)
308 raise error.Abort(_('unexpected remote reply: %s') % r)
312 for d in iter(lambda: fp.read(4096), ''):
309 for d in iter(lambda: fp.read(4096), ''):
313 self._send(d)
310 self._send(d)
314 self._send("", flush=True)
311 self._send("", flush=True)
315 return self.pipei
312 return self.pipei
316
313
317 def _getamount(self):
314 def _getamount(self):
318 l = self.pipei.readline()
315 l = self.pipei.readline()
319 if l == '\n':
316 if l == '\n':
320 self.readerr()
317 self.readerr()
321 msg = _('check previous remote output')
318 msg = _('check previous remote output')
322 self._abort(error.OutOfBandError(hint=msg))
319 self._abort(error.OutOfBandError(hint=msg))
323 self.readerr()
320 self.readerr()
324 try:
321 try:
325 return int(l)
322 return int(l)
326 except ValueError:
323 except ValueError:
327 self._abort(error.ResponseError(_("unexpected response:"), l))
324 self._abort(error.ResponseError(_("unexpected response:"), l))
328
325
329 def _recv(self):
326 def _recv(self):
330 return self.pipei.read(self._getamount())
327 return self.pipei.read(self._getamount())
331
328
332 def _send(self, data, flush=False):
329 def _send(self, data, flush=False):
333 self.pipeo.write("%d\n" % len(data))
330 self.pipeo.write("%d\n" % len(data))
334 if data:
331 if data:
335 self.pipeo.write(data)
332 self.pipeo.write(data)
336 if flush:
333 if flush:
337 self.pipeo.flush()
334 self.pipeo.flush()
338 self.readerr()
335 self.readerr()
339
336
340 def lock(self):
337 def lock(self):
341 self._call("lock")
338 self._call("lock")
342 return remotelock(self)
339 return remotelock(self)
343
340
344 def unlock(self):
341 def unlock(self):
345 self._call("unlock")
342 self._call("unlock")
346
343
347 def addchangegroup(self, cg, source, url, lock=None):
344 def addchangegroup(self, cg, source, url, lock=None):
348 '''Send a changegroup to the remote server. Return an integer
345 '''Send a changegroup to the remote server. Return an integer
349 similar to unbundle(). DEPRECATED, since it requires locking the
346 similar to unbundle(). DEPRECATED, since it requires locking the
350 remote.'''
347 remote.'''
351 d = self._call("addchangegroup")
348 d = self._call("addchangegroup")
352 if d:
349 if d:
353 self._abort(error.RepoError(_("push refused: %s") % d))
350 self._abort(error.RepoError(_("push refused: %s") % d))
354 for d in iter(lambda: cg.read(4096), ''):
351 for d in iter(lambda: cg.read(4096), ''):
355 self.pipeo.write(d)
352 self.pipeo.write(d)
356 self.readerr()
353 self.readerr()
357
354
358 self.pipeo.flush()
355 self.pipeo.flush()
359
356
360 self.readerr()
357 self.readerr()
361 r = self._recv()
358 r = self._recv()
362 if not r:
359 if not r:
363 return 1
360 return 1
364 try:
361 try:
365 return int(r)
362 return int(r)
366 except ValueError:
363 except ValueError:
367 self._abort(error.ResponseError(_("unexpected response:"), r))
364 self._abort(error.ResponseError(_("unexpected response:"), r))
368
365
369 instance = sshpeer
366 instance = sshpeer
@@ -1,477 +1,480
1 # windows.py - Windows utility function implementations for Mercurial
1 # windows.py - Windows utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import msvcrt
11 import msvcrt
12 import os
12 import os
13 import re
13 import re
14 import stat
14 import stat
15 import sys
15 import sys
16
16
17 from .i18n import _
17 from .i18n import _
18 from . import (
18 from . import (
19 encoding,
19 encoding,
20 error,
20 error,
21 osutil,
21 osutil,
22 pycompat,
22 pycompat,
23 win32,
23 win32,
24 )
24 )
25
25
26 try:
26 try:
27 import _winreg as winreg
27 import _winreg as winreg
28 winreg.CloseKey
28 winreg.CloseKey
29 except ImportError:
29 except ImportError:
30 import winreg
30 import winreg
31
31
32 executablepath = win32.executablepath
32 executablepath = win32.executablepath
33 getuser = win32.getuser
33 getuser = win32.getuser
34 hidewindow = win32.hidewindow
34 hidewindow = win32.hidewindow
35 makedir = win32.makedir
35 makedir = win32.makedir
36 nlinks = win32.nlinks
36 nlinks = win32.nlinks
37 oslink = win32.oslink
37 oslink = win32.oslink
38 samedevice = win32.samedevice
38 samedevice = win32.samedevice
39 samefile = win32.samefile
39 samefile = win32.samefile
40 setsignalhandler = win32.setsignalhandler
40 setsignalhandler = win32.setsignalhandler
41 spawndetached = win32.spawndetached
41 spawndetached = win32.spawndetached
42 split = os.path.split
42 split = os.path.split
43 testpid = win32.testpid
43 testpid = win32.testpid
44 unlink = win32.unlink
44 unlink = win32.unlink
45
45
46 umask = 0o022
46 umask = 0o022
47
47
48 class mixedfilemodewrapper(object):
48 class mixedfilemodewrapper(object):
49 """Wraps a file handle when it is opened in read/write mode.
49 """Wraps a file handle when it is opened in read/write mode.
50
50
51 fopen() and fdopen() on Windows have a specific-to-Windows requirement
51 fopen() and fdopen() on Windows have a specific-to-Windows requirement
52 that files opened with mode r+, w+, or a+ make a call to a file positioning
52 that files opened with mode r+, w+, or a+ make a call to a file positioning
53 function when switching between reads and writes. Without this extra call,
53 function when switching between reads and writes. Without this extra call,
54 Python will raise a not very intuitive "IOError: [Errno 0] Error."
54 Python will raise a not very intuitive "IOError: [Errno 0] Error."
55
55
56 This class wraps posixfile instances when the file is opened in read/write
56 This class wraps posixfile instances when the file is opened in read/write
57 mode and automatically adds checks or inserts appropriate file positioning
57 mode and automatically adds checks or inserts appropriate file positioning
58 calls when necessary.
58 calls when necessary.
59 """
59 """
60 OPNONE = 0
60 OPNONE = 0
61 OPREAD = 1
61 OPREAD = 1
62 OPWRITE = 2
62 OPWRITE = 2
63
63
64 def __init__(self, fp):
64 def __init__(self, fp):
65 object.__setattr__(self, r'_fp', fp)
65 object.__setattr__(self, r'_fp', fp)
66 object.__setattr__(self, r'_lastop', 0)
66 object.__setattr__(self, r'_lastop', 0)
67
67
68 def __enter__(self):
68 def __enter__(self):
69 return self._fp.__enter__()
69 return self._fp.__enter__()
70
70
71 def __exit__(self, exc_type, exc_val, exc_tb):
71 def __exit__(self, exc_type, exc_val, exc_tb):
72 self._fp.__exit__(exc_type, exc_val, exc_tb)
72 self._fp.__exit__(exc_type, exc_val, exc_tb)
73
73
74 def __getattr__(self, name):
74 def __getattr__(self, name):
75 return getattr(self._fp, name)
75 return getattr(self._fp, name)
76
76
77 def __setattr__(self, name, value):
77 def __setattr__(self, name, value):
78 return self._fp.__setattr__(name, value)
78 return self._fp.__setattr__(name, value)
79
79
80 def _noopseek(self):
80 def _noopseek(self):
81 self._fp.seek(0, os.SEEK_CUR)
81 self._fp.seek(0, os.SEEK_CUR)
82
82
83 def seek(self, *args, **kwargs):
83 def seek(self, *args, **kwargs):
84 object.__setattr__(self, r'_lastop', self.OPNONE)
84 object.__setattr__(self, r'_lastop', self.OPNONE)
85 return self._fp.seek(*args, **kwargs)
85 return self._fp.seek(*args, **kwargs)
86
86
87 def write(self, d):
87 def write(self, d):
88 if self._lastop == self.OPREAD:
88 if self._lastop == self.OPREAD:
89 self._noopseek()
89 self._noopseek()
90
90
91 object.__setattr__(self, r'_lastop', self.OPWRITE)
91 object.__setattr__(self, r'_lastop', self.OPWRITE)
92 return self._fp.write(d)
92 return self._fp.write(d)
93
93
94 def writelines(self, *args, **kwargs):
94 def writelines(self, *args, **kwargs):
95 if self._lastop == self.OPREAD:
95 if self._lastop == self.OPREAD:
96 self._noopeseek()
96 self._noopeseek()
97
97
98 object.__setattr__(self, r'_lastop', self.OPWRITE)
98 object.__setattr__(self, r'_lastop', self.OPWRITE)
99 return self._fp.writelines(*args, **kwargs)
99 return self._fp.writelines(*args, **kwargs)
100
100
101 def read(self, *args, **kwargs):
101 def read(self, *args, **kwargs):
102 if self._lastop == self.OPWRITE:
102 if self._lastop == self.OPWRITE:
103 self._noopseek()
103 self._noopseek()
104
104
105 object.__setattr__(self, r'_lastop', self.OPREAD)
105 object.__setattr__(self, r'_lastop', self.OPREAD)
106 return self._fp.read(*args, **kwargs)
106 return self._fp.read(*args, **kwargs)
107
107
108 def readline(self, *args, **kwargs):
108 def readline(self, *args, **kwargs):
109 if self._lastop == self.OPWRITE:
109 if self._lastop == self.OPWRITE:
110 self._noopseek()
110 self._noopseek()
111
111
112 object.__setattr__(self, r'_lastop', self.OPREAD)
112 object.__setattr__(self, r'_lastop', self.OPREAD)
113 return self._fp.readline(*args, **kwargs)
113 return self._fp.readline(*args, **kwargs)
114
114
115 def readlines(self, *args, **kwargs):
115 def readlines(self, *args, **kwargs):
116 if self._lastop == self.OPWRITE:
116 if self._lastop == self.OPWRITE:
117 self._noopseek()
117 self._noopseek()
118
118
119 object.__setattr__(self, r'_lastop', self.OPREAD)
119 object.__setattr__(self, r'_lastop', self.OPREAD)
120 return self._fp.readlines(*args, **kwargs)
120 return self._fp.readlines(*args, **kwargs)
121
121
122 def posixfile(name, mode='r', buffering=-1):
122 def posixfile(name, mode='r', buffering=-1):
123 '''Open a file with even more POSIX-like semantics'''
123 '''Open a file with even more POSIX-like semantics'''
124 try:
124 try:
125 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
125 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
126
126
127 # The position when opening in append mode is implementation defined, so
127 # The position when opening in append mode is implementation defined, so
128 # make it consistent with other platforms, which position at EOF.
128 # make it consistent with other platforms, which position at EOF.
129 if 'a' in mode:
129 if 'a' in mode:
130 fp.seek(0, os.SEEK_END)
130 fp.seek(0, os.SEEK_END)
131
131
132 if '+' in mode:
132 if '+' in mode:
133 return mixedfilemodewrapper(fp)
133 return mixedfilemodewrapper(fp)
134
134
135 return fp
135 return fp
136 except WindowsError as err:
136 except WindowsError as err:
137 # convert to a friendlier exception
137 # convert to a friendlier exception
138 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
138 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
139
139
140 class winstdout(object):
140 class winstdout(object):
141 '''stdout on windows misbehaves if sent through a pipe'''
141 '''stdout on windows misbehaves if sent through a pipe'''
142
142
143 def __init__(self, fp):
143 def __init__(self, fp):
144 self.fp = fp
144 self.fp = fp
145
145
146 def __getattr__(self, key):
146 def __getattr__(self, key):
147 return getattr(self.fp, key)
147 return getattr(self.fp, key)
148
148
149 def close(self):
149 def close(self):
150 try:
150 try:
151 self.fp.close()
151 self.fp.close()
152 except IOError:
152 except IOError:
153 pass
153 pass
154
154
155 def write(self, s):
155 def write(self, s):
156 try:
156 try:
157 # This is workaround for "Not enough space" error on
157 # This is workaround for "Not enough space" error on
158 # writing large size of data to console.
158 # writing large size of data to console.
159 limit = 16000
159 limit = 16000
160 l = len(s)
160 l = len(s)
161 start = 0
161 start = 0
162 self.softspace = 0
162 self.softspace = 0
163 while start < l:
163 while start < l:
164 end = start + limit
164 end = start + limit
165 self.fp.write(s[start:end])
165 self.fp.write(s[start:end])
166 start = end
166 start = end
167 except IOError as inst:
167 except IOError as inst:
168 if inst.errno != 0:
168 if inst.errno != 0:
169 raise
169 raise
170 self.close()
170 self.close()
171 raise IOError(errno.EPIPE, 'Broken pipe')
171 raise IOError(errno.EPIPE, 'Broken pipe')
172
172
173 def flush(self):
173 def flush(self):
174 try:
174 try:
175 return self.fp.flush()
175 return self.fp.flush()
176 except IOError as inst:
176 except IOError as inst:
177 if inst.errno != errno.EINVAL:
177 if inst.errno != errno.EINVAL:
178 raise
178 raise
179 self.close()
179 self.close()
180 raise IOError(errno.EPIPE, 'Broken pipe')
180 raise IOError(errno.EPIPE, 'Broken pipe')
181
181
182 def _is_win_9x():
182 def _is_win_9x():
183 '''return true if run on windows 95, 98 or me.'''
183 '''return true if run on windows 95, 98 or me.'''
184 try:
184 try:
185 return sys.getwindowsversion()[3] == 1
185 return sys.getwindowsversion()[3] == 1
186 except AttributeError:
186 except AttributeError:
187 return 'command' in encoding.environ.get('comspec', '')
187 return 'command' in encoding.environ.get('comspec', '')
188
188
189 def openhardlinks():
189 def openhardlinks():
190 return not _is_win_9x()
190 return not _is_win_9x()
191
191
192 def parsepatchoutput(output_line):
192 def parsepatchoutput(output_line):
193 """parses the output produced by patch and returns the filename"""
193 """parses the output produced by patch and returns the filename"""
194 pf = output_line[14:]
194 pf = output_line[14:]
195 if pf[0] == '`':
195 if pf[0] == '`':
196 pf = pf[1:-1] # Remove the quotes
196 pf = pf[1:-1] # Remove the quotes
197 return pf
197 return pf
198
198
199 def sshargs(sshcmd, host, user, port):
199 def sshargs(sshcmd, host, user, port):
200 '''Build argument list for ssh or Plink'''
200 '''Build argument list for ssh or Plink'''
201 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
201 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
202 args = user and ("%s@%s" % (user, host)) or host
202 args = user and ("%s@%s" % (user, host)) or host
203 if args.startswith('-') or args.startswith('/'):
203 if args.startswith('-') or args.startswith('/'):
204 raise error.Abort(
204 raise error.Abort(
205 _('illegal ssh hostname or username starting with - or /: %s') %
205 _('illegal ssh hostname or username starting with - or /: %s') %
206 args)
206 args)
207 return port and ("%s %s %s" % (args, pflag, port)) or args
207 args = shellquote(args)
208 if port:
209 args = '%s %s %s' % (pflag, shellquote(port), args)
210 return args
208
211
209 def setflags(f, l, x):
212 def setflags(f, l, x):
210 pass
213 pass
211
214
212 def copymode(src, dst, mode=None):
215 def copymode(src, dst, mode=None):
213 pass
216 pass
214
217
215 def checkexec(path):
218 def checkexec(path):
216 return False
219 return False
217
220
218 def checklink(path):
221 def checklink(path):
219 return False
222 return False
220
223
221 def setbinary(fd):
224 def setbinary(fd):
222 # When run without console, pipes may expose invalid
225 # When run without console, pipes may expose invalid
223 # fileno(), usually set to -1.
226 # fileno(), usually set to -1.
224 fno = getattr(fd, 'fileno', None)
227 fno = getattr(fd, 'fileno', None)
225 if fno is not None and fno() >= 0:
228 if fno is not None and fno() >= 0:
226 msvcrt.setmode(fno(), os.O_BINARY)
229 msvcrt.setmode(fno(), os.O_BINARY)
227
230
228 def pconvert(path):
231 def pconvert(path):
229 return path.replace(pycompat.ossep, '/')
232 return path.replace(pycompat.ossep, '/')
230
233
231 def localpath(path):
234 def localpath(path):
232 return path.replace('/', '\\')
235 return path.replace('/', '\\')
233
236
234 def normpath(path):
237 def normpath(path):
235 return pconvert(os.path.normpath(path))
238 return pconvert(os.path.normpath(path))
236
239
237 def normcase(path):
240 def normcase(path):
238 return encoding.upper(path) # NTFS compares via upper()
241 return encoding.upper(path) # NTFS compares via upper()
239
242
240 # see posix.py for definitions
243 # see posix.py for definitions
241 normcasespec = encoding.normcasespecs.upper
244 normcasespec = encoding.normcasespecs.upper
242 normcasefallback = encoding.upperfallback
245 normcasefallback = encoding.upperfallback
243
246
244 def samestat(s1, s2):
247 def samestat(s1, s2):
245 return False
248 return False
246
249
247 # A sequence of backslashes is special iff it precedes a double quote:
250 # A sequence of backslashes is special iff it precedes a double quote:
248 # - if there's an even number of backslashes, the double quote is not
251 # - if there's an even number of backslashes, the double quote is not
249 # quoted (i.e. it ends the quoted region)
252 # quoted (i.e. it ends the quoted region)
250 # - if there's an odd number of backslashes, the double quote is quoted
253 # - if there's an odd number of backslashes, the double quote is quoted
251 # - in both cases, every pair of backslashes is unquoted into a single
254 # - in both cases, every pair of backslashes is unquoted into a single
252 # backslash
255 # backslash
253 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
256 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
254 # So, to quote a string, we must surround it in double quotes, double
257 # So, to quote a string, we must surround it in double quotes, double
255 # the number of backslashes that precede double quotes and add another
258 # the number of backslashes that precede double quotes and add another
256 # backslash before every double quote (being careful with the double
259 # backslash before every double quote (being careful with the double
257 # quote we've appended to the end)
260 # quote we've appended to the end)
258 _quotere = None
261 _quotere = None
259 _needsshellquote = None
262 _needsshellquote = None
260 def shellquote(s):
263 def shellquote(s):
261 r"""
264 r"""
262 >>> shellquote(r'C:\Users\xyz')
265 >>> shellquote(r'C:\Users\xyz')
263 '"C:\\Users\\xyz"'
266 '"C:\\Users\\xyz"'
264 >>> shellquote(r'C:\Users\xyz/mixed')
267 >>> shellquote(r'C:\Users\xyz/mixed')
265 '"C:\\Users\\xyz/mixed"'
268 '"C:\\Users\\xyz/mixed"'
266 >>> # Would be safe not to quote too, since it is all double backslashes
269 >>> # Would be safe not to quote too, since it is all double backslashes
267 >>> shellquote(r'C:\\Users\\xyz')
270 >>> shellquote(r'C:\\Users\\xyz')
268 '"C:\\\\Users\\\\xyz"'
271 '"C:\\\\Users\\\\xyz"'
269 >>> # But this must be quoted
272 >>> # But this must be quoted
270 >>> shellquote(r'C:\\Users\\xyz/abc')
273 >>> shellquote(r'C:\\Users\\xyz/abc')
271 '"C:\\\\Users\\\\xyz/abc"'
274 '"C:\\\\Users\\\\xyz/abc"'
272 """
275 """
273 global _quotere
276 global _quotere
274 if _quotere is None:
277 if _quotere is None:
275 _quotere = re.compile(r'(\\*)("|\\$)')
278 _quotere = re.compile(r'(\\*)("|\\$)')
276 global _needsshellquote
279 global _needsshellquote
277 if _needsshellquote is None:
280 if _needsshellquote is None:
278 # ":" is also treated as "safe character", because it is used as a part
281 # ":" is also treated as "safe character", because it is used as a part
279 # of path name on Windows. "\" is also part of a path name, but isn't
282 # of path name on Windows. "\" is also part of a path name, but isn't
280 # safe because shlex.split() (kind of) treats it as an escape char and
283 # safe because shlex.split() (kind of) treats it as an escape char and
281 # drops it. It will leave the next character, even if it is another
284 # drops it. It will leave the next character, even if it is another
282 # "\".
285 # "\".
283 _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/-]').search
286 _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/-]').search
284 if s and not _needsshellquote(s) and not _quotere.search(s):
287 if s and not _needsshellquote(s) and not _quotere.search(s):
285 # "s" shouldn't have to be quoted
288 # "s" shouldn't have to be quoted
286 return s
289 return s
287 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
290 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
288
291
289 def quotecommand(cmd):
292 def quotecommand(cmd):
290 """Build a command string suitable for os.popen* calls."""
293 """Build a command string suitable for os.popen* calls."""
291 if sys.version_info < (2, 7, 1):
294 if sys.version_info < (2, 7, 1):
292 # Python versions since 2.7.1 do this extra quoting themselves
295 # Python versions since 2.7.1 do this extra quoting themselves
293 return '"' + cmd + '"'
296 return '"' + cmd + '"'
294 return cmd
297 return cmd
295
298
296 def popen(command, mode='r'):
299 def popen(command, mode='r'):
297 # Work around "popen spawned process may not write to stdout
300 # Work around "popen spawned process may not write to stdout
298 # under windows"
301 # under windows"
299 # http://bugs.python.org/issue1366
302 # http://bugs.python.org/issue1366
300 command += " 2> %s" % os.devnull
303 command += " 2> %s" % os.devnull
301 return os.popen(quotecommand(command), mode)
304 return os.popen(quotecommand(command), mode)
302
305
303 def explainexit(code):
306 def explainexit(code):
304 return _("exited with status %d") % code, code
307 return _("exited with status %d") % code, code
305
308
306 # if you change this stub into a real check, please try to implement the
309 # if you change this stub into a real check, please try to implement the
307 # username and groupname functions above, too.
310 # username and groupname functions above, too.
308 def isowner(st):
311 def isowner(st):
309 return True
312 return True
310
313
311 def findexe(command):
314 def findexe(command):
312 '''Find executable for command searching like cmd.exe does.
315 '''Find executable for command searching like cmd.exe does.
313 If command is a basename then PATH is searched for command.
316 If command is a basename then PATH is searched for command.
314 PATH isn't searched if command is an absolute or relative path.
317 PATH isn't searched if command is an absolute or relative path.
315 An extension from PATHEXT is found and added if not present.
318 An extension from PATHEXT is found and added if not present.
316 If command isn't found None is returned.'''
319 If command isn't found None is returned.'''
317 pathext = encoding.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
320 pathext = encoding.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
318 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
321 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
319 if os.path.splitext(command)[1].lower() in pathexts:
322 if os.path.splitext(command)[1].lower() in pathexts:
320 pathexts = ['']
323 pathexts = ['']
321
324
322 def findexisting(pathcommand):
325 def findexisting(pathcommand):
323 'Will append extension (if needed) and return existing file'
326 'Will append extension (if needed) and return existing file'
324 for ext in pathexts:
327 for ext in pathexts:
325 executable = pathcommand + ext
328 executable = pathcommand + ext
326 if os.path.exists(executable):
329 if os.path.exists(executable):
327 return executable
330 return executable
328 return None
331 return None
329
332
330 if pycompat.ossep in command:
333 if pycompat.ossep in command:
331 return findexisting(command)
334 return findexisting(command)
332
335
333 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
336 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
334 executable = findexisting(os.path.join(path, command))
337 executable = findexisting(os.path.join(path, command))
335 if executable is not None:
338 if executable is not None:
336 return executable
339 return executable
337 return findexisting(os.path.expanduser(os.path.expandvars(command)))
340 return findexisting(os.path.expanduser(os.path.expandvars(command)))
338
341
339 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
342 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
340
343
341 def statfiles(files):
344 def statfiles(files):
342 '''Stat each file in files. Yield each stat, or None if a file
345 '''Stat each file in files. Yield each stat, or None if a file
343 does not exist or has a type we don't care about.
346 does not exist or has a type we don't care about.
344
347
345 Cluster and cache stat per directory to minimize number of OS stat calls.'''
348 Cluster and cache stat per directory to minimize number of OS stat calls.'''
346 dircache = {} # dirname -> filename -> status | None if file does not exist
349 dircache = {} # dirname -> filename -> status | None if file does not exist
347 getkind = stat.S_IFMT
350 getkind = stat.S_IFMT
348 for nf in files:
351 for nf in files:
349 nf = normcase(nf)
352 nf = normcase(nf)
350 dir, base = os.path.split(nf)
353 dir, base = os.path.split(nf)
351 if not dir:
354 if not dir:
352 dir = '.'
355 dir = '.'
353 cache = dircache.get(dir, None)
356 cache = dircache.get(dir, None)
354 if cache is None:
357 if cache is None:
355 try:
358 try:
356 dmap = dict([(normcase(n), s)
359 dmap = dict([(normcase(n), s)
357 for n, k, s in osutil.listdir(dir, True)
360 for n, k, s in osutil.listdir(dir, True)
358 if getkind(s.st_mode) in _wantedkinds])
361 if getkind(s.st_mode) in _wantedkinds])
359 except OSError as err:
362 except OSError as err:
360 # Python >= 2.5 returns ENOENT and adds winerror field
363 # Python >= 2.5 returns ENOENT and adds winerror field
361 # EINVAL is raised if dir is not a directory.
364 # EINVAL is raised if dir is not a directory.
362 if err.errno not in (errno.ENOENT, errno.EINVAL,
365 if err.errno not in (errno.ENOENT, errno.EINVAL,
363 errno.ENOTDIR):
366 errno.ENOTDIR):
364 raise
367 raise
365 dmap = {}
368 dmap = {}
366 cache = dircache.setdefault(dir, dmap)
369 cache = dircache.setdefault(dir, dmap)
367 yield cache.get(base, None)
370 yield cache.get(base, None)
368
371
369 def username(uid=None):
372 def username(uid=None):
370 """Return the name of the user with the given uid.
373 """Return the name of the user with the given uid.
371
374
372 If uid is None, return the name of the current user."""
375 If uid is None, return the name of the current user."""
373 return None
376 return None
374
377
375 def groupname(gid=None):
378 def groupname(gid=None):
376 """Return the name of the group with the given gid.
379 """Return the name of the group with the given gid.
377
380
378 If gid is None, return the name of the current group."""
381 If gid is None, return the name of the current group."""
379 return None
382 return None
380
383
381 def removedirs(name):
384 def removedirs(name):
382 """special version of os.removedirs that does not remove symlinked
385 """special version of os.removedirs that does not remove symlinked
383 directories or junction points if they actually contain files"""
386 directories or junction points if they actually contain files"""
384 if osutil.listdir(name):
387 if osutil.listdir(name):
385 return
388 return
386 os.rmdir(name)
389 os.rmdir(name)
387 head, tail = os.path.split(name)
390 head, tail = os.path.split(name)
388 if not tail:
391 if not tail:
389 head, tail = os.path.split(head)
392 head, tail = os.path.split(head)
390 while head and tail:
393 while head and tail:
391 try:
394 try:
392 if osutil.listdir(head):
395 if osutil.listdir(head):
393 return
396 return
394 os.rmdir(head)
397 os.rmdir(head)
395 except (ValueError, OSError):
398 except (ValueError, OSError):
396 break
399 break
397 head, tail = os.path.split(head)
400 head, tail = os.path.split(head)
398
401
399 def rename(src, dst):
402 def rename(src, dst):
400 '''atomically rename file src to dst, replacing dst if it exists'''
403 '''atomically rename file src to dst, replacing dst if it exists'''
401 try:
404 try:
402 os.rename(src, dst)
405 os.rename(src, dst)
403 except OSError as e:
406 except OSError as e:
404 if e.errno != errno.EEXIST:
407 if e.errno != errno.EEXIST:
405 raise
408 raise
406 unlink(dst)
409 unlink(dst)
407 os.rename(src, dst)
410 os.rename(src, dst)
408
411
409 def gethgcmd():
412 def gethgcmd():
410 return [sys.executable] + sys.argv[:1]
413 return [sys.executable] + sys.argv[:1]
411
414
412 def groupmembers(name):
415 def groupmembers(name):
413 # Don't support groups on Windows for now
416 # Don't support groups on Windows for now
414 raise KeyError
417 raise KeyError
415
418
416 def isexec(f):
419 def isexec(f):
417 return False
420 return False
418
421
419 class cachestat(object):
422 class cachestat(object):
420 def __init__(self, path):
423 def __init__(self, path):
421 pass
424 pass
422
425
423 def cacheable(self):
426 def cacheable(self):
424 return False
427 return False
425
428
426 def lookupreg(key, valname=None, scope=None):
429 def lookupreg(key, valname=None, scope=None):
427 ''' Look up a key/value name in the Windows registry.
430 ''' Look up a key/value name in the Windows registry.
428
431
429 valname: value name. If unspecified, the default value for the key
432 valname: value name. If unspecified, the default value for the key
430 is used.
433 is used.
431 scope: optionally specify scope for registry lookup, this can be
434 scope: optionally specify scope for registry lookup, this can be
432 a sequence of scopes to look up in order. Default (CURRENT_USER,
435 a sequence of scopes to look up in order. Default (CURRENT_USER,
433 LOCAL_MACHINE).
436 LOCAL_MACHINE).
434 '''
437 '''
435 if scope is None:
438 if scope is None:
436 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
439 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
437 elif not isinstance(scope, (list, tuple)):
440 elif not isinstance(scope, (list, tuple)):
438 scope = (scope,)
441 scope = (scope,)
439 for s in scope:
442 for s in scope:
440 try:
443 try:
441 val = winreg.QueryValueEx(winreg.OpenKey(s, key), valname)[0]
444 val = winreg.QueryValueEx(winreg.OpenKey(s, key), valname)[0]
442 # never let a Unicode string escape into the wild
445 # never let a Unicode string escape into the wild
443 return encoding.unitolocal(val)
446 return encoding.unitolocal(val)
444 except EnvironmentError:
447 except EnvironmentError:
445 pass
448 pass
446
449
447 expandglobs = True
450 expandglobs = True
448
451
449 def statislink(st):
452 def statislink(st):
450 '''check whether a stat result is a symlink'''
453 '''check whether a stat result is a symlink'''
451 return False
454 return False
452
455
453 def statisexec(st):
456 def statisexec(st):
454 '''check whether a stat result is an executable file'''
457 '''check whether a stat result is an executable file'''
455 return False
458 return False
456
459
457 def poll(fds):
460 def poll(fds):
458 # see posix.py for description
461 # see posix.py for description
459 raise NotImplementedError()
462 raise NotImplementedError()
460
463
461 def readpipe(pipe):
464 def readpipe(pipe):
462 """Read all available data from a pipe."""
465 """Read all available data from a pipe."""
463 chunks = []
466 chunks = []
464 while True:
467 while True:
465 size = win32.peekpipe(pipe)
468 size = win32.peekpipe(pipe)
466 if not size:
469 if not size:
467 break
470 break
468
471
469 s = pipe.read(size)
472 s = pipe.read(size)
470 if not s:
473 if not s:
471 break
474 break
472 chunks.append(s)
475 chunks.append(s)
473
476
474 return ''.join(chunks)
477 return ''.join(chunks)
475
478
476 def bindunixsocket(sock, path):
479 def bindunixsocket(sock, path):
477 raise NotImplementedError('unsupported platform')
480 raise NotImplementedError('unsupported platform')
@@ -1,1116 +1,1157
1 Prepare repo a:
1 Prepare repo a:
2
2
3 $ hg init a
3 $ hg init a
4 $ cd a
4 $ cd a
5 $ echo a > a
5 $ echo a > a
6 $ hg add a
6 $ hg add a
7 $ hg commit -m test
7 $ hg commit -m test
8 $ echo first line > b
8 $ echo first line > b
9 $ hg add b
9 $ hg add b
10
10
11 Create a non-inlined filelog:
11 Create a non-inlined filelog:
12
12
13 $ $PYTHON -c 'file("data1", "wb").write("".join("%s\n" % x for x in range(10000)))'
13 $ $PYTHON -c 'file("data1", "wb").write("".join("%s\n" % x for x in range(10000)))'
14 $ for j in 0 1 2 3 4 5 6 7 8 9; do
14 $ for j in 0 1 2 3 4 5 6 7 8 9; do
15 > cat data1 >> b
15 > cat data1 >> b
16 > hg commit -m test
16 > hg commit -m test
17 > done
17 > done
18
18
19 List files in store/data (should show a 'b.d'):
19 List files in store/data (should show a 'b.d'):
20
20
21 $ for i in .hg/store/data/*; do
21 $ for i in .hg/store/data/*; do
22 > echo $i
22 > echo $i
23 > done
23 > done
24 .hg/store/data/a.i
24 .hg/store/data/a.i
25 .hg/store/data/b.d
25 .hg/store/data/b.d
26 .hg/store/data/b.i
26 .hg/store/data/b.i
27
27
28 Trigger branchcache creation:
28 Trigger branchcache creation:
29
29
30 $ hg branches
30 $ hg branches
31 default 10:a7949464abda
31 default 10:a7949464abda
32 $ ls .hg/cache
32 $ ls .hg/cache
33 branch2-served
33 branch2-served
34 checkisexec (execbit !)
34 checkisexec (execbit !)
35 checklink (symlink !)
35 checklink (symlink !)
36 checklink-target (symlink !)
36 checklink-target (symlink !)
37 checknoexec (execbit !)
37 checknoexec (execbit !)
38 rbc-names-v1
38 rbc-names-v1
39 rbc-revs-v1
39 rbc-revs-v1
40
40
41 Default operation:
41 Default operation:
42
42
43 $ hg clone . ../b
43 $ hg clone . ../b
44 updating to branch default
44 updating to branch default
45 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 $ cd ../b
46 $ cd ../b
47
47
48 Ensure branchcache got copied over:
48 Ensure branchcache got copied over:
49
49
50 $ ls .hg/cache
50 $ ls .hg/cache
51 branch2-served
51 branch2-served
52 checkisexec (execbit !)
52 checkisexec (execbit !)
53 checklink (symlink !)
53 checklink (symlink !)
54 checklink-target (symlink !)
54 checklink-target (symlink !)
55
55
56 $ cat a
56 $ cat a
57 a
57 a
58 $ hg verify
58 $ hg verify
59 checking changesets
59 checking changesets
60 checking manifests
60 checking manifests
61 crosschecking files in changesets and manifests
61 crosschecking files in changesets and manifests
62 checking files
62 checking files
63 2 files, 11 changesets, 11 total revisions
63 2 files, 11 changesets, 11 total revisions
64
64
65 Invalid dest '' must abort:
65 Invalid dest '' must abort:
66
66
67 $ hg clone . ''
67 $ hg clone . ''
68 abort: empty destination path is not valid
68 abort: empty destination path is not valid
69 [255]
69 [255]
70
70
71 No update, with debug option:
71 No update, with debug option:
72
72
73 #if hardlink
73 #if hardlink
74 $ hg --debug clone -U . ../c --config progress.debug=true
74 $ hg --debug clone -U . ../c --config progress.debug=true
75 linking: 1
75 linking: 1
76 linking: 2
76 linking: 2
77 linking: 3
77 linking: 3
78 linking: 4
78 linking: 4
79 linking: 5
79 linking: 5
80 linking: 6
80 linking: 6
81 linking: 7
81 linking: 7
82 linking: 8
82 linking: 8
83 linked 8 files
83 linked 8 files
84 #else
84 #else
85 $ hg --debug clone -U . ../c --config progress.debug=true
85 $ hg --debug clone -U . ../c --config progress.debug=true
86 linking: 1
86 linking: 1
87 copying: 2
87 copying: 2
88 copying: 3
88 copying: 3
89 copying: 4
89 copying: 4
90 copying: 5
90 copying: 5
91 copying: 6
91 copying: 6
92 copying: 7
92 copying: 7
93 copying: 8
93 copying: 8
94 copied 8 files
94 copied 8 files
95 #endif
95 #endif
96 $ cd ../c
96 $ cd ../c
97
97
98 Ensure branchcache got copied over:
98 Ensure branchcache got copied over:
99
99
100 $ ls .hg/cache
100 $ ls .hg/cache
101 branch2-served
101 branch2-served
102
102
103 $ cat a 2>/dev/null || echo "a not present"
103 $ cat a 2>/dev/null || echo "a not present"
104 a not present
104 a not present
105 $ hg verify
105 $ hg verify
106 checking changesets
106 checking changesets
107 checking manifests
107 checking manifests
108 crosschecking files in changesets and manifests
108 crosschecking files in changesets and manifests
109 checking files
109 checking files
110 2 files, 11 changesets, 11 total revisions
110 2 files, 11 changesets, 11 total revisions
111
111
112 Default destination:
112 Default destination:
113
113
114 $ mkdir ../d
114 $ mkdir ../d
115 $ cd ../d
115 $ cd ../d
116 $ hg clone ../a
116 $ hg clone ../a
117 destination directory: a
117 destination directory: a
118 updating to branch default
118 updating to branch default
119 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
119 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
120 $ cd a
120 $ cd a
121 $ hg cat a
121 $ hg cat a
122 a
122 a
123 $ cd ../..
123 $ cd ../..
124
124
125 Check that we drop the 'file:' from the path before writing the .hgrc:
125 Check that we drop the 'file:' from the path before writing the .hgrc:
126
126
127 $ hg clone file:a e
127 $ hg clone file:a e
128 updating to branch default
128 updating to branch default
129 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
129 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
130 $ grep 'file:' e/.hg/hgrc
130 $ grep 'file:' e/.hg/hgrc
131 [1]
131 [1]
132
132
133 Check that path aliases are expanded:
133 Check that path aliases are expanded:
134
134
135 $ hg clone -q -U --config 'paths.foobar=a#0' foobar f
135 $ hg clone -q -U --config 'paths.foobar=a#0' foobar f
136 $ hg -R f showconfig paths.default
136 $ hg -R f showconfig paths.default
137 $TESTTMP/a#0 (glob)
137 $TESTTMP/a#0 (glob)
138
138
139 Use --pull:
139 Use --pull:
140
140
141 $ hg clone --pull a g
141 $ hg clone --pull a g
142 requesting all changes
142 requesting all changes
143 adding changesets
143 adding changesets
144 adding manifests
144 adding manifests
145 adding file changes
145 adding file changes
146 added 11 changesets with 11 changes to 2 files
146 added 11 changesets with 11 changes to 2 files
147 updating to branch default
147 updating to branch default
148 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
148 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
149 $ hg -R g verify
149 $ hg -R g verify
150 checking changesets
150 checking changesets
151 checking manifests
151 checking manifests
152 crosschecking files in changesets and manifests
152 crosschecking files in changesets and manifests
153 checking files
153 checking files
154 2 files, 11 changesets, 11 total revisions
154 2 files, 11 changesets, 11 total revisions
155
155
156 Invalid dest '' with --pull must abort (issue2528):
156 Invalid dest '' with --pull must abort (issue2528):
157
157
158 $ hg clone --pull a ''
158 $ hg clone --pull a ''
159 abort: empty destination path is not valid
159 abort: empty destination path is not valid
160 [255]
160 [255]
161
161
162 Clone to '.':
162 Clone to '.':
163
163
164 $ mkdir h
164 $ mkdir h
165 $ cd h
165 $ cd h
166 $ hg clone ../a .
166 $ hg clone ../a .
167 updating to branch default
167 updating to branch default
168 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
168 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
169 $ cd ..
169 $ cd ..
170
170
171
171
172 *** Tests for option -u ***
172 *** Tests for option -u ***
173
173
174 Adding some more history to repo a:
174 Adding some more history to repo a:
175
175
176 $ cd a
176 $ cd a
177 $ hg tag ref1
177 $ hg tag ref1
178 $ echo the quick brown fox >a
178 $ echo the quick brown fox >a
179 $ hg ci -m "hacked default"
179 $ hg ci -m "hacked default"
180 $ hg up ref1
180 $ hg up ref1
181 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
181 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
182 $ hg branch stable
182 $ hg branch stable
183 marked working directory as branch stable
183 marked working directory as branch stable
184 (branches are permanent and global, did you want a bookmark?)
184 (branches are permanent and global, did you want a bookmark?)
185 $ echo some text >a
185 $ echo some text >a
186 $ hg ci -m "starting branch stable"
186 $ hg ci -m "starting branch stable"
187 $ hg tag ref2
187 $ hg tag ref2
188 $ echo some more text >a
188 $ echo some more text >a
189 $ hg ci -m "another change for branch stable"
189 $ hg ci -m "another change for branch stable"
190 $ hg up ref2
190 $ hg up ref2
191 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
191 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
192 $ hg parents
192 $ hg parents
193 changeset: 13:e8ece76546a6
193 changeset: 13:e8ece76546a6
194 branch: stable
194 branch: stable
195 tag: ref2
195 tag: ref2
196 parent: 10:a7949464abda
196 parent: 10:a7949464abda
197 user: test
197 user: test
198 date: Thu Jan 01 00:00:00 1970 +0000
198 date: Thu Jan 01 00:00:00 1970 +0000
199 summary: starting branch stable
199 summary: starting branch stable
200
200
201
201
202 Repo a has two heads:
202 Repo a has two heads:
203
203
204 $ hg heads
204 $ hg heads
205 changeset: 15:0aae7cf88f0d
205 changeset: 15:0aae7cf88f0d
206 branch: stable
206 branch: stable
207 tag: tip
207 tag: tip
208 user: test
208 user: test
209 date: Thu Jan 01 00:00:00 1970 +0000
209 date: Thu Jan 01 00:00:00 1970 +0000
210 summary: another change for branch stable
210 summary: another change for branch stable
211
211
212 changeset: 12:f21241060d6a
212 changeset: 12:f21241060d6a
213 user: test
213 user: test
214 date: Thu Jan 01 00:00:00 1970 +0000
214 date: Thu Jan 01 00:00:00 1970 +0000
215 summary: hacked default
215 summary: hacked default
216
216
217
217
218 $ cd ..
218 $ cd ..
219
219
220
220
221 Testing --noupdate with --updaterev (must abort):
221 Testing --noupdate with --updaterev (must abort):
222
222
223 $ hg clone --noupdate --updaterev 1 a ua
223 $ hg clone --noupdate --updaterev 1 a ua
224 abort: cannot specify both --noupdate and --updaterev
224 abort: cannot specify both --noupdate and --updaterev
225 [255]
225 [255]
226
226
227
227
228 Testing clone -u:
228 Testing clone -u:
229
229
230 $ hg clone -u . a ua
230 $ hg clone -u . a ua
231 updating to branch stable
231 updating to branch stable
232 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
233
233
234 Repo ua has both heads:
234 Repo ua has both heads:
235
235
236 $ hg -R ua heads
236 $ hg -R ua heads
237 changeset: 15:0aae7cf88f0d
237 changeset: 15:0aae7cf88f0d
238 branch: stable
238 branch: stable
239 tag: tip
239 tag: tip
240 user: test
240 user: test
241 date: Thu Jan 01 00:00:00 1970 +0000
241 date: Thu Jan 01 00:00:00 1970 +0000
242 summary: another change for branch stable
242 summary: another change for branch stable
243
243
244 changeset: 12:f21241060d6a
244 changeset: 12:f21241060d6a
245 user: test
245 user: test
246 date: Thu Jan 01 00:00:00 1970 +0000
246 date: Thu Jan 01 00:00:00 1970 +0000
247 summary: hacked default
247 summary: hacked default
248
248
249
249
250 Same revision checked out in repo a and ua:
250 Same revision checked out in repo a and ua:
251
251
252 $ hg -R a parents --template "{node|short}\n"
252 $ hg -R a parents --template "{node|short}\n"
253 e8ece76546a6
253 e8ece76546a6
254 $ hg -R ua parents --template "{node|short}\n"
254 $ hg -R ua parents --template "{node|short}\n"
255 e8ece76546a6
255 e8ece76546a6
256
256
257 $ rm -r ua
257 $ rm -r ua
258
258
259
259
260 Testing clone --pull -u:
260 Testing clone --pull -u:
261
261
262 $ hg clone --pull -u . a ua
262 $ hg clone --pull -u . a ua
263 requesting all changes
263 requesting all changes
264 adding changesets
264 adding changesets
265 adding manifests
265 adding manifests
266 adding file changes
266 adding file changes
267 added 16 changesets with 16 changes to 3 files (+1 heads)
267 added 16 changesets with 16 changes to 3 files (+1 heads)
268 updating to branch stable
268 updating to branch stable
269 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
270
270
271 Repo ua has both heads:
271 Repo ua has both heads:
272
272
273 $ hg -R ua heads
273 $ hg -R ua heads
274 changeset: 15:0aae7cf88f0d
274 changeset: 15:0aae7cf88f0d
275 branch: stable
275 branch: stable
276 tag: tip
276 tag: tip
277 user: test
277 user: test
278 date: Thu Jan 01 00:00:00 1970 +0000
278 date: Thu Jan 01 00:00:00 1970 +0000
279 summary: another change for branch stable
279 summary: another change for branch stable
280
280
281 changeset: 12:f21241060d6a
281 changeset: 12:f21241060d6a
282 user: test
282 user: test
283 date: Thu Jan 01 00:00:00 1970 +0000
283 date: Thu Jan 01 00:00:00 1970 +0000
284 summary: hacked default
284 summary: hacked default
285
285
286
286
287 Same revision checked out in repo a and ua:
287 Same revision checked out in repo a and ua:
288
288
289 $ hg -R a parents --template "{node|short}\n"
289 $ hg -R a parents --template "{node|short}\n"
290 e8ece76546a6
290 e8ece76546a6
291 $ hg -R ua parents --template "{node|short}\n"
291 $ hg -R ua parents --template "{node|short}\n"
292 e8ece76546a6
292 e8ece76546a6
293
293
294 $ rm -r ua
294 $ rm -r ua
295
295
296
296
297 Testing clone -u <branch>:
297 Testing clone -u <branch>:
298
298
299 $ hg clone -u stable a ua
299 $ hg clone -u stable a ua
300 updating to branch stable
300 updating to branch stable
301 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
301 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
302
302
303 Repo ua has both heads:
303 Repo ua has both heads:
304
304
305 $ hg -R ua heads
305 $ hg -R ua heads
306 changeset: 15:0aae7cf88f0d
306 changeset: 15:0aae7cf88f0d
307 branch: stable
307 branch: stable
308 tag: tip
308 tag: tip
309 user: test
309 user: test
310 date: Thu Jan 01 00:00:00 1970 +0000
310 date: Thu Jan 01 00:00:00 1970 +0000
311 summary: another change for branch stable
311 summary: another change for branch stable
312
312
313 changeset: 12:f21241060d6a
313 changeset: 12:f21241060d6a
314 user: test
314 user: test
315 date: Thu Jan 01 00:00:00 1970 +0000
315 date: Thu Jan 01 00:00:00 1970 +0000
316 summary: hacked default
316 summary: hacked default
317
317
318
318
319 Branch 'stable' is checked out:
319 Branch 'stable' is checked out:
320
320
321 $ hg -R ua parents
321 $ hg -R ua parents
322 changeset: 15:0aae7cf88f0d
322 changeset: 15:0aae7cf88f0d
323 branch: stable
323 branch: stable
324 tag: tip
324 tag: tip
325 user: test
325 user: test
326 date: Thu Jan 01 00:00:00 1970 +0000
326 date: Thu Jan 01 00:00:00 1970 +0000
327 summary: another change for branch stable
327 summary: another change for branch stable
328
328
329
329
330 $ rm -r ua
330 $ rm -r ua
331
331
332
332
333 Testing default checkout:
333 Testing default checkout:
334
334
335 $ hg clone a ua
335 $ hg clone a ua
336 updating to branch default
336 updating to branch default
337 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
337 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
338
338
339 Repo ua has both heads:
339 Repo ua has both heads:
340
340
341 $ hg -R ua heads
341 $ hg -R ua heads
342 changeset: 15:0aae7cf88f0d
342 changeset: 15:0aae7cf88f0d
343 branch: stable
343 branch: stable
344 tag: tip
344 tag: tip
345 user: test
345 user: test
346 date: Thu Jan 01 00:00:00 1970 +0000
346 date: Thu Jan 01 00:00:00 1970 +0000
347 summary: another change for branch stable
347 summary: another change for branch stable
348
348
349 changeset: 12:f21241060d6a
349 changeset: 12:f21241060d6a
350 user: test
350 user: test
351 date: Thu Jan 01 00:00:00 1970 +0000
351 date: Thu Jan 01 00:00:00 1970 +0000
352 summary: hacked default
352 summary: hacked default
353
353
354
354
355 Branch 'default' is checked out:
355 Branch 'default' is checked out:
356
356
357 $ hg -R ua parents
357 $ hg -R ua parents
358 changeset: 12:f21241060d6a
358 changeset: 12:f21241060d6a
359 user: test
359 user: test
360 date: Thu Jan 01 00:00:00 1970 +0000
360 date: Thu Jan 01 00:00:00 1970 +0000
361 summary: hacked default
361 summary: hacked default
362
362
363 Test clone with a branch named "@" (issue3677)
363 Test clone with a branch named "@" (issue3677)
364
364
365 $ hg -R ua branch @
365 $ hg -R ua branch @
366 marked working directory as branch @
366 marked working directory as branch @
367 $ hg -R ua commit -m 'created branch @'
367 $ hg -R ua commit -m 'created branch @'
368 $ hg clone ua atbranch
368 $ hg clone ua atbranch
369 updating to branch default
369 updating to branch default
370 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
370 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
371 $ hg -R atbranch heads
371 $ hg -R atbranch heads
372 changeset: 16:798b6d97153e
372 changeset: 16:798b6d97153e
373 branch: @
373 branch: @
374 tag: tip
374 tag: tip
375 parent: 12:f21241060d6a
375 parent: 12:f21241060d6a
376 user: test
376 user: test
377 date: Thu Jan 01 00:00:00 1970 +0000
377 date: Thu Jan 01 00:00:00 1970 +0000
378 summary: created branch @
378 summary: created branch @
379
379
380 changeset: 15:0aae7cf88f0d
380 changeset: 15:0aae7cf88f0d
381 branch: stable
381 branch: stable
382 user: test
382 user: test
383 date: Thu Jan 01 00:00:00 1970 +0000
383 date: Thu Jan 01 00:00:00 1970 +0000
384 summary: another change for branch stable
384 summary: another change for branch stable
385
385
386 changeset: 12:f21241060d6a
386 changeset: 12:f21241060d6a
387 user: test
387 user: test
388 date: Thu Jan 01 00:00:00 1970 +0000
388 date: Thu Jan 01 00:00:00 1970 +0000
389 summary: hacked default
389 summary: hacked default
390
390
391 $ hg -R atbranch parents
391 $ hg -R atbranch parents
392 changeset: 12:f21241060d6a
392 changeset: 12:f21241060d6a
393 user: test
393 user: test
394 date: Thu Jan 01 00:00:00 1970 +0000
394 date: Thu Jan 01 00:00:00 1970 +0000
395 summary: hacked default
395 summary: hacked default
396
396
397
397
398 $ rm -r ua atbranch
398 $ rm -r ua atbranch
399
399
400
400
401 Testing #<branch>:
401 Testing #<branch>:
402
402
403 $ hg clone -u . a#stable ua
403 $ hg clone -u . a#stable ua
404 adding changesets
404 adding changesets
405 adding manifests
405 adding manifests
406 adding file changes
406 adding file changes
407 added 14 changesets with 14 changes to 3 files
407 added 14 changesets with 14 changes to 3 files
408 updating to branch stable
408 updating to branch stable
409 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
409 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
410
410
411 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
411 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
412
412
413 $ hg -R ua heads
413 $ hg -R ua heads
414 changeset: 13:0aae7cf88f0d
414 changeset: 13:0aae7cf88f0d
415 branch: stable
415 branch: stable
416 tag: tip
416 tag: tip
417 user: test
417 user: test
418 date: Thu Jan 01 00:00:00 1970 +0000
418 date: Thu Jan 01 00:00:00 1970 +0000
419 summary: another change for branch stable
419 summary: another change for branch stable
420
420
421 changeset: 10:a7949464abda
421 changeset: 10:a7949464abda
422 user: test
422 user: test
423 date: Thu Jan 01 00:00:00 1970 +0000
423 date: Thu Jan 01 00:00:00 1970 +0000
424 summary: test
424 summary: test
425
425
426
426
427 Same revision checked out in repo a and ua:
427 Same revision checked out in repo a and ua:
428
428
429 $ hg -R a parents --template "{node|short}\n"
429 $ hg -R a parents --template "{node|short}\n"
430 e8ece76546a6
430 e8ece76546a6
431 $ hg -R ua parents --template "{node|short}\n"
431 $ hg -R ua parents --template "{node|short}\n"
432 e8ece76546a6
432 e8ece76546a6
433
433
434 $ rm -r ua
434 $ rm -r ua
435
435
436
436
437 Testing -u -r <branch>:
437 Testing -u -r <branch>:
438
438
439 $ hg clone -u . -r stable a ua
439 $ hg clone -u . -r stable a ua
440 adding changesets
440 adding changesets
441 adding manifests
441 adding manifests
442 adding file changes
442 adding file changes
443 added 14 changesets with 14 changes to 3 files
443 added 14 changesets with 14 changes to 3 files
444 updating to branch stable
444 updating to branch stable
445 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
445 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
446
446
447 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
447 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
448
448
449 $ hg -R ua heads
449 $ hg -R ua heads
450 changeset: 13:0aae7cf88f0d
450 changeset: 13:0aae7cf88f0d
451 branch: stable
451 branch: stable
452 tag: tip
452 tag: tip
453 user: test
453 user: test
454 date: Thu Jan 01 00:00:00 1970 +0000
454 date: Thu Jan 01 00:00:00 1970 +0000
455 summary: another change for branch stable
455 summary: another change for branch stable
456
456
457 changeset: 10:a7949464abda
457 changeset: 10:a7949464abda
458 user: test
458 user: test
459 date: Thu Jan 01 00:00:00 1970 +0000
459 date: Thu Jan 01 00:00:00 1970 +0000
460 summary: test
460 summary: test
461
461
462
462
463 Same revision checked out in repo a and ua:
463 Same revision checked out in repo a and ua:
464
464
465 $ hg -R a parents --template "{node|short}\n"
465 $ hg -R a parents --template "{node|short}\n"
466 e8ece76546a6
466 e8ece76546a6
467 $ hg -R ua parents --template "{node|short}\n"
467 $ hg -R ua parents --template "{node|short}\n"
468 e8ece76546a6
468 e8ece76546a6
469
469
470 $ rm -r ua
470 $ rm -r ua
471
471
472
472
473 Testing -r <branch>:
473 Testing -r <branch>:
474
474
475 $ hg clone -r stable a ua
475 $ hg clone -r stable a ua
476 adding changesets
476 adding changesets
477 adding manifests
477 adding manifests
478 adding file changes
478 adding file changes
479 added 14 changesets with 14 changes to 3 files
479 added 14 changesets with 14 changes to 3 files
480 updating to branch stable
480 updating to branch stable
481 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
481 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
482
482
483 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
483 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
484
484
485 $ hg -R ua heads
485 $ hg -R ua heads
486 changeset: 13:0aae7cf88f0d
486 changeset: 13:0aae7cf88f0d
487 branch: stable
487 branch: stable
488 tag: tip
488 tag: tip
489 user: test
489 user: test
490 date: Thu Jan 01 00:00:00 1970 +0000
490 date: Thu Jan 01 00:00:00 1970 +0000
491 summary: another change for branch stable
491 summary: another change for branch stable
492
492
493 changeset: 10:a7949464abda
493 changeset: 10:a7949464abda
494 user: test
494 user: test
495 date: Thu Jan 01 00:00:00 1970 +0000
495 date: Thu Jan 01 00:00:00 1970 +0000
496 summary: test
496 summary: test
497
497
498
498
499 Branch 'stable' is checked out:
499 Branch 'stable' is checked out:
500
500
501 $ hg -R ua parents
501 $ hg -R ua parents
502 changeset: 13:0aae7cf88f0d
502 changeset: 13:0aae7cf88f0d
503 branch: stable
503 branch: stable
504 tag: tip
504 tag: tip
505 user: test
505 user: test
506 date: Thu Jan 01 00:00:00 1970 +0000
506 date: Thu Jan 01 00:00:00 1970 +0000
507 summary: another change for branch stable
507 summary: another change for branch stable
508
508
509
509
510 $ rm -r ua
510 $ rm -r ua
511
511
512
512
513 Issue2267: Error in 1.6 hg.py: TypeError: 'NoneType' object is not
513 Issue2267: Error in 1.6 hg.py: TypeError: 'NoneType' object is not
514 iterable in addbranchrevs()
514 iterable in addbranchrevs()
515
515
516 $ cat <<EOF > simpleclone.py
516 $ cat <<EOF > simpleclone.py
517 > from mercurial import ui, hg
517 > from mercurial import ui, hg
518 > myui = ui.ui.load()
518 > myui = ui.ui.load()
519 > repo = hg.repository(myui, 'a')
519 > repo = hg.repository(myui, 'a')
520 > hg.clone(myui, {}, repo, dest="ua")
520 > hg.clone(myui, {}, repo, dest="ua")
521 > EOF
521 > EOF
522
522
523 $ python simpleclone.py
523 $ python simpleclone.py
524 updating to branch default
524 updating to branch default
525 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
525 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
526
526
527 $ rm -r ua
527 $ rm -r ua
528
528
529 $ cat <<EOF > branchclone.py
529 $ cat <<EOF > branchclone.py
530 > from mercurial import ui, hg, extensions
530 > from mercurial import ui, hg, extensions
531 > myui = ui.ui.load()
531 > myui = ui.ui.load()
532 > extensions.loadall(myui)
532 > extensions.loadall(myui)
533 > repo = hg.repository(myui, 'a')
533 > repo = hg.repository(myui, 'a')
534 > hg.clone(myui, {}, repo, dest="ua", branch=["stable",])
534 > hg.clone(myui, {}, repo, dest="ua", branch=["stable",])
535 > EOF
535 > EOF
536
536
537 $ python branchclone.py
537 $ python branchclone.py
538 adding changesets
538 adding changesets
539 adding manifests
539 adding manifests
540 adding file changes
540 adding file changes
541 added 14 changesets with 14 changes to 3 files
541 added 14 changesets with 14 changes to 3 files
542 updating to branch stable
542 updating to branch stable
543 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
543 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
544 $ rm -r ua
544 $ rm -r ua
545
545
546
546
547 Test clone with special '@' bookmark:
547 Test clone with special '@' bookmark:
548 $ cd a
548 $ cd a
549 $ hg bookmark -r a7949464abda @ # branch point of stable from default
549 $ hg bookmark -r a7949464abda @ # branch point of stable from default
550 $ hg clone . ../i
550 $ hg clone . ../i
551 updating to bookmark @
551 updating to bookmark @
552 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
552 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
553 $ hg id -i ../i
553 $ hg id -i ../i
554 a7949464abda
554 a7949464abda
555 $ rm -r ../i
555 $ rm -r ../i
556
556
557 $ hg bookmark -f -r stable @
557 $ hg bookmark -f -r stable @
558 $ hg bookmarks
558 $ hg bookmarks
559 @ 15:0aae7cf88f0d
559 @ 15:0aae7cf88f0d
560 $ hg clone . ../i
560 $ hg clone . ../i
561 updating to bookmark @ on branch stable
561 updating to bookmark @ on branch stable
562 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
562 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
563 $ hg id -i ../i
563 $ hg id -i ../i
564 0aae7cf88f0d
564 0aae7cf88f0d
565 $ cd "$TESTTMP"
565 $ cd "$TESTTMP"
566
566
567
567
568 Testing failures:
568 Testing failures:
569
569
570 $ mkdir fail
570 $ mkdir fail
571 $ cd fail
571 $ cd fail
572
572
573 No local source
573 No local source
574
574
575 $ hg clone a b
575 $ hg clone a b
576 abort: repository a not found!
576 abort: repository a not found!
577 [255]
577 [255]
578
578
579 No remote source
579 No remote source
580
580
581 #if windows
581 #if windows
582 $ hg clone http://$LOCALIP:3121/a b
582 $ hg clone http://$LOCALIP:3121/a b
583 abort: error: * (glob)
583 abort: error: * (glob)
584 [255]
584 [255]
585 #else
585 #else
586 $ hg clone http://$LOCALIP:3121/a b
586 $ hg clone http://$LOCALIP:3121/a b
587 abort: error: *refused* (glob)
587 abort: error: *refused* (glob)
588 [255]
588 [255]
589 #endif
589 #endif
590 $ rm -rf b # work around bug with http clone
590 $ rm -rf b # work around bug with http clone
591
591
592
592
593 #if unix-permissions no-root
593 #if unix-permissions no-root
594
594
595 Inaccessible source
595 Inaccessible source
596
596
597 $ mkdir a
597 $ mkdir a
598 $ chmod 000 a
598 $ chmod 000 a
599 $ hg clone a b
599 $ hg clone a b
600 abort: repository a not found!
600 abort: repository a not found!
601 [255]
601 [255]
602
602
603 Inaccessible destination
603 Inaccessible destination
604
604
605 $ hg init b
605 $ hg init b
606 $ cd b
606 $ cd b
607 $ hg clone . ../a
607 $ hg clone . ../a
608 abort: Permission denied: '../a'
608 abort: Permission denied: '../a'
609 [255]
609 [255]
610 $ cd ..
610 $ cd ..
611 $ chmod 700 a
611 $ chmod 700 a
612 $ rm -r a b
612 $ rm -r a b
613
613
614 #endif
614 #endif
615
615
616
616
617 #if fifo
617 #if fifo
618
618
619 Source of wrong type
619 Source of wrong type
620
620
621 $ mkfifo a
621 $ mkfifo a
622 $ hg clone a b
622 $ hg clone a b
623 abort: repository a not found!
623 abort: repository a not found!
624 [255]
624 [255]
625 $ rm a
625 $ rm a
626
626
627 #endif
627 #endif
628
628
629 Default destination, same directory
629 Default destination, same directory
630
630
631 $ hg init q
631 $ hg init q
632 $ hg clone q
632 $ hg clone q
633 destination directory: q
633 destination directory: q
634 abort: destination 'q' is not empty
634 abort: destination 'q' is not empty
635 [255]
635 [255]
636
636
637 destination directory not empty
637 destination directory not empty
638
638
639 $ mkdir a
639 $ mkdir a
640 $ echo stuff > a/a
640 $ echo stuff > a/a
641 $ hg clone q a
641 $ hg clone q a
642 abort: destination 'a' is not empty
642 abort: destination 'a' is not empty
643 [255]
643 [255]
644
644
645
645
646 #if unix-permissions no-root
646 #if unix-permissions no-root
647
647
648 leave existing directory in place after clone failure
648 leave existing directory in place after clone failure
649
649
650 $ hg init c
650 $ hg init c
651 $ cd c
651 $ cd c
652 $ echo c > c
652 $ echo c > c
653 $ hg commit -A -m test
653 $ hg commit -A -m test
654 adding c
654 adding c
655 $ chmod -rx .hg/store/data
655 $ chmod -rx .hg/store/data
656 $ cd ..
656 $ cd ..
657 $ mkdir d
657 $ mkdir d
658 $ hg clone c d 2> err
658 $ hg clone c d 2> err
659 [255]
659 [255]
660 $ test -d d
660 $ test -d d
661 $ test -d d/.hg
661 $ test -d d/.hg
662 [1]
662 [1]
663
663
664 re-enable perm to allow deletion
664 re-enable perm to allow deletion
665
665
666 $ chmod +rx c/.hg/store/data
666 $ chmod +rx c/.hg/store/data
667
667
668 #endif
668 #endif
669
669
670 $ cd ..
670 $ cd ..
671
671
672 Test clone from the repository in (emulated) revlog format 0 (issue4203):
672 Test clone from the repository in (emulated) revlog format 0 (issue4203):
673
673
674 $ mkdir issue4203
674 $ mkdir issue4203
675 $ mkdir -p src/.hg
675 $ mkdir -p src/.hg
676 $ echo foo > src/foo
676 $ echo foo > src/foo
677 $ hg -R src add src/foo
677 $ hg -R src add src/foo
678 $ hg -R src commit -m '#0'
678 $ hg -R src commit -m '#0'
679 $ hg -R src log -q
679 $ hg -R src log -q
680 0:e1bab28bca43
680 0:e1bab28bca43
681 $ hg clone -U -q src dst
681 $ hg clone -U -q src dst
682 $ hg -R dst log -q
682 $ hg -R dst log -q
683 0:e1bab28bca43
683 0:e1bab28bca43
684
684
685 Create repositories to test auto sharing functionality
685 Create repositories to test auto sharing functionality
686
686
687 $ cat >> $HGRCPATH << EOF
687 $ cat >> $HGRCPATH << EOF
688 > [extensions]
688 > [extensions]
689 > share=
689 > share=
690 > EOF
690 > EOF
691
691
692 $ hg init empty
692 $ hg init empty
693 $ hg init source1a
693 $ hg init source1a
694 $ cd source1a
694 $ cd source1a
695 $ echo initial1 > foo
695 $ echo initial1 > foo
696 $ hg -q commit -A -m initial
696 $ hg -q commit -A -m initial
697 $ echo second > foo
697 $ echo second > foo
698 $ hg commit -m second
698 $ hg commit -m second
699 $ cd ..
699 $ cd ..
700
700
701 $ hg init filteredrev0
701 $ hg init filteredrev0
702 $ cd filteredrev0
702 $ cd filteredrev0
703 $ cat >> .hg/hgrc << EOF
703 $ cat >> .hg/hgrc << EOF
704 > [experimental]
704 > [experimental]
705 > evolution=createmarkers
705 > evolution=createmarkers
706 > EOF
706 > EOF
707 $ echo initial1 > foo
707 $ echo initial1 > foo
708 $ hg -q commit -A -m initial0
708 $ hg -q commit -A -m initial0
709 $ hg -q up -r null
709 $ hg -q up -r null
710 $ echo initial2 > foo
710 $ echo initial2 > foo
711 $ hg -q commit -A -m initial1
711 $ hg -q commit -A -m initial1
712 $ hg debugobsolete c05d5c47a5cf81401869999f3d05f7d699d2b29a e082c1832e09a7d1e78b7fd49a592d372de854c8
712 $ hg debugobsolete c05d5c47a5cf81401869999f3d05f7d699d2b29a e082c1832e09a7d1e78b7fd49a592d372de854c8
713 $ cd ..
713 $ cd ..
714
714
715 $ hg -q clone --pull source1a source1b
715 $ hg -q clone --pull source1a source1b
716 $ cd source1a
716 $ cd source1a
717 $ hg bookmark bookA
717 $ hg bookmark bookA
718 $ echo 1a > foo
718 $ echo 1a > foo
719 $ hg commit -m 1a
719 $ hg commit -m 1a
720 $ cd ../source1b
720 $ cd ../source1b
721 $ hg -q up -r 0
721 $ hg -q up -r 0
722 $ echo head1 > foo
722 $ echo head1 > foo
723 $ hg commit -m head1
723 $ hg commit -m head1
724 created new head
724 created new head
725 $ hg bookmark head1
725 $ hg bookmark head1
726 $ hg -q up -r 0
726 $ hg -q up -r 0
727 $ echo head2 > foo
727 $ echo head2 > foo
728 $ hg commit -m head2
728 $ hg commit -m head2
729 created new head
729 created new head
730 $ hg bookmark head2
730 $ hg bookmark head2
731 $ hg -q up -r 0
731 $ hg -q up -r 0
732 $ hg branch branch1
732 $ hg branch branch1
733 marked working directory as branch branch1
733 marked working directory as branch branch1
734 (branches are permanent and global, did you want a bookmark?)
734 (branches are permanent and global, did you want a bookmark?)
735 $ echo branch1 > foo
735 $ echo branch1 > foo
736 $ hg commit -m branch1
736 $ hg commit -m branch1
737 $ hg -q up -r 0
737 $ hg -q up -r 0
738 $ hg branch branch2
738 $ hg branch branch2
739 marked working directory as branch branch2
739 marked working directory as branch branch2
740 $ echo branch2 > foo
740 $ echo branch2 > foo
741 $ hg commit -m branch2
741 $ hg commit -m branch2
742 $ cd ..
742 $ cd ..
743 $ hg init source2
743 $ hg init source2
744 $ cd source2
744 $ cd source2
745 $ echo initial2 > foo
745 $ echo initial2 > foo
746 $ hg -q commit -A -m initial2
746 $ hg -q commit -A -m initial2
747 $ echo second > foo
747 $ echo second > foo
748 $ hg commit -m second
748 $ hg commit -m second
749 $ cd ..
749 $ cd ..
750
750
751 Clone with auto share from an empty repo should not result in share
751 Clone with auto share from an empty repo should not result in share
752
752
753 $ mkdir share
753 $ mkdir share
754 $ hg --config share.pool=share clone empty share-empty
754 $ hg --config share.pool=share clone empty share-empty
755 (not using pooled storage: remote appears to be empty)
755 (not using pooled storage: remote appears to be empty)
756 updating to branch default
756 updating to branch default
757 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
757 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
758 $ ls share
758 $ ls share
759 $ test -d share-empty/.hg/store
759 $ test -d share-empty/.hg/store
760 $ test -f share-empty/.hg/sharedpath
760 $ test -f share-empty/.hg/sharedpath
761 [1]
761 [1]
762
762
763 Clone with auto share from a repo with filtered revision 0 should not result in share
763 Clone with auto share from a repo with filtered revision 0 should not result in share
764
764
765 $ hg --config share.pool=share clone filteredrev0 share-filtered
765 $ hg --config share.pool=share clone filteredrev0 share-filtered
766 (not using pooled storage: unable to resolve identity of remote)
766 (not using pooled storage: unable to resolve identity of remote)
767 requesting all changes
767 requesting all changes
768 adding changesets
768 adding changesets
769 adding manifests
769 adding manifests
770 adding file changes
770 adding file changes
771 added 1 changesets with 1 changes to 1 files
771 added 1 changesets with 1 changes to 1 files
772 updating to branch default
772 updating to branch default
773 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
773 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
774
774
775 Clone from repo with content should result in shared store being created
775 Clone from repo with content should result in shared store being created
776
776
777 $ hg --config share.pool=share clone source1a share-dest1a
777 $ hg --config share.pool=share clone source1a share-dest1a
778 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
778 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
779 requesting all changes
779 requesting all changes
780 adding changesets
780 adding changesets
781 adding manifests
781 adding manifests
782 adding file changes
782 adding file changes
783 added 3 changesets with 3 changes to 1 files
783 added 3 changesets with 3 changes to 1 files
784 searching for changes
784 searching for changes
785 no changes found
785 no changes found
786 adding remote bookmark bookA
786 adding remote bookmark bookA
787 updating working directory
787 updating working directory
788 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
788 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
789
789
790 The shared repo should have been created
790 The shared repo should have been created
791
791
792 $ ls share
792 $ ls share
793 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
793 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
794
794
795 The destination should point to it
795 The destination should point to it
796
796
797 $ cat share-dest1a/.hg/sharedpath; echo
797 $ cat share-dest1a/.hg/sharedpath; echo
798 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg (glob)
798 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg (glob)
799
799
800 The destination should have bookmarks
800 The destination should have bookmarks
801
801
802 $ hg -R share-dest1a bookmarks
802 $ hg -R share-dest1a bookmarks
803 bookA 2:e5bfe23c0b47
803 bookA 2:e5bfe23c0b47
804
804
805 The default path should be the remote, not the share
805 The default path should be the remote, not the share
806
806
807 $ hg -R share-dest1a config paths.default
807 $ hg -R share-dest1a config paths.default
808 $TESTTMP/source1a (glob)
808 $TESTTMP/source1a (glob)
809
809
810 Clone with existing share dir should result in pull + share
810 Clone with existing share dir should result in pull + share
811
811
812 $ hg --config share.pool=share clone source1b share-dest1b
812 $ hg --config share.pool=share clone source1b share-dest1b
813 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
813 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
814 searching for changes
814 searching for changes
815 adding changesets
815 adding changesets
816 adding manifests
816 adding manifests
817 adding file changes
817 adding file changes
818 added 4 changesets with 4 changes to 1 files (+4 heads)
818 added 4 changesets with 4 changes to 1 files (+4 heads)
819 adding remote bookmark head1
819 adding remote bookmark head1
820 adding remote bookmark head2
820 adding remote bookmark head2
821 updating working directory
821 updating working directory
822 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
822 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
823
823
824 $ ls share
824 $ ls share
825 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
825 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
826
826
827 $ cat share-dest1b/.hg/sharedpath; echo
827 $ cat share-dest1b/.hg/sharedpath; echo
828 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg (glob)
828 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg (glob)
829
829
830 We only get bookmarks from the remote, not everything in the share
830 We only get bookmarks from the remote, not everything in the share
831
831
832 $ hg -R share-dest1b bookmarks
832 $ hg -R share-dest1b bookmarks
833 head1 3:4a8dc1ab4c13
833 head1 3:4a8dc1ab4c13
834 head2 4:99f71071f117
834 head2 4:99f71071f117
835
835
836 Default path should be source, not share.
836 Default path should be source, not share.
837
837
838 $ hg -R share-dest1b config paths.default
838 $ hg -R share-dest1b config paths.default
839 $TESTTMP/source1b (glob)
839 $TESTTMP/source1b (glob)
840
840
841 Checked out revision should be head of default branch
841 Checked out revision should be head of default branch
842
842
843 $ hg -R share-dest1b log -r .
843 $ hg -R share-dest1b log -r .
844 changeset: 4:99f71071f117
844 changeset: 4:99f71071f117
845 bookmark: head2
845 bookmark: head2
846 parent: 0:b5f04eac9d8f
846 parent: 0:b5f04eac9d8f
847 user: test
847 user: test
848 date: Thu Jan 01 00:00:00 1970 +0000
848 date: Thu Jan 01 00:00:00 1970 +0000
849 summary: head2
849 summary: head2
850
850
851
851
852 Clone from unrelated repo should result in new share
852 Clone from unrelated repo should result in new share
853
853
854 $ hg --config share.pool=share clone source2 share-dest2
854 $ hg --config share.pool=share clone source2 share-dest2
855 (sharing from new pooled repository 22aeff664783fd44c6d9b435618173c118c3448e)
855 (sharing from new pooled repository 22aeff664783fd44c6d9b435618173c118c3448e)
856 requesting all changes
856 requesting all changes
857 adding changesets
857 adding changesets
858 adding manifests
858 adding manifests
859 adding file changes
859 adding file changes
860 added 2 changesets with 2 changes to 1 files
860 added 2 changesets with 2 changes to 1 files
861 searching for changes
861 searching for changes
862 no changes found
862 no changes found
863 updating working directory
863 updating working directory
864 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
864 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
865
865
866 $ ls share
866 $ ls share
867 22aeff664783fd44c6d9b435618173c118c3448e
867 22aeff664783fd44c6d9b435618173c118c3448e
868 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
868 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
869
869
870 remote naming mode works as advertised
870 remote naming mode works as advertised
871
871
872 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1a share-remote1a
872 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1a share-remote1a
873 (sharing from new pooled repository 195bb1fcdb595c14a6c13e0269129ed78f6debde)
873 (sharing from new pooled repository 195bb1fcdb595c14a6c13e0269129ed78f6debde)
874 requesting all changes
874 requesting all changes
875 adding changesets
875 adding changesets
876 adding manifests
876 adding manifests
877 adding file changes
877 adding file changes
878 added 3 changesets with 3 changes to 1 files
878 added 3 changesets with 3 changes to 1 files
879 searching for changes
879 searching for changes
880 no changes found
880 no changes found
881 adding remote bookmark bookA
881 adding remote bookmark bookA
882 updating working directory
882 updating working directory
883 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
883 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
884
884
885 $ ls shareremote
885 $ ls shareremote
886 195bb1fcdb595c14a6c13e0269129ed78f6debde
886 195bb1fcdb595c14a6c13e0269129ed78f6debde
887
887
888 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1b share-remote1b
888 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1b share-remote1b
889 (sharing from new pooled repository c0d4f83847ca2a873741feb7048a45085fd47c46)
889 (sharing from new pooled repository c0d4f83847ca2a873741feb7048a45085fd47c46)
890 requesting all changes
890 requesting all changes
891 adding changesets
891 adding changesets
892 adding manifests
892 adding manifests
893 adding file changes
893 adding file changes
894 added 6 changesets with 6 changes to 1 files (+4 heads)
894 added 6 changesets with 6 changes to 1 files (+4 heads)
895 searching for changes
895 searching for changes
896 no changes found
896 no changes found
897 adding remote bookmark head1
897 adding remote bookmark head1
898 adding remote bookmark head2
898 adding remote bookmark head2
899 updating working directory
899 updating working directory
900 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
900 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
901
901
902 $ ls shareremote
902 $ ls shareremote
903 195bb1fcdb595c14a6c13e0269129ed78f6debde
903 195bb1fcdb595c14a6c13e0269129ed78f6debde
904 c0d4f83847ca2a873741feb7048a45085fd47c46
904 c0d4f83847ca2a873741feb7048a45085fd47c46
905
905
906 request to clone a single revision is respected in sharing mode
906 request to clone a single revision is respected in sharing mode
907
907
908 $ hg --config share.pool=sharerevs clone -r 4a8dc1ab4c13 source1b share-1arev
908 $ hg --config share.pool=sharerevs clone -r 4a8dc1ab4c13 source1b share-1arev
909 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
909 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
910 adding changesets
910 adding changesets
911 adding manifests
911 adding manifests
912 adding file changes
912 adding file changes
913 added 2 changesets with 2 changes to 1 files
913 added 2 changesets with 2 changes to 1 files
914 no changes found
914 no changes found
915 adding remote bookmark head1
915 adding remote bookmark head1
916 updating working directory
916 updating working directory
917 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
917 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
918
918
919 $ hg -R share-1arev log -G
919 $ hg -R share-1arev log -G
920 @ changeset: 1:4a8dc1ab4c13
920 @ changeset: 1:4a8dc1ab4c13
921 | bookmark: head1
921 | bookmark: head1
922 | tag: tip
922 | tag: tip
923 | user: test
923 | user: test
924 | date: Thu Jan 01 00:00:00 1970 +0000
924 | date: Thu Jan 01 00:00:00 1970 +0000
925 | summary: head1
925 | summary: head1
926 |
926 |
927 o changeset: 0:b5f04eac9d8f
927 o changeset: 0:b5f04eac9d8f
928 user: test
928 user: test
929 date: Thu Jan 01 00:00:00 1970 +0000
929 date: Thu Jan 01 00:00:00 1970 +0000
930 summary: initial
930 summary: initial
931
931
932
932
933 making another clone should only pull down requested rev
933 making another clone should only pull down requested rev
934
934
935 $ hg --config share.pool=sharerevs clone -r 99f71071f117 source1b share-1brev
935 $ hg --config share.pool=sharerevs clone -r 99f71071f117 source1b share-1brev
936 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
936 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
937 searching for changes
937 searching for changes
938 adding changesets
938 adding changesets
939 adding manifests
939 adding manifests
940 adding file changes
940 adding file changes
941 added 1 changesets with 1 changes to 1 files (+1 heads)
941 added 1 changesets with 1 changes to 1 files (+1 heads)
942 adding remote bookmark head1
942 adding remote bookmark head1
943 adding remote bookmark head2
943 adding remote bookmark head2
944 updating working directory
944 updating working directory
945 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
945 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
946
946
947 $ hg -R share-1brev log -G
947 $ hg -R share-1brev log -G
948 @ changeset: 2:99f71071f117
948 @ changeset: 2:99f71071f117
949 | bookmark: head2
949 | bookmark: head2
950 | tag: tip
950 | tag: tip
951 | parent: 0:b5f04eac9d8f
951 | parent: 0:b5f04eac9d8f
952 | user: test
952 | user: test
953 | date: Thu Jan 01 00:00:00 1970 +0000
953 | date: Thu Jan 01 00:00:00 1970 +0000
954 | summary: head2
954 | summary: head2
955 |
955 |
956 | o changeset: 1:4a8dc1ab4c13
956 | o changeset: 1:4a8dc1ab4c13
957 |/ bookmark: head1
957 |/ bookmark: head1
958 | user: test
958 | user: test
959 | date: Thu Jan 01 00:00:00 1970 +0000
959 | date: Thu Jan 01 00:00:00 1970 +0000
960 | summary: head1
960 | summary: head1
961 |
961 |
962 o changeset: 0:b5f04eac9d8f
962 o changeset: 0:b5f04eac9d8f
963 user: test
963 user: test
964 date: Thu Jan 01 00:00:00 1970 +0000
964 date: Thu Jan 01 00:00:00 1970 +0000
965 summary: initial
965 summary: initial
966
966
967
967
968 Request to clone a single branch is respected in sharing mode
968 Request to clone a single branch is respected in sharing mode
969
969
970 $ hg --config share.pool=sharebranch clone -b branch1 source1b share-1bbranch1
970 $ hg --config share.pool=sharebranch clone -b branch1 source1b share-1bbranch1
971 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
971 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
972 adding changesets
972 adding changesets
973 adding manifests
973 adding manifests
974 adding file changes
974 adding file changes
975 added 2 changesets with 2 changes to 1 files
975 added 2 changesets with 2 changes to 1 files
976 no changes found
976 no changes found
977 updating working directory
977 updating working directory
978 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
978 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
979
979
980 $ hg -R share-1bbranch1 log -G
980 $ hg -R share-1bbranch1 log -G
981 o changeset: 1:5f92a6c1a1b1
981 o changeset: 1:5f92a6c1a1b1
982 | branch: branch1
982 | branch: branch1
983 | tag: tip
983 | tag: tip
984 | user: test
984 | user: test
985 | date: Thu Jan 01 00:00:00 1970 +0000
985 | date: Thu Jan 01 00:00:00 1970 +0000
986 | summary: branch1
986 | summary: branch1
987 |
987 |
988 @ changeset: 0:b5f04eac9d8f
988 @ changeset: 0:b5f04eac9d8f
989 user: test
989 user: test
990 date: Thu Jan 01 00:00:00 1970 +0000
990 date: Thu Jan 01 00:00:00 1970 +0000
991 summary: initial
991 summary: initial
992
992
993
993
994 $ hg --config share.pool=sharebranch clone -b branch2 source1b share-1bbranch2
994 $ hg --config share.pool=sharebranch clone -b branch2 source1b share-1bbranch2
995 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
995 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
996 searching for changes
996 searching for changes
997 adding changesets
997 adding changesets
998 adding manifests
998 adding manifests
999 adding file changes
999 adding file changes
1000 added 1 changesets with 1 changes to 1 files (+1 heads)
1000 added 1 changesets with 1 changes to 1 files (+1 heads)
1001 updating working directory
1001 updating working directory
1002 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1002 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1003
1003
1004 $ hg -R share-1bbranch2 log -G
1004 $ hg -R share-1bbranch2 log -G
1005 o changeset: 2:6bacf4683960
1005 o changeset: 2:6bacf4683960
1006 | branch: branch2
1006 | branch: branch2
1007 | tag: tip
1007 | tag: tip
1008 | parent: 0:b5f04eac9d8f
1008 | parent: 0:b5f04eac9d8f
1009 | user: test
1009 | user: test
1010 | date: Thu Jan 01 00:00:00 1970 +0000
1010 | date: Thu Jan 01 00:00:00 1970 +0000
1011 | summary: branch2
1011 | summary: branch2
1012 |
1012 |
1013 | o changeset: 1:5f92a6c1a1b1
1013 | o changeset: 1:5f92a6c1a1b1
1014 |/ branch: branch1
1014 |/ branch: branch1
1015 | user: test
1015 | user: test
1016 | date: Thu Jan 01 00:00:00 1970 +0000
1016 | date: Thu Jan 01 00:00:00 1970 +0000
1017 | summary: branch1
1017 | summary: branch1
1018 |
1018 |
1019 @ changeset: 0:b5f04eac9d8f
1019 @ changeset: 0:b5f04eac9d8f
1020 user: test
1020 user: test
1021 date: Thu Jan 01 00:00:00 1970 +0000
1021 date: Thu Jan 01 00:00:00 1970 +0000
1022 summary: initial
1022 summary: initial
1023
1023
1024
1024
1025 -U is respected in share clone mode
1025 -U is respected in share clone mode
1026
1026
1027 $ hg --config share.pool=share clone -U source1a share-1anowc
1027 $ hg --config share.pool=share clone -U source1a share-1anowc
1028 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1028 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1029 searching for changes
1029 searching for changes
1030 no changes found
1030 no changes found
1031 adding remote bookmark bookA
1031 adding remote bookmark bookA
1032
1032
1033 $ ls share-1anowc
1033 $ ls share-1anowc
1034
1034
1035 Test that auto sharing doesn't cause failure of "hg clone local remote"
1035 Test that auto sharing doesn't cause failure of "hg clone local remote"
1036
1036
1037 $ cd $TESTTMP
1037 $ cd $TESTTMP
1038 $ hg -R a id -r 0
1038 $ hg -R a id -r 0
1039 acb14030fe0a
1039 acb14030fe0a
1040 $ hg id -R remote -r 0
1040 $ hg id -R remote -r 0
1041 abort: repository remote not found!
1041 abort: repository remote not found!
1042 [255]
1042 [255]
1043 $ hg --config share.pool=share -q clone -e "python \"$TESTDIR/dummyssh\"" a ssh://user@dummy/remote
1043 $ hg --config share.pool=share -q clone -e "python \"$TESTDIR/dummyssh\"" a ssh://user@dummy/remote
1044 $ hg -R remote id -r 0
1044 $ hg -R remote id -r 0
1045 acb14030fe0a
1045 acb14030fe0a
1046
1046
1047 Cloning into pooled storage doesn't race (issue5104)
1047 Cloning into pooled storage doesn't race (issue5104)
1048
1048
1049 $ HGPOSTLOCKDELAY=2.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace1 > race1.log 2>&1 &
1049 $ HGPOSTLOCKDELAY=2.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace1 > race1.log 2>&1 &
1050 $ HGPRELOCKDELAY=1.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace2 > race2.log 2>&1
1050 $ HGPRELOCKDELAY=1.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace2 > race2.log 2>&1
1051 $ wait
1051 $ wait
1052
1052
1053 $ hg -R share-destrace1 log -r tip
1053 $ hg -R share-destrace1 log -r tip
1054 changeset: 2:e5bfe23c0b47
1054 changeset: 2:e5bfe23c0b47
1055 bookmark: bookA
1055 bookmark: bookA
1056 tag: tip
1056 tag: tip
1057 user: test
1057 user: test
1058 date: Thu Jan 01 00:00:00 1970 +0000
1058 date: Thu Jan 01 00:00:00 1970 +0000
1059 summary: 1a
1059 summary: 1a
1060
1060
1061
1061
1062 $ hg -R share-destrace2 log -r tip
1062 $ hg -R share-destrace2 log -r tip
1063 changeset: 2:e5bfe23c0b47
1063 changeset: 2:e5bfe23c0b47
1064 bookmark: bookA
1064 bookmark: bookA
1065 tag: tip
1065 tag: tip
1066 user: test
1066 user: test
1067 date: Thu Jan 01 00:00:00 1970 +0000
1067 date: Thu Jan 01 00:00:00 1970 +0000
1068 summary: 1a
1068 summary: 1a
1069
1069
1070 One repo should be new, the other should be shared from the pool. We
1070 One repo should be new, the other should be shared from the pool. We
1071 don't care which is which, so we just make sure we always print the
1071 don't care which is which, so we just make sure we always print the
1072 one containing "new pooled" first, then one one containing "existing
1072 one containing "new pooled" first, then one one containing "existing
1073 pooled".
1073 pooled".
1074
1074
1075 $ (grep 'new pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1075 $ (grep 'new pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1076 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1076 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1077 requesting all changes
1077 requesting all changes
1078 adding changesets
1078 adding changesets
1079 adding manifests
1079 adding manifests
1080 adding file changes
1080 adding file changes
1081 added 3 changesets with 3 changes to 1 files
1081 added 3 changesets with 3 changes to 1 files
1082 searching for changes
1082 searching for changes
1083 no changes found
1083 no changes found
1084 adding remote bookmark bookA
1084 adding remote bookmark bookA
1085 updating working directory
1085 updating working directory
1086 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1086 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1087
1087
1088 $ (grep 'existing pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1088 $ (grep 'existing pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1089 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1089 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1090 searching for changes
1090 searching for changes
1091 no changes found
1091 no changes found
1092 adding remote bookmark bookA
1092 adding remote bookmark bookA
1093 updating working directory
1093 updating working directory
1094 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1094 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1095
1095
1096 SEC: check for unsafe ssh url
1096 SEC: check for unsafe ssh url
1097
1097
1098 $ cat >> $HGRCPATH << EOF
1099 > [ui]
1100 > ssh = sh -c "read l; read l; read l"
1101 > EOF
1102
1098 $ hg clone 'ssh://-oProxyCommand=touch${IFS}owned/path'
1103 $ hg clone 'ssh://-oProxyCommand=touch${IFS}owned/path'
1099 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1104 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1100 [255]
1105 [255]
1101 $ hg clone 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
1106 $ hg clone 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
1102 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1107 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1103 [255]
1108 [255]
1104 $ hg clone 'ssh://fakehost|shellcommand/path'
1109 $ hg clone 'ssh://fakehost|shellcommand/path'
1105 abort: potentially unsafe url: 'ssh://fakehost|shellcommand/path'
1110 abort: potentially unsafe url: 'ssh://fakehost|shellcommand/path'
1106 [255]
1111 [255]
1107 $ hg clone 'ssh://fakehost%7Cshellcommand/path'
1112 $ hg clone 'ssh://fakehost%7Cshellcommand/path'
1108 abort: potentially unsafe url: 'ssh://fakehost|shellcommand/path'
1113 abort: potentially unsafe url: 'ssh://fakehost|shellcommand/path'
1109 [255]
1114 [255]
1110
1115
1111 $ hg clone 'ssh://-oProxyCommand=touch owned%20foo@example.com/nonexistent/path'
1116 $ hg clone 'ssh://-oProxyCommand=touch owned%20foo@example.com/nonexistent/path'
1112 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned foo@example.com/nonexistent/path'
1117 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned foo@example.com/nonexistent/path'
1113 [255]
1118 [255]
1119
1120 #if windows
1121 $ hg clone "ssh://%26touch%20owned%20/" --debug
1122 running sh -c "read l; read l; read l" "&touch owned " "hg -R . serve --stdio"
1123 sending hello command
1124 sending between command
1125 abort: no suitable response from remote hg!
1126 [255]
1127 $ hg clone "ssh://example.com:%26touch%20owned%20/" --debug
1128 running sh -c "read l; read l; read l" -p "&touch owned " example.com "hg -R . serve --stdio"
1129 sending hello command
1130 sending between command
1131 abort: no suitable response from remote hg!
1132 [255]
1133 #else
1134 $ hg clone "ssh://%3btouch%20owned%20/" --debug
1135 running sh -c "read l; read l; read l" ';touch owned ' 'hg -R . serve --stdio'
1136 sending hello command
1137 sending between command
1138 abort: no suitable response from remote hg!
1139 [255]
1140 $ hg clone "ssh://example.com:%3btouch%20owned%20/" --debug
1141 running sh -c "read l; read l; read l" -p ';touch owned ' example.com 'hg -R . serve --stdio'
1142 sending hello command
1143 sending between command
1144 abort: no suitable response from remote hg!
1145 [255]
1146 #endif
1147
1148 $ hg clone "ssh://v-alid.example.com/" --debug
1149 running sh -c "read l; read l; read l" v-alid\.example\.com ['"]hg -R \. serve --stdio['"] (re)
1150 sending hello command
1151 sending between command
1152 abort: no suitable response from remote hg!
1153 [255]
1154
1114 We should not have created a file named owned - if it exists, the
1155 We should not have created a file named owned - if it exists, the
1115 attack succeeded.
1156 attack succeeded.
1116 $ if test -f owned; then echo 'you got owned'; fi
1157 $ if test -f owned; then echo 'you got owned'; fi
@@ -1,562 +1,562
1 This test is a duplicate of 'test-http.t' feel free to factor out
1 This test is a duplicate of 'test-http.t' feel free to factor out
2 parts that are not bundle1/bundle2 specific.
2 parts that are not bundle1/bundle2 specific.
3
3
4 $ cat << EOF >> $HGRCPATH
4 $ cat << EOF >> $HGRCPATH
5 > [devel]
5 > [devel]
6 > # This test is dedicated to interaction through old bundle
6 > # This test is dedicated to interaction through old bundle
7 > legacy.exchange = bundle1
7 > legacy.exchange = bundle1
8 > [format] # temporary settings
8 > [format] # temporary settings
9 > usegeneraldelta=yes
9 > usegeneraldelta=yes
10 > EOF
10 > EOF
11
11
12
12
13 This test tries to exercise the ssh functionality with a dummy script
13 This test tries to exercise the ssh functionality with a dummy script
14
14
15 creating 'remote' repo
15 creating 'remote' repo
16
16
17 $ hg init remote
17 $ hg init remote
18 $ cd remote
18 $ cd remote
19 $ echo this > foo
19 $ echo this > foo
20 $ echo this > fooO
20 $ echo this > fooO
21 $ hg ci -A -m "init" foo fooO
21 $ hg ci -A -m "init" foo fooO
22
22
23 insert a closed branch (issue4428)
23 insert a closed branch (issue4428)
24
24
25 $ hg up null
25 $ hg up null
26 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
26 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
27 $ hg branch closed
27 $ hg branch closed
28 marked working directory as branch closed
28 marked working directory as branch closed
29 (branches are permanent and global, did you want a bookmark?)
29 (branches are permanent and global, did you want a bookmark?)
30 $ hg ci -mc0
30 $ hg ci -mc0
31 $ hg ci --close-branch -mc1
31 $ hg ci --close-branch -mc1
32 $ hg up -q default
32 $ hg up -q default
33
33
34 configure for serving
34 configure for serving
35
35
36 $ cat <<EOF > .hg/hgrc
36 $ cat <<EOF > .hg/hgrc
37 > [server]
37 > [server]
38 > uncompressed = True
38 > uncompressed = True
39 >
39 >
40 > [hooks]
40 > [hooks]
41 > changegroup = sh -c "printenv.py changegroup-in-remote 0 ../dummylog"
41 > changegroup = sh -c "printenv.py changegroup-in-remote 0 ../dummylog"
42 > EOF
42 > EOF
43 $ cd ..
43 $ cd ..
44
44
45 repo not found error
45 repo not found error
46
46
47 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
47 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
48 remote: abort: repository nonexistent not found!
48 remote: abort: repository nonexistent not found!
49 abort: no suitable response from remote hg!
49 abort: no suitable response from remote hg!
50 [255]
50 [255]
51
51
52 non-existent absolute path
52 non-existent absolute path
53
53
54 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local
54 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local
55 remote: abort: repository /$TESTTMP/nonexistent not found!
55 remote: abort: repository /$TESTTMP/nonexistent not found!
56 abort: no suitable response from remote hg!
56 abort: no suitable response from remote hg!
57 [255]
57 [255]
58
58
59 clone remote via stream
59 clone remote via stream
60
60
61 $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/remote local-stream
61 $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/remote local-stream
62 streaming all changes
62 streaming all changes
63 4 files to transfer, 602 bytes of data
63 4 files to transfer, 602 bytes of data
64 transferred 602 bytes in * seconds (*) (glob)
64 transferred 602 bytes in * seconds (*) (glob)
65 searching for changes
65 searching for changes
66 no changes found
66 no changes found
67 updating to branch default
67 updating to branch default
68 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
68 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
69 $ cd local-stream
69 $ cd local-stream
70 $ hg verify
70 $ hg verify
71 checking changesets
71 checking changesets
72 checking manifests
72 checking manifests
73 crosschecking files in changesets and manifests
73 crosschecking files in changesets and manifests
74 checking files
74 checking files
75 2 files, 3 changesets, 2 total revisions
75 2 files, 3 changesets, 2 total revisions
76 $ hg branches
76 $ hg branches
77 default 0:1160648e36ce
77 default 0:1160648e36ce
78 $ cd ..
78 $ cd ..
79
79
80 clone bookmarks via stream
80 clone bookmarks via stream
81
81
82 $ hg -R local-stream book mybook
82 $ hg -R local-stream book mybook
83 $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/local-stream stream2
83 $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/local-stream stream2
84 streaming all changes
84 streaming all changes
85 4 files to transfer, 602 bytes of data
85 4 files to transfer, 602 bytes of data
86 transferred 602 bytes in * seconds (*) (glob)
86 transferred 602 bytes in * seconds (*) (glob)
87 searching for changes
87 searching for changes
88 no changes found
88 no changes found
89 updating to branch default
89 updating to branch default
90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 $ cd stream2
91 $ cd stream2
92 $ hg book
92 $ hg book
93 mybook 0:1160648e36ce
93 mybook 0:1160648e36ce
94 $ cd ..
94 $ cd ..
95 $ rm -rf local-stream stream2
95 $ rm -rf local-stream stream2
96
96
97 clone remote via pull
97 clone remote via pull
98
98
99 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
99 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
100 requesting all changes
100 requesting all changes
101 adding changesets
101 adding changesets
102 adding manifests
102 adding manifests
103 adding file changes
103 adding file changes
104 added 3 changesets with 2 changes to 2 files
104 added 3 changesets with 2 changes to 2 files
105 updating to branch default
105 updating to branch default
106 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
107
107
108 verify
108 verify
109
109
110 $ cd local
110 $ cd local
111 $ hg verify
111 $ hg verify
112 checking changesets
112 checking changesets
113 checking manifests
113 checking manifests
114 crosschecking files in changesets and manifests
114 crosschecking files in changesets and manifests
115 checking files
115 checking files
116 2 files, 3 changesets, 2 total revisions
116 2 files, 3 changesets, 2 total revisions
117 $ cat >> .hg/hgrc <<EOF
117 $ cat >> .hg/hgrc <<EOF
118 > [hooks]
118 > [hooks]
119 > changegroup = sh -c "printenv.py changegroup-in-local 0 ../dummylog"
119 > changegroup = sh -c "printenv.py changegroup-in-local 0 ../dummylog"
120 > EOF
120 > EOF
121
121
122 empty default pull
122 empty default pull
123
123
124 $ hg paths
124 $ hg paths
125 default = ssh://user@dummy/remote
125 default = ssh://user@dummy/remote
126 $ hg pull -e "python \"$TESTDIR/dummyssh\""
126 $ hg pull -e "python \"$TESTDIR/dummyssh\""
127 pulling from ssh://user@dummy/remote
127 pulling from ssh://user@dummy/remote
128 searching for changes
128 searching for changes
129 no changes found
129 no changes found
130
130
131 pull from wrong ssh URL
131 pull from wrong ssh URL
132
132
133 $ hg pull -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist
133 $ hg pull -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist
134 pulling from ssh://user@dummy/doesnotexist
134 pulling from ssh://user@dummy/doesnotexist
135 remote: abort: repository doesnotexist not found!
135 remote: abort: repository doesnotexist not found!
136 abort: no suitable response from remote hg!
136 abort: no suitable response from remote hg!
137 [255]
137 [255]
138
138
139 local change
139 local change
140
140
141 $ echo bleah > foo
141 $ echo bleah > foo
142 $ hg ci -m "add"
142 $ hg ci -m "add"
143
143
144 updating rc
144 updating rc
145
145
146 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
146 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
147 $ echo "[ui]" >> .hg/hgrc
147 $ echo "[ui]" >> .hg/hgrc
148 $ echo "ssh = python \"$TESTDIR/dummyssh\"" >> .hg/hgrc
148 $ echo "ssh = python \"$TESTDIR/dummyssh\"" >> .hg/hgrc
149
149
150 find outgoing
150 find outgoing
151
151
152 $ hg out ssh://user@dummy/remote
152 $ hg out ssh://user@dummy/remote
153 comparing with ssh://user@dummy/remote
153 comparing with ssh://user@dummy/remote
154 searching for changes
154 searching for changes
155 changeset: 3:a28a9d1a809c
155 changeset: 3:a28a9d1a809c
156 tag: tip
156 tag: tip
157 parent: 0:1160648e36ce
157 parent: 0:1160648e36ce
158 user: test
158 user: test
159 date: Thu Jan 01 00:00:00 1970 +0000
159 date: Thu Jan 01 00:00:00 1970 +0000
160 summary: add
160 summary: add
161
161
162
162
163 find incoming on the remote side
163 find incoming on the remote side
164
164
165 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
165 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
166 comparing with ssh://user@dummy/local
166 comparing with ssh://user@dummy/local
167 searching for changes
167 searching for changes
168 changeset: 3:a28a9d1a809c
168 changeset: 3:a28a9d1a809c
169 tag: tip
169 tag: tip
170 parent: 0:1160648e36ce
170 parent: 0:1160648e36ce
171 user: test
171 user: test
172 date: Thu Jan 01 00:00:00 1970 +0000
172 date: Thu Jan 01 00:00:00 1970 +0000
173 summary: add
173 summary: add
174
174
175
175
176 find incoming on the remote side (using absolute path)
176 find incoming on the remote side (using absolute path)
177
177
178 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
178 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
179 comparing with ssh://user@dummy/$TESTTMP/local
179 comparing with ssh://user@dummy/$TESTTMP/local
180 searching for changes
180 searching for changes
181 changeset: 3:a28a9d1a809c
181 changeset: 3:a28a9d1a809c
182 tag: tip
182 tag: tip
183 parent: 0:1160648e36ce
183 parent: 0:1160648e36ce
184 user: test
184 user: test
185 date: Thu Jan 01 00:00:00 1970 +0000
185 date: Thu Jan 01 00:00:00 1970 +0000
186 summary: add
186 summary: add
187
187
188
188
189 push
189 push
190
190
191 $ hg push
191 $ hg push
192 pushing to ssh://user@dummy/remote
192 pushing to ssh://user@dummy/remote
193 searching for changes
193 searching for changes
194 remote: adding changesets
194 remote: adding changesets
195 remote: adding manifests
195 remote: adding manifests
196 remote: adding file changes
196 remote: adding file changes
197 remote: added 1 changesets with 1 changes to 1 files
197 remote: added 1 changesets with 1 changes to 1 files
198 $ cd ../remote
198 $ cd ../remote
199
199
200 check remote tip
200 check remote tip
201
201
202 $ hg tip
202 $ hg tip
203 changeset: 3:a28a9d1a809c
203 changeset: 3:a28a9d1a809c
204 tag: tip
204 tag: tip
205 parent: 0:1160648e36ce
205 parent: 0:1160648e36ce
206 user: test
206 user: test
207 date: Thu Jan 01 00:00:00 1970 +0000
207 date: Thu Jan 01 00:00:00 1970 +0000
208 summary: add
208 summary: add
209
209
210 $ hg verify
210 $ hg verify
211 checking changesets
211 checking changesets
212 checking manifests
212 checking manifests
213 crosschecking files in changesets and manifests
213 crosschecking files in changesets and manifests
214 checking files
214 checking files
215 2 files, 4 changesets, 3 total revisions
215 2 files, 4 changesets, 3 total revisions
216 $ hg cat -r tip foo
216 $ hg cat -r tip foo
217 bleah
217 bleah
218 $ echo z > z
218 $ echo z > z
219 $ hg ci -A -m z z
219 $ hg ci -A -m z z
220 created new head
220 created new head
221
221
222 test pushkeys and bookmarks
222 test pushkeys and bookmarks
223
223
224 $ cd ../local
224 $ cd ../local
225 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
225 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
226 bookmarks
226 bookmarks
227 namespaces
227 namespaces
228 phases
228 phases
229 $ hg book foo -r 0
229 $ hg book foo -r 0
230 $ hg out -B
230 $ hg out -B
231 comparing with ssh://user@dummy/remote
231 comparing with ssh://user@dummy/remote
232 searching for changed bookmarks
232 searching for changed bookmarks
233 foo 1160648e36ce
233 foo 1160648e36ce
234 $ hg push -B foo
234 $ hg push -B foo
235 pushing to ssh://user@dummy/remote
235 pushing to ssh://user@dummy/remote
236 searching for changes
236 searching for changes
237 no changes found
237 no changes found
238 exporting bookmark foo
238 exporting bookmark foo
239 [1]
239 [1]
240 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
240 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
241 foo 1160648e36cec0054048a7edc4110c6f84fde594
241 foo 1160648e36cec0054048a7edc4110c6f84fde594
242 $ hg book -f foo
242 $ hg book -f foo
243 $ hg push --traceback
243 $ hg push --traceback
244 pushing to ssh://user@dummy/remote
244 pushing to ssh://user@dummy/remote
245 searching for changes
245 searching for changes
246 no changes found
246 no changes found
247 updating bookmark foo
247 updating bookmark foo
248 [1]
248 [1]
249 $ hg book -d foo
249 $ hg book -d foo
250 $ hg in -B
250 $ hg in -B
251 comparing with ssh://user@dummy/remote
251 comparing with ssh://user@dummy/remote
252 searching for changed bookmarks
252 searching for changed bookmarks
253 foo a28a9d1a809c
253 foo a28a9d1a809c
254 $ hg book -f -r 0 foo
254 $ hg book -f -r 0 foo
255 $ hg pull -B foo
255 $ hg pull -B foo
256 pulling from ssh://user@dummy/remote
256 pulling from ssh://user@dummy/remote
257 no changes found
257 no changes found
258 updating bookmark foo
258 updating bookmark foo
259 $ hg book -d foo
259 $ hg book -d foo
260 $ hg push -B foo
260 $ hg push -B foo
261 pushing to ssh://user@dummy/remote
261 pushing to ssh://user@dummy/remote
262 searching for changes
262 searching for changes
263 no changes found
263 no changes found
264 deleting remote bookmark foo
264 deleting remote bookmark foo
265 [1]
265 [1]
266
266
267 a bad, evil hook that prints to stdout
267 a bad, evil hook that prints to stdout
268
268
269 $ cat <<EOF > $TESTTMP/badhook
269 $ cat <<EOF > $TESTTMP/badhook
270 > import sys
270 > import sys
271 > sys.stdout.write("KABOOM\n")
271 > sys.stdout.write("KABOOM\n")
272 > EOF
272 > EOF
273
273
274 $ echo '[hooks]' >> ../remote/.hg/hgrc
274 $ echo '[hooks]' >> ../remote/.hg/hgrc
275 $ echo "changegroup.stdout = python $TESTTMP/badhook" >> ../remote/.hg/hgrc
275 $ echo "changegroup.stdout = python $TESTTMP/badhook" >> ../remote/.hg/hgrc
276 $ echo r > r
276 $ echo r > r
277 $ hg ci -A -m z r
277 $ hg ci -A -m z r
278
278
279 push should succeed even though it has an unexpected response
279 push should succeed even though it has an unexpected response
280
280
281 $ hg push
281 $ hg push
282 pushing to ssh://user@dummy/remote
282 pushing to ssh://user@dummy/remote
283 searching for changes
283 searching for changes
284 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
284 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
285 remote: adding changesets
285 remote: adding changesets
286 remote: adding manifests
286 remote: adding manifests
287 remote: adding file changes
287 remote: adding file changes
288 remote: added 1 changesets with 1 changes to 1 files
288 remote: added 1 changesets with 1 changes to 1 files
289 remote: KABOOM
289 remote: KABOOM
290 $ hg -R ../remote heads
290 $ hg -R ../remote heads
291 changeset: 5:1383141674ec
291 changeset: 5:1383141674ec
292 tag: tip
292 tag: tip
293 parent: 3:a28a9d1a809c
293 parent: 3:a28a9d1a809c
294 user: test
294 user: test
295 date: Thu Jan 01 00:00:00 1970 +0000
295 date: Thu Jan 01 00:00:00 1970 +0000
296 summary: z
296 summary: z
297
297
298 changeset: 4:6c0482d977a3
298 changeset: 4:6c0482d977a3
299 parent: 0:1160648e36ce
299 parent: 0:1160648e36ce
300 user: test
300 user: test
301 date: Thu Jan 01 00:00:00 1970 +0000
301 date: Thu Jan 01 00:00:00 1970 +0000
302 summary: z
302 summary: z
303
303
304
304
305 clone bookmarks
305 clone bookmarks
306
306
307 $ hg -R ../remote bookmark test
307 $ hg -R ../remote bookmark test
308 $ hg -R ../remote bookmarks
308 $ hg -R ../remote bookmarks
309 * test 4:6c0482d977a3
309 * test 4:6c0482d977a3
310 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
310 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
311 requesting all changes
311 requesting all changes
312 adding changesets
312 adding changesets
313 adding manifests
313 adding manifests
314 adding file changes
314 adding file changes
315 added 6 changesets with 5 changes to 4 files (+1 heads)
315 added 6 changesets with 5 changes to 4 files (+1 heads)
316 updating to branch default
316 updating to branch default
317 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
317 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
318 $ hg -R local-bookmarks bookmarks
318 $ hg -R local-bookmarks bookmarks
319 test 4:6c0482d977a3
319 test 4:6c0482d977a3
320
320
321 passwords in ssh urls are not supported
321 passwords in ssh urls are not supported
322 (we use a glob here because different Python versions give different
322 (we use a glob here because different Python versions give different
323 results here)
323 results here)
324
324
325 $ hg push ssh://user:erroneouspwd@dummy/remote
325 $ hg push ssh://user:erroneouspwd@dummy/remote
326 pushing to ssh://user:*@dummy/remote (glob)
326 pushing to ssh://user:*@dummy/remote (glob)
327 abort: password in URL not supported!
327 abort: password in URL not supported!
328 [255]
328 [255]
329
329
330 $ cd ..
330 $ cd ..
331
331
332 hide outer repo
332 hide outer repo
333 $ hg init
333 $ hg init
334
334
335 Test remote paths with spaces (issue2983):
335 Test remote paths with spaces (issue2983):
336
336
337 $ hg init --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
337 $ hg init --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
338 $ touch "$TESTTMP/a repo/test"
338 $ touch "$TESTTMP/a repo/test"
339 $ hg -R 'a repo' commit -A -m "test"
339 $ hg -R 'a repo' commit -A -m "test"
340 adding test
340 adding test
341 $ hg -R 'a repo' tag tag
341 $ hg -R 'a repo' tag tag
342 $ hg id --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
342 $ hg id --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
343 73649e48688a
343 73649e48688a
344
344
345 $ hg id --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo#noNoNO"
345 $ hg id --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo#noNoNO"
346 abort: unknown revision 'noNoNO'!
346 abort: unknown revision 'noNoNO'!
347 [255]
347 [255]
348
348
349 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
349 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
350
350
351 $ hg clone --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
351 $ hg clone --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
352 destination directory: a repo
352 destination directory: a repo
353 abort: destination 'a repo' is not empty
353 abort: destination 'a repo' is not empty
354 [255]
354 [255]
355
355
356 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
356 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
357 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
357 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
358 parameters:
358 parameters:
359
359
360 $ cat > ssh.sh << EOF
360 $ cat > ssh.sh << EOF
361 > userhost="\$1"
361 > userhost="\$1"
362 > SSH_ORIGINAL_COMMAND="\$2"
362 > SSH_ORIGINAL_COMMAND="\$2"
363 > export SSH_ORIGINAL_COMMAND
363 > export SSH_ORIGINAL_COMMAND
364 > PYTHONPATH="$PYTHONPATH"
364 > PYTHONPATH="$PYTHONPATH"
365 > export PYTHONPATH
365 > export PYTHONPATH
366 > python "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
366 > python "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
367 > EOF
367 > EOF
368
368
369 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
369 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
370 73649e48688a
370 73649e48688a
371
371
372 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
372 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
373 remote: Illegal repository "$TESTTMP/a'repo" (glob)
373 remote: Illegal repository "$TESTTMP/a'repo" (glob)
374 abort: no suitable response from remote hg!
374 abort: no suitable response from remote hg!
375 [255]
375 [255]
376
376
377 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
377 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
378 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
378 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
379 abort: no suitable response from remote hg!
379 abort: no suitable response from remote hg!
380 [255]
380 [255]
381
381
382 $ SSH_ORIGINAL_COMMAND="'hg' serve -R 'a'repo' --stdio" python "$TESTDIR/../contrib/hg-ssh"
382 $ SSH_ORIGINAL_COMMAND="'hg' serve -R 'a'repo' --stdio" python "$TESTDIR/../contrib/hg-ssh"
383 Illegal command "'hg' serve -R 'a'repo' --stdio": No closing quotation
383 Illegal command "'hg' serve -R 'a'repo' --stdio": No closing quotation
384 [255]
384 [255]
385
385
386 Test hg-ssh in read-only mode:
386 Test hg-ssh in read-only mode:
387
387
388 $ cat > ssh.sh << EOF
388 $ cat > ssh.sh << EOF
389 > userhost="\$1"
389 > userhost="\$1"
390 > SSH_ORIGINAL_COMMAND="\$2"
390 > SSH_ORIGINAL_COMMAND="\$2"
391 > export SSH_ORIGINAL_COMMAND
391 > export SSH_ORIGINAL_COMMAND
392 > PYTHONPATH="$PYTHONPATH"
392 > PYTHONPATH="$PYTHONPATH"
393 > export PYTHONPATH
393 > export PYTHONPATH
394 > python "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
394 > python "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
395 > EOF
395 > EOF
396
396
397 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
397 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
398 requesting all changes
398 requesting all changes
399 adding changesets
399 adding changesets
400 adding manifests
400 adding manifests
401 adding file changes
401 adding file changes
402 added 6 changesets with 5 changes to 4 files (+1 heads)
402 added 6 changesets with 5 changes to 4 files (+1 heads)
403 updating to branch default
403 updating to branch default
404 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
404 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
405
405
406 $ cd read-only-local
406 $ cd read-only-local
407 $ echo "baz" > bar
407 $ echo "baz" > bar
408 $ hg ci -A -m "unpushable commit" bar
408 $ hg ci -A -m "unpushable commit" bar
409 $ hg push --ssh "sh ../ssh.sh"
409 $ hg push --ssh "sh ../ssh.sh"
410 pushing to ssh://user@dummy/*/remote (glob)
410 pushing to ssh://user@dummy/*/remote (glob)
411 searching for changes
411 searching for changes
412 remote: Permission denied
412 remote: Permission denied
413 remote: abort: pretxnopen.hg-ssh hook failed
413 remote: abort: pretxnopen.hg-ssh hook failed
414 remote: Permission denied
414 remote: Permission denied
415 remote: pushkey-abort: prepushkey.hg-ssh hook failed
415 remote: pushkey-abort: prepushkey.hg-ssh hook failed
416 updating 6c0482d977a3 to public failed!
416 updating 6c0482d977a3 to public failed!
417 [1]
417 [1]
418
418
419 $ cd ..
419 $ cd ..
420
420
421 stderr from remote commands should be printed before stdout from local code (issue4336)
421 stderr from remote commands should be printed before stdout from local code (issue4336)
422
422
423 $ hg clone remote stderr-ordering
423 $ hg clone remote stderr-ordering
424 updating to branch default
424 updating to branch default
425 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
425 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
426 $ cd stderr-ordering
426 $ cd stderr-ordering
427 $ cat >> localwrite.py << EOF
427 $ cat >> localwrite.py << EOF
428 > from mercurial import exchange, extensions
428 > from mercurial import exchange, extensions
429 >
429 >
430 > def wrappedpush(orig, repo, *args, **kwargs):
430 > def wrappedpush(orig, repo, *args, **kwargs):
431 > res = orig(repo, *args, **kwargs)
431 > res = orig(repo, *args, **kwargs)
432 > repo.ui.write('local stdout\n')
432 > repo.ui.write('local stdout\n')
433 > return res
433 > return res
434 >
434 >
435 > def extsetup(ui):
435 > def extsetup(ui):
436 > extensions.wrapfunction(exchange, 'push', wrappedpush)
436 > extensions.wrapfunction(exchange, 'push', wrappedpush)
437 > EOF
437 > EOF
438
438
439 $ cat >> .hg/hgrc << EOF
439 $ cat >> .hg/hgrc << EOF
440 > [paths]
440 > [paths]
441 > default-push = ssh://user@dummy/remote
441 > default-push = ssh://user@dummy/remote
442 > [ui]
442 > [ui]
443 > ssh = python "$TESTDIR/dummyssh"
443 > ssh = python "$TESTDIR/dummyssh"
444 > [extensions]
444 > [extensions]
445 > localwrite = localwrite.py
445 > localwrite = localwrite.py
446 > EOF
446 > EOF
447
447
448 $ echo localwrite > foo
448 $ echo localwrite > foo
449 $ hg commit -m 'testing localwrite'
449 $ hg commit -m 'testing localwrite'
450 $ hg push
450 $ hg push
451 pushing to ssh://user@dummy/remote
451 pushing to ssh://user@dummy/remote
452 searching for changes
452 searching for changes
453 remote: adding changesets
453 remote: adding changesets
454 remote: adding manifests
454 remote: adding manifests
455 remote: adding file changes
455 remote: adding file changes
456 remote: added 1 changesets with 1 changes to 1 files
456 remote: added 1 changesets with 1 changes to 1 files
457 remote: KABOOM
457 remote: KABOOM
458 local stdout
458 local stdout
459
459
460 debug output
460 debug output
461
461
462 $ hg pull --debug ssh://user@dummy/remote
462 $ hg pull --debug ssh://user@dummy/remote
463 pulling from ssh://user@dummy/remote
463 pulling from ssh://user@dummy/remote
464 running python ".*/dummyssh" user@dummy ('|")hg -R remote serve --stdio('|") (re)
464 running python ".*/dummyssh" ['"]user@dummy['"] ('|")hg -R remote serve --stdio('|") (re)
465 sending hello command
465 sending hello command
466 sending between command
466 sending between command
467 remote: 355
467 remote: 355
468 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN
468 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN
469 remote: 1
469 remote: 1
470 preparing listkeys for "bookmarks"
470 preparing listkeys for "bookmarks"
471 sending listkeys command
471 sending listkeys command
472 received listkey for "bookmarks": 45 bytes
472 received listkey for "bookmarks": 45 bytes
473 query 1; heads
473 query 1; heads
474 sending batch command
474 sending batch command
475 searching for changes
475 searching for changes
476 all remote heads known locally
476 all remote heads known locally
477 no changes found
477 no changes found
478 preparing listkeys for "phases"
478 preparing listkeys for "phases"
479 sending listkeys command
479 sending listkeys command
480 received listkey for "phases": 15 bytes
480 received listkey for "phases": 15 bytes
481 checking for updated bookmarks
481 checking for updated bookmarks
482
482
483 $ cd ..
483 $ cd ..
484
484
485 $ cat dummylog
485 $ cat dummylog
486 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
486 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
487 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
487 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
488 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
488 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
489 Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio
489 Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio
490 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
490 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
491 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
491 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
492 Got arguments 1:user@dummy 2:hg -R doesnotexist serve --stdio
492 Got arguments 1:user@dummy 2:hg -R doesnotexist serve --stdio
493 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
493 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
494 Got arguments 1:user@dummy 2:hg -R local serve --stdio
494 Got arguments 1:user@dummy 2:hg -R local serve --stdio
495 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
495 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
496 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
496 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
497 changegroup-in-remote hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_NODE_LAST=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
497 changegroup-in-remote hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_NODE_LAST=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
498 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
498 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
499 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
499 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
500 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
500 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
501 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
501 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
502 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
502 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
503 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
503 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
504 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
504 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
505 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
505 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
506 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
506 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
507 changegroup-in-remote hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_NODE_LAST=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
507 changegroup-in-remote hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_NODE_LAST=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
508 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
508 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
509 Got arguments 1:user@dummy 2:hg init 'a repo'
509 Got arguments 1:user@dummy 2:hg init 'a repo'
510 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
510 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
511 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
511 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
512 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
512 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
513 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
513 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
514 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
514 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
515 changegroup-in-remote hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_NODE_LAST=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
515 changegroup-in-remote hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_NODE_LAST=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
516 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
516 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
517
517
518 remote hook failure is attributed to remote
518 remote hook failure is attributed to remote
519
519
520 $ cat > $TESTTMP/failhook << EOF
520 $ cat > $TESTTMP/failhook << EOF
521 > def hook(ui, repo, **kwargs):
521 > def hook(ui, repo, **kwargs):
522 > ui.write('hook failure!\n')
522 > ui.write('hook failure!\n')
523 > ui.flush()
523 > ui.flush()
524 > return 1
524 > return 1
525 > EOF
525 > EOF
526
526
527 $ echo "pretxnchangegroup.fail = python:$TESTTMP/failhook:hook" >> remote/.hg/hgrc
527 $ echo "pretxnchangegroup.fail = python:$TESTTMP/failhook:hook" >> remote/.hg/hgrc
528
528
529 $ hg -q --config ui.ssh="python $TESTDIR/dummyssh" clone ssh://user@dummy/remote hookout
529 $ hg -q --config ui.ssh="python $TESTDIR/dummyssh" clone ssh://user@dummy/remote hookout
530 $ cd hookout
530 $ cd hookout
531 $ touch hookfailure
531 $ touch hookfailure
532 $ hg -q commit -A -m 'remote hook failure'
532 $ hg -q commit -A -m 'remote hook failure'
533 $ hg --config ui.ssh="python $TESTDIR/dummyssh" push
533 $ hg --config ui.ssh="python $TESTDIR/dummyssh" push
534 pushing to ssh://user@dummy/remote
534 pushing to ssh://user@dummy/remote
535 searching for changes
535 searching for changes
536 remote: adding changesets
536 remote: adding changesets
537 remote: adding manifests
537 remote: adding manifests
538 remote: adding file changes
538 remote: adding file changes
539 remote: added 1 changesets with 1 changes to 1 files
539 remote: added 1 changesets with 1 changes to 1 files
540 remote: hook failure!
540 remote: hook failure!
541 remote: transaction abort!
541 remote: transaction abort!
542 remote: rollback completed
542 remote: rollback completed
543 remote: abort: pretxnchangegroup.fail hook failed
543 remote: abort: pretxnchangegroup.fail hook failed
544 [1]
544 [1]
545
545
546 abort during pull is properly reported as such
546 abort during pull is properly reported as such
547
547
548 $ echo morefoo >> ../remote/foo
548 $ echo morefoo >> ../remote/foo
549 $ hg -R ../remote commit --message "more foo to be pulled"
549 $ hg -R ../remote commit --message "more foo to be pulled"
550 $ cat >> ../remote/.hg/hgrc << EOF
550 $ cat >> ../remote/.hg/hgrc << EOF
551 > [extensions]
551 > [extensions]
552 > crash = ${TESTDIR}/crashgetbundler.py
552 > crash = ${TESTDIR}/crashgetbundler.py
553 > EOF
553 > EOF
554 $ hg --config ui.ssh="python $TESTDIR/dummyssh" pull
554 $ hg --config ui.ssh="python $TESTDIR/dummyssh" pull
555 pulling from ssh://user@dummy/remote
555 pulling from ssh://user@dummy/remote
556 searching for changes
556 searching for changes
557 adding changesets
557 adding changesets
558 remote: abort: this is an exercise
558 remote: abort: this is an exercise
559 transaction abort!
559 transaction abort!
560 rollback completed
560 rollback completed
561 abort: stream ended unexpectedly (got 0 bytes, expected 4)
561 abort: stream ended unexpectedly (got 0 bytes, expected 4)
562 [255]
562 [255]
@@ -1,577 +1,577
1
1
2 This test tries to exercise the ssh functionality with a dummy script
2 This test tries to exercise the ssh functionality with a dummy script
3
3
4 $ cat <<EOF >> $HGRCPATH
4 $ cat <<EOF >> $HGRCPATH
5 > [format]
5 > [format]
6 > usegeneraldelta=yes
6 > usegeneraldelta=yes
7 > EOF
7 > EOF
8
8
9 creating 'remote' repo
9 creating 'remote' repo
10
10
11 $ hg init remote
11 $ hg init remote
12 $ cd remote
12 $ cd remote
13 $ echo this > foo
13 $ echo this > foo
14 $ echo this > fooO
14 $ echo this > fooO
15 $ hg ci -A -m "init" foo fooO
15 $ hg ci -A -m "init" foo fooO
16
16
17 insert a closed branch (issue4428)
17 insert a closed branch (issue4428)
18
18
19 $ hg up null
19 $ hg up null
20 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
20 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
21 $ hg branch closed
21 $ hg branch closed
22 marked working directory as branch closed
22 marked working directory as branch closed
23 (branches are permanent and global, did you want a bookmark?)
23 (branches are permanent and global, did you want a bookmark?)
24 $ hg ci -mc0
24 $ hg ci -mc0
25 $ hg ci --close-branch -mc1
25 $ hg ci --close-branch -mc1
26 $ hg up -q default
26 $ hg up -q default
27
27
28 configure for serving
28 configure for serving
29
29
30 $ cat <<EOF > .hg/hgrc
30 $ cat <<EOF > .hg/hgrc
31 > [server]
31 > [server]
32 > uncompressed = True
32 > uncompressed = True
33 >
33 >
34 > [hooks]
34 > [hooks]
35 > changegroup = sh -c "printenv.py changegroup-in-remote 0 ../dummylog"
35 > changegroup = sh -c "printenv.py changegroup-in-remote 0 ../dummylog"
36 > EOF
36 > EOF
37 $ cd ..
37 $ cd ..
38
38
39 repo not found error
39 repo not found error
40
40
41 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
41 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
42 remote: abort: repository nonexistent not found!
42 remote: abort: repository nonexistent not found!
43 abort: no suitable response from remote hg!
43 abort: no suitable response from remote hg!
44 [255]
44 [255]
45
45
46 non-existent absolute path
46 non-existent absolute path
47
47
48 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/`pwd`/nonexistent local
48 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/`pwd`/nonexistent local
49 remote: abort: repository $TESTTMP/nonexistent not found!
49 remote: abort: repository $TESTTMP/nonexistent not found!
50 abort: no suitable response from remote hg!
50 abort: no suitable response from remote hg!
51 [255]
51 [255]
52
52
53 clone remote via stream
53 clone remote via stream
54
54
55 $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/remote local-stream
55 $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/remote local-stream
56 streaming all changes
56 streaming all changes
57 4 files to transfer, 602 bytes of data
57 4 files to transfer, 602 bytes of data
58 transferred 602 bytes in * seconds (*) (glob)
58 transferred 602 bytes in * seconds (*) (glob)
59 searching for changes
59 searching for changes
60 no changes found
60 no changes found
61 updating to branch default
61 updating to branch default
62 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
62 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
63 $ cd local-stream
63 $ cd local-stream
64 $ hg verify
64 $ hg verify
65 checking changesets
65 checking changesets
66 checking manifests
66 checking manifests
67 crosschecking files in changesets and manifests
67 crosschecking files in changesets and manifests
68 checking files
68 checking files
69 2 files, 3 changesets, 2 total revisions
69 2 files, 3 changesets, 2 total revisions
70 $ hg branches
70 $ hg branches
71 default 0:1160648e36ce
71 default 0:1160648e36ce
72 $ cd ..
72 $ cd ..
73
73
74 clone bookmarks via stream
74 clone bookmarks via stream
75
75
76 $ hg -R local-stream book mybook
76 $ hg -R local-stream book mybook
77 $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/local-stream stream2
77 $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/local-stream stream2
78 streaming all changes
78 streaming all changes
79 4 files to transfer, 602 bytes of data
79 4 files to transfer, 602 bytes of data
80 transferred 602 bytes in * seconds (*) (glob)
80 transferred 602 bytes in * seconds (*) (glob)
81 searching for changes
81 searching for changes
82 no changes found
82 no changes found
83 updating to branch default
83 updating to branch default
84 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
85 $ cd stream2
85 $ cd stream2
86 $ hg book
86 $ hg book
87 mybook 0:1160648e36ce
87 mybook 0:1160648e36ce
88 $ cd ..
88 $ cd ..
89 $ rm -rf local-stream stream2
89 $ rm -rf local-stream stream2
90
90
91 clone remote via pull
91 clone remote via pull
92
92
93 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
93 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
94 requesting all changes
94 requesting all changes
95 adding changesets
95 adding changesets
96 adding manifests
96 adding manifests
97 adding file changes
97 adding file changes
98 added 3 changesets with 2 changes to 2 files
98 added 3 changesets with 2 changes to 2 files
99 updating to branch default
99 updating to branch default
100 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
101
101
102 verify
102 verify
103
103
104 $ cd local
104 $ cd local
105 $ hg verify
105 $ hg verify
106 checking changesets
106 checking changesets
107 checking manifests
107 checking manifests
108 crosschecking files in changesets and manifests
108 crosschecking files in changesets and manifests
109 checking files
109 checking files
110 2 files, 3 changesets, 2 total revisions
110 2 files, 3 changesets, 2 total revisions
111 $ cat >> .hg/hgrc <<EOF
111 $ cat >> .hg/hgrc <<EOF
112 > [hooks]
112 > [hooks]
113 > changegroup = sh -c "printenv.py changegroup-in-local 0 ../dummylog"
113 > changegroup = sh -c "printenv.py changegroup-in-local 0 ../dummylog"
114 > EOF
114 > EOF
115
115
116 empty default pull
116 empty default pull
117
117
118 $ hg paths
118 $ hg paths
119 default = ssh://user@dummy/remote
119 default = ssh://user@dummy/remote
120 $ hg pull -e "python \"$TESTDIR/dummyssh\""
120 $ hg pull -e "python \"$TESTDIR/dummyssh\""
121 pulling from ssh://user@dummy/remote
121 pulling from ssh://user@dummy/remote
122 searching for changes
122 searching for changes
123 no changes found
123 no changes found
124
124
125 pull from wrong ssh URL
125 pull from wrong ssh URL
126
126
127 $ hg pull -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist
127 $ hg pull -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist
128 pulling from ssh://user@dummy/doesnotexist
128 pulling from ssh://user@dummy/doesnotexist
129 remote: abort: repository doesnotexist not found!
129 remote: abort: repository doesnotexist not found!
130 abort: no suitable response from remote hg!
130 abort: no suitable response from remote hg!
131 [255]
131 [255]
132
132
133 local change
133 local change
134
134
135 $ echo bleah > foo
135 $ echo bleah > foo
136 $ hg ci -m "add"
136 $ hg ci -m "add"
137
137
138 updating rc
138 updating rc
139
139
140 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
140 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
141 $ echo "[ui]" >> .hg/hgrc
141 $ echo "[ui]" >> .hg/hgrc
142 $ echo "ssh = python \"$TESTDIR/dummyssh\"" >> .hg/hgrc
142 $ echo "ssh = python \"$TESTDIR/dummyssh\"" >> .hg/hgrc
143
143
144 find outgoing
144 find outgoing
145
145
146 $ hg out ssh://user@dummy/remote
146 $ hg out ssh://user@dummy/remote
147 comparing with ssh://user@dummy/remote
147 comparing with ssh://user@dummy/remote
148 searching for changes
148 searching for changes
149 changeset: 3:a28a9d1a809c
149 changeset: 3:a28a9d1a809c
150 tag: tip
150 tag: tip
151 parent: 0:1160648e36ce
151 parent: 0:1160648e36ce
152 user: test
152 user: test
153 date: Thu Jan 01 00:00:00 1970 +0000
153 date: Thu Jan 01 00:00:00 1970 +0000
154 summary: add
154 summary: add
155
155
156
156
157 find incoming on the remote side
157 find incoming on the remote side
158
158
159 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
159 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
160 comparing with ssh://user@dummy/local
160 comparing with ssh://user@dummy/local
161 searching for changes
161 searching for changes
162 changeset: 3:a28a9d1a809c
162 changeset: 3:a28a9d1a809c
163 tag: tip
163 tag: tip
164 parent: 0:1160648e36ce
164 parent: 0:1160648e36ce
165 user: test
165 user: test
166 date: Thu Jan 01 00:00:00 1970 +0000
166 date: Thu Jan 01 00:00:00 1970 +0000
167 summary: add
167 summary: add
168
168
169
169
170 find incoming on the remote side (using absolute path)
170 find incoming on the remote side (using absolute path)
171
171
172 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
172 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
173 comparing with ssh://user@dummy/$TESTTMP/local
173 comparing with ssh://user@dummy/$TESTTMP/local
174 searching for changes
174 searching for changes
175 changeset: 3:a28a9d1a809c
175 changeset: 3:a28a9d1a809c
176 tag: tip
176 tag: tip
177 parent: 0:1160648e36ce
177 parent: 0:1160648e36ce
178 user: test
178 user: test
179 date: Thu Jan 01 00:00:00 1970 +0000
179 date: Thu Jan 01 00:00:00 1970 +0000
180 summary: add
180 summary: add
181
181
182
182
183 push
183 push
184
184
185 $ hg push
185 $ hg push
186 pushing to ssh://user@dummy/remote
186 pushing to ssh://user@dummy/remote
187 searching for changes
187 searching for changes
188 remote: adding changesets
188 remote: adding changesets
189 remote: adding manifests
189 remote: adding manifests
190 remote: adding file changes
190 remote: adding file changes
191 remote: added 1 changesets with 1 changes to 1 files
191 remote: added 1 changesets with 1 changes to 1 files
192 $ cd ../remote
192 $ cd ../remote
193
193
194 check remote tip
194 check remote tip
195
195
196 $ hg tip
196 $ hg tip
197 changeset: 3:a28a9d1a809c
197 changeset: 3:a28a9d1a809c
198 tag: tip
198 tag: tip
199 parent: 0:1160648e36ce
199 parent: 0:1160648e36ce
200 user: test
200 user: test
201 date: Thu Jan 01 00:00:00 1970 +0000
201 date: Thu Jan 01 00:00:00 1970 +0000
202 summary: add
202 summary: add
203
203
204 $ hg verify
204 $ hg verify
205 checking changesets
205 checking changesets
206 checking manifests
206 checking manifests
207 crosschecking files in changesets and manifests
207 crosschecking files in changesets and manifests
208 checking files
208 checking files
209 2 files, 4 changesets, 3 total revisions
209 2 files, 4 changesets, 3 total revisions
210 $ hg cat -r tip foo
210 $ hg cat -r tip foo
211 bleah
211 bleah
212 $ echo z > z
212 $ echo z > z
213 $ hg ci -A -m z z
213 $ hg ci -A -m z z
214 created new head
214 created new head
215
215
216 test pushkeys and bookmarks
216 test pushkeys and bookmarks
217
217
218 $ cd ../local
218 $ cd ../local
219 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
219 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
220 bookmarks
220 bookmarks
221 namespaces
221 namespaces
222 phases
222 phases
223 $ hg book foo -r 0
223 $ hg book foo -r 0
224 $ hg out -B
224 $ hg out -B
225 comparing with ssh://user@dummy/remote
225 comparing with ssh://user@dummy/remote
226 searching for changed bookmarks
226 searching for changed bookmarks
227 foo 1160648e36ce
227 foo 1160648e36ce
228 $ hg push -B foo
228 $ hg push -B foo
229 pushing to ssh://user@dummy/remote
229 pushing to ssh://user@dummy/remote
230 searching for changes
230 searching for changes
231 no changes found
231 no changes found
232 exporting bookmark foo
232 exporting bookmark foo
233 [1]
233 [1]
234 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
234 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
235 foo 1160648e36cec0054048a7edc4110c6f84fde594
235 foo 1160648e36cec0054048a7edc4110c6f84fde594
236 $ hg book -f foo
236 $ hg book -f foo
237 $ hg push --traceback
237 $ hg push --traceback
238 pushing to ssh://user@dummy/remote
238 pushing to ssh://user@dummy/remote
239 searching for changes
239 searching for changes
240 no changes found
240 no changes found
241 updating bookmark foo
241 updating bookmark foo
242 [1]
242 [1]
243 $ hg book -d foo
243 $ hg book -d foo
244 $ hg in -B
244 $ hg in -B
245 comparing with ssh://user@dummy/remote
245 comparing with ssh://user@dummy/remote
246 searching for changed bookmarks
246 searching for changed bookmarks
247 foo a28a9d1a809c
247 foo a28a9d1a809c
248 $ hg book -f -r 0 foo
248 $ hg book -f -r 0 foo
249 $ hg pull -B foo
249 $ hg pull -B foo
250 pulling from ssh://user@dummy/remote
250 pulling from ssh://user@dummy/remote
251 no changes found
251 no changes found
252 updating bookmark foo
252 updating bookmark foo
253 $ hg book -d foo
253 $ hg book -d foo
254 $ hg push -B foo
254 $ hg push -B foo
255 pushing to ssh://user@dummy/remote
255 pushing to ssh://user@dummy/remote
256 searching for changes
256 searching for changes
257 no changes found
257 no changes found
258 deleting remote bookmark foo
258 deleting remote bookmark foo
259 [1]
259 [1]
260
260
261 a bad, evil hook that prints to stdout
261 a bad, evil hook that prints to stdout
262
262
263 $ cat <<EOF > $TESTTMP/badhook
263 $ cat <<EOF > $TESTTMP/badhook
264 > import sys
264 > import sys
265 > sys.stdout.write("KABOOM\n")
265 > sys.stdout.write("KABOOM\n")
266 > EOF
266 > EOF
267
267
268 $ cat <<EOF > $TESTTMP/badpyhook.py
268 $ cat <<EOF > $TESTTMP/badpyhook.py
269 > import sys
269 > import sys
270 > def hook(ui, repo, hooktype, **kwargs):
270 > def hook(ui, repo, hooktype, **kwargs):
271 > sys.stdout.write("KABOOM IN PROCESS\n")
271 > sys.stdout.write("KABOOM IN PROCESS\n")
272 > EOF
272 > EOF
273
273
274 $ cat <<EOF >> ../remote/.hg/hgrc
274 $ cat <<EOF >> ../remote/.hg/hgrc
275 > [hooks]
275 > [hooks]
276 > changegroup.stdout = python $TESTTMP/badhook
276 > changegroup.stdout = python $TESTTMP/badhook
277 > changegroup.pystdout = python:$TESTTMP/badpyhook.py:hook
277 > changegroup.pystdout = python:$TESTTMP/badpyhook.py:hook
278 > EOF
278 > EOF
279 $ echo r > r
279 $ echo r > r
280 $ hg ci -A -m z r
280 $ hg ci -A -m z r
281
281
282 push should succeed even though it has an unexpected response
282 push should succeed even though it has an unexpected response
283
283
284 $ hg push
284 $ hg push
285 pushing to ssh://user@dummy/remote
285 pushing to ssh://user@dummy/remote
286 searching for changes
286 searching for changes
287 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
287 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
288 remote: adding changesets
288 remote: adding changesets
289 remote: adding manifests
289 remote: adding manifests
290 remote: adding file changes
290 remote: adding file changes
291 remote: added 1 changesets with 1 changes to 1 files
291 remote: added 1 changesets with 1 changes to 1 files
292 remote: KABOOM
292 remote: KABOOM
293 remote: KABOOM IN PROCESS
293 remote: KABOOM IN PROCESS
294 $ hg -R ../remote heads
294 $ hg -R ../remote heads
295 changeset: 5:1383141674ec
295 changeset: 5:1383141674ec
296 tag: tip
296 tag: tip
297 parent: 3:a28a9d1a809c
297 parent: 3:a28a9d1a809c
298 user: test
298 user: test
299 date: Thu Jan 01 00:00:00 1970 +0000
299 date: Thu Jan 01 00:00:00 1970 +0000
300 summary: z
300 summary: z
301
301
302 changeset: 4:6c0482d977a3
302 changeset: 4:6c0482d977a3
303 parent: 0:1160648e36ce
303 parent: 0:1160648e36ce
304 user: test
304 user: test
305 date: Thu Jan 01 00:00:00 1970 +0000
305 date: Thu Jan 01 00:00:00 1970 +0000
306 summary: z
306 summary: z
307
307
308
308
309 clone bookmarks
309 clone bookmarks
310
310
311 $ hg -R ../remote bookmark test
311 $ hg -R ../remote bookmark test
312 $ hg -R ../remote bookmarks
312 $ hg -R ../remote bookmarks
313 * test 4:6c0482d977a3
313 * test 4:6c0482d977a3
314 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
314 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
315 requesting all changes
315 requesting all changes
316 adding changesets
316 adding changesets
317 adding manifests
317 adding manifests
318 adding file changes
318 adding file changes
319 added 6 changesets with 5 changes to 4 files (+1 heads)
319 added 6 changesets with 5 changes to 4 files (+1 heads)
320 updating to branch default
320 updating to branch default
321 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
321 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
322 $ hg -R local-bookmarks bookmarks
322 $ hg -R local-bookmarks bookmarks
323 test 4:6c0482d977a3
323 test 4:6c0482d977a3
324
324
325 passwords in ssh urls are not supported
325 passwords in ssh urls are not supported
326 (we use a glob here because different Python versions give different
326 (we use a glob here because different Python versions give different
327 results here)
327 results here)
328
328
329 $ hg push ssh://user:erroneouspwd@dummy/remote
329 $ hg push ssh://user:erroneouspwd@dummy/remote
330 pushing to ssh://user:*@dummy/remote (glob)
330 pushing to ssh://user:*@dummy/remote (glob)
331 abort: password in URL not supported!
331 abort: password in URL not supported!
332 [255]
332 [255]
333
333
334 $ cd ..
334 $ cd ..
335
335
336 hide outer repo
336 hide outer repo
337 $ hg init
337 $ hg init
338
338
339 Test remote paths with spaces (issue2983):
339 Test remote paths with spaces (issue2983):
340
340
341 $ hg init --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
341 $ hg init --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
342 $ touch "$TESTTMP/a repo/test"
342 $ touch "$TESTTMP/a repo/test"
343 $ hg -R 'a repo' commit -A -m "test"
343 $ hg -R 'a repo' commit -A -m "test"
344 adding test
344 adding test
345 $ hg -R 'a repo' tag tag
345 $ hg -R 'a repo' tag tag
346 $ hg id --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
346 $ hg id --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
347 73649e48688a
347 73649e48688a
348
348
349 $ hg id --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo#noNoNO"
349 $ hg id --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo#noNoNO"
350 abort: unknown revision 'noNoNO'!
350 abort: unknown revision 'noNoNO'!
351 [255]
351 [255]
352
352
353 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
353 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
354
354
355 $ hg clone --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
355 $ hg clone --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
356 destination directory: a repo
356 destination directory: a repo
357 abort: destination 'a repo' is not empty
357 abort: destination 'a repo' is not empty
358 [255]
358 [255]
359
359
360 Make sure hg is really paranoid in serve --stdio mode. It used to be
360 Make sure hg is really paranoid in serve --stdio mode. It used to be
361 possible to get a debugger REPL by specifying a repo named --debugger.
361 possible to get a debugger REPL by specifying a repo named --debugger.
362 $ hg -R --debugger serve --stdio
362 $ hg -R --debugger serve --stdio
363 abort: potentially unsafe serve --stdio invocation: ['-R', '--debugger', 'serve', '--stdio']
363 abort: potentially unsafe serve --stdio invocation: ['-R', '--debugger', 'serve', '--stdio']
364 [255]
364 [255]
365 $ hg -R --config=ui.debugger=yes serve --stdio
365 $ hg -R --config=ui.debugger=yes serve --stdio
366 abort: potentially unsafe serve --stdio invocation: ['-R', '--config=ui.debugger=yes', 'serve', '--stdio']
366 abort: potentially unsafe serve --stdio invocation: ['-R', '--config=ui.debugger=yes', 'serve', '--stdio']
367 [255]
367 [255]
368 Abbreviations of 'serve' also don't work, to avoid shenanigans.
368 Abbreviations of 'serve' also don't work, to avoid shenanigans.
369 $ hg -R narf serv --stdio
369 $ hg -R narf serv --stdio
370 abort: potentially unsafe serve --stdio invocation: ['-R', 'narf', 'serv', '--stdio']
370 abort: potentially unsafe serve --stdio invocation: ['-R', 'narf', 'serv', '--stdio']
371 [255]
371 [255]
372
372
373 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
373 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
374 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
374 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
375 parameters:
375 parameters:
376
376
377 $ cat > ssh.sh << EOF
377 $ cat > ssh.sh << EOF
378 > userhost="\$1"
378 > userhost="\$1"
379 > SSH_ORIGINAL_COMMAND="\$2"
379 > SSH_ORIGINAL_COMMAND="\$2"
380 > export SSH_ORIGINAL_COMMAND
380 > export SSH_ORIGINAL_COMMAND
381 > PYTHONPATH="$PYTHONPATH"
381 > PYTHONPATH="$PYTHONPATH"
382 > export PYTHONPATH
382 > export PYTHONPATH
383 > python "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
383 > python "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
384 > EOF
384 > EOF
385
385
386 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
386 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
387 73649e48688a
387 73649e48688a
388
388
389 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
389 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
390 remote: Illegal repository "$TESTTMP/a'repo" (glob)
390 remote: Illegal repository "$TESTTMP/a'repo" (glob)
391 abort: no suitable response from remote hg!
391 abort: no suitable response from remote hg!
392 [255]
392 [255]
393
393
394 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
394 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
395 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
395 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
396 abort: no suitable response from remote hg!
396 abort: no suitable response from remote hg!
397 [255]
397 [255]
398
398
399 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a'repo' serve --stdio" python "$TESTDIR/../contrib/hg-ssh"
399 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a'repo' serve --stdio" python "$TESTDIR/../contrib/hg-ssh"
400 Illegal command "'hg' -R 'a'repo' serve --stdio": No closing quotation
400 Illegal command "'hg' -R 'a'repo' serve --stdio": No closing quotation
401 [255]
401 [255]
402
402
403 Test hg-ssh in read-only mode:
403 Test hg-ssh in read-only mode:
404
404
405 $ cat > ssh.sh << EOF
405 $ cat > ssh.sh << EOF
406 > userhost="\$1"
406 > userhost="\$1"
407 > SSH_ORIGINAL_COMMAND="\$2"
407 > SSH_ORIGINAL_COMMAND="\$2"
408 > export SSH_ORIGINAL_COMMAND
408 > export SSH_ORIGINAL_COMMAND
409 > PYTHONPATH="$PYTHONPATH"
409 > PYTHONPATH="$PYTHONPATH"
410 > export PYTHONPATH
410 > export PYTHONPATH
411 > python "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
411 > python "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
412 > EOF
412 > EOF
413
413
414 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
414 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
415 requesting all changes
415 requesting all changes
416 adding changesets
416 adding changesets
417 adding manifests
417 adding manifests
418 adding file changes
418 adding file changes
419 added 6 changesets with 5 changes to 4 files (+1 heads)
419 added 6 changesets with 5 changes to 4 files (+1 heads)
420 updating to branch default
420 updating to branch default
421 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
421 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
422
422
423 $ cd read-only-local
423 $ cd read-only-local
424 $ echo "baz" > bar
424 $ echo "baz" > bar
425 $ hg ci -A -m "unpushable commit" bar
425 $ hg ci -A -m "unpushable commit" bar
426 $ hg push --ssh "sh ../ssh.sh"
426 $ hg push --ssh "sh ../ssh.sh"
427 pushing to ssh://user@dummy/*/remote (glob)
427 pushing to ssh://user@dummy/*/remote (glob)
428 searching for changes
428 searching for changes
429 remote: Permission denied
429 remote: Permission denied
430 remote: pretxnopen.hg-ssh hook failed
430 remote: pretxnopen.hg-ssh hook failed
431 abort: push failed on remote
431 abort: push failed on remote
432 [255]
432 [255]
433
433
434 $ cd ..
434 $ cd ..
435
435
436 stderr from remote commands should be printed before stdout from local code (issue4336)
436 stderr from remote commands should be printed before stdout from local code (issue4336)
437
437
438 $ hg clone remote stderr-ordering
438 $ hg clone remote stderr-ordering
439 updating to branch default
439 updating to branch default
440 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
440 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
441 $ cd stderr-ordering
441 $ cd stderr-ordering
442 $ cat >> localwrite.py << EOF
442 $ cat >> localwrite.py << EOF
443 > from mercurial import exchange, extensions
443 > from mercurial import exchange, extensions
444 >
444 >
445 > def wrappedpush(orig, repo, *args, **kwargs):
445 > def wrappedpush(orig, repo, *args, **kwargs):
446 > res = orig(repo, *args, **kwargs)
446 > res = orig(repo, *args, **kwargs)
447 > repo.ui.write('local stdout\n')
447 > repo.ui.write('local stdout\n')
448 > return res
448 > return res
449 >
449 >
450 > def extsetup(ui):
450 > def extsetup(ui):
451 > extensions.wrapfunction(exchange, 'push', wrappedpush)
451 > extensions.wrapfunction(exchange, 'push', wrappedpush)
452 > EOF
452 > EOF
453
453
454 $ cat >> .hg/hgrc << EOF
454 $ cat >> .hg/hgrc << EOF
455 > [paths]
455 > [paths]
456 > default-push = ssh://user@dummy/remote
456 > default-push = ssh://user@dummy/remote
457 > [ui]
457 > [ui]
458 > ssh = python "$TESTDIR/dummyssh"
458 > ssh = python "$TESTDIR/dummyssh"
459 > [extensions]
459 > [extensions]
460 > localwrite = localwrite.py
460 > localwrite = localwrite.py
461 > EOF
461 > EOF
462
462
463 $ echo localwrite > foo
463 $ echo localwrite > foo
464 $ hg commit -m 'testing localwrite'
464 $ hg commit -m 'testing localwrite'
465 $ hg push
465 $ hg push
466 pushing to ssh://user@dummy/remote
466 pushing to ssh://user@dummy/remote
467 searching for changes
467 searching for changes
468 remote: adding changesets
468 remote: adding changesets
469 remote: adding manifests
469 remote: adding manifests
470 remote: adding file changes
470 remote: adding file changes
471 remote: added 1 changesets with 1 changes to 1 files
471 remote: added 1 changesets with 1 changes to 1 files
472 remote: KABOOM
472 remote: KABOOM
473 remote: KABOOM IN PROCESS
473 remote: KABOOM IN PROCESS
474 local stdout
474 local stdout
475
475
476 debug output
476 debug output
477
477
478 $ hg pull --debug ssh://user@dummy/remote
478 $ hg pull --debug ssh://user@dummy/remote
479 pulling from ssh://user@dummy/remote
479 pulling from ssh://user@dummy/remote
480 running python ".*/dummyssh" user@dummy ('|")hg -R remote serve --stdio('|") (re)
480 running python ".*/dummyssh" ['"]user@dummy['"] ('|")hg -R remote serve --stdio('|") (re)
481 sending hello command
481 sending hello command
482 sending between command
482 sending between command
483 remote: 355
483 remote: 355
484 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN
484 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN
485 remote: 1
485 remote: 1
486 query 1; heads
486 query 1; heads
487 sending batch command
487 sending batch command
488 searching for changes
488 searching for changes
489 all remote heads known locally
489 all remote heads known locally
490 no changes found
490 no changes found
491 sending getbundle command
491 sending getbundle command
492 bundle2-input-bundle: with-transaction
492 bundle2-input-bundle: with-transaction
493 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
493 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
494 bundle2-input-part: total payload size 15
494 bundle2-input-part: total payload size 15
495 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
495 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
496 bundle2-input-part: total payload size 45
496 bundle2-input-part: total payload size 45
497 bundle2-input-bundle: 1 parts total
497 bundle2-input-bundle: 1 parts total
498 checking for updated bookmarks
498 checking for updated bookmarks
499
499
500 $ cd ..
500 $ cd ..
501
501
502 $ cat dummylog
502 $ cat dummylog
503 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
503 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
504 Got arguments 1:user@dummy 2:hg -R $TESTTMP/nonexistent serve --stdio
504 Got arguments 1:user@dummy 2:hg -R $TESTTMP/nonexistent serve --stdio
505 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
505 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
506 Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio
506 Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio
507 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
507 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
508 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
508 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
509 Got arguments 1:user@dummy 2:hg -R doesnotexist serve --stdio
509 Got arguments 1:user@dummy 2:hg -R doesnotexist serve --stdio
510 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
510 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
511 Got arguments 1:user@dummy 2:hg -R local serve --stdio
511 Got arguments 1:user@dummy 2:hg -R local serve --stdio
512 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
512 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
513 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
513 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
514 changegroup-in-remote hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_NODE_LAST=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
514 changegroup-in-remote hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_NODE_LAST=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
515 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
515 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
516 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
516 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
517 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
517 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
518 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
518 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
519 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
519 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
520 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
520 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
521 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
521 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
522 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
522 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
523 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
523 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
524 changegroup-in-remote hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_NODE_LAST=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
524 changegroup-in-remote hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_NODE_LAST=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
525 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
525 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
526 Got arguments 1:user@dummy 2:hg init 'a repo'
526 Got arguments 1:user@dummy 2:hg init 'a repo'
527 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
527 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
528 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
528 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
529 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
529 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
530 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
530 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
531 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
531 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
532 changegroup-in-remote hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_NODE_LAST=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
532 changegroup-in-remote hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_NODE_LAST=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
533 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
533 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
534
534
535 remote hook failure is attributed to remote
535 remote hook failure is attributed to remote
536
536
537 $ cat > $TESTTMP/failhook << EOF
537 $ cat > $TESTTMP/failhook << EOF
538 > def hook(ui, repo, **kwargs):
538 > def hook(ui, repo, **kwargs):
539 > ui.write('hook failure!\n')
539 > ui.write('hook failure!\n')
540 > ui.flush()
540 > ui.flush()
541 > return 1
541 > return 1
542 > EOF
542 > EOF
543
543
544 $ echo "pretxnchangegroup.fail = python:$TESTTMP/failhook:hook" >> remote/.hg/hgrc
544 $ echo "pretxnchangegroup.fail = python:$TESTTMP/failhook:hook" >> remote/.hg/hgrc
545
545
546 $ hg -q --config ui.ssh="python $TESTDIR/dummyssh" clone ssh://user@dummy/remote hookout
546 $ hg -q --config ui.ssh="python $TESTDIR/dummyssh" clone ssh://user@dummy/remote hookout
547 $ cd hookout
547 $ cd hookout
548 $ touch hookfailure
548 $ touch hookfailure
549 $ hg -q commit -A -m 'remote hook failure'
549 $ hg -q commit -A -m 'remote hook failure'
550 $ hg --config ui.ssh="python $TESTDIR/dummyssh" push
550 $ hg --config ui.ssh="python $TESTDIR/dummyssh" push
551 pushing to ssh://user@dummy/remote
551 pushing to ssh://user@dummy/remote
552 searching for changes
552 searching for changes
553 remote: adding changesets
553 remote: adding changesets
554 remote: adding manifests
554 remote: adding manifests
555 remote: adding file changes
555 remote: adding file changes
556 remote: added 1 changesets with 1 changes to 1 files
556 remote: added 1 changesets with 1 changes to 1 files
557 remote: hook failure!
557 remote: hook failure!
558 remote: transaction abort!
558 remote: transaction abort!
559 remote: rollback completed
559 remote: rollback completed
560 remote: pretxnchangegroup.fail hook failed
560 remote: pretxnchangegroup.fail hook failed
561 abort: push failed on remote
561 abort: push failed on remote
562 [255]
562 [255]
563
563
564 abort during pull is properly reported as such
564 abort during pull is properly reported as such
565
565
566 $ echo morefoo >> ../remote/foo
566 $ echo morefoo >> ../remote/foo
567 $ hg -R ../remote commit --message "more foo to be pulled"
567 $ hg -R ../remote commit --message "more foo to be pulled"
568 $ cat >> ../remote/.hg/hgrc << EOF
568 $ cat >> ../remote/.hg/hgrc << EOF
569 > [extensions]
569 > [extensions]
570 > crash = ${TESTDIR}/crashgetbundler.py
570 > crash = ${TESTDIR}/crashgetbundler.py
571 > EOF
571 > EOF
572 $ hg --config ui.ssh="python $TESTDIR/dummyssh" pull
572 $ hg --config ui.ssh="python $TESTDIR/dummyssh" pull
573 pulling from ssh://user@dummy/remote
573 pulling from ssh://user@dummy/remote
574 searching for changes
574 searching for changes
575 remote: abort: this is an exercise
575 remote: abort: this is an exercise
576 abort: pull failed on remote
576 abort: pull failed on remote
577 [255]
577 [255]
General Comments 0
You need to be logged in to leave comments. Login now