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