##// END OF EJS Templates
util: enable hardlink for copyfile...
Jun Wu -
r31577:e7a02e9a default
parent child Browse files
Show More
@@ -1,3605 +1,3600
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 zlib
41 import zlib
42
42
43 from . import (
43 from . import (
44 encoding,
44 encoding,
45 error,
45 error,
46 i18n,
46 i18n,
47 osutil,
47 osutil,
48 parsers,
48 parsers,
49 pycompat,
49 pycompat,
50 )
50 )
51
51
52 empty = pycompat.empty
52 empty = pycompat.empty
53 httplib = pycompat.httplib
53 httplib = pycompat.httplib
54 httpserver = pycompat.httpserver
54 httpserver = pycompat.httpserver
55 pickle = pycompat.pickle
55 pickle = pycompat.pickle
56 queue = pycompat.queue
56 queue = pycompat.queue
57 socketserver = pycompat.socketserver
57 socketserver = pycompat.socketserver
58 stderr = pycompat.stderr
58 stderr = pycompat.stderr
59 stdin = pycompat.stdin
59 stdin = pycompat.stdin
60 stdout = pycompat.stdout
60 stdout = pycompat.stdout
61 stringio = pycompat.stringio
61 stringio = pycompat.stringio
62 urlerr = pycompat.urlerr
62 urlerr = pycompat.urlerr
63 urlreq = pycompat.urlreq
63 urlreq = pycompat.urlreq
64 xmlrpclib = pycompat.xmlrpclib
64 xmlrpclib = pycompat.xmlrpclib
65
65
66 def isatty(fp):
66 def isatty(fp):
67 try:
67 try:
68 return fp.isatty()
68 return fp.isatty()
69 except AttributeError:
69 except AttributeError:
70 return False
70 return False
71
71
72 # glibc determines buffering on first write to stdout - if we replace a TTY
72 # glibc determines buffering on first write to stdout - if we replace a TTY
73 # destined stdout with a pipe destined stdout (e.g. pager), we want line
73 # destined stdout with a pipe destined stdout (e.g. pager), we want line
74 # buffering
74 # buffering
75 if isatty(stdout):
75 if isatty(stdout):
76 stdout = os.fdopen(stdout.fileno(), pycompat.sysstr('wb'), 1)
76 stdout = os.fdopen(stdout.fileno(), pycompat.sysstr('wb'), 1)
77
77
78 if pycompat.osname == 'nt':
78 if pycompat.osname == 'nt':
79 from . import windows as platform
79 from . import windows as platform
80 stdout = platform.winstdout(stdout)
80 stdout = platform.winstdout(stdout)
81 else:
81 else:
82 from . import posix as platform
82 from . import posix as platform
83
83
84 _ = i18n._
84 _ = i18n._
85
85
86 bindunixsocket = platform.bindunixsocket
86 bindunixsocket = platform.bindunixsocket
87 cachestat = platform.cachestat
87 cachestat = platform.cachestat
88 checkexec = platform.checkexec
88 checkexec = platform.checkexec
89 checklink = platform.checklink
89 checklink = platform.checklink
90 copymode = platform.copymode
90 copymode = platform.copymode
91 executablepath = platform.executablepath
91 executablepath = platform.executablepath
92 expandglobs = platform.expandglobs
92 expandglobs = platform.expandglobs
93 explainexit = platform.explainexit
93 explainexit = platform.explainexit
94 findexe = platform.findexe
94 findexe = platform.findexe
95 gethgcmd = platform.gethgcmd
95 gethgcmd = platform.gethgcmd
96 getuser = platform.getuser
96 getuser = platform.getuser
97 getpid = os.getpid
97 getpid = os.getpid
98 groupmembers = platform.groupmembers
98 groupmembers = platform.groupmembers
99 groupname = platform.groupname
99 groupname = platform.groupname
100 hidewindow = platform.hidewindow
100 hidewindow = platform.hidewindow
101 isexec = platform.isexec
101 isexec = platform.isexec
102 isowner = platform.isowner
102 isowner = platform.isowner
103 localpath = platform.localpath
103 localpath = platform.localpath
104 lookupreg = platform.lookupreg
104 lookupreg = platform.lookupreg
105 makedir = platform.makedir
105 makedir = platform.makedir
106 nlinks = platform.nlinks
106 nlinks = platform.nlinks
107 normpath = platform.normpath
107 normpath = platform.normpath
108 normcase = platform.normcase
108 normcase = platform.normcase
109 normcasespec = platform.normcasespec
109 normcasespec = platform.normcasespec
110 normcasefallback = platform.normcasefallback
110 normcasefallback = platform.normcasefallback
111 openhardlinks = platform.openhardlinks
111 openhardlinks = platform.openhardlinks
112 oslink = platform.oslink
112 oslink = platform.oslink
113 parsepatchoutput = platform.parsepatchoutput
113 parsepatchoutput = platform.parsepatchoutput
114 pconvert = platform.pconvert
114 pconvert = platform.pconvert
115 poll = platform.poll
115 poll = platform.poll
116 popen = platform.popen
116 popen = platform.popen
117 posixfile = platform.posixfile
117 posixfile = platform.posixfile
118 quotecommand = platform.quotecommand
118 quotecommand = platform.quotecommand
119 readpipe = platform.readpipe
119 readpipe = platform.readpipe
120 rename = platform.rename
120 rename = platform.rename
121 removedirs = platform.removedirs
121 removedirs = platform.removedirs
122 samedevice = platform.samedevice
122 samedevice = platform.samedevice
123 samefile = platform.samefile
123 samefile = platform.samefile
124 samestat = platform.samestat
124 samestat = platform.samestat
125 setbinary = platform.setbinary
125 setbinary = platform.setbinary
126 setflags = platform.setflags
126 setflags = platform.setflags
127 setsignalhandler = platform.setsignalhandler
127 setsignalhandler = platform.setsignalhandler
128 shellquote = platform.shellquote
128 shellquote = platform.shellquote
129 spawndetached = platform.spawndetached
129 spawndetached = platform.spawndetached
130 split = platform.split
130 split = platform.split
131 sshargs = platform.sshargs
131 sshargs = platform.sshargs
132 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
132 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
133 statisexec = platform.statisexec
133 statisexec = platform.statisexec
134 statislink = platform.statislink
134 statislink = platform.statislink
135 testpid = platform.testpid
135 testpid = platform.testpid
136 umask = platform.umask
136 umask = platform.umask
137 unlink = platform.unlink
137 unlink = platform.unlink
138 username = platform.username
138 username = platform.username
139
139
140 # Python compatibility
140 # Python compatibility
141
141
142 _notset = object()
142 _notset = object()
143
143
144 # disable Python's problematic floating point timestamps (issue4836)
144 # disable Python's problematic floating point timestamps (issue4836)
145 # (Python hypocritically says you shouldn't change this behavior in
145 # (Python hypocritically says you shouldn't change this behavior in
146 # libraries, and sure enough Mercurial is not a library.)
146 # libraries, and sure enough Mercurial is not a library.)
147 os.stat_float_times(False)
147 os.stat_float_times(False)
148
148
149 def safehasattr(thing, attr):
149 def safehasattr(thing, attr):
150 return getattr(thing, attr, _notset) is not _notset
150 return getattr(thing, attr, _notset) is not _notset
151
151
152 def bitsfrom(container):
152 def bitsfrom(container):
153 bits = 0
153 bits = 0
154 for bit in container:
154 for bit in container:
155 bits |= bit
155 bits |= bit
156 return bits
156 return bits
157
157
158 DIGESTS = {
158 DIGESTS = {
159 'md5': hashlib.md5,
159 'md5': hashlib.md5,
160 'sha1': hashlib.sha1,
160 'sha1': hashlib.sha1,
161 'sha512': hashlib.sha512,
161 'sha512': hashlib.sha512,
162 }
162 }
163 # List of digest types from strongest to weakest
163 # List of digest types from strongest to weakest
164 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
164 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
165
165
166 for k in DIGESTS_BY_STRENGTH:
166 for k in DIGESTS_BY_STRENGTH:
167 assert k in DIGESTS
167 assert k in DIGESTS
168
168
169 class digester(object):
169 class digester(object):
170 """helper to compute digests.
170 """helper to compute digests.
171
171
172 This helper can be used to compute one or more digests given their name.
172 This helper can be used to compute one or more digests given their name.
173
173
174 >>> d = digester(['md5', 'sha1'])
174 >>> d = digester(['md5', 'sha1'])
175 >>> d.update('foo')
175 >>> d.update('foo')
176 >>> [k for k in sorted(d)]
176 >>> [k for k in sorted(d)]
177 ['md5', 'sha1']
177 ['md5', 'sha1']
178 >>> d['md5']
178 >>> d['md5']
179 'acbd18db4cc2f85cedef654fccc4a4d8'
179 'acbd18db4cc2f85cedef654fccc4a4d8'
180 >>> d['sha1']
180 >>> d['sha1']
181 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
181 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
182 >>> digester.preferred(['md5', 'sha1'])
182 >>> digester.preferred(['md5', 'sha1'])
183 'sha1'
183 'sha1'
184 """
184 """
185
185
186 def __init__(self, digests, s=''):
186 def __init__(self, digests, s=''):
187 self._hashes = {}
187 self._hashes = {}
188 for k in digests:
188 for k in digests:
189 if k not in DIGESTS:
189 if k not in DIGESTS:
190 raise Abort(_('unknown digest type: %s') % k)
190 raise Abort(_('unknown digest type: %s') % k)
191 self._hashes[k] = DIGESTS[k]()
191 self._hashes[k] = DIGESTS[k]()
192 if s:
192 if s:
193 self.update(s)
193 self.update(s)
194
194
195 def update(self, data):
195 def update(self, data):
196 for h in self._hashes.values():
196 for h in self._hashes.values():
197 h.update(data)
197 h.update(data)
198
198
199 def __getitem__(self, key):
199 def __getitem__(self, key):
200 if key not in DIGESTS:
200 if key not in DIGESTS:
201 raise Abort(_('unknown digest type: %s') % k)
201 raise Abort(_('unknown digest type: %s') % k)
202 return self._hashes[key].hexdigest()
202 return self._hashes[key].hexdigest()
203
203
204 def __iter__(self):
204 def __iter__(self):
205 return iter(self._hashes)
205 return iter(self._hashes)
206
206
207 @staticmethod
207 @staticmethod
208 def preferred(supported):
208 def preferred(supported):
209 """returns the strongest digest type in both supported and DIGESTS."""
209 """returns the strongest digest type in both supported and DIGESTS."""
210
210
211 for k in DIGESTS_BY_STRENGTH:
211 for k in DIGESTS_BY_STRENGTH:
212 if k in supported:
212 if k in supported:
213 return k
213 return k
214 return None
214 return None
215
215
216 class digestchecker(object):
216 class digestchecker(object):
217 """file handle wrapper that additionally checks content against a given
217 """file handle wrapper that additionally checks content against a given
218 size and digests.
218 size and digests.
219
219
220 d = digestchecker(fh, size, {'md5': '...'})
220 d = digestchecker(fh, size, {'md5': '...'})
221
221
222 When multiple digests are given, all of them are validated.
222 When multiple digests are given, all of them are validated.
223 """
223 """
224
224
225 def __init__(self, fh, size, digests):
225 def __init__(self, fh, size, digests):
226 self._fh = fh
226 self._fh = fh
227 self._size = size
227 self._size = size
228 self._got = 0
228 self._got = 0
229 self._digests = dict(digests)
229 self._digests = dict(digests)
230 self._digester = digester(self._digests.keys())
230 self._digester = digester(self._digests.keys())
231
231
232 def read(self, length=-1):
232 def read(self, length=-1):
233 content = self._fh.read(length)
233 content = self._fh.read(length)
234 self._digester.update(content)
234 self._digester.update(content)
235 self._got += len(content)
235 self._got += len(content)
236 return content
236 return content
237
237
238 def validate(self):
238 def validate(self):
239 if self._size != self._got:
239 if self._size != self._got:
240 raise Abort(_('size mismatch: expected %d, got %d') %
240 raise Abort(_('size mismatch: expected %d, got %d') %
241 (self._size, self._got))
241 (self._size, self._got))
242 for k, v in self._digests.items():
242 for k, v in self._digests.items():
243 if v != self._digester[k]:
243 if v != self._digester[k]:
244 # i18n: first parameter is a digest name
244 # i18n: first parameter is a digest name
245 raise Abort(_('%s mismatch: expected %s, got %s') %
245 raise Abort(_('%s mismatch: expected %s, got %s') %
246 (k, v, self._digester[k]))
246 (k, v, self._digester[k]))
247
247
248 try:
248 try:
249 buffer = buffer
249 buffer = buffer
250 except NameError:
250 except NameError:
251 if not pycompat.ispy3:
251 if not pycompat.ispy3:
252 def buffer(sliceable, offset=0, length=None):
252 def buffer(sliceable, offset=0, length=None):
253 if length is not None:
253 if length is not None:
254 return sliceable[offset:offset + length]
254 return sliceable[offset:offset + length]
255 return sliceable[offset:]
255 return sliceable[offset:]
256 else:
256 else:
257 def buffer(sliceable, offset=0, length=None):
257 def buffer(sliceable, offset=0, length=None):
258 if length is not None:
258 if length is not None:
259 return memoryview(sliceable)[offset:offset + length]
259 return memoryview(sliceable)[offset:offset + length]
260 return memoryview(sliceable)[offset:]
260 return memoryview(sliceable)[offset:]
261
261
262 closefds = pycompat.osname == 'posix'
262 closefds = pycompat.osname == 'posix'
263
263
264 _chunksize = 4096
264 _chunksize = 4096
265
265
266 class bufferedinputpipe(object):
266 class bufferedinputpipe(object):
267 """a manually buffered input pipe
267 """a manually buffered input pipe
268
268
269 Python will not let us use buffered IO and lazy reading with 'polling' at
269 Python will not let us use buffered IO and lazy reading with 'polling' at
270 the same time. We cannot probe the buffer state and select will not detect
270 the same time. We cannot probe the buffer state and select will not detect
271 that data are ready to read if they are already buffered.
271 that data are ready to read if they are already buffered.
272
272
273 This class let us work around that by implementing its own buffering
273 This class let us work around that by implementing its own buffering
274 (allowing efficient readline) while offering a way to know if the buffer is
274 (allowing efficient readline) while offering a way to know if the buffer is
275 empty from the output (allowing collaboration of the buffer with polling).
275 empty from the output (allowing collaboration of the buffer with polling).
276
276
277 This class lives in the 'util' module because it makes use of the 'os'
277 This class lives in the 'util' module because it makes use of the 'os'
278 module from the python stdlib.
278 module from the python stdlib.
279 """
279 """
280
280
281 def __init__(self, input):
281 def __init__(self, input):
282 self._input = input
282 self._input = input
283 self._buffer = []
283 self._buffer = []
284 self._eof = False
284 self._eof = False
285 self._lenbuf = 0
285 self._lenbuf = 0
286
286
287 @property
287 @property
288 def hasbuffer(self):
288 def hasbuffer(self):
289 """True is any data is currently buffered
289 """True is any data is currently buffered
290
290
291 This will be used externally a pre-step for polling IO. If there is
291 This will be used externally a pre-step for polling IO. If there is
292 already data then no polling should be set in place."""
292 already data then no polling should be set in place."""
293 return bool(self._buffer)
293 return bool(self._buffer)
294
294
295 @property
295 @property
296 def closed(self):
296 def closed(self):
297 return self._input.closed
297 return self._input.closed
298
298
299 def fileno(self):
299 def fileno(self):
300 return self._input.fileno()
300 return self._input.fileno()
301
301
302 def close(self):
302 def close(self):
303 return self._input.close()
303 return self._input.close()
304
304
305 def read(self, size):
305 def read(self, size):
306 while (not self._eof) and (self._lenbuf < size):
306 while (not self._eof) and (self._lenbuf < size):
307 self._fillbuffer()
307 self._fillbuffer()
308 return self._frombuffer(size)
308 return self._frombuffer(size)
309
309
310 def readline(self, *args, **kwargs):
310 def readline(self, *args, **kwargs):
311 if 1 < len(self._buffer):
311 if 1 < len(self._buffer):
312 # this should not happen because both read and readline end with a
312 # this should not happen because both read and readline end with a
313 # _frombuffer call that collapse it.
313 # _frombuffer call that collapse it.
314 self._buffer = [''.join(self._buffer)]
314 self._buffer = [''.join(self._buffer)]
315 self._lenbuf = len(self._buffer[0])
315 self._lenbuf = len(self._buffer[0])
316 lfi = -1
316 lfi = -1
317 if self._buffer:
317 if self._buffer:
318 lfi = self._buffer[-1].find('\n')
318 lfi = self._buffer[-1].find('\n')
319 while (not self._eof) and lfi < 0:
319 while (not self._eof) and lfi < 0:
320 self._fillbuffer()
320 self._fillbuffer()
321 if self._buffer:
321 if self._buffer:
322 lfi = self._buffer[-1].find('\n')
322 lfi = self._buffer[-1].find('\n')
323 size = lfi + 1
323 size = lfi + 1
324 if lfi < 0: # end of file
324 if lfi < 0: # end of file
325 size = self._lenbuf
325 size = self._lenbuf
326 elif 1 < len(self._buffer):
326 elif 1 < len(self._buffer):
327 # we need to take previous chunks into account
327 # we need to take previous chunks into account
328 size += self._lenbuf - len(self._buffer[-1])
328 size += self._lenbuf - len(self._buffer[-1])
329 return self._frombuffer(size)
329 return self._frombuffer(size)
330
330
331 def _frombuffer(self, size):
331 def _frombuffer(self, size):
332 """return at most 'size' data from the buffer
332 """return at most 'size' data from the buffer
333
333
334 The data are removed from the buffer."""
334 The data are removed from the buffer."""
335 if size == 0 or not self._buffer:
335 if size == 0 or not self._buffer:
336 return ''
336 return ''
337 buf = self._buffer[0]
337 buf = self._buffer[0]
338 if 1 < len(self._buffer):
338 if 1 < len(self._buffer):
339 buf = ''.join(self._buffer)
339 buf = ''.join(self._buffer)
340
340
341 data = buf[:size]
341 data = buf[:size]
342 buf = buf[len(data):]
342 buf = buf[len(data):]
343 if buf:
343 if buf:
344 self._buffer = [buf]
344 self._buffer = [buf]
345 self._lenbuf = len(buf)
345 self._lenbuf = len(buf)
346 else:
346 else:
347 self._buffer = []
347 self._buffer = []
348 self._lenbuf = 0
348 self._lenbuf = 0
349 return data
349 return data
350
350
351 def _fillbuffer(self):
351 def _fillbuffer(self):
352 """read data to the buffer"""
352 """read data to the buffer"""
353 data = os.read(self._input.fileno(), _chunksize)
353 data = os.read(self._input.fileno(), _chunksize)
354 if not data:
354 if not data:
355 self._eof = True
355 self._eof = True
356 else:
356 else:
357 self._lenbuf += len(data)
357 self._lenbuf += len(data)
358 self._buffer.append(data)
358 self._buffer.append(data)
359
359
360 def popen2(cmd, env=None, newlines=False):
360 def popen2(cmd, env=None, newlines=False):
361 # Setting bufsize to -1 lets the system decide the buffer size.
361 # Setting bufsize to -1 lets the system decide the buffer size.
362 # The default for bufsize is 0, meaning unbuffered. This leads to
362 # The default for bufsize is 0, meaning unbuffered. This leads to
363 # poor performance on Mac OS X: http://bugs.python.org/issue4194
363 # poor performance on Mac OS X: http://bugs.python.org/issue4194
364 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
364 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
365 close_fds=closefds,
365 close_fds=closefds,
366 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
366 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
367 universal_newlines=newlines,
367 universal_newlines=newlines,
368 env=env)
368 env=env)
369 return p.stdin, p.stdout
369 return p.stdin, p.stdout
370
370
371 def popen3(cmd, env=None, newlines=False):
371 def popen3(cmd, env=None, newlines=False):
372 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
372 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
373 return stdin, stdout, stderr
373 return stdin, stdout, stderr
374
374
375 def popen4(cmd, env=None, newlines=False, bufsize=-1):
375 def popen4(cmd, env=None, newlines=False, bufsize=-1):
376 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
376 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
377 close_fds=closefds,
377 close_fds=closefds,
378 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
378 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
379 stderr=subprocess.PIPE,
379 stderr=subprocess.PIPE,
380 universal_newlines=newlines,
380 universal_newlines=newlines,
381 env=env)
381 env=env)
382 return p.stdin, p.stdout, p.stderr, p
382 return p.stdin, p.stdout, p.stderr, p
383
383
384 def version():
384 def version():
385 """Return version information if available."""
385 """Return version information if available."""
386 try:
386 try:
387 from . import __version__
387 from . import __version__
388 return __version__.version
388 return __version__.version
389 except ImportError:
389 except ImportError:
390 return 'unknown'
390 return 'unknown'
391
391
392 def versiontuple(v=None, n=4):
392 def versiontuple(v=None, n=4):
393 """Parses a Mercurial version string into an N-tuple.
393 """Parses a Mercurial version string into an N-tuple.
394
394
395 The version string to be parsed is specified with the ``v`` argument.
395 The version string to be parsed is specified with the ``v`` argument.
396 If it isn't defined, the current Mercurial version string will be parsed.
396 If it isn't defined, the current Mercurial version string will be parsed.
397
397
398 ``n`` can be 2, 3, or 4. Here is how some version strings map to
398 ``n`` can be 2, 3, or 4. Here is how some version strings map to
399 returned values:
399 returned values:
400
400
401 >>> v = '3.6.1+190-df9b73d2d444'
401 >>> v = '3.6.1+190-df9b73d2d444'
402 >>> versiontuple(v, 2)
402 >>> versiontuple(v, 2)
403 (3, 6)
403 (3, 6)
404 >>> versiontuple(v, 3)
404 >>> versiontuple(v, 3)
405 (3, 6, 1)
405 (3, 6, 1)
406 >>> versiontuple(v, 4)
406 >>> versiontuple(v, 4)
407 (3, 6, 1, '190-df9b73d2d444')
407 (3, 6, 1, '190-df9b73d2d444')
408
408
409 >>> versiontuple('3.6.1+190-df9b73d2d444+20151118')
409 >>> versiontuple('3.6.1+190-df9b73d2d444+20151118')
410 (3, 6, 1, '190-df9b73d2d444+20151118')
410 (3, 6, 1, '190-df9b73d2d444+20151118')
411
411
412 >>> v = '3.6'
412 >>> v = '3.6'
413 >>> versiontuple(v, 2)
413 >>> versiontuple(v, 2)
414 (3, 6)
414 (3, 6)
415 >>> versiontuple(v, 3)
415 >>> versiontuple(v, 3)
416 (3, 6, None)
416 (3, 6, None)
417 >>> versiontuple(v, 4)
417 >>> versiontuple(v, 4)
418 (3, 6, None, None)
418 (3, 6, None, None)
419
419
420 >>> v = '3.9-rc'
420 >>> v = '3.9-rc'
421 >>> versiontuple(v, 2)
421 >>> versiontuple(v, 2)
422 (3, 9)
422 (3, 9)
423 >>> versiontuple(v, 3)
423 >>> versiontuple(v, 3)
424 (3, 9, None)
424 (3, 9, None)
425 >>> versiontuple(v, 4)
425 >>> versiontuple(v, 4)
426 (3, 9, None, 'rc')
426 (3, 9, None, 'rc')
427
427
428 >>> v = '3.9-rc+2-02a8fea4289b'
428 >>> v = '3.9-rc+2-02a8fea4289b'
429 >>> versiontuple(v, 2)
429 >>> versiontuple(v, 2)
430 (3, 9)
430 (3, 9)
431 >>> versiontuple(v, 3)
431 >>> versiontuple(v, 3)
432 (3, 9, None)
432 (3, 9, None)
433 >>> versiontuple(v, 4)
433 >>> versiontuple(v, 4)
434 (3, 9, None, 'rc+2-02a8fea4289b')
434 (3, 9, None, 'rc+2-02a8fea4289b')
435 """
435 """
436 if not v:
436 if not v:
437 v = version()
437 v = version()
438 parts = remod.split('[\+-]', v, 1)
438 parts = remod.split('[\+-]', v, 1)
439 if len(parts) == 1:
439 if len(parts) == 1:
440 vparts, extra = parts[0], None
440 vparts, extra = parts[0], None
441 else:
441 else:
442 vparts, extra = parts
442 vparts, extra = parts
443
443
444 vints = []
444 vints = []
445 for i in vparts.split('.'):
445 for i in vparts.split('.'):
446 try:
446 try:
447 vints.append(int(i))
447 vints.append(int(i))
448 except ValueError:
448 except ValueError:
449 break
449 break
450 # (3, 6) -> (3, 6, None)
450 # (3, 6) -> (3, 6, None)
451 while len(vints) < 3:
451 while len(vints) < 3:
452 vints.append(None)
452 vints.append(None)
453
453
454 if n == 2:
454 if n == 2:
455 return (vints[0], vints[1])
455 return (vints[0], vints[1])
456 if n == 3:
456 if n == 3:
457 return (vints[0], vints[1], vints[2])
457 return (vints[0], vints[1], vints[2])
458 if n == 4:
458 if n == 4:
459 return (vints[0], vints[1], vints[2], extra)
459 return (vints[0], vints[1], vints[2], extra)
460
460
461 # used by parsedate
461 # used by parsedate
462 defaultdateformats = (
462 defaultdateformats = (
463 '%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601
463 '%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601
464 '%Y-%m-%dT%H:%M', # without seconds
464 '%Y-%m-%dT%H:%M', # without seconds
465 '%Y-%m-%dT%H%M%S', # another awful but legal variant without :
465 '%Y-%m-%dT%H%M%S', # another awful but legal variant without :
466 '%Y-%m-%dT%H%M', # without seconds
466 '%Y-%m-%dT%H%M', # without seconds
467 '%Y-%m-%d %H:%M:%S', # our common legal variant
467 '%Y-%m-%d %H:%M:%S', # our common legal variant
468 '%Y-%m-%d %H:%M', # without seconds
468 '%Y-%m-%d %H:%M', # without seconds
469 '%Y-%m-%d %H%M%S', # without :
469 '%Y-%m-%d %H%M%S', # without :
470 '%Y-%m-%d %H%M', # without seconds
470 '%Y-%m-%d %H%M', # without seconds
471 '%Y-%m-%d %I:%M:%S%p',
471 '%Y-%m-%d %I:%M:%S%p',
472 '%Y-%m-%d %H:%M',
472 '%Y-%m-%d %H:%M',
473 '%Y-%m-%d %I:%M%p',
473 '%Y-%m-%d %I:%M%p',
474 '%Y-%m-%d',
474 '%Y-%m-%d',
475 '%m-%d',
475 '%m-%d',
476 '%m/%d',
476 '%m/%d',
477 '%m/%d/%y',
477 '%m/%d/%y',
478 '%m/%d/%Y',
478 '%m/%d/%Y',
479 '%a %b %d %H:%M:%S %Y',
479 '%a %b %d %H:%M:%S %Y',
480 '%a %b %d %I:%M:%S%p %Y',
480 '%a %b %d %I:%M:%S%p %Y',
481 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
481 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
482 '%b %d %H:%M:%S %Y',
482 '%b %d %H:%M:%S %Y',
483 '%b %d %I:%M:%S%p %Y',
483 '%b %d %I:%M:%S%p %Y',
484 '%b %d %H:%M:%S',
484 '%b %d %H:%M:%S',
485 '%b %d %I:%M:%S%p',
485 '%b %d %I:%M:%S%p',
486 '%b %d %H:%M',
486 '%b %d %H:%M',
487 '%b %d %I:%M%p',
487 '%b %d %I:%M%p',
488 '%b %d %Y',
488 '%b %d %Y',
489 '%b %d',
489 '%b %d',
490 '%H:%M:%S',
490 '%H:%M:%S',
491 '%I:%M:%S%p',
491 '%I:%M:%S%p',
492 '%H:%M',
492 '%H:%M',
493 '%I:%M%p',
493 '%I:%M%p',
494 )
494 )
495
495
496 extendeddateformats = defaultdateformats + (
496 extendeddateformats = defaultdateformats + (
497 "%Y",
497 "%Y",
498 "%Y-%m",
498 "%Y-%m",
499 "%b",
499 "%b",
500 "%b %Y",
500 "%b %Y",
501 )
501 )
502
502
503 def cachefunc(func):
503 def cachefunc(func):
504 '''cache the result of function calls'''
504 '''cache the result of function calls'''
505 # XXX doesn't handle keywords args
505 # XXX doesn't handle keywords args
506 if func.__code__.co_argcount == 0:
506 if func.__code__.co_argcount == 0:
507 cache = []
507 cache = []
508 def f():
508 def f():
509 if len(cache) == 0:
509 if len(cache) == 0:
510 cache.append(func())
510 cache.append(func())
511 return cache[0]
511 return cache[0]
512 return f
512 return f
513 cache = {}
513 cache = {}
514 if func.__code__.co_argcount == 1:
514 if func.__code__.co_argcount == 1:
515 # we gain a small amount of time because
515 # we gain a small amount of time because
516 # we don't need to pack/unpack the list
516 # we don't need to pack/unpack the list
517 def f(arg):
517 def f(arg):
518 if arg not in cache:
518 if arg not in cache:
519 cache[arg] = func(arg)
519 cache[arg] = func(arg)
520 return cache[arg]
520 return cache[arg]
521 else:
521 else:
522 def f(*args):
522 def f(*args):
523 if args not in cache:
523 if args not in cache:
524 cache[args] = func(*args)
524 cache[args] = func(*args)
525 return cache[args]
525 return cache[args]
526
526
527 return f
527 return f
528
528
529 class sortdict(dict):
529 class sortdict(dict):
530 '''a simple sorted dictionary'''
530 '''a simple sorted dictionary'''
531 def __init__(self, data=None):
531 def __init__(self, data=None):
532 self._list = []
532 self._list = []
533 if data:
533 if data:
534 self.update(data)
534 self.update(data)
535 def copy(self):
535 def copy(self):
536 return sortdict(self)
536 return sortdict(self)
537 def __setitem__(self, key, val):
537 def __setitem__(self, key, val):
538 if key in self:
538 if key in self:
539 self._list.remove(key)
539 self._list.remove(key)
540 self._list.append(key)
540 self._list.append(key)
541 dict.__setitem__(self, key, val)
541 dict.__setitem__(self, key, val)
542 def __iter__(self):
542 def __iter__(self):
543 return self._list.__iter__()
543 return self._list.__iter__()
544 def update(self, src):
544 def update(self, src):
545 if isinstance(src, dict):
545 if isinstance(src, dict):
546 src = src.iteritems()
546 src = src.iteritems()
547 for k, v in src:
547 for k, v in src:
548 self[k] = v
548 self[k] = v
549 def clear(self):
549 def clear(self):
550 dict.clear(self)
550 dict.clear(self)
551 self._list = []
551 self._list = []
552 def items(self):
552 def items(self):
553 return [(k, self[k]) for k in self._list]
553 return [(k, self[k]) for k in self._list]
554 def __delitem__(self, key):
554 def __delitem__(self, key):
555 dict.__delitem__(self, key)
555 dict.__delitem__(self, key)
556 self._list.remove(key)
556 self._list.remove(key)
557 def pop(self, key, *args, **kwargs):
557 def pop(self, key, *args, **kwargs):
558 dict.pop(self, key, *args, **kwargs)
558 dict.pop(self, key, *args, **kwargs)
559 try:
559 try:
560 self._list.remove(key)
560 self._list.remove(key)
561 except ValueError:
561 except ValueError:
562 pass
562 pass
563 def keys(self):
563 def keys(self):
564 return self._list[:]
564 return self._list[:]
565 def iterkeys(self):
565 def iterkeys(self):
566 return self._list.__iter__()
566 return self._list.__iter__()
567 def iteritems(self):
567 def iteritems(self):
568 for k in self._list:
568 for k in self._list:
569 yield k, self[k]
569 yield k, self[k]
570 def insert(self, index, key, val):
570 def insert(self, index, key, val):
571 self._list.insert(index, key)
571 self._list.insert(index, key)
572 dict.__setitem__(self, key, val)
572 dict.__setitem__(self, key, val)
573 def __repr__(self):
573 def __repr__(self):
574 if not self:
574 if not self:
575 return '%s()' % self.__class__.__name__
575 return '%s()' % self.__class__.__name__
576 return '%s(%r)' % (self.__class__.__name__, self.items())
576 return '%s(%r)' % (self.__class__.__name__, self.items())
577
577
578 class _lrucachenode(object):
578 class _lrucachenode(object):
579 """A node in a doubly linked list.
579 """A node in a doubly linked list.
580
580
581 Holds a reference to nodes on either side as well as a key-value
581 Holds a reference to nodes on either side as well as a key-value
582 pair for the dictionary entry.
582 pair for the dictionary entry.
583 """
583 """
584 __slots__ = (u'next', u'prev', u'key', u'value')
584 __slots__ = (u'next', u'prev', u'key', u'value')
585
585
586 def __init__(self):
586 def __init__(self):
587 self.next = None
587 self.next = None
588 self.prev = None
588 self.prev = None
589
589
590 self.key = _notset
590 self.key = _notset
591 self.value = None
591 self.value = None
592
592
593 def markempty(self):
593 def markempty(self):
594 """Mark the node as emptied."""
594 """Mark the node as emptied."""
595 self.key = _notset
595 self.key = _notset
596
596
597 class lrucachedict(object):
597 class lrucachedict(object):
598 """Dict that caches most recent accesses and sets.
598 """Dict that caches most recent accesses and sets.
599
599
600 The dict consists of an actual backing dict - indexed by original
600 The dict consists of an actual backing dict - indexed by original
601 key - and a doubly linked circular list defining the order of entries in
601 key - and a doubly linked circular list defining the order of entries in
602 the cache.
602 the cache.
603
603
604 The head node is the newest entry in the cache. If the cache is full,
604 The head node is the newest entry in the cache. If the cache is full,
605 we recycle head.prev and make it the new head. Cache accesses result in
605 we recycle head.prev and make it the new head. Cache accesses result in
606 the node being moved to before the existing head and being marked as the
606 the node being moved to before the existing head and being marked as the
607 new head node.
607 new head node.
608 """
608 """
609 def __init__(self, max):
609 def __init__(self, max):
610 self._cache = {}
610 self._cache = {}
611
611
612 self._head = head = _lrucachenode()
612 self._head = head = _lrucachenode()
613 head.prev = head
613 head.prev = head
614 head.next = head
614 head.next = head
615 self._size = 1
615 self._size = 1
616 self._capacity = max
616 self._capacity = max
617
617
618 def __len__(self):
618 def __len__(self):
619 return len(self._cache)
619 return len(self._cache)
620
620
621 def __contains__(self, k):
621 def __contains__(self, k):
622 return k in self._cache
622 return k in self._cache
623
623
624 def __iter__(self):
624 def __iter__(self):
625 # We don't have to iterate in cache order, but why not.
625 # We don't have to iterate in cache order, but why not.
626 n = self._head
626 n = self._head
627 for i in range(len(self._cache)):
627 for i in range(len(self._cache)):
628 yield n.key
628 yield n.key
629 n = n.next
629 n = n.next
630
630
631 def __getitem__(self, k):
631 def __getitem__(self, k):
632 node = self._cache[k]
632 node = self._cache[k]
633 self._movetohead(node)
633 self._movetohead(node)
634 return node.value
634 return node.value
635
635
636 def __setitem__(self, k, v):
636 def __setitem__(self, k, v):
637 node = self._cache.get(k)
637 node = self._cache.get(k)
638 # Replace existing value and mark as newest.
638 # Replace existing value and mark as newest.
639 if node is not None:
639 if node is not None:
640 node.value = v
640 node.value = v
641 self._movetohead(node)
641 self._movetohead(node)
642 return
642 return
643
643
644 if self._size < self._capacity:
644 if self._size < self._capacity:
645 node = self._addcapacity()
645 node = self._addcapacity()
646 else:
646 else:
647 # Grab the last/oldest item.
647 # Grab the last/oldest item.
648 node = self._head.prev
648 node = self._head.prev
649
649
650 # At capacity. Kill the old entry.
650 # At capacity. Kill the old entry.
651 if node.key is not _notset:
651 if node.key is not _notset:
652 del self._cache[node.key]
652 del self._cache[node.key]
653
653
654 node.key = k
654 node.key = k
655 node.value = v
655 node.value = v
656 self._cache[k] = node
656 self._cache[k] = node
657 # And mark it as newest entry. No need to adjust order since it
657 # And mark it as newest entry. No need to adjust order since it
658 # is already self._head.prev.
658 # is already self._head.prev.
659 self._head = node
659 self._head = node
660
660
661 def __delitem__(self, k):
661 def __delitem__(self, k):
662 node = self._cache.pop(k)
662 node = self._cache.pop(k)
663 node.markempty()
663 node.markempty()
664
664
665 # Temporarily mark as newest item before re-adjusting head to make
665 # Temporarily mark as newest item before re-adjusting head to make
666 # this node the oldest item.
666 # this node the oldest item.
667 self._movetohead(node)
667 self._movetohead(node)
668 self._head = node.next
668 self._head = node.next
669
669
670 # Additional dict methods.
670 # Additional dict methods.
671
671
672 def get(self, k, default=None):
672 def get(self, k, default=None):
673 try:
673 try:
674 return self._cache[k].value
674 return self._cache[k].value
675 except KeyError:
675 except KeyError:
676 return default
676 return default
677
677
678 def clear(self):
678 def clear(self):
679 n = self._head
679 n = self._head
680 while n.key is not _notset:
680 while n.key is not _notset:
681 n.markempty()
681 n.markempty()
682 n = n.next
682 n = n.next
683
683
684 self._cache.clear()
684 self._cache.clear()
685
685
686 def copy(self):
686 def copy(self):
687 result = lrucachedict(self._capacity)
687 result = lrucachedict(self._capacity)
688 n = self._head.prev
688 n = self._head.prev
689 # Iterate in oldest-to-newest order, so the copy has the right ordering
689 # Iterate in oldest-to-newest order, so the copy has the right ordering
690 for i in range(len(self._cache)):
690 for i in range(len(self._cache)):
691 result[n.key] = n.value
691 result[n.key] = n.value
692 n = n.prev
692 n = n.prev
693 return result
693 return result
694
694
695 def _movetohead(self, node):
695 def _movetohead(self, node):
696 """Mark a node as the newest, making it the new head.
696 """Mark a node as the newest, making it the new head.
697
697
698 When a node is accessed, it becomes the freshest entry in the LRU
698 When a node is accessed, it becomes the freshest entry in the LRU
699 list, which is denoted by self._head.
699 list, which is denoted by self._head.
700
700
701 Visually, let's make ``N`` the new head node (* denotes head):
701 Visually, let's make ``N`` the new head node (* denotes head):
702
702
703 previous/oldest <-> head <-> next/next newest
703 previous/oldest <-> head <-> next/next newest
704
704
705 ----<->--- A* ---<->-----
705 ----<->--- A* ---<->-----
706 | |
706 | |
707 E <-> D <-> N <-> C <-> B
707 E <-> D <-> N <-> C <-> B
708
708
709 To:
709 To:
710
710
711 ----<->--- N* ---<->-----
711 ----<->--- N* ---<->-----
712 | |
712 | |
713 E <-> D <-> C <-> B <-> A
713 E <-> D <-> C <-> B <-> A
714
714
715 This requires the following moves:
715 This requires the following moves:
716
716
717 C.next = D (node.prev.next = node.next)
717 C.next = D (node.prev.next = node.next)
718 D.prev = C (node.next.prev = node.prev)
718 D.prev = C (node.next.prev = node.prev)
719 E.next = N (head.prev.next = node)
719 E.next = N (head.prev.next = node)
720 N.prev = E (node.prev = head.prev)
720 N.prev = E (node.prev = head.prev)
721 N.next = A (node.next = head)
721 N.next = A (node.next = head)
722 A.prev = N (head.prev = node)
722 A.prev = N (head.prev = node)
723 """
723 """
724 head = self._head
724 head = self._head
725 # C.next = D
725 # C.next = D
726 node.prev.next = node.next
726 node.prev.next = node.next
727 # D.prev = C
727 # D.prev = C
728 node.next.prev = node.prev
728 node.next.prev = node.prev
729 # N.prev = E
729 # N.prev = E
730 node.prev = head.prev
730 node.prev = head.prev
731 # N.next = A
731 # N.next = A
732 # It is tempting to do just "head" here, however if node is
732 # It is tempting to do just "head" here, however if node is
733 # adjacent to head, this will do bad things.
733 # adjacent to head, this will do bad things.
734 node.next = head.prev.next
734 node.next = head.prev.next
735 # E.next = N
735 # E.next = N
736 node.next.prev = node
736 node.next.prev = node
737 # A.prev = N
737 # A.prev = N
738 node.prev.next = node
738 node.prev.next = node
739
739
740 self._head = node
740 self._head = node
741
741
742 def _addcapacity(self):
742 def _addcapacity(self):
743 """Add a node to the circular linked list.
743 """Add a node to the circular linked list.
744
744
745 The new node is inserted before the head node.
745 The new node is inserted before the head node.
746 """
746 """
747 head = self._head
747 head = self._head
748 node = _lrucachenode()
748 node = _lrucachenode()
749 head.prev.next = node
749 head.prev.next = node
750 node.prev = head.prev
750 node.prev = head.prev
751 node.next = head
751 node.next = head
752 head.prev = node
752 head.prev = node
753 self._size += 1
753 self._size += 1
754 return node
754 return node
755
755
756 def lrucachefunc(func):
756 def lrucachefunc(func):
757 '''cache most recent results of function calls'''
757 '''cache most recent results of function calls'''
758 cache = {}
758 cache = {}
759 order = collections.deque()
759 order = collections.deque()
760 if func.__code__.co_argcount == 1:
760 if func.__code__.co_argcount == 1:
761 def f(arg):
761 def f(arg):
762 if arg not in cache:
762 if arg not in cache:
763 if len(cache) > 20:
763 if len(cache) > 20:
764 del cache[order.popleft()]
764 del cache[order.popleft()]
765 cache[arg] = func(arg)
765 cache[arg] = func(arg)
766 else:
766 else:
767 order.remove(arg)
767 order.remove(arg)
768 order.append(arg)
768 order.append(arg)
769 return cache[arg]
769 return cache[arg]
770 else:
770 else:
771 def f(*args):
771 def f(*args):
772 if args not in cache:
772 if args not in cache:
773 if len(cache) > 20:
773 if len(cache) > 20:
774 del cache[order.popleft()]
774 del cache[order.popleft()]
775 cache[args] = func(*args)
775 cache[args] = func(*args)
776 else:
776 else:
777 order.remove(args)
777 order.remove(args)
778 order.append(args)
778 order.append(args)
779 return cache[args]
779 return cache[args]
780
780
781 return f
781 return f
782
782
783 class propertycache(object):
783 class propertycache(object):
784 def __init__(self, func):
784 def __init__(self, func):
785 self.func = func
785 self.func = func
786 self.name = func.__name__
786 self.name = func.__name__
787 def __get__(self, obj, type=None):
787 def __get__(self, obj, type=None):
788 result = self.func(obj)
788 result = self.func(obj)
789 self.cachevalue(obj, result)
789 self.cachevalue(obj, result)
790 return result
790 return result
791
791
792 def cachevalue(self, obj, value):
792 def cachevalue(self, obj, value):
793 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
793 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
794 obj.__dict__[self.name] = value
794 obj.__dict__[self.name] = value
795
795
796 def pipefilter(s, cmd):
796 def pipefilter(s, cmd):
797 '''filter string S through command CMD, returning its output'''
797 '''filter string S through command CMD, returning its output'''
798 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
798 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
799 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
799 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
800 pout, perr = p.communicate(s)
800 pout, perr = p.communicate(s)
801 return pout
801 return pout
802
802
803 def tempfilter(s, cmd):
803 def tempfilter(s, cmd):
804 '''filter string S through a pair of temporary files with CMD.
804 '''filter string S through a pair of temporary files with CMD.
805 CMD is used as a template to create the real command to be run,
805 CMD is used as a template to create the real command to be run,
806 with the strings INFILE and OUTFILE replaced by the real names of
806 with the strings INFILE and OUTFILE replaced by the real names of
807 the temporary files generated.'''
807 the temporary files generated.'''
808 inname, outname = None, None
808 inname, outname = None, None
809 try:
809 try:
810 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
810 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
811 fp = os.fdopen(infd, pycompat.sysstr('wb'))
811 fp = os.fdopen(infd, pycompat.sysstr('wb'))
812 fp.write(s)
812 fp.write(s)
813 fp.close()
813 fp.close()
814 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
814 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
815 os.close(outfd)
815 os.close(outfd)
816 cmd = cmd.replace('INFILE', inname)
816 cmd = cmd.replace('INFILE', inname)
817 cmd = cmd.replace('OUTFILE', outname)
817 cmd = cmd.replace('OUTFILE', outname)
818 code = os.system(cmd)
818 code = os.system(cmd)
819 if pycompat.sysplatform == 'OpenVMS' and code & 1:
819 if pycompat.sysplatform == 'OpenVMS' and code & 1:
820 code = 0
820 code = 0
821 if code:
821 if code:
822 raise Abort(_("command '%s' failed: %s") %
822 raise Abort(_("command '%s' failed: %s") %
823 (cmd, explainexit(code)))
823 (cmd, explainexit(code)))
824 return readfile(outname)
824 return readfile(outname)
825 finally:
825 finally:
826 try:
826 try:
827 if inname:
827 if inname:
828 os.unlink(inname)
828 os.unlink(inname)
829 except OSError:
829 except OSError:
830 pass
830 pass
831 try:
831 try:
832 if outname:
832 if outname:
833 os.unlink(outname)
833 os.unlink(outname)
834 except OSError:
834 except OSError:
835 pass
835 pass
836
836
837 filtertable = {
837 filtertable = {
838 'tempfile:': tempfilter,
838 'tempfile:': tempfilter,
839 'pipe:': pipefilter,
839 'pipe:': pipefilter,
840 }
840 }
841
841
842 def filter(s, cmd):
842 def filter(s, cmd):
843 "filter a string through a command that transforms its input to its output"
843 "filter a string through a command that transforms its input to its output"
844 for name, fn in filtertable.iteritems():
844 for name, fn in filtertable.iteritems():
845 if cmd.startswith(name):
845 if cmd.startswith(name):
846 return fn(s, cmd[len(name):].lstrip())
846 return fn(s, cmd[len(name):].lstrip())
847 return pipefilter(s, cmd)
847 return pipefilter(s, cmd)
848
848
849 def binary(s):
849 def binary(s):
850 """return true if a string is binary data"""
850 """return true if a string is binary data"""
851 return bool(s and '\0' in s)
851 return bool(s and '\0' in s)
852
852
853 def increasingchunks(source, min=1024, max=65536):
853 def increasingchunks(source, min=1024, max=65536):
854 '''return no less than min bytes per chunk while data remains,
854 '''return no less than min bytes per chunk while data remains,
855 doubling min after each chunk until it reaches max'''
855 doubling min after each chunk until it reaches max'''
856 def log2(x):
856 def log2(x):
857 if not x:
857 if not x:
858 return 0
858 return 0
859 i = 0
859 i = 0
860 while x:
860 while x:
861 x >>= 1
861 x >>= 1
862 i += 1
862 i += 1
863 return i - 1
863 return i - 1
864
864
865 buf = []
865 buf = []
866 blen = 0
866 blen = 0
867 for chunk in source:
867 for chunk in source:
868 buf.append(chunk)
868 buf.append(chunk)
869 blen += len(chunk)
869 blen += len(chunk)
870 if blen >= min:
870 if blen >= min:
871 if min < max:
871 if min < max:
872 min = min << 1
872 min = min << 1
873 nmin = 1 << log2(blen)
873 nmin = 1 << log2(blen)
874 if nmin > min:
874 if nmin > min:
875 min = nmin
875 min = nmin
876 if min > max:
876 if min > max:
877 min = max
877 min = max
878 yield ''.join(buf)
878 yield ''.join(buf)
879 blen = 0
879 blen = 0
880 buf = []
880 buf = []
881 if buf:
881 if buf:
882 yield ''.join(buf)
882 yield ''.join(buf)
883
883
884 Abort = error.Abort
884 Abort = error.Abort
885
885
886 def always(fn):
886 def always(fn):
887 return True
887 return True
888
888
889 def never(fn):
889 def never(fn):
890 return False
890 return False
891
891
892 def nogc(func):
892 def nogc(func):
893 """disable garbage collector
893 """disable garbage collector
894
894
895 Python's garbage collector triggers a GC each time a certain number of
895 Python's garbage collector triggers a GC each time a certain number of
896 container objects (the number being defined by gc.get_threshold()) are
896 container objects (the number being defined by gc.get_threshold()) are
897 allocated even when marked not to be tracked by the collector. Tracking has
897 allocated even when marked not to be tracked by the collector. Tracking has
898 no effect on when GCs are triggered, only on what objects the GC looks
898 no effect on when GCs are triggered, only on what objects the GC looks
899 into. As a workaround, disable GC while building complex (huge)
899 into. As a workaround, disable GC while building complex (huge)
900 containers.
900 containers.
901
901
902 This garbage collector issue have been fixed in 2.7.
902 This garbage collector issue have been fixed in 2.7.
903 """
903 """
904 if sys.version_info >= (2, 7):
904 if sys.version_info >= (2, 7):
905 return func
905 return func
906 def wrapper(*args, **kwargs):
906 def wrapper(*args, **kwargs):
907 gcenabled = gc.isenabled()
907 gcenabled = gc.isenabled()
908 gc.disable()
908 gc.disable()
909 try:
909 try:
910 return func(*args, **kwargs)
910 return func(*args, **kwargs)
911 finally:
911 finally:
912 if gcenabled:
912 if gcenabled:
913 gc.enable()
913 gc.enable()
914 return wrapper
914 return wrapper
915
915
916 def pathto(root, n1, n2):
916 def pathto(root, n1, n2):
917 '''return the relative path from one place to another.
917 '''return the relative path from one place to another.
918 root should use os.sep to separate directories
918 root should use os.sep to separate directories
919 n1 should use os.sep to separate directories
919 n1 should use os.sep to separate directories
920 n2 should use "/" to separate directories
920 n2 should use "/" to separate directories
921 returns an os.sep-separated path.
921 returns an os.sep-separated path.
922
922
923 If n1 is a relative path, it's assumed it's
923 If n1 is a relative path, it's assumed it's
924 relative to root.
924 relative to root.
925 n2 should always be relative to root.
925 n2 should always be relative to root.
926 '''
926 '''
927 if not n1:
927 if not n1:
928 return localpath(n2)
928 return localpath(n2)
929 if os.path.isabs(n1):
929 if os.path.isabs(n1):
930 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
930 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
931 return os.path.join(root, localpath(n2))
931 return os.path.join(root, localpath(n2))
932 n2 = '/'.join((pconvert(root), n2))
932 n2 = '/'.join((pconvert(root), n2))
933 a, b = splitpath(n1), n2.split('/')
933 a, b = splitpath(n1), n2.split('/')
934 a.reverse()
934 a.reverse()
935 b.reverse()
935 b.reverse()
936 while a and b and a[-1] == b[-1]:
936 while a and b and a[-1] == b[-1]:
937 a.pop()
937 a.pop()
938 b.pop()
938 b.pop()
939 b.reverse()
939 b.reverse()
940 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
940 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
941
941
942 def mainfrozen():
942 def mainfrozen():
943 """return True if we are a frozen executable.
943 """return True if we are a frozen executable.
944
944
945 The code supports py2exe (most common, Windows only) and tools/freeze
945 The code supports py2exe (most common, Windows only) and tools/freeze
946 (portable, not much used).
946 (portable, not much used).
947 """
947 """
948 return (safehasattr(sys, "frozen") or # new py2exe
948 return (safehasattr(sys, "frozen") or # new py2exe
949 safehasattr(sys, "importers") or # old py2exe
949 safehasattr(sys, "importers") or # old py2exe
950 imp.is_frozen(u"__main__")) # tools/freeze
950 imp.is_frozen(u"__main__")) # tools/freeze
951
951
952 # the location of data files matching the source code
952 # the location of data files matching the source code
953 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
953 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
954 # executable version (py2exe) doesn't support __file__
954 # executable version (py2exe) doesn't support __file__
955 datapath = os.path.dirname(pycompat.sysexecutable)
955 datapath = os.path.dirname(pycompat.sysexecutable)
956 else:
956 else:
957 datapath = os.path.dirname(pycompat.fsencode(__file__))
957 datapath = os.path.dirname(pycompat.fsencode(__file__))
958
958
959 i18n.setdatapath(datapath)
959 i18n.setdatapath(datapath)
960
960
961 _hgexecutable = None
961 _hgexecutable = None
962
962
963 def hgexecutable():
963 def hgexecutable():
964 """return location of the 'hg' executable.
964 """return location of the 'hg' executable.
965
965
966 Defaults to $HG or 'hg' in the search path.
966 Defaults to $HG or 'hg' in the search path.
967 """
967 """
968 if _hgexecutable is None:
968 if _hgexecutable is None:
969 hg = encoding.environ.get('HG')
969 hg = encoding.environ.get('HG')
970 mainmod = sys.modules[pycompat.sysstr('__main__')]
970 mainmod = sys.modules[pycompat.sysstr('__main__')]
971 if hg:
971 if hg:
972 _sethgexecutable(hg)
972 _sethgexecutable(hg)
973 elif mainfrozen():
973 elif mainfrozen():
974 if getattr(sys, 'frozen', None) == 'macosx_app':
974 if getattr(sys, 'frozen', None) == 'macosx_app':
975 # Env variable set by py2app
975 # Env variable set by py2app
976 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
976 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
977 else:
977 else:
978 _sethgexecutable(pycompat.sysexecutable)
978 _sethgexecutable(pycompat.sysexecutable)
979 elif (os.path.basename(
979 elif (os.path.basename(
980 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
980 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
981 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
981 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
982 else:
982 else:
983 exe = findexe('hg') or os.path.basename(sys.argv[0])
983 exe = findexe('hg') or os.path.basename(sys.argv[0])
984 _sethgexecutable(exe)
984 _sethgexecutable(exe)
985 return _hgexecutable
985 return _hgexecutable
986
986
987 def _sethgexecutable(path):
987 def _sethgexecutable(path):
988 """set location of the 'hg' executable"""
988 """set location of the 'hg' executable"""
989 global _hgexecutable
989 global _hgexecutable
990 _hgexecutable = path
990 _hgexecutable = path
991
991
992 def _isstdout(f):
992 def _isstdout(f):
993 fileno = getattr(f, 'fileno', None)
993 fileno = getattr(f, 'fileno', None)
994 return fileno and fileno() == sys.__stdout__.fileno()
994 return fileno and fileno() == sys.__stdout__.fileno()
995
995
996 def shellenviron(environ=None):
996 def shellenviron(environ=None):
997 """return environ with optional override, useful for shelling out"""
997 """return environ with optional override, useful for shelling out"""
998 def py2shell(val):
998 def py2shell(val):
999 'convert python object into string that is useful to shell'
999 'convert python object into string that is useful to shell'
1000 if val is None or val is False:
1000 if val is None or val is False:
1001 return '0'
1001 return '0'
1002 if val is True:
1002 if val is True:
1003 return '1'
1003 return '1'
1004 return str(val)
1004 return str(val)
1005 env = dict(encoding.environ)
1005 env = dict(encoding.environ)
1006 if environ:
1006 if environ:
1007 env.update((k, py2shell(v)) for k, v in environ.iteritems())
1007 env.update((k, py2shell(v)) for k, v in environ.iteritems())
1008 env['HG'] = hgexecutable()
1008 env['HG'] = hgexecutable()
1009 return env
1009 return env
1010
1010
1011 def system(cmd, environ=None, cwd=None, out=None):
1011 def system(cmd, environ=None, cwd=None, out=None):
1012 '''enhanced shell command execution.
1012 '''enhanced shell command execution.
1013 run with environment maybe modified, maybe in different dir.
1013 run with environment maybe modified, maybe in different dir.
1014
1014
1015 if out is specified, it is assumed to be a file-like object that has a
1015 if out is specified, it is assumed to be a file-like object that has a
1016 write() method. stdout and stderr will be redirected to out.'''
1016 write() method. stdout and stderr will be redirected to out.'''
1017 try:
1017 try:
1018 stdout.flush()
1018 stdout.flush()
1019 except Exception:
1019 except Exception:
1020 pass
1020 pass
1021 cmd = quotecommand(cmd)
1021 cmd = quotecommand(cmd)
1022 if pycompat.sysplatform == 'plan9' and (sys.version_info[0] == 2
1022 if pycompat.sysplatform == 'plan9' and (sys.version_info[0] == 2
1023 and sys.version_info[1] < 7):
1023 and sys.version_info[1] < 7):
1024 # subprocess kludge to work around issues in half-baked Python
1024 # subprocess kludge to work around issues in half-baked Python
1025 # ports, notably bichued/python:
1025 # ports, notably bichued/python:
1026 if not cwd is None:
1026 if not cwd is None:
1027 os.chdir(cwd)
1027 os.chdir(cwd)
1028 rc = os.system(cmd)
1028 rc = os.system(cmd)
1029 else:
1029 else:
1030 env = shellenviron(environ)
1030 env = shellenviron(environ)
1031 if out is None or _isstdout(out):
1031 if out is None or _isstdout(out):
1032 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
1032 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
1033 env=env, cwd=cwd)
1033 env=env, cwd=cwd)
1034 else:
1034 else:
1035 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1035 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1036 env=env, cwd=cwd, stdout=subprocess.PIPE,
1036 env=env, cwd=cwd, stdout=subprocess.PIPE,
1037 stderr=subprocess.STDOUT)
1037 stderr=subprocess.STDOUT)
1038 for line in iter(proc.stdout.readline, ''):
1038 for line in iter(proc.stdout.readline, ''):
1039 out.write(line)
1039 out.write(line)
1040 proc.wait()
1040 proc.wait()
1041 rc = proc.returncode
1041 rc = proc.returncode
1042 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
1042 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
1043 rc = 0
1043 rc = 0
1044 return rc
1044 return rc
1045
1045
1046 def checksignature(func):
1046 def checksignature(func):
1047 '''wrap a function with code to check for calling errors'''
1047 '''wrap a function with code to check for calling errors'''
1048 def check(*args, **kwargs):
1048 def check(*args, **kwargs):
1049 try:
1049 try:
1050 return func(*args, **kwargs)
1050 return func(*args, **kwargs)
1051 except TypeError:
1051 except TypeError:
1052 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1052 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1053 raise error.SignatureError
1053 raise error.SignatureError
1054 raise
1054 raise
1055
1055
1056 return check
1056 return check
1057
1057
1058 # Hardlinks are problematic on CIFS, do not allow hardlinks
1059 # until we find a way to work around it cleanly (issue4546).
1060 # This is a variable so extensions can opt-in to using them.
1061 allowhardlinks = False
1062
1063 # a whilelist of known filesystems where hardlink works reliably
1058 # a whilelist of known filesystems where hardlink works reliably
1064 _hardlinkfswhitelist = set([
1059 _hardlinkfswhitelist = set([
1065 'btrfs',
1060 'btrfs',
1066 'ext2',
1061 'ext2',
1067 'ext3',
1062 'ext3',
1068 'ext4',
1063 'ext4',
1069 'jfs',
1064 'jfs',
1070 'reiserfs',
1065 'reiserfs',
1071 'tmpfs',
1066 'tmpfs',
1072 'xfs',
1067 'xfs',
1073 ])
1068 ])
1074
1069
1075 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1070 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1076 '''copy a file, preserving mode and optionally other stat info like
1071 '''copy a file, preserving mode and optionally other stat info like
1077 atime/mtime
1072 atime/mtime
1078
1073
1079 checkambig argument is used with filestat, and is useful only if
1074 checkambig argument is used with filestat, and is useful only if
1080 destination file is guarded by any lock (e.g. repo.lock or
1075 destination file is guarded by any lock (e.g. repo.lock or
1081 repo.wlock).
1076 repo.wlock).
1082
1077
1083 copystat and checkambig should be exclusive.
1078 copystat and checkambig should be exclusive.
1084 '''
1079 '''
1085 assert not (copystat and checkambig)
1080 assert not (copystat and checkambig)
1086 oldstat = None
1081 oldstat = None
1087 if os.path.lexists(dest):
1082 if os.path.lexists(dest):
1088 if checkambig:
1083 if checkambig:
1089 oldstat = checkambig and filestat(dest)
1084 oldstat = checkambig and filestat(dest)
1090 unlink(dest)
1085 unlink(dest)
1091 if hardlink:
1086 if hardlink:
1092 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1087 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1093 # unless we are confident that dest is on a whitelisted filesystem.
1088 # unless we are confident that dest is on a whitelisted filesystem.
1094 destdir = os.path.dirname(dest)
1089 destdir = os.path.dirname(dest)
1095 fstype = getattr(osutil, 'getfstype', lambda x: None)(destdir)
1090 fstype = getattr(osutil, 'getfstype', lambda x: None)(destdir)
1096 if fstype not in _hardlinkfswhitelist:
1091 if fstype not in _hardlinkfswhitelist:
1097 hardlink = False
1092 hardlink = False
1098 if allowhardlinks and hardlink:
1093 if hardlink:
1099 try:
1094 try:
1100 oslink(src, dest)
1095 oslink(src, dest)
1101 return
1096 return
1102 except (IOError, OSError):
1097 except (IOError, OSError):
1103 pass # fall back to normal copy
1098 pass # fall back to normal copy
1104 if os.path.islink(src):
1099 if os.path.islink(src):
1105 os.symlink(os.readlink(src), dest)
1100 os.symlink(os.readlink(src), dest)
1106 # copytime is ignored for symlinks, but in general copytime isn't needed
1101 # copytime is ignored for symlinks, but in general copytime isn't needed
1107 # for them anyway
1102 # for them anyway
1108 else:
1103 else:
1109 try:
1104 try:
1110 shutil.copyfile(src, dest)
1105 shutil.copyfile(src, dest)
1111 if copystat:
1106 if copystat:
1112 # copystat also copies mode
1107 # copystat also copies mode
1113 shutil.copystat(src, dest)
1108 shutil.copystat(src, dest)
1114 else:
1109 else:
1115 shutil.copymode(src, dest)
1110 shutil.copymode(src, dest)
1116 if oldstat and oldstat.stat:
1111 if oldstat and oldstat.stat:
1117 newstat = filestat(dest)
1112 newstat = filestat(dest)
1118 if newstat.isambig(oldstat):
1113 if newstat.isambig(oldstat):
1119 # stat of copied file is ambiguous to original one
1114 # stat of copied file is ambiguous to original one
1120 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1115 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1121 os.utime(dest, (advanced, advanced))
1116 os.utime(dest, (advanced, advanced))
1122 except shutil.Error as inst:
1117 except shutil.Error as inst:
1123 raise Abort(str(inst))
1118 raise Abort(str(inst))
1124
1119
1125 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1120 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1126 """Copy a directory tree using hardlinks if possible."""
1121 """Copy a directory tree using hardlinks if possible."""
1127 num = 0
1122 num = 0
1128
1123
1129 if hardlink is None:
1124 if hardlink is None:
1130 hardlink = (os.stat(src).st_dev ==
1125 hardlink = (os.stat(src).st_dev ==
1131 os.stat(os.path.dirname(dst)).st_dev)
1126 os.stat(os.path.dirname(dst)).st_dev)
1132 if hardlink:
1127 if hardlink:
1133 topic = _('linking')
1128 topic = _('linking')
1134 else:
1129 else:
1135 topic = _('copying')
1130 topic = _('copying')
1136
1131
1137 if os.path.isdir(src):
1132 if os.path.isdir(src):
1138 os.mkdir(dst)
1133 os.mkdir(dst)
1139 for name, kind in osutil.listdir(src):
1134 for name, kind in osutil.listdir(src):
1140 srcname = os.path.join(src, name)
1135 srcname = os.path.join(src, name)
1141 dstname = os.path.join(dst, name)
1136 dstname = os.path.join(dst, name)
1142 def nprog(t, pos):
1137 def nprog(t, pos):
1143 if pos is not None:
1138 if pos is not None:
1144 return progress(t, pos + num)
1139 return progress(t, pos + num)
1145 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1140 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1146 num += n
1141 num += n
1147 else:
1142 else:
1148 if hardlink:
1143 if hardlink:
1149 try:
1144 try:
1150 oslink(src, dst)
1145 oslink(src, dst)
1151 except (IOError, OSError):
1146 except (IOError, OSError):
1152 hardlink = False
1147 hardlink = False
1153 shutil.copy(src, dst)
1148 shutil.copy(src, dst)
1154 else:
1149 else:
1155 shutil.copy(src, dst)
1150 shutil.copy(src, dst)
1156 num += 1
1151 num += 1
1157 progress(topic, num)
1152 progress(topic, num)
1158 progress(topic, None)
1153 progress(topic, None)
1159
1154
1160 return hardlink, num
1155 return hardlink, num
1161
1156
1162 _winreservednames = '''con prn aux nul
1157 _winreservednames = '''con prn aux nul
1163 com1 com2 com3 com4 com5 com6 com7 com8 com9
1158 com1 com2 com3 com4 com5 com6 com7 com8 com9
1164 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
1159 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
1165 _winreservedchars = ':*?"<>|'
1160 _winreservedchars = ':*?"<>|'
1166 def checkwinfilename(path):
1161 def checkwinfilename(path):
1167 r'''Check that the base-relative path is a valid filename on Windows.
1162 r'''Check that the base-relative path is a valid filename on Windows.
1168 Returns None if the path is ok, or a UI string describing the problem.
1163 Returns None if the path is ok, or a UI string describing the problem.
1169
1164
1170 >>> checkwinfilename("just/a/normal/path")
1165 >>> checkwinfilename("just/a/normal/path")
1171 >>> checkwinfilename("foo/bar/con.xml")
1166 >>> checkwinfilename("foo/bar/con.xml")
1172 "filename contains 'con', which is reserved on Windows"
1167 "filename contains 'con', which is reserved on Windows"
1173 >>> checkwinfilename("foo/con.xml/bar")
1168 >>> checkwinfilename("foo/con.xml/bar")
1174 "filename contains 'con', which is reserved on Windows"
1169 "filename contains 'con', which is reserved on Windows"
1175 >>> checkwinfilename("foo/bar/xml.con")
1170 >>> checkwinfilename("foo/bar/xml.con")
1176 >>> checkwinfilename("foo/bar/AUX/bla.txt")
1171 >>> checkwinfilename("foo/bar/AUX/bla.txt")
1177 "filename contains 'AUX', which is reserved on Windows"
1172 "filename contains 'AUX', which is reserved on Windows"
1178 >>> checkwinfilename("foo/bar/bla:.txt")
1173 >>> checkwinfilename("foo/bar/bla:.txt")
1179 "filename contains ':', which is reserved on Windows"
1174 "filename contains ':', which is reserved on Windows"
1180 >>> checkwinfilename("foo/bar/b\07la.txt")
1175 >>> checkwinfilename("foo/bar/b\07la.txt")
1181 "filename contains '\\x07', which is invalid on Windows"
1176 "filename contains '\\x07', which is invalid on Windows"
1182 >>> checkwinfilename("foo/bar/bla ")
1177 >>> checkwinfilename("foo/bar/bla ")
1183 "filename ends with ' ', which is not allowed on Windows"
1178 "filename ends with ' ', which is not allowed on Windows"
1184 >>> checkwinfilename("../bar")
1179 >>> checkwinfilename("../bar")
1185 >>> checkwinfilename("foo\\")
1180 >>> checkwinfilename("foo\\")
1186 "filename ends with '\\', which is invalid on Windows"
1181 "filename ends with '\\', which is invalid on Windows"
1187 >>> checkwinfilename("foo\\/bar")
1182 >>> checkwinfilename("foo\\/bar")
1188 "directory name ends with '\\', which is invalid on Windows"
1183 "directory name ends with '\\', which is invalid on Windows"
1189 '''
1184 '''
1190 if path.endswith('\\'):
1185 if path.endswith('\\'):
1191 return _("filename ends with '\\', which is invalid on Windows")
1186 return _("filename ends with '\\', which is invalid on Windows")
1192 if '\\/' in path:
1187 if '\\/' in path:
1193 return _("directory name ends with '\\', which is invalid on Windows")
1188 return _("directory name ends with '\\', which is invalid on Windows")
1194 for n in path.replace('\\', '/').split('/'):
1189 for n in path.replace('\\', '/').split('/'):
1195 if not n:
1190 if not n:
1196 continue
1191 continue
1197 for c in pycompat.bytestr(n):
1192 for c in pycompat.bytestr(n):
1198 if c in _winreservedchars:
1193 if c in _winreservedchars:
1199 return _("filename contains '%s', which is reserved "
1194 return _("filename contains '%s', which is reserved "
1200 "on Windows") % c
1195 "on Windows") % c
1201 if ord(c) <= 31:
1196 if ord(c) <= 31:
1202 return _("filename contains %r, which is invalid "
1197 return _("filename contains %r, which is invalid "
1203 "on Windows") % c
1198 "on Windows") % c
1204 base = n.split('.')[0]
1199 base = n.split('.')[0]
1205 if base and base.lower() in _winreservednames:
1200 if base and base.lower() in _winreservednames:
1206 return _("filename contains '%s', which is reserved "
1201 return _("filename contains '%s', which is reserved "
1207 "on Windows") % base
1202 "on Windows") % base
1208 t = n[-1]
1203 t = n[-1]
1209 if t in '. ' and n not in '..':
1204 if t in '. ' and n not in '..':
1210 return _("filename ends with '%s', which is not allowed "
1205 return _("filename ends with '%s', which is not allowed "
1211 "on Windows") % t
1206 "on Windows") % t
1212
1207
1213 if pycompat.osname == 'nt':
1208 if pycompat.osname == 'nt':
1214 checkosfilename = checkwinfilename
1209 checkosfilename = checkwinfilename
1215 timer = time.clock
1210 timer = time.clock
1216 else:
1211 else:
1217 checkosfilename = platform.checkosfilename
1212 checkosfilename = platform.checkosfilename
1218 timer = time.time
1213 timer = time.time
1219
1214
1220 if safehasattr(time, "perf_counter"):
1215 if safehasattr(time, "perf_counter"):
1221 timer = time.perf_counter
1216 timer = time.perf_counter
1222
1217
1223 def makelock(info, pathname):
1218 def makelock(info, pathname):
1224 try:
1219 try:
1225 return os.symlink(info, pathname)
1220 return os.symlink(info, pathname)
1226 except OSError as why:
1221 except OSError as why:
1227 if why.errno == errno.EEXIST:
1222 if why.errno == errno.EEXIST:
1228 raise
1223 raise
1229 except AttributeError: # no symlink in os
1224 except AttributeError: # no symlink in os
1230 pass
1225 pass
1231
1226
1232 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1227 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1233 os.write(ld, info)
1228 os.write(ld, info)
1234 os.close(ld)
1229 os.close(ld)
1235
1230
1236 def readlock(pathname):
1231 def readlock(pathname):
1237 try:
1232 try:
1238 return os.readlink(pathname)
1233 return os.readlink(pathname)
1239 except OSError as why:
1234 except OSError as why:
1240 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1235 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1241 raise
1236 raise
1242 except AttributeError: # no symlink in os
1237 except AttributeError: # no symlink in os
1243 pass
1238 pass
1244 fp = posixfile(pathname)
1239 fp = posixfile(pathname)
1245 r = fp.read()
1240 r = fp.read()
1246 fp.close()
1241 fp.close()
1247 return r
1242 return r
1248
1243
1249 def fstat(fp):
1244 def fstat(fp):
1250 '''stat file object that may not have fileno method.'''
1245 '''stat file object that may not have fileno method.'''
1251 try:
1246 try:
1252 return os.fstat(fp.fileno())
1247 return os.fstat(fp.fileno())
1253 except AttributeError:
1248 except AttributeError:
1254 return os.stat(fp.name)
1249 return os.stat(fp.name)
1255
1250
1256 # File system features
1251 # File system features
1257
1252
1258 def fscasesensitive(path):
1253 def fscasesensitive(path):
1259 """
1254 """
1260 Return true if the given path is on a case-sensitive filesystem
1255 Return true if the given path is on a case-sensitive filesystem
1261
1256
1262 Requires a path (like /foo/.hg) ending with a foldable final
1257 Requires a path (like /foo/.hg) ending with a foldable final
1263 directory component.
1258 directory component.
1264 """
1259 """
1265 s1 = os.lstat(path)
1260 s1 = os.lstat(path)
1266 d, b = os.path.split(path)
1261 d, b = os.path.split(path)
1267 b2 = b.upper()
1262 b2 = b.upper()
1268 if b == b2:
1263 if b == b2:
1269 b2 = b.lower()
1264 b2 = b.lower()
1270 if b == b2:
1265 if b == b2:
1271 return True # no evidence against case sensitivity
1266 return True # no evidence against case sensitivity
1272 p2 = os.path.join(d, b2)
1267 p2 = os.path.join(d, b2)
1273 try:
1268 try:
1274 s2 = os.lstat(p2)
1269 s2 = os.lstat(p2)
1275 if s2 == s1:
1270 if s2 == s1:
1276 return False
1271 return False
1277 return True
1272 return True
1278 except OSError:
1273 except OSError:
1279 return True
1274 return True
1280
1275
1281 try:
1276 try:
1282 import re2
1277 import re2
1283 _re2 = None
1278 _re2 = None
1284 except ImportError:
1279 except ImportError:
1285 _re2 = False
1280 _re2 = False
1286
1281
1287 class _re(object):
1282 class _re(object):
1288 def _checkre2(self):
1283 def _checkre2(self):
1289 global _re2
1284 global _re2
1290 try:
1285 try:
1291 # check if match works, see issue3964
1286 # check if match works, see issue3964
1292 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1287 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1293 except ImportError:
1288 except ImportError:
1294 _re2 = False
1289 _re2 = False
1295
1290
1296 def compile(self, pat, flags=0):
1291 def compile(self, pat, flags=0):
1297 '''Compile a regular expression, using re2 if possible
1292 '''Compile a regular expression, using re2 if possible
1298
1293
1299 For best performance, use only re2-compatible regexp features. The
1294 For best performance, use only re2-compatible regexp features. The
1300 only flags from the re module that are re2-compatible are
1295 only flags from the re module that are re2-compatible are
1301 IGNORECASE and MULTILINE.'''
1296 IGNORECASE and MULTILINE.'''
1302 if _re2 is None:
1297 if _re2 is None:
1303 self._checkre2()
1298 self._checkre2()
1304 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1299 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1305 if flags & remod.IGNORECASE:
1300 if flags & remod.IGNORECASE:
1306 pat = '(?i)' + pat
1301 pat = '(?i)' + pat
1307 if flags & remod.MULTILINE:
1302 if flags & remod.MULTILINE:
1308 pat = '(?m)' + pat
1303 pat = '(?m)' + pat
1309 try:
1304 try:
1310 return re2.compile(pat)
1305 return re2.compile(pat)
1311 except re2.error:
1306 except re2.error:
1312 pass
1307 pass
1313 return remod.compile(pat, flags)
1308 return remod.compile(pat, flags)
1314
1309
1315 @propertycache
1310 @propertycache
1316 def escape(self):
1311 def escape(self):
1317 '''Return the version of escape corresponding to self.compile.
1312 '''Return the version of escape corresponding to self.compile.
1318
1313
1319 This is imperfect because whether re2 or re is used for a particular
1314 This is imperfect because whether re2 or re is used for a particular
1320 function depends on the flags, etc, but it's the best we can do.
1315 function depends on the flags, etc, but it's the best we can do.
1321 '''
1316 '''
1322 global _re2
1317 global _re2
1323 if _re2 is None:
1318 if _re2 is None:
1324 self._checkre2()
1319 self._checkre2()
1325 if _re2:
1320 if _re2:
1326 return re2.escape
1321 return re2.escape
1327 else:
1322 else:
1328 return remod.escape
1323 return remod.escape
1329
1324
1330 re = _re()
1325 re = _re()
1331
1326
1332 _fspathcache = {}
1327 _fspathcache = {}
1333 def fspath(name, root):
1328 def fspath(name, root):
1334 '''Get name in the case stored in the filesystem
1329 '''Get name in the case stored in the filesystem
1335
1330
1336 The name should be relative to root, and be normcase-ed for efficiency.
1331 The name should be relative to root, and be normcase-ed for efficiency.
1337
1332
1338 Note that this function is unnecessary, and should not be
1333 Note that this function is unnecessary, and should not be
1339 called, for case-sensitive filesystems (simply because it's expensive).
1334 called, for case-sensitive filesystems (simply because it's expensive).
1340
1335
1341 The root should be normcase-ed, too.
1336 The root should be normcase-ed, too.
1342 '''
1337 '''
1343 def _makefspathcacheentry(dir):
1338 def _makefspathcacheentry(dir):
1344 return dict((normcase(n), n) for n in os.listdir(dir))
1339 return dict((normcase(n), n) for n in os.listdir(dir))
1345
1340
1346 seps = pycompat.ossep
1341 seps = pycompat.ossep
1347 if pycompat.osaltsep:
1342 if pycompat.osaltsep:
1348 seps = seps + pycompat.osaltsep
1343 seps = seps + pycompat.osaltsep
1349 # Protect backslashes. This gets silly very quickly.
1344 # Protect backslashes. This gets silly very quickly.
1350 seps.replace('\\','\\\\')
1345 seps.replace('\\','\\\\')
1351 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
1346 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
1352 dir = os.path.normpath(root)
1347 dir = os.path.normpath(root)
1353 result = []
1348 result = []
1354 for part, sep in pattern.findall(name):
1349 for part, sep in pattern.findall(name):
1355 if sep:
1350 if sep:
1356 result.append(sep)
1351 result.append(sep)
1357 continue
1352 continue
1358
1353
1359 if dir not in _fspathcache:
1354 if dir not in _fspathcache:
1360 _fspathcache[dir] = _makefspathcacheentry(dir)
1355 _fspathcache[dir] = _makefspathcacheentry(dir)
1361 contents = _fspathcache[dir]
1356 contents = _fspathcache[dir]
1362
1357
1363 found = contents.get(part)
1358 found = contents.get(part)
1364 if not found:
1359 if not found:
1365 # retry "once per directory" per "dirstate.walk" which
1360 # retry "once per directory" per "dirstate.walk" which
1366 # may take place for each patches of "hg qpush", for example
1361 # may take place for each patches of "hg qpush", for example
1367 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1362 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1368 found = contents.get(part)
1363 found = contents.get(part)
1369
1364
1370 result.append(found or part)
1365 result.append(found or part)
1371 dir = os.path.join(dir, part)
1366 dir = os.path.join(dir, part)
1372
1367
1373 return ''.join(result)
1368 return ''.join(result)
1374
1369
1375 def checknlink(testfile):
1370 def checknlink(testfile):
1376 '''check whether hardlink count reporting works properly'''
1371 '''check whether hardlink count reporting works properly'''
1377
1372
1378 # testfile may be open, so we need a separate file for checking to
1373 # testfile may be open, so we need a separate file for checking to
1379 # work around issue2543 (or testfile may get lost on Samba shares)
1374 # work around issue2543 (or testfile may get lost on Samba shares)
1380 f1 = testfile + ".hgtmp1"
1375 f1 = testfile + ".hgtmp1"
1381 if os.path.lexists(f1):
1376 if os.path.lexists(f1):
1382 return False
1377 return False
1383 try:
1378 try:
1384 posixfile(f1, 'w').close()
1379 posixfile(f1, 'w').close()
1385 except IOError:
1380 except IOError:
1386 try:
1381 try:
1387 os.unlink(f1)
1382 os.unlink(f1)
1388 except OSError:
1383 except OSError:
1389 pass
1384 pass
1390 return False
1385 return False
1391
1386
1392 f2 = testfile + ".hgtmp2"
1387 f2 = testfile + ".hgtmp2"
1393 fd = None
1388 fd = None
1394 try:
1389 try:
1395 oslink(f1, f2)
1390 oslink(f1, f2)
1396 # nlinks() may behave differently for files on Windows shares if
1391 # nlinks() may behave differently for files on Windows shares if
1397 # the file is open.
1392 # the file is open.
1398 fd = posixfile(f2)
1393 fd = posixfile(f2)
1399 return nlinks(f2) > 1
1394 return nlinks(f2) > 1
1400 except OSError:
1395 except OSError:
1401 return False
1396 return False
1402 finally:
1397 finally:
1403 if fd is not None:
1398 if fd is not None:
1404 fd.close()
1399 fd.close()
1405 for f in (f1, f2):
1400 for f in (f1, f2):
1406 try:
1401 try:
1407 os.unlink(f)
1402 os.unlink(f)
1408 except OSError:
1403 except OSError:
1409 pass
1404 pass
1410
1405
1411 def endswithsep(path):
1406 def endswithsep(path):
1412 '''Check path ends with os.sep or os.altsep.'''
1407 '''Check path ends with os.sep or os.altsep.'''
1413 return (path.endswith(pycompat.ossep)
1408 return (path.endswith(pycompat.ossep)
1414 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
1409 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
1415
1410
1416 def splitpath(path):
1411 def splitpath(path):
1417 '''Split path by os.sep.
1412 '''Split path by os.sep.
1418 Note that this function does not use os.altsep because this is
1413 Note that this function does not use os.altsep because this is
1419 an alternative of simple "xxx.split(os.sep)".
1414 an alternative of simple "xxx.split(os.sep)".
1420 It is recommended to use os.path.normpath() before using this
1415 It is recommended to use os.path.normpath() before using this
1421 function if need.'''
1416 function if need.'''
1422 return path.split(pycompat.ossep)
1417 return path.split(pycompat.ossep)
1423
1418
1424 def gui():
1419 def gui():
1425 '''Are we running in a GUI?'''
1420 '''Are we running in a GUI?'''
1426 if pycompat.sysplatform == 'darwin':
1421 if pycompat.sysplatform == 'darwin':
1427 if 'SSH_CONNECTION' in encoding.environ:
1422 if 'SSH_CONNECTION' in encoding.environ:
1428 # handle SSH access to a box where the user is logged in
1423 # handle SSH access to a box where the user is logged in
1429 return False
1424 return False
1430 elif getattr(osutil, 'isgui', None):
1425 elif getattr(osutil, 'isgui', None):
1431 # check if a CoreGraphics session is available
1426 # check if a CoreGraphics session is available
1432 return osutil.isgui()
1427 return osutil.isgui()
1433 else:
1428 else:
1434 # pure build; use a safe default
1429 # pure build; use a safe default
1435 return True
1430 return True
1436 else:
1431 else:
1437 return pycompat.osname == "nt" or encoding.environ.get("DISPLAY")
1432 return pycompat.osname == "nt" or encoding.environ.get("DISPLAY")
1438
1433
1439 def mktempcopy(name, emptyok=False, createmode=None):
1434 def mktempcopy(name, emptyok=False, createmode=None):
1440 """Create a temporary file with the same contents from name
1435 """Create a temporary file with the same contents from name
1441
1436
1442 The permission bits are copied from the original file.
1437 The permission bits are copied from the original file.
1443
1438
1444 If the temporary file is going to be truncated immediately, you
1439 If the temporary file is going to be truncated immediately, you
1445 can use emptyok=True as an optimization.
1440 can use emptyok=True as an optimization.
1446
1441
1447 Returns the name of the temporary file.
1442 Returns the name of the temporary file.
1448 """
1443 """
1449 d, fn = os.path.split(name)
1444 d, fn = os.path.split(name)
1450 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1445 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1451 os.close(fd)
1446 os.close(fd)
1452 # Temporary files are created with mode 0600, which is usually not
1447 # Temporary files are created with mode 0600, which is usually not
1453 # what we want. If the original file already exists, just copy
1448 # what we want. If the original file already exists, just copy
1454 # its mode. Otherwise, manually obey umask.
1449 # its mode. Otherwise, manually obey umask.
1455 copymode(name, temp, createmode)
1450 copymode(name, temp, createmode)
1456 if emptyok:
1451 if emptyok:
1457 return temp
1452 return temp
1458 try:
1453 try:
1459 try:
1454 try:
1460 ifp = posixfile(name, "rb")
1455 ifp = posixfile(name, "rb")
1461 except IOError as inst:
1456 except IOError as inst:
1462 if inst.errno == errno.ENOENT:
1457 if inst.errno == errno.ENOENT:
1463 return temp
1458 return temp
1464 if not getattr(inst, 'filename', None):
1459 if not getattr(inst, 'filename', None):
1465 inst.filename = name
1460 inst.filename = name
1466 raise
1461 raise
1467 ofp = posixfile(temp, "wb")
1462 ofp = posixfile(temp, "wb")
1468 for chunk in filechunkiter(ifp):
1463 for chunk in filechunkiter(ifp):
1469 ofp.write(chunk)
1464 ofp.write(chunk)
1470 ifp.close()
1465 ifp.close()
1471 ofp.close()
1466 ofp.close()
1472 except: # re-raises
1467 except: # re-raises
1473 try: os.unlink(temp)
1468 try: os.unlink(temp)
1474 except OSError: pass
1469 except OSError: pass
1475 raise
1470 raise
1476 return temp
1471 return temp
1477
1472
1478 class filestat(object):
1473 class filestat(object):
1479 """help to exactly detect change of a file
1474 """help to exactly detect change of a file
1480
1475
1481 'stat' attribute is result of 'os.stat()' if specified 'path'
1476 'stat' attribute is result of 'os.stat()' if specified 'path'
1482 exists. Otherwise, it is None. This can avoid preparative
1477 exists. Otherwise, it is None. This can avoid preparative
1483 'exists()' examination on client side of this class.
1478 'exists()' examination on client side of this class.
1484 """
1479 """
1485 def __init__(self, path):
1480 def __init__(self, path):
1486 try:
1481 try:
1487 self.stat = os.stat(path)
1482 self.stat = os.stat(path)
1488 except OSError as err:
1483 except OSError as err:
1489 if err.errno != errno.ENOENT:
1484 if err.errno != errno.ENOENT:
1490 raise
1485 raise
1491 self.stat = None
1486 self.stat = None
1492
1487
1493 __hash__ = object.__hash__
1488 __hash__ = object.__hash__
1494
1489
1495 def __eq__(self, old):
1490 def __eq__(self, old):
1496 try:
1491 try:
1497 # if ambiguity between stat of new and old file is
1492 # if ambiguity between stat of new and old file is
1498 # avoided, comparison of size, ctime and mtime is enough
1493 # avoided, comparison of size, ctime and mtime is enough
1499 # to exactly detect change of a file regardless of platform
1494 # to exactly detect change of a file regardless of platform
1500 return (self.stat.st_size == old.stat.st_size and
1495 return (self.stat.st_size == old.stat.st_size and
1501 self.stat.st_ctime == old.stat.st_ctime and
1496 self.stat.st_ctime == old.stat.st_ctime and
1502 self.stat.st_mtime == old.stat.st_mtime)
1497 self.stat.st_mtime == old.stat.st_mtime)
1503 except AttributeError:
1498 except AttributeError:
1504 return False
1499 return False
1505
1500
1506 def isambig(self, old):
1501 def isambig(self, old):
1507 """Examine whether new (= self) stat is ambiguous against old one
1502 """Examine whether new (= self) stat is ambiguous against old one
1508
1503
1509 "S[N]" below means stat of a file at N-th change:
1504 "S[N]" below means stat of a file at N-th change:
1510
1505
1511 - S[n-1].ctime < S[n].ctime: can detect change of a file
1506 - S[n-1].ctime < S[n].ctime: can detect change of a file
1512 - S[n-1].ctime == S[n].ctime
1507 - S[n-1].ctime == S[n].ctime
1513 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1508 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1514 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1509 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1515 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1510 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1516 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1511 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1517
1512
1518 Case (*2) above means that a file was changed twice or more at
1513 Case (*2) above means that a file was changed twice or more at
1519 same time in sec (= S[n-1].ctime), and comparison of timestamp
1514 same time in sec (= S[n-1].ctime), and comparison of timestamp
1520 is ambiguous.
1515 is ambiguous.
1521
1516
1522 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1517 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1523 timestamp is ambiguous".
1518 timestamp is ambiguous".
1524
1519
1525 But advancing mtime only in case (*2) doesn't work as
1520 But advancing mtime only in case (*2) doesn't work as
1526 expected, because naturally advanced S[n].mtime in case (*1)
1521 expected, because naturally advanced S[n].mtime in case (*1)
1527 might be equal to manually advanced S[n-1 or earlier].mtime.
1522 might be equal to manually advanced S[n-1 or earlier].mtime.
1528
1523
1529 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1524 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1530 treated as ambiguous regardless of mtime, to avoid overlooking
1525 treated as ambiguous regardless of mtime, to avoid overlooking
1531 by confliction between such mtime.
1526 by confliction between such mtime.
1532
1527
1533 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
1528 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
1534 S[n].mtime", even if size of a file isn't changed.
1529 S[n].mtime", even if size of a file isn't changed.
1535 """
1530 """
1536 try:
1531 try:
1537 return (self.stat.st_ctime == old.stat.st_ctime)
1532 return (self.stat.st_ctime == old.stat.st_ctime)
1538 except AttributeError:
1533 except AttributeError:
1539 return False
1534 return False
1540
1535
1541 def avoidambig(self, path, old):
1536 def avoidambig(self, path, old):
1542 """Change file stat of specified path to avoid ambiguity
1537 """Change file stat of specified path to avoid ambiguity
1543
1538
1544 'old' should be previous filestat of 'path'.
1539 'old' should be previous filestat of 'path'.
1545
1540
1546 This skips avoiding ambiguity, if a process doesn't have
1541 This skips avoiding ambiguity, if a process doesn't have
1547 appropriate privileges for 'path'.
1542 appropriate privileges for 'path'.
1548 """
1543 """
1549 advanced = (old.stat.st_mtime + 1) & 0x7fffffff
1544 advanced = (old.stat.st_mtime + 1) & 0x7fffffff
1550 try:
1545 try:
1551 os.utime(path, (advanced, advanced))
1546 os.utime(path, (advanced, advanced))
1552 except OSError as inst:
1547 except OSError as inst:
1553 if inst.errno == errno.EPERM:
1548 if inst.errno == errno.EPERM:
1554 # utime() on the file created by another user causes EPERM,
1549 # utime() on the file created by another user causes EPERM,
1555 # if a process doesn't have appropriate privileges
1550 # if a process doesn't have appropriate privileges
1556 return
1551 return
1557 raise
1552 raise
1558
1553
1559 def __ne__(self, other):
1554 def __ne__(self, other):
1560 return not self == other
1555 return not self == other
1561
1556
1562 class atomictempfile(object):
1557 class atomictempfile(object):
1563 '''writable file object that atomically updates a file
1558 '''writable file object that atomically updates a file
1564
1559
1565 All writes will go to a temporary copy of the original file. Call
1560 All writes will go to a temporary copy of the original file. Call
1566 close() when you are done writing, and atomictempfile will rename
1561 close() when you are done writing, and atomictempfile will rename
1567 the temporary copy to the original name, making the changes
1562 the temporary copy to the original name, making the changes
1568 visible. If the object is destroyed without being closed, all your
1563 visible. If the object is destroyed without being closed, all your
1569 writes are discarded.
1564 writes are discarded.
1570
1565
1571 checkambig argument of constructor is used with filestat, and is
1566 checkambig argument of constructor is used with filestat, and is
1572 useful only if target file is guarded by any lock (e.g. repo.lock
1567 useful only if target file is guarded by any lock (e.g. repo.lock
1573 or repo.wlock).
1568 or repo.wlock).
1574 '''
1569 '''
1575 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1570 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1576 self.__name = name # permanent name
1571 self.__name = name # permanent name
1577 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1572 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1578 createmode=createmode)
1573 createmode=createmode)
1579 self._fp = posixfile(self._tempname, mode)
1574 self._fp = posixfile(self._tempname, mode)
1580 self._checkambig = checkambig
1575 self._checkambig = checkambig
1581
1576
1582 # delegated methods
1577 # delegated methods
1583 self.read = self._fp.read
1578 self.read = self._fp.read
1584 self.write = self._fp.write
1579 self.write = self._fp.write
1585 self.seek = self._fp.seek
1580 self.seek = self._fp.seek
1586 self.tell = self._fp.tell
1581 self.tell = self._fp.tell
1587 self.fileno = self._fp.fileno
1582 self.fileno = self._fp.fileno
1588
1583
1589 def close(self):
1584 def close(self):
1590 if not self._fp.closed:
1585 if not self._fp.closed:
1591 self._fp.close()
1586 self._fp.close()
1592 filename = localpath(self.__name)
1587 filename = localpath(self.__name)
1593 oldstat = self._checkambig and filestat(filename)
1588 oldstat = self._checkambig and filestat(filename)
1594 if oldstat and oldstat.stat:
1589 if oldstat and oldstat.stat:
1595 rename(self._tempname, filename)
1590 rename(self._tempname, filename)
1596 newstat = filestat(filename)
1591 newstat = filestat(filename)
1597 if newstat.isambig(oldstat):
1592 if newstat.isambig(oldstat):
1598 # stat of changed file is ambiguous to original one
1593 # stat of changed file is ambiguous to original one
1599 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1594 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1600 os.utime(filename, (advanced, advanced))
1595 os.utime(filename, (advanced, advanced))
1601 else:
1596 else:
1602 rename(self._tempname, filename)
1597 rename(self._tempname, filename)
1603
1598
1604 def discard(self):
1599 def discard(self):
1605 if not self._fp.closed:
1600 if not self._fp.closed:
1606 try:
1601 try:
1607 os.unlink(self._tempname)
1602 os.unlink(self._tempname)
1608 except OSError:
1603 except OSError:
1609 pass
1604 pass
1610 self._fp.close()
1605 self._fp.close()
1611
1606
1612 def __del__(self):
1607 def __del__(self):
1613 if safehasattr(self, '_fp'): # constructor actually did something
1608 if safehasattr(self, '_fp'): # constructor actually did something
1614 self.discard()
1609 self.discard()
1615
1610
1616 def __enter__(self):
1611 def __enter__(self):
1617 return self
1612 return self
1618
1613
1619 def __exit__(self, exctype, excvalue, traceback):
1614 def __exit__(self, exctype, excvalue, traceback):
1620 if exctype is not None:
1615 if exctype is not None:
1621 self.discard()
1616 self.discard()
1622 else:
1617 else:
1623 self.close()
1618 self.close()
1624
1619
1625 def unlinkpath(f, ignoremissing=False):
1620 def unlinkpath(f, ignoremissing=False):
1626 """unlink and remove the directory if it is empty"""
1621 """unlink and remove the directory if it is empty"""
1627 if ignoremissing:
1622 if ignoremissing:
1628 tryunlink(f)
1623 tryunlink(f)
1629 else:
1624 else:
1630 unlink(f)
1625 unlink(f)
1631 # try removing directories that might now be empty
1626 # try removing directories that might now be empty
1632 try:
1627 try:
1633 removedirs(os.path.dirname(f))
1628 removedirs(os.path.dirname(f))
1634 except OSError:
1629 except OSError:
1635 pass
1630 pass
1636
1631
1637 def tryunlink(f):
1632 def tryunlink(f):
1638 """Attempt to remove a file, ignoring ENOENT errors."""
1633 """Attempt to remove a file, ignoring ENOENT errors."""
1639 try:
1634 try:
1640 unlink(f)
1635 unlink(f)
1641 except OSError as e:
1636 except OSError as e:
1642 if e.errno != errno.ENOENT:
1637 if e.errno != errno.ENOENT:
1643 raise
1638 raise
1644
1639
1645 def makedirs(name, mode=None, notindexed=False):
1640 def makedirs(name, mode=None, notindexed=False):
1646 """recursive directory creation with parent mode inheritance
1641 """recursive directory creation with parent mode inheritance
1647
1642
1648 Newly created directories are marked as "not to be indexed by
1643 Newly created directories are marked as "not to be indexed by
1649 the content indexing service", if ``notindexed`` is specified
1644 the content indexing service", if ``notindexed`` is specified
1650 for "write" mode access.
1645 for "write" mode access.
1651 """
1646 """
1652 try:
1647 try:
1653 makedir(name, notindexed)
1648 makedir(name, notindexed)
1654 except OSError as err:
1649 except OSError as err:
1655 if err.errno == errno.EEXIST:
1650 if err.errno == errno.EEXIST:
1656 return
1651 return
1657 if err.errno != errno.ENOENT or not name:
1652 if err.errno != errno.ENOENT or not name:
1658 raise
1653 raise
1659 parent = os.path.dirname(os.path.abspath(name))
1654 parent = os.path.dirname(os.path.abspath(name))
1660 if parent == name:
1655 if parent == name:
1661 raise
1656 raise
1662 makedirs(parent, mode, notindexed)
1657 makedirs(parent, mode, notindexed)
1663 try:
1658 try:
1664 makedir(name, notindexed)
1659 makedir(name, notindexed)
1665 except OSError as err:
1660 except OSError as err:
1666 # Catch EEXIST to handle races
1661 # Catch EEXIST to handle races
1667 if err.errno == errno.EEXIST:
1662 if err.errno == errno.EEXIST:
1668 return
1663 return
1669 raise
1664 raise
1670 if mode is not None:
1665 if mode is not None:
1671 os.chmod(name, mode)
1666 os.chmod(name, mode)
1672
1667
1673 def readfile(path):
1668 def readfile(path):
1674 with open(path, 'rb') as fp:
1669 with open(path, 'rb') as fp:
1675 return fp.read()
1670 return fp.read()
1676
1671
1677 def writefile(path, text):
1672 def writefile(path, text):
1678 with open(path, 'wb') as fp:
1673 with open(path, 'wb') as fp:
1679 fp.write(text)
1674 fp.write(text)
1680
1675
1681 def appendfile(path, text):
1676 def appendfile(path, text):
1682 with open(path, 'ab') as fp:
1677 with open(path, 'ab') as fp:
1683 fp.write(text)
1678 fp.write(text)
1684
1679
1685 class chunkbuffer(object):
1680 class chunkbuffer(object):
1686 """Allow arbitrary sized chunks of data to be efficiently read from an
1681 """Allow arbitrary sized chunks of data to be efficiently read from an
1687 iterator over chunks of arbitrary size."""
1682 iterator over chunks of arbitrary size."""
1688
1683
1689 def __init__(self, in_iter):
1684 def __init__(self, in_iter):
1690 """in_iter is the iterator that's iterating over the input chunks.
1685 """in_iter is the iterator that's iterating over the input chunks.
1691 targetsize is how big a buffer to try to maintain."""
1686 targetsize is how big a buffer to try to maintain."""
1692 def splitbig(chunks):
1687 def splitbig(chunks):
1693 for chunk in chunks:
1688 for chunk in chunks:
1694 if len(chunk) > 2**20:
1689 if len(chunk) > 2**20:
1695 pos = 0
1690 pos = 0
1696 while pos < len(chunk):
1691 while pos < len(chunk):
1697 end = pos + 2 ** 18
1692 end = pos + 2 ** 18
1698 yield chunk[pos:end]
1693 yield chunk[pos:end]
1699 pos = end
1694 pos = end
1700 else:
1695 else:
1701 yield chunk
1696 yield chunk
1702 self.iter = splitbig(in_iter)
1697 self.iter = splitbig(in_iter)
1703 self._queue = collections.deque()
1698 self._queue = collections.deque()
1704 self._chunkoffset = 0
1699 self._chunkoffset = 0
1705
1700
1706 def read(self, l=None):
1701 def read(self, l=None):
1707 """Read L bytes of data from the iterator of chunks of data.
1702 """Read L bytes of data from the iterator of chunks of data.
1708 Returns less than L bytes if the iterator runs dry.
1703 Returns less than L bytes if the iterator runs dry.
1709
1704
1710 If size parameter is omitted, read everything"""
1705 If size parameter is omitted, read everything"""
1711 if l is None:
1706 if l is None:
1712 return ''.join(self.iter)
1707 return ''.join(self.iter)
1713
1708
1714 left = l
1709 left = l
1715 buf = []
1710 buf = []
1716 queue = self._queue
1711 queue = self._queue
1717 while left > 0:
1712 while left > 0:
1718 # refill the queue
1713 # refill the queue
1719 if not queue:
1714 if not queue:
1720 target = 2**18
1715 target = 2**18
1721 for chunk in self.iter:
1716 for chunk in self.iter:
1722 queue.append(chunk)
1717 queue.append(chunk)
1723 target -= len(chunk)
1718 target -= len(chunk)
1724 if target <= 0:
1719 if target <= 0:
1725 break
1720 break
1726 if not queue:
1721 if not queue:
1727 break
1722 break
1728
1723
1729 # The easy way to do this would be to queue.popleft(), modify the
1724 # The easy way to do this would be to queue.popleft(), modify the
1730 # chunk (if necessary), then queue.appendleft(). However, for cases
1725 # chunk (if necessary), then queue.appendleft(). However, for cases
1731 # where we read partial chunk content, this incurs 2 dequeue
1726 # where we read partial chunk content, this incurs 2 dequeue
1732 # mutations and creates a new str for the remaining chunk in the
1727 # mutations and creates a new str for the remaining chunk in the
1733 # queue. Our code below avoids this overhead.
1728 # queue. Our code below avoids this overhead.
1734
1729
1735 chunk = queue[0]
1730 chunk = queue[0]
1736 chunkl = len(chunk)
1731 chunkl = len(chunk)
1737 offset = self._chunkoffset
1732 offset = self._chunkoffset
1738
1733
1739 # Use full chunk.
1734 # Use full chunk.
1740 if offset == 0 and left >= chunkl:
1735 if offset == 0 and left >= chunkl:
1741 left -= chunkl
1736 left -= chunkl
1742 queue.popleft()
1737 queue.popleft()
1743 buf.append(chunk)
1738 buf.append(chunk)
1744 # self._chunkoffset remains at 0.
1739 # self._chunkoffset remains at 0.
1745 continue
1740 continue
1746
1741
1747 chunkremaining = chunkl - offset
1742 chunkremaining = chunkl - offset
1748
1743
1749 # Use all of unconsumed part of chunk.
1744 # Use all of unconsumed part of chunk.
1750 if left >= chunkremaining:
1745 if left >= chunkremaining:
1751 left -= chunkremaining
1746 left -= chunkremaining
1752 queue.popleft()
1747 queue.popleft()
1753 # offset == 0 is enabled by block above, so this won't merely
1748 # offset == 0 is enabled by block above, so this won't merely
1754 # copy via ``chunk[0:]``.
1749 # copy via ``chunk[0:]``.
1755 buf.append(chunk[offset:])
1750 buf.append(chunk[offset:])
1756 self._chunkoffset = 0
1751 self._chunkoffset = 0
1757
1752
1758 # Partial chunk needed.
1753 # Partial chunk needed.
1759 else:
1754 else:
1760 buf.append(chunk[offset:offset + left])
1755 buf.append(chunk[offset:offset + left])
1761 self._chunkoffset += left
1756 self._chunkoffset += left
1762 left -= chunkremaining
1757 left -= chunkremaining
1763
1758
1764 return ''.join(buf)
1759 return ''.join(buf)
1765
1760
1766 def filechunkiter(f, size=131072, limit=None):
1761 def filechunkiter(f, size=131072, limit=None):
1767 """Create a generator that produces the data in the file size
1762 """Create a generator that produces the data in the file size
1768 (default 131072) bytes at a time, up to optional limit (default is
1763 (default 131072) bytes at a time, up to optional limit (default is
1769 to read all data). Chunks may be less than size bytes if the
1764 to read all data). Chunks may be less than size bytes if the
1770 chunk is the last chunk in the file, or the file is a socket or
1765 chunk is the last chunk in the file, or the file is a socket or
1771 some other type of file that sometimes reads less data than is
1766 some other type of file that sometimes reads less data than is
1772 requested."""
1767 requested."""
1773 assert size >= 0
1768 assert size >= 0
1774 assert limit is None or limit >= 0
1769 assert limit is None or limit >= 0
1775 while True:
1770 while True:
1776 if limit is None:
1771 if limit is None:
1777 nbytes = size
1772 nbytes = size
1778 else:
1773 else:
1779 nbytes = min(limit, size)
1774 nbytes = min(limit, size)
1780 s = nbytes and f.read(nbytes)
1775 s = nbytes and f.read(nbytes)
1781 if not s:
1776 if not s:
1782 break
1777 break
1783 if limit:
1778 if limit:
1784 limit -= len(s)
1779 limit -= len(s)
1785 yield s
1780 yield s
1786
1781
1787 def makedate(timestamp=None):
1782 def makedate(timestamp=None):
1788 '''Return a unix timestamp (or the current time) as a (unixtime,
1783 '''Return a unix timestamp (or the current time) as a (unixtime,
1789 offset) tuple based off the local timezone.'''
1784 offset) tuple based off the local timezone.'''
1790 if timestamp is None:
1785 if timestamp is None:
1791 timestamp = time.time()
1786 timestamp = time.time()
1792 if timestamp < 0:
1787 if timestamp < 0:
1793 hint = _("check your clock")
1788 hint = _("check your clock")
1794 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1789 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1795 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1790 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1796 datetime.datetime.fromtimestamp(timestamp))
1791 datetime.datetime.fromtimestamp(timestamp))
1797 tz = delta.days * 86400 + delta.seconds
1792 tz = delta.days * 86400 + delta.seconds
1798 return timestamp, tz
1793 return timestamp, tz
1799
1794
1800 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1795 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1801 """represent a (unixtime, offset) tuple as a localized time.
1796 """represent a (unixtime, offset) tuple as a localized time.
1802 unixtime is seconds since the epoch, and offset is the time zone's
1797 unixtime is seconds since the epoch, and offset is the time zone's
1803 number of seconds away from UTC.
1798 number of seconds away from UTC.
1804
1799
1805 >>> datestr((0, 0))
1800 >>> datestr((0, 0))
1806 'Thu Jan 01 00:00:00 1970 +0000'
1801 'Thu Jan 01 00:00:00 1970 +0000'
1807 >>> datestr((42, 0))
1802 >>> datestr((42, 0))
1808 'Thu Jan 01 00:00:42 1970 +0000'
1803 'Thu Jan 01 00:00:42 1970 +0000'
1809 >>> datestr((-42, 0))
1804 >>> datestr((-42, 0))
1810 'Wed Dec 31 23:59:18 1969 +0000'
1805 'Wed Dec 31 23:59:18 1969 +0000'
1811 >>> datestr((0x7fffffff, 0))
1806 >>> datestr((0x7fffffff, 0))
1812 'Tue Jan 19 03:14:07 2038 +0000'
1807 'Tue Jan 19 03:14:07 2038 +0000'
1813 >>> datestr((-0x80000000, 0))
1808 >>> datestr((-0x80000000, 0))
1814 'Fri Dec 13 20:45:52 1901 +0000'
1809 'Fri Dec 13 20:45:52 1901 +0000'
1815 """
1810 """
1816 t, tz = date or makedate()
1811 t, tz = date or makedate()
1817 if "%1" in format or "%2" in format or "%z" in format:
1812 if "%1" in format or "%2" in format or "%z" in format:
1818 sign = (tz > 0) and "-" or "+"
1813 sign = (tz > 0) and "-" or "+"
1819 minutes = abs(tz) // 60
1814 minutes = abs(tz) // 60
1820 q, r = divmod(minutes, 60)
1815 q, r = divmod(minutes, 60)
1821 format = format.replace("%z", "%1%2")
1816 format = format.replace("%z", "%1%2")
1822 format = format.replace("%1", "%c%02d" % (sign, q))
1817 format = format.replace("%1", "%c%02d" % (sign, q))
1823 format = format.replace("%2", "%02d" % r)
1818 format = format.replace("%2", "%02d" % r)
1824 d = t - tz
1819 d = t - tz
1825 if d > 0x7fffffff:
1820 if d > 0x7fffffff:
1826 d = 0x7fffffff
1821 d = 0x7fffffff
1827 elif d < -0x80000000:
1822 elif d < -0x80000000:
1828 d = -0x80000000
1823 d = -0x80000000
1829 # Never use time.gmtime() and datetime.datetime.fromtimestamp()
1824 # Never use time.gmtime() and datetime.datetime.fromtimestamp()
1830 # because they use the gmtime() system call which is buggy on Windows
1825 # because they use the gmtime() system call which is buggy on Windows
1831 # for negative values.
1826 # for negative values.
1832 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
1827 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
1833 s = encoding.strtolocal(t.strftime(encoding.strfromlocal(format)))
1828 s = encoding.strtolocal(t.strftime(encoding.strfromlocal(format)))
1834 return s
1829 return s
1835
1830
1836 def shortdate(date=None):
1831 def shortdate(date=None):
1837 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1832 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1838 return datestr(date, format='%Y-%m-%d')
1833 return datestr(date, format='%Y-%m-%d')
1839
1834
1840 def parsetimezone(s):
1835 def parsetimezone(s):
1841 """find a trailing timezone, if any, in string, and return a
1836 """find a trailing timezone, if any, in string, and return a
1842 (offset, remainder) pair"""
1837 (offset, remainder) pair"""
1843
1838
1844 if s.endswith("GMT") or s.endswith("UTC"):
1839 if s.endswith("GMT") or s.endswith("UTC"):
1845 return 0, s[:-3].rstrip()
1840 return 0, s[:-3].rstrip()
1846
1841
1847 # Unix-style timezones [+-]hhmm
1842 # Unix-style timezones [+-]hhmm
1848 if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit():
1843 if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit():
1849 sign = (s[-5] == "+") and 1 or -1
1844 sign = (s[-5] == "+") and 1 or -1
1850 hours = int(s[-4:-2])
1845 hours = int(s[-4:-2])
1851 minutes = int(s[-2:])
1846 minutes = int(s[-2:])
1852 return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip()
1847 return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip()
1853
1848
1854 # ISO8601 trailing Z
1849 # ISO8601 trailing Z
1855 if s.endswith("Z") and s[-2:-1].isdigit():
1850 if s.endswith("Z") and s[-2:-1].isdigit():
1856 return 0, s[:-1]
1851 return 0, s[:-1]
1857
1852
1858 # ISO8601-style [+-]hh:mm
1853 # ISO8601-style [+-]hh:mm
1859 if (len(s) >= 6 and s[-6] in "+-" and s[-3] == ":" and
1854 if (len(s) >= 6 and s[-6] in "+-" and s[-3] == ":" and
1860 s[-5:-3].isdigit() and s[-2:].isdigit()):
1855 s[-5:-3].isdigit() and s[-2:].isdigit()):
1861 sign = (s[-6] == "+") and 1 or -1
1856 sign = (s[-6] == "+") and 1 or -1
1862 hours = int(s[-5:-3])
1857 hours = int(s[-5:-3])
1863 minutes = int(s[-2:])
1858 minutes = int(s[-2:])
1864 return -sign * (hours * 60 + minutes) * 60, s[:-6]
1859 return -sign * (hours * 60 + minutes) * 60, s[:-6]
1865
1860
1866 return None, s
1861 return None, s
1867
1862
1868 def strdate(string, format, defaults=None):
1863 def strdate(string, format, defaults=None):
1869 """parse a localized time string and return a (unixtime, offset) tuple.
1864 """parse a localized time string and return a (unixtime, offset) tuple.
1870 if the string cannot be parsed, ValueError is raised."""
1865 if the string cannot be parsed, ValueError is raised."""
1871 if defaults is None:
1866 if defaults is None:
1872 defaults = {}
1867 defaults = {}
1873
1868
1874 # NOTE: unixtime = localunixtime + offset
1869 # NOTE: unixtime = localunixtime + offset
1875 offset, date = parsetimezone(string)
1870 offset, date = parsetimezone(string)
1876
1871
1877 # add missing elements from defaults
1872 # add missing elements from defaults
1878 usenow = False # default to using biased defaults
1873 usenow = False # default to using biased defaults
1879 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1874 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1880 found = [True for p in part if ("%"+p) in format]
1875 found = [True for p in part if ("%"+p) in format]
1881 if not found:
1876 if not found:
1882 date += "@" + defaults[part][usenow]
1877 date += "@" + defaults[part][usenow]
1883 format += "@%" + part[0]
1878 format += "@%" + part[0]
1884 else:
1879 else:
1885 # We've found a specific time element, less specific time
1880 # We've found a specific time element, less specific time
1886 # elements are relative to today
1881 # elements are relative to today
1887 usenow = True
1882 usenow = True
1888
1883
1889 timetuple = time.strptime(date, format)
1884 timetuple = time.strptime(date, format)
1890 localunixtime = int(calendar.timegm(timetuple))
1885 localunixtime = int(calendar.timegm(timetuple))
1891 if offset is None:
1886 if offset is None:
1892 # local timezone
1887 # local timezone
1893 unixtime = int(time.mktime(timetuple))
1888 unixtime = int(time.mktime(timetuple))
1894 offset = unixtime - localunixtime
1889 offset = unixtime - localunixtime
1895 else:
1890 else:
1896 unixtime = localunixtime + offset
1891 unixtime = localunixtime + offset
1897 return unixtime, offset
1892 return unixtime, offset
1898
1893
1899 def parsedate(date, formats=None, bias=None):
1894 def parsedate(date, formats=None, bias=None):
1900 """parse a localized date/time and return a (unixtime, offset) tuple.
1895 """parse a localized date/time and return a (unixtime, offset) tuple.
1901
1896
1902 The date may be a "unixtime offset" string or in one of the specified
1897 The date may be a "unixtime offset" string or in one of the specified
1903 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1898 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1904
1899
1905 >>> parsedate(' today ') == parsedate(\
1900 >>> parsedate(' today ') == parsedate(\
1906 datetime.date.today().strftime('%b %d'))
1901 datetime.date.today().strftime('%b %d'))
1907 True
1902 True
1908 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1903 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1909 datetime.timedelta(days=1)\
1904 datetime.timedelta(days=1)\
1910 ).strftime('%b %d'))
1905 ).strftime('%b %d'))
1911 True
1906 True
1912 >>> now, tz = makedate()
1907 >>> now, tz = makedate()
1913 >>> strnow, strtz = parsedate('now')
1908 >>> strnow, strtz = parsedate('now')
1914 >>> (strnow - now) < 1
1909 >>> (strnow - now) < 1
1915 True
1910 True
1916 >>> tz == strtz
1911 >>> tz == strtz
1917 True
1912 True
1918 """
1913 """
1919 if bias is None:
1914 if bias is None:
1920 bias = {}
1915 bias = {}
1921 if not date:
1916 if not date:
1922 return 0, 0
1917 return 0, 0
1923 if isinstance(date, tuple) and len(date) == 2:
1918 if isinstance(date, tuple) and len(date) == 2:
1924 return date
1919 return date
1925 if not formats:
1920 if not formats:
1926 formats = defaultdateformats
1921 formats = defaultdateformats
1927 date = date.strip()
1922 date = date.strip()
1928
1923
1929 if date == 'now' or date == _('now'):
1924 if date == 'now' or date == _('now'):
1930 return makedate()
1925 return makedate()
1931 if date == 'today' or date == _('today'):
1926 if date == 'today' or date == _('today'):
1932 date = datetime.date.today().strftime('%b %d')
1927 date = datetime.date.today().strftime('%b %d')
1933 elif date == 'yesterday' or date == _('yesterday'):
1928 elif date == 'yesterday' or date == _('yesterday'):
1934 date = (datetime.date.today() -
1929 date = (datetime.date.today() -
1935 datetime.timedelta(days=1)).strftime('%b %d')
1930 datetime.timedelta(days=1)).strftime('%b %d')
1936
1931
1937 try:
1932 try:
1938 when, offset = map(int, date.split(' '))
1933 when, offset = map(int, date.split(' '))
1939 except ValueError:
1934 except ValueError:
1940 # fill out defaults
1935 # fill out defaults
1941 now = makedate()
1936 now = makedate()
1942 defaults = {}
1937 defaults = {}
1943 for part in ("d", "mb", "yY", "HI", "M", "S"):
1938 for part in ("d", "mb", "yY", "HI", "M", "S"):
1944 # this piece is for rounding the specific end of unknowns
1939 # this piece is for rounding the specific end of unknowns
1945 b = bias.get(part)
1940 b = bias.get(part)
1946 if b is None:
1941 if b is None:
1947 if part[0] in "HMS":
1942 if part[0] in "HMS":
1948 b = "00"
1943 b = "00"
1949 else:
1944 else:
1950 b = "0"
1945 b = "0"
1951
1946
1952 # this piece is for matching the generic end to today's date
1947 # this piece is for matching the generic end to today's date
1953 n = datestr(now, "%" + part[0])
1948 n = datestr(now, "%" + part[0])
1954
1949
1955 defaults[part] = (b, n)
1950 defaults[part] = (b, n)
1956
1951
1957 for format in formats:
1952 for format in formats:
1958 try:
1953 try:
1959 when, offset = strdate(date, format, defaults)
1954 when, offset = strdate(date, format, defaults)
1960 except (ValueError, OverflowError):
1955 except (ValueError, OverflowError):
1961 pass
1956 pass
1962 else:
1957 else:
1963 break
1958 break
1964 else:
1959 else:
1965 raise Abort(_('invalid date: %r') % date)
1960 raise Abort(_('invalid date: %r') % date)
1966 # validate explicit (probably user-specified) date and
1961 # validate explicit (probably user-specified) date and
1967 # time zone offset. values must fit in signed 32 bits for
1962 # time zone offset. values must fit in signed 32 bits for
1968 # current 32-bit linux runtimes. timezones go from UTC-12
1963 # current 32-bit linux runtimes. timezones go from UTC-12
1969 # to UTC+14
1964 # to UTC+14
1970 if when < -0x80000000 or when > 0x7fffffff:
1965 if when < -0x80000000 or when > 0x7fffffff:
1971 raise Abort(_('date exceeds 32 bits: %d') % when)
1966 raise Abort(_('date exceeds 32 bits: %d') % when)
1972 if offset < -50400 or offset > 43200:
1967 if offset < -50400 or offset > 43200:
1973 raise Abort(_('impossible time zone offset: %d') % offset)
1968 raise Abort(_('impossible time zone offset: %d') % offset)
1974 return when, offset
1969 return when, offset
1975
1970
1976 def matchdate(date):
1971 def matchdate(date):
1977 """Return a function that matches a given date match specifier
1972 """Return a function that matches a given date match specifier
1978
1973
1979 Formats include:
1974 Formats include:
1980
1975
1981 '{date}' match a given date to the accuracy provided
1976 '{date}' match a given date to the accuracy provided
1982
1977
1983 '<{date}' on or before a given date
1978 '<{date}' on or before a given date
1984
1979
1985 '>{date}' on or after a given date
1980 '>{date}' on or after a given date
1986
1981
1987 >>> p1 = parsedate("10:29:59")
1982 >>> p1 = parsedate("10:29:59")
1988 >>> p2 = parsedate("10:30:00")
1983 >>> p2 = parsedate("10:30:00")
1989 >>> p3 = parsedate("10:30:59")
1984 >>> p3 = parsedate("10:30:59")
1990 >>> p4 = parsedate("10:31:00")
1985 >>> p4 = parsedate("10:31:00")
1991 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1986 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1992 >>> f = matchdate("10:30")
1987 >>> f = matchdate("10:30")
1993 >>> f(p1[0])
1988 >>> f(p1[0])
1994 False
1989 False
1995 >>> f(p2[0])
1990 >>> f(p2[0])
1996 True
1991 True
1997 >>> f(p3[0])
1992 >>> f(p3[0])
1998 True
1993 True
1999 >>> f(p4[0])
1994 >>> f(p4[0])
2000 False
1995 False
2001 >>> f(p5[0])
1996 >>> f(p5[0])
2002 False
1997 False
2003 """
1998 """
2004
1999
2005 def lower(date):
2000 def lower(date):
2006 d = {'mb': "1", 'd': "1"}
2001 d = {'mb': "1", 'd': "1"}
2007 return parsedate(date, extendeddateformats, d)[0]
2002 return parsedate(date, extendeddateformats, d)[0]
2008
2003
2009 def upper(date):
2004 def upper(date):
2010 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
2005 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
2011 for days in ("31", "30", "29"):
2006 for days in ("31", "30", "29"):
2012 try:
2007 try:
2013 d["d"] = days
2008 d["d"] = days
2014 return parsedate(date, extendeddateformats, d)[0]
2009 return parsedate(date, extendeddateformats, d)[0]
2015 except Abort:
2010 except Abort:
2016 pass
2011 pass
2017 d["d"] = "28"
2012 d["d"] = "28"
2018 return parsedate(date, extendeddateformats, d)[0]
2013 return parsedate(date, extendeddateformats, d)[0]
2019
2014
2020 date = date.strip()
2015 date = date.strip()
2021
2016
2022 if not date:
2017 if not date:
2023 raise Abort(_("dates cannot consist entirely of whitespace"))
2018 raise Abort(_("dates cannot consist entirely of whitespace"))
2024 elif date[0] == "<":
2019 elif date[0] == "<":
2025 if not date[1:]:
2020 if not date[1:]:
2026 raise Abort(_("invalid day spec, use '<DATE'"))
2021 raise Abort(_("invalid day spec, use '<DATE'"))
2027 when = upper(date[1:])
2022 when = upper(date[1:])
2028 return lambda x: x <= when
2023 return lambda x: x <= when
2029 elif date[0] == ">":
2024 elif date[0] == ">":
2030 if not date[1:]:
2025 if not date[1:]:
2031 raise Abort(_("invalid day spec, use '>DATE'"))
2026 raise Abort(_("invalid day spec, use '>DATE'"))
2032 when = lower(date[1:])
2027 when = lower(date[1:])
2033 return lambda x: x >= when
2028 return lambda x: x >= when
2034 elif date[0] == "-":
2029 elif date[0] == "-":
2035 try:
2030 try:
2036 days = int(date[1:])
2031 days = int(date[1:])
2037 except ValueError:
2032 except ValueError:
2038 raise Abort(_("invalid day spec: %s") % date[1:])
2033 raise Abort(_("invalid day spec: %s") % date[1:])
2039 if days < 0:
2034 if days < 0:
2040 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
2035 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
2041 % date[1:])
2036 % date[1:])
2042 when = makedate()[0] - days * 3600 * 24
2037 when = makedate()[0] - days * 3600 * 24
2043 return lambda x: x >= when
2038 return lambda x: x >= when
2044 elif " to " in date:
2039 elif " to " in date:
2045 a, b = date.split(" to ")
2040 a, b = date.split(" to ")
2046 start, stop = lower(a), upper(b)
2041 start, stop = lower(a), upper(b)
2047 return lambda x: x >= start and x <= stop
2042 return lambda x: x >= start and x <= stop
2048 else:
2043 else:
2049 start, stop = lower(date), upper(date)
2044 start, stop = lower(date), upper(date)
2050 return lambda x: x >= start and x <= stop
2045 return lambda x: x >= start and x <= stop
2051
2046
2052 def stringmatcher(pattern, casesensitive=True):
2047 def stringmatcher(pattern, casesensitive=True):
2053 """
2048 """
2054 accepts a string, possibly starting with 're:' or 'literal:' prefix.
2049 accepts a string, possibly starting with 're:' or 'literal:' prefix.
2055 returns the matcher name, pattern, and matcher function.
2050 returns the matcher name, pattern, and matcher function.
2056 missing or unknown prefixes are treated as literal matches.
2051 missing or unknown prefixes are treated as literal matches.
2057
2052
2058 helper for tests:
2053 helper for tests:
2059 >>> def test(pattern, *tests):
2054 >>> def test(pattern, *tests):
2060 ... kind, pattern, matcher = stringmatcher(pattern)
2055 ... kind, pattern, matcher = stringmatcher(pattern)
2061 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2056 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2062 >>> def itest(pattern, *tests):
2057 >>> def itest(pattern, *tests):
2063 ... kind, pattern, matcher = stringmatcher(pattern, casesensitive=False)
2058 ... kind, pattern, matcher = stringmatcher(pattern, casesensitive=False)
2064 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2059 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2065
2060
2066 exact matching (no prefix):
2061 exact matching (no prefix):
2067 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
2062 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
2068 ('literal', 'abcdefg', [False, False, True])
2063 ('literal', 'abcdefg', [False, False, True])
2069
2064
2070 regex matching ('re:' prefix)
2065 regex matching ('re:' prefix)
2071 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
2066 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
2072 ('re', 'a.+b', [False, False, True])
2067 ('re', 'a.+b', [False, False, True])
2073
2068
2074 force exact matches ('literal:' prefix)
2069 force exact matches ('literal:' prefix)
2075 >>> test('literal:re:foobar', 'foobar', 're:foobar')
2070 >>> test('literal:re:foobar', 'foobar', 're:foobar')
2076 ('literal', 're:foobar', [False, True])
2071 ('literal', 're:foobar', [False, True])
2077
2072
2078 unknown prefixes are ignored and treated as literals
2073 unknown prefixes are ignored and treated as literals
2079 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
2074 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
2080 ('literal', 'foo:bar', [False, False, True])
2075 ('literal', 'foo:bar', [False, False, True])
2081
2076
2082 case insensitive regex matches
2077 case insensitive regex matches
2083 >>> itest('re:A.+b', 'nomatch', 'fooadef', 'fooadefBar')
2078 >>> itest('re:A.+b', 'nomatch', 'fooadef', 'fooadefBar')
2084 ('re', 'A.+b', [False, False, True])
2079 ('re', 'A.+b', [False, False, True])
2085
2080
2086 case insensitive literal matches
2081 case insensitive literal matches
2087 >>> itest('ABCDEFG', 'abc', 'def', 'abcdefg')
2082 >>> itest('ABCDEFG', 'abc', 'def', 'abcdefg')
2088 ('literal', 'ABCDEFG', [False, False, True])
2083 ('literal', 'ABCDEFG', [False, False, True])
2089 """
2084 """
2090 if pattern.startswith('re:'):
2085 if pattern.startswith('re:'):
2091 pattern = pattern[3:]
2086 pattern = pattern[3:]
2092 try:
2087 try:
2093 flags = 0
2088 flags = 0
2094 if not casesensitive:
2089 if not casesensitive:
2095 flags = remod.I
2090 flags = remod.I
2096 regex = remod.compile(pattern, flags)
2091 regex = remod.compile(pattern, flags)
2097 except remod.error as e:
2092 except remod.error as e:
2098 raise error.ParseError(_('invalid regular expression: %s')
2093 raise error.ParseError(_('invalid regular expression: %s')
2099 % e)
2094 % e)
2100 return 're', pattern, regex.search
2095 return 're', pattern, regex.search
2101 elif pattern.startswith('literal:'):
2096 elif pattern.startswith('literal:'):
2102 pattern = pattern[8:]
2097 pattern = pattern[8:]
2103
2098
2104 match = pattern.__eq__
2099 match = pattern.__eq__
2105
2100
2106 if not casesensitive:
2101 if not casesensitive:
2107 ipat = encoding.lower(pattern)
2102 ipat = encoding.lower(pattern)
2108 match = lambda s: ipat == encoding.lower(s)
2103 match = lambda s: ipat == encoding.lower(s)
2109 return 'literal', pattern, match
2104 return 'literal', pattern, match
2110
2105
2111 def shortuser(user):
2106 def shortuser(user):
2112 """Return a short representation of a user name or email address."""
2107 """Return a short representation of a user name or email address."""
2113 f = user.find('@')
2108 f = user.find('@')
2114 if f >= 0:
2109 if f >= 0:
2115 user = user[:f]
2110 user = user[:f]
2116 f = user.find('<')
2111 f = user.find('<')
2117 if f >= 0:
2112 if f >= 0:
2118 user = user[f + 1:]
2113 user = user[f + 1:]
2119 f = user.find(' ')
2114 f = user.find(' ')
2120 if f >= 0:
2115 if f >= 0:
2121 user = user[:f]
2116 user = user[:f]
2122 f = user.find('.')
2117 f = user.find('.')
2123 if f >= 0:
2118 if f >= 0:
2124 user = user[:f]
2119 user = user[:f]
2125 return user
2120 return user
2126
2121
2127 def emailuser(user):
2122 def emailuser(user):
2128 """Return the user portion of an email address."""
2123 """Return the user portion of an email address."""
2129 f = user.find('@')
2124 f = user.find('@')
2130 if f >= 0:
2125 if f >= 0:
2131 user = user[:f]
2126 user = user[:f]
2132 f = user.find('<')
2127 f = user.find('<')
2133 if f >= 0:
2128 if f >= 0:
2134 user = user[f + 1:]
2129 user = user[f + 1:]
2135 return user
2130 return user
2136
2131
2137 def email(author):
2132 def email(author):
2138 '''get email of author.'''
2133 '''get email of author.'''
2139 r = author.find('>')
2134 r = author.find('>')
2140 if r == -1:
2135 if r == -1:
2141 r = None
2136 r = None
2142 return author[author.find('<') + 1:r]
2137 return author[author.find('<') + 1:r]
2143
2138
2144 def ellipsis(text, maxlength=400):
2139 def ellipsis(text, maxlength=400):
2145 """Trim string to at most maxlength (default: 400) columns in display."""
2140 """Trim string to at most maxlength (default: 400) columns in display."""
2146 return encoding.trim(text, maxlength, ellipsis='...')
2141 return encoding.trim(text, maxlength, ellipsis='...')
2147
2142
2148 def unitcountfn(*unittable):
2143 def unitcountfn(*unittable):
2149 '''return a function that renders a readable count of some quantity'''
2144 '''return a function that renders a readable count of some quantity'''
2150
2145
2151 def go(count):
2146 def go(count):
2152 for multiplier, divisor, format in unittable:
2147 for multiplier, divisor, format in unittable:
2153 if count >= divisor * multiplier:
2148 if count >= divisor * multiplier:
2154 return format % (count / float(divisor))
2149 return format % (count / float(divisor))
2155 return unittable[-1][2] % count
2150 return unittable[-1][2] % count
2156
2151
2157 return go
2152 return go
2158
2153
2159 bytecount = unitcountfn(
2154 bytecount = unitcountfn(
2160 (100, 1 << 30, _('%.0f GB')),
2155 (100, 1 << 30, _('%.0f GB')),
2161 (10, 1 << 30, _('%.1f GB')),
2156 (10, 1 << 30, _('%.1f GB')),
2162 (1, 1 << 30, _('%.2f GB')),
2157 (1, 1 << 30, _('%.2f GB')),
2163 (100, 1 << 20, _('%.0f MB')),
2158 (100, 1 << 20, _('%.0f MB')),
2164 (10, 1 << 20, _('%.1f MB')),
2159 (10, 1 << 20, _('%.1f MB')),
2165 (1, 1 << 20, _('%.2f MB')),
2160 (1, 1 << 20, _('%.2f MB')),
2166 (100, 1 << 10, _('%.0f KB')),
2161 (100, 1 << 10, _('%.0f KB')),
2167 (10, 1 << 10, _('%.1f KB')),
2162 (10, 1 << 10, _('%.1f KB')),
2168 (1, 1 << 10, _('%.2f KB')),
2163 (1, 1 << 10, _('%.2f KB')),
2169 (1, 1, _('%.0f bytes')),
2164 (1, 1, _('%.0f bytes')),
2170 )
2165 )
2171
2166
2172 def escapestr(s):
2167 def escapestr(s):
2173 # call underlying function of s.encode('string_escape') directly for
2168 # call underlying function of s.encode('string_escape') directly for
2174 # Python 3 compatibility
2169 # Python 3 compatibility
2175 return codecs.escape_encode(s)[0]
2170 return codecs.escape_encode(s)[0]
2176
2171
2177 def unescapestr(s):
2172 def unescapestr(s):
2178 return codecs.escape_decode(s)[0]
2173 return codecs.escape_decode(s)[0]
2179
2174
2180 def uirepr(s):
2175 def uirepr(s):
2181 # Avoid double backslash in Windows path repr()
2176 # Avoid double backslash in Windows path repr()
2182 return repr(s).replace('\\\\', '\\')
2177 return repr(s).replace('\\\\', '\\')
2183
2178
2184 # delay import of textwrap
2179 # delay import of textwrap
2185 def MBTextWrapper(**kwargs):
2180 def MBTextWrapper(**kwargs):
2186 class tw(textwrap.TextWrapper):
2181 class tw(textwrap.TextWrapper):
2187 """
2182 """
2188 Extend TextWrapper for width-awareness.
2183 Extend TextWrapper for width-awareness.
2189
2184
2190 Neither number of 'bytes' in any encoding nor 'characters' is
2185 Neither number of 'bytes' in any encoding nor 'characters' is
2191 appropriate to calculate terminal columns for specified string.
2186 appropriate to calculate terminal columns for specified string.
2192
2187
2193 Original TextWrapper implementation uses built-in 'len()' directly,
2188 Original TextWrapper implementation uses built-in 'len()' directly,
2194 so overriding is needed to use width information of each characters.
2189 so overriding is needed to use width information of each characters.
2195
2190
2196 In addition, characters classified into 'ambiguous' width are
2191 In addition, characters classified into 'ambiguous' width are
2197 treated as wide in East Asian area, but as narrow in other.
2192 treated as wide in East Asian area, but as narrow in other.
2198
2193
2199 This requires use decision to determine width of such characters.
2194 This requires use decision to determine width of such characters.
2200 """
2195 """
2201 def _cutdown(self, ucstr, space_left):
2196 def _cutdown(self, ucstr, space_left):
2202 l = 0
2197 l = 0
2203 colwidth = encoding.ucolwidth
2198 colwidth = encoding.ucolwidth
2204 for i in xrange(len(ucstr)):
2199 for i in xrange(len(ucstr)):
2205 l += colwidth(ucstr[i])
2200 l += colwidth(ucstr[i])
2206 if space_left < l:
2201 if space_left < l:
2207 return (ucstr[:i], ucstr[i:])
2202 return (ucstr[:i], ucstr[i:])
2208 return ucstr, ''
2203 return ucstr, ''
2209
2204
2210 # overriding of base class
2205 # overriding of base class
2211 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2206 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2212 space_left = max(width - cur_len, 1)
2207 space_left = max(width - cur_len, 1)
2213
2208
2214 if self.break_long_words:
2209 if self.break_long_words:
2215 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2210 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2216 cur_line.append(cut)
2211 cur_line.append(cut)
2217 reversed_chunks[-1] = res
2212 reversed_chunks[-1] = res
2218 elif not cur_line:
2213 elif not cur_line:
2219 cur_line.append(reversed_chunks.pop())
2214 cur_line.append(reversed_chunks.pop())
2220
2215
2221 # this overriding code is imported from TextWrapper of Python 2.6
2216 # this overriding code is imported from TextWrapper of Python 2.6
2222 # to calculate columns of string by 'encoding.ucolwidth()'
2217 # to calculate columns of string by 'encoding.ucolwidth()'
2223 def _wrap_chunks(self, chunks):
2218 def _wrap_chunks(self, chunks):
2224 colwidth = encoding.ucolwidth
2219 colwidth = encoding.ucolwidth
2225
2220
2226 lines = []
2221 lines = []
2227 if self.width <= 0:
2222 if self.width <= 0:
2228 raise ValueError("invalid width %r (must be > 0)" % self.width)
2223 raise ValueError("invalid width %r (must be > 0)" % self.width)
2229
2224
2230 # Arrange in reverse order so items can be efficiently popped
2225 # Arrange in reverse order so items can be efficiently popped
2231 # from a stack of chucks.
2226 # from a stack of chucks.
2232 chunks.reverse()
2227 chunks.reverse()
2233
2228
2234 while chunks:
2229 while chunks:
2235
2230
2236 # Start the list of chunks that will make up the current line.
2231 # Start the list of chunks that will make up the current line.
2237 # cur_len is just the length of all the chunks in cur_line.
2232 # cur_len is just the length of all the chunks in cur_line.
2238 cur_line = []
2233 cur_line = []
2239 cur_len = 0
2234 cur_len = 0
2240
2235
2241 # Figure out which static string will prefix this line.
2236 # Figure out which static string will prefix this line.
2242 if lines:
2237 if lines:
2243 indent = self.subsequent_indent
2238 indent = self.subsequent_indent
2244 else:
2239 else:
2245 indent = self.initial_indent
2240 indent = self.initial_indent
2246
2241
2247 # Maximum width for this line.
2242 # Maximum width for this line.
2248 width = self.width - len(indent)
2243 width = self.width - len(indent)
2249
2244
2250 # First chunk on line is whitespace -- drop it, unless this
2245 # First chunk on line is whitespace -- drop it, unless this
2251 # is the very beginning of the text (i.e. no lines started yet).
2246 # is the very beginning of the text (i.e. no lines started yet).
2252 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
2247 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
2253 del chunks[-1]
2248 del chunks[-1]
2254
2249
2255 while chunks:
2250 while chunks:
2256 l = colwidth(chunks[-1])
2251 l = colwidth(chunks[-1])
2257
2252
2258 # Can at least squeeze this chunk onto the current line.
2253 # Can at least squeeze this chunk onto the current line.
2259 if cur_len + l <= width:
2254 if cur_len + l <= width:
2260 cur_line.append(chunks.pop())
2255 cur_line.append(chunks.pop())
2261 cur_len += l
2256 cur_len += l
2262
2257
2263 # Nope, this line is full.
2258 # Nope, this line is full.
2264 else:
2259 else:
2265 break
2260 break
2266
2261
2267 # The current line is full, and the next chunk is too big to
2262 # The current line is full, and the next chunk is too big to
2268 # fit on *any* line (not just this one).
2263 # fit on *any* line (not just this one).
2269 if chunks and colwidth(chunks[-1]) > width:
2264 if chunks and colwidth(chunks[-1]) > width:
2270 self._handle_long_word(chunks, cur_line, cur_len, width)
2265 self._handle_long_word(chunks, cur_line, cur_len, width)
2271
2266
2272 # If the last chunk on this line is all whitespace, drop it.
2267 # If the last chunk on this line is all whitespace, drop it.
2273 if (self.drop_whitespace and
2268 if (self.drop_whitespace and
2274 cur_line and cur_line[-1].strip() == ''):
2269 cur_line and cur_line[-1].strip() == ''):
2275 del cur_line[-1]
2270 del cur_line[-1]
2276
2271
2277 # Convert current line back to a string and store it in list
2272 # Convert current line back to a string and store it in list
2278 # of all lines (return value).
2273 # of all lines (return value).
2279 if cur_line:
2274 if cur_line:
2280 lines.append(indent + ''.join(cur_line))
2275 lines.append(indent + ''.join(cur_line))
2281
2276
2282 return lines
2277 return lines
2283
2278
2284 global MBTextWrapper
2279 global MBTextWrapper
2285 MBTextWrapper = tw
2280 MBTextWrapper = tw
2286 return tw(**kwargs)
2281 return tw(**kwargs)
2287
2282
2288 def wrap(line, width, initindent='', hangindent=''):
2283 def wrap(line, width, initindent='', hangindent=''):
2289 maxindent = max(len(hangindent), len(initindent))
2284 maxindent = max(len(hangindent), len(initindent))
2290 if width <= maxindent:
2285 if width <= maxindent:
2291 # adjust for weird terminal size
2286 # adjust for weird terminal size
2292 width = max(78, maxindent + 1)
2287 width = max(78, maxindent + 1)
2293 line = line.decode(pycompat.sysstr(encoding.encoding),
2288 line = line.decode(pycompat.sysstr(encoding.encoding),
2294 pycompat.sysstr(encoding.encodingmode))
2289 pycompat.sysstr(encoding.encodingmode))
2295 initindent = initindent.decode(pycompat.sysstr(encoding.encoding),
2290 initindent = initindent.decode(pycompat.sysstr(encoding.encoding),
2296 pycompat.sysstr(encoding.encodingmode))
2291 pycompat.sysstr(encoding.encodingmode))
2297 hangindent = hangindent.decode(pycompat.sysstr(encoding.encoding),
2292 hangindent = hangindent.decode(pycompat.sysstr(encoding.encoding),
2298 pycompat.sysstr(encoding.encodingmode))
2293 pycompat.sysstr(encoding.encodingmode))
2299 wrapper = MBTextWrapper(width=width,
2294 wrapper = MBTextWrapper(width=width,
2300 initial_indent=initindent,
2295 initial_indent=initindent,
2301 subsequent_indent=hangindent)
2296 subsequent_indent=hangindent)
2302 return wrapper.fill(line).encode(pycompat.sysstr(encoding.encoding))
2297 return wrapper.fill(line).encode(pycompat.sysstr(encoding.encoding))
2303
2298
2304 if (pyplatform.python_implementation() == 'CPython' and
2299 if (pyplatform.python_implementation() == 'CPython' and
2305 sys.version_info < (3, 0)):
2300 sys.version_info < (3, 0)):
2306 # There is an issue in CPython that some IO methods do not handle EINTR
2301 # There is an issue in CPython that some IO methods do not handle EINTR
2307 # correctly. The following table shows what CPython version (and functions)
2302 # correctly. The following table shows what CPython version (and functions)
2308 # are affected (buggy: has the EINTR bug, okay: otherwise):
2303 # are affected (buggy: has the EINTR bug, okay: otherwise):
2309 #
2304 #
2310 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2305 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2311 # --------------------------------------------------
2306 # --------------------------------------------------
2312 # fp.__iter__ | buggy | buggy | okay
2307 # fp.__iter__ | buggy | buggy | okay
2313 # fp.read* | buggy | okay [1] | okay
2308 # fp.read* | buggy | okay [1] | okay
2314 #
2309 #
2315 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2310 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2316 #
2311 #
2317 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2312 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2318 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2313 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2319 #
2314 #
2320 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2315 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2321 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2316 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2322 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2317 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2323 # fp.__iter__ but not other fp.read* methods.
2318 # fp.__iter__ but not other fp.read* methods.
2324 #
2319 #
2325 # On modern systems like Linux, the "read" syscall cannot be interrupted
2320 # On modern systems like Linux, the "read" syscall cannot be interrupted
2326 # when reading "fast" files like on-disk files. So the EINTR issue only
2321 # when reading "fast" files like on-disk files. So the EINTR issue only
2327 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2322 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2328 # files approximately as "fast" files and use the fast (unsafe) code path,
2323 # files approximately as "fast" files and use the fast (unsafe) code path,
2329 # to minimize the performance impact.
2324 # to minimize the performance impact.
2330 if sys.version_info >= (2, 7, 4):
2325 if sys.version_info >= (2, 7, 4):
2331 # fp.readline deals with EINTR correctly, use it as a workaround.
2326 # fp.readline deals with EINTR correctly, use it as a workaround.
2332 def _safeiterfile(fp):
2327 def _safeiterfile(fp):
2333 return iter(fp.readline, '')
2328 return iter(fp.readline, '')
2334 else:
2329 else:
2335 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2330 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2336 # note: this may block longer than necessary because of bufsize.
2331 # note: this may block longer than necessary because of bufsize.
2337 def _safeiterfile(fp, bufsize=4096):
2332 def _safeiterfile(fp, bufsize=4096):
2338 fd = fp.fileno()
2333 fd = fp.fileno()
2339 line = ''
2334 line = ''
2340 while True:
2335 while True:
2341 try:
2336 try:
2342 buf = os.read(fd, bufsize)
2337 buf = os.read(fd, bufsize)
2343 except OSError as ex:
2338 except OSError as ex:
2344 # os.read only raises EINTR before any data is read
2339 # os.read only raises EINTR before any data is read
2345 if ex.errno == errno.EINTR:
2340 if ex.errno == errno.EINTR:
2346 continue
2341 continue
2347 else:
2342 else:
2348 raise
2343 raise
2349 line += buf
2344 line += buf
2350 if '\n' in buf:
2345 if '\n' in buf:
2351 splitted = line.splitlines(True)
2346 splitted = line.splitlines(True)
2352 line = ''
2347 line = ''
2353 for l in splitted:
2348 for l in splitted:
2354 if l[-1] == '\n':
2349 if l[-1] == '\n':
2355 yield l
2350 yield l
2356 else:
2351 else:
2357 line = l
2352 line = l
2358 if not buf:
2353 if not buf:
2359 break
2354 break
2360 if line:
2355 if line:
2361 yield line
2356 yield line
2362
2357
2363 def iterfile(fp):
2358 def iterfile(fp):
2364 fastpath = True
2359 fastpath = True
2365 if type(fp) is file:
2360 if type(fp) is file:
2366 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2361 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2367 if fastpath:
2362 if fastpath:
2368 return fp
2363 return fp
2369 else:
2364 else:
2370 return _safeiterfile(fp)
2365 return _safeiterfile(fp)
2371 else:
2366 else:
2372 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2367 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2373 def iterfile(fp):
2368 def iterfile(fp):
2374 return fp
2369 return fp
2375
2370
2376 def iterlines(iterator):
2371 def iterlines(iterator):
2377 for chunk in iterator:
2372 for chunk in iterator:
2378 for line in chunk.splitlines():
2373 for line in chunk.splitlines():
2379 yield line
2374 yield line
2380
2375
2381 def expandpath(path):
2376 def expandpath(path):
2382 return os.path.expanduser(os.path.expandvars(path))
2377 return os.path.expanduser(os.path.expandvars(path))
2383
2378
2384 def hgcmd():
2379 def hgcmd():
2385 """Return the command used to execute current hg
2380 """Return the command used to execute current hg
2386
2381
2387 This is different from hgexecutable() because on Windows we want
2382 This is different from hgexecutable() because on Windows we want
2388 to avoid things opening new shell windows like batch files, so we
2383 to avoid things opening new shell windows like batch files, so we
2389 get either the python call or current executable.
2384 get either the python call or current executable.
2390 """
2385 """
2391 if mainfrozen():
2386 if mainfrozen():
2392 if getattr(sys, 'frozen', None) == 'macosx_app':
2387 if getattr(sys, 'frozen', None) == 'macosx_app':
2393 # Env variable set by py2app
2388 # Env variable set by py2app
2394 return [encoding.environ['EXECUTABLEPATH']]
2389 return [encoding.environ['EXECUTABLEPATH']]
2395 else:
2390 else:
2396 return [pycompat.sysexecutable]
2391 return [pycompat.sysexecutable]
2397 return gethgcmd()
2392 return gethgcmd()
2398
2393
2399 def rundetached(args, condfn):
2394 def rundetached(args, condfn):
2400 """Execute the argument list in a detached process.
2395 """Execute the argument list in a detached process.
2401
2396
2402 condfn is a callable which is called repeatedly and should return
2397 condfn is a callable which is called repeatedly and should return
2403 True once the child process is known to have started successfully.
2398 True once the child process is known to have started successfully.
2404 At this point, the child process PID is returned. If the child
2399 At this point, the child process PID is returned. If the child
2405 process fails to start or finishes before condfn() evaluates to
2400 process fails to start or finishes before condfn() evaluates to
2406 True, return -1.
2401 True, return -1.
2407 """
2402 """
2408 # Windows case is easier because the child process is either
2403 # Windows case is easier because the child process is either
2409 # successfully starting and validating the condition or exiting
2404 # successfully starting and validating the condition or exiting
2410 # on failure. We just poll on its PID. On Unix, if the child
2405 # on failure. We just poll on its PID. On Unix, if the child
2411 # process fails to start, it will be left in a zombie state until
2406 # process fails to start, it will be left in a zombie state until
2412 # the parent wait on it, which we cannot do since we expect a long
2407 # the parent wait on it, which we cannot do since we expect a long
2413 # running process on success. Instead we listen for SIGCHLD telling
2408 # running process on success. Instead we listen for SIGCHLD telling
2414 # us our child process terminated.
2409 # us our child process terminated.
2415 terminated = set()
2410 terminated = set()
2416 def handler(signum, frame):
2411 def handler(signum, frame):
2417 terminated.add(os.wait())
2412 terminated.add(os.wait())
2418 prevhandler = None
2413 prevhandler = None
2419 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2414 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2420 if SIGCHLD is not None:
2415 if SIGCHLD is not None:
2421 prevhandler = signal.signal(SIGCHLD, handler)
2416 prevhandler = signal.signal(SIGCHLD, handler)
2422 try:
2417 try:
2423 pid = spawndetached(args)
2418 pid = spawndetached(args)
2424 while not condfn():
2419 while not condfn():
2425 if ((pid in terminated or not testpid(pid))
2420 if ((pid in terminated or not testpid(pid))
2426 and not condfn()):
2421 and not condfn()):
2427 return -1
2422 return -1
2428 time.sleep(0.1)
2423 time.sleep(0.1)
2429 return pid
2424 return pid
2430 finally:
2425 finally:
2431 if prevhandler is not None:
2426 if prevhandler is not None:
2432 signal.signal(signal.SIGCHLD, prevhandler)
2427 signal.signal(signal.SIGCHLD, prevhandler)
2433
2428
2434 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2429 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2435 """Return the result of interpolating items in the mapping into string s.
2430 """Return the result of interpolating items in the mapping into string s.
2436
2431
2437 prefix is a single character string, or a two character string with
2432 prefix is a single character string, or a two character string with
2438 a backslash as the first character if the prefix needs to be escaped in
2433 a backslash as the first character if the prefix needs to be escaped in
2439 a regular expression.
2434 a regular expression.
2440
2435
2441 fn is an optional function that will be applied to the replacement text
2436 fn is an optional function that will be applied to the replacement text
2442 just before replacement.
2437 just before replacement.
2443
2438
2444 escape_prefix is an optional flag that allows using doubled prefix for
2439 escape_prefix is an optional flag that allows using doubled prefix for
2445 its escaping.
2440 its escaping.
2446 """
2441 """
2447 fn = fn or (lambda s: s)
2442 fn = fn or (lambda s: s)
2448 patterns = '|'.join(mapping.keys())
2443 patterns = '|'.join(mapping.keys())
2449 if escape_prefix:
2444 if escape_prefix:
2450 patterns += '|' + prefix
2445 patterns += '|' + prefix
2451 if len(prefix) > 1:
2446 if len(prefix) > 1:
2452 prefix_char = prefix[1:]
2447 prefix_char = prefix[1:]
2453 else:
2448 else:
2454 prefix_char = prefix
2449 prefix_char = prefix
2455 mapping[prefix_char] = prefix_char
2450 mapping[prefix_char] = prefix_char
2456 r = remod.compile(r'%s(%s)' % (prefix, patterns))
2451 r = remod.compile(r'%s(%s)' % (prefix, patterns))
2457 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2452 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2458
2453
2459 def getport(port):
2454 def getport(port):
2460 """Return the port for a given network service.
2455 """Return the port for a given network service.
2461
2456
2462 If port is an integer, it's returned as is. If it's a string, it's
2457 If port is an integer, it's returned as is. If it's a string, it's
2463 looked up using socket.getservbyname(). If there's no matching
2458 looked up using socket.getservbyname(). If there's no matching
2464 service, error.Abort is raised.
2459 service, error.Abort is raised.
2465 """
2460 """
2466 try:
2461 try:
2467 return int(port)
2462 return int(port)
2468 except ValueError:
2463 except ValueError:
2469 pass
2464 pass
2470
2465
2471 try:
2466 try:
2472 return socket.getservbyname(port)
2467 return socket.getservbyname(port)
2473 except socket.error:
2468 except socket.error:
2474 raise Abort(_("no port number associated with service '%s'") % port)
2469 raise Abort(_("no port number associated with service '%s'") % port)
2475
2470
2476 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2471 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2477 '0': False, 'no': False, 'false': False, 'off': False,
2472 '0': False, 'no': False, 'false': False, 'off': False,
2478 'never': False}
2473 'never': False}
2479
2474
2480 def parsebool(s):
2475 def parsebool(s):
2481 """Parse s into a boolean.
2476 """Parse s into a boolean.
2482
2477
2483 If s is not a valid boolean, returns None.
2478 If s is not a valid boolean, returns None.
2484 """
2479 """
2485 return _booleans.get(s.lower(), None)
2480 return _booleans.get(s.lower(), None)
2486
2481
2487 _hextochr = dict((a + b, chr(int(a + b, 16)))
2482 _hextochr = dict((a + b, chr(int(a + b, 16)))
2488 for a in string.hexdigits for b in string.hexdigits)
2483 for a in string.hexdigits for b in string.hexdigits)
2489
2484
2490 class url(object):
2485 class url(object):
2491 r"""Reliable URL parser.
2486 r"""Reliable URL parser.
2492
2487
2493 This parses URLs and provides attributes for the following
2488 This parses URLs and provides attributes for the following
2494 components:
2489 components:
2495
2490
2496 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2491 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2497
2492
2498 Missing components are set to None. The only exception is
2493 Missing components are set to None. The only exception is
2499 fragment, which is set to '' if present but empty.
2494 fragment, which is set to '' if present but empty.
2500
2495
2501 If parsefragment is False, fragment is included in query. If
2496 If parsefragment is False, fragment is included in query. If
2502 parsequery is False, query is included in path. If both are
2497 parsequery is False, query is included in path. If both are
2503 False, both fragment and query are included in path.
2498 False, both fragment and query are included in path.
2504
2499
2505 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2500 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2506
2501
2507 Note that for backward compatibility reasons, bundle URLs do not
2502 Note that for backward compatibility reasons, bundle URLs do not
2508 take host names. That means 'bundle://../' has a path of '../'.
2503 take host names. That means 'bundle://../' has a path of '../'.
2509
2504
2510 Examples:
2505 Examples:
2511
2506
2512 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
2507 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
2513 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2508 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2514 >>> url('ssh://[::1]:2200//home/joe/repo')
2509 >>> url('ssh://[::1]:2200//home/joe/repo')
2515 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2510 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2516 >>> url('file:///home/joe/repo')
2511 >>> url('file:///home/joe/repo')
2517 <url scheme: 'file', path: '/home/joe/repo'>
2512 <url scheme: 'file', path: '/home/joe/repo'>
2518 >>> url('file:///c:/temp/foo/')
2513 >>> url('file:///c:/temp/foo/')
2519 <url scheme: 'file', path: 'c:/temp/foo/'>
2514 <url scheme: 'file', path: 'c:/temp/foo/'>
2520 >>> url('bundle:foo')
2515 >>> url('bundle:foo')
2521 <url scheme: 'bundle', path: 'foo'>
2516 <url scheme: 'bundle', path: 'foo'>
2522 >>> url('bundle://../foo')
2517 >>> url('bundle://../foo')
2523 <url scheme: 'bundle', path: '../foo'>
2518 <url scheme: 'bundle', path: '../foo'>
2524 >>> url(r'c:\foo\bar')
2519 >>> url(r'c:\foo\bar')
2525 <url path: 'c:\\foo\\bar'>
2520 <url path: 'c:\\foo\\bar'>
2526 >>> url(r'\\blah\blah\blah')
2521 >>> url(r'\\blah\blah\blah')
2527 <url path: '\\\\blah\\blah\\blah'>
2522 <url path: '\\\\blah\\blah\\blah'>
2528 >>> url(r'\\blah\blah\blah#baz')
2523 >>> url(r'\\blah\blah\blah#baz')
2529 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2524 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2530 >>> url(r'file:///C:\users\me')
2525 >>> url(r'file:///C:\users\me')
2531 <url scheme: 'file', path: 'C:\\users\\me'>
2526 <url scheme: 'file', path: 'C:\\users\\me'>
2532
2527
2533 Authentication credentials:
2528 Authentication credentials:
2534
2529
2535 >>> url('ssh://joe:xyz@x/repo')
2530 >>> url('ssh://joe:xyz@x/repo')
2536 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2531 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2537 >>> url('ssh://joe@x/repo')
2532 >>> url('ssh://joe@x/repo')
2538 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2533 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2539
2534
2540 Query strings and fragments:
2535 Query strings and fragments:
2541
2536
2542 >>> url('http://host/a?b#c')
2537 >>> url('http://host/a?b#c')
2543 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2538 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2544 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2539 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2545 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2540 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2546
2541
2547 Empty path:
2542 Empty path:
2548
2543
2549 >>> url('')
2544 >>> url('')
2550 <url path: ''>
2545 <url path: ''>
2551 >>> url('#a')
2546 >>> url('#a')
2552 <url path: '', fragment: 'a'>
2547 <url path: '', fragment: 'a'>
2553 >>> url('http://host/')
2548 >>> url('http://host/')
2554 <url scheme: 'http', host: 'host', path: ''>
2549 <url scheme: 'http', host: 'host', path: ''>
2555 >>> url('http://host/#a')
2550 >>> url('http://host/#a')
2556 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2551 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2557
2552
2558 Only scheme:
2553 Only scheme:
2559
2554
2560 >>> url('http:')
2555 >>> url('http:')
2561 <url scheme: 'http'>
2556 <url scheme: 'http'>
2562 """
2557 """
2563
2558
2564 _safechars = "!~*'()+"
2559 _safechars = "!~*'()+"
2565 _safepchars = "/!~*'()+:\\"
2560 _safepchars = "/!~*'()+:\\"
2566 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2561 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2567
2562
2568 def __init__(self, path, parsequery=True, parsefragment=True):
2563 def __init__(self, path, parsequery=True, parsefragment=True):
2569 # We slowly chomp away at path until we have only the path left
2564 # We slowly chomp away at path until we have only the path left
2570 self.scheme = self.user = self.passwd = self.host = None
2565 self.scheme = self.user = self.passwd = self.host = None
2571 self.port = self.path = self.query = self.fragment = None
2566 self.port = self.path = self.query = self.fragment = None
2572 self._localpath = True
2567 self._localpath = True
2573 self._hostport = ''
2568 self._hostport = ''
2574 self._origpath = path
2569 self._origpath = path
2575
2570
2576 if parsefragment and '#' in path:
2571 if parsefragment and '#' in path:
2577 path, self.fragment = path.split('#', 1)
2572 path, self.fragment = path.split('#', 1)
2578
2573
2579 # special case for Windows drive letters and UNC paths
2574 # special case for Windows drive letters and UNC paths
2580 if hasdriveletter(path) or path.startswith('\\\\'):
2575 if hasdriveletter(path) or path.startswith('\\\\'):
2581 self.path = path
2576 self.path = path
2582 return
2577 return
2583
2578
2584 # For compatibility reasons, we can't handle bundle paths as
2579 # For compatibility reasons, we can't handle bundle paths as
2585 # normal URLS
2580 # normal URLS
2586 if path.startswith('bundle:'):
2581 if path.startswith('bundle:'):
2587 self.scheme = 'bundle'
2582 self.scheme = 'bundle'
2588 path = path[7:]
2583 path = path[7:]
2589 if path.startswith('//'):
2584 if path.startswith('//'):
2590 path = path[2:]
2585 path = path[2:]
2591 self.path = path
2586 self.path = path
2592 return
2587 return
2593
2588
2594 if self._matchscheme(path):
2589 if self._matchscheme(path):
2595 parts = path.split(':', 1)
2590 parts = path.split(':', 1)
2596 if parts[0]:
2591 if parts[0]:
2597 self.scheme, path = parts
2592 self.scheme, path = parts
2598 self._localpath = False
2593 self._localpath = False
2599
2594
2600 if not path:
2595 if not path:
2601 path = None
2596 path = None
2602 if self._localpath:
2597 if self._localpath:
2603 self.path = ''
2598 self.path = ''
2604 return
2599 return
2605 else:
2600 else:
2606 if self._localpath:
2601 if self._localpath:
2607 self.path = path
2602 self.path = path
2608 return
2603 return
2609
2604
2610 if parsequery and '?' in path:
2605 if parsequery and '?' in path:
2611 path, self.query = path.split('?', 1)
2606 path, self.query = path.split('?', 1)
2612 if not path:
2607 if not path:
2613 path = None
2608 path = None
2614 if not self.query:
2609 if not self.query:
2615 self.query = None
2610 self.query = None
2616
2611
2617 # // is required to specify a host/authority
2612 # // is required to specify a host/authority
2618 if path and path.startswith('//'):
2613 if path and path.startswith('//'):
2619 parts = path[2:].split('/', 1)
2614 parts = path[2:].split('/', 1)
2620 if len(parts) > 1:
2615 if len(parts) > 1:
2621 self.host, path = parts
2616 self.host, path = parts
2622 else:
2617 else:
2623 self.host = parts[0]
2618 self.host = parts[0]
2624 path = None
2619 path = None
2625 if not self.host:
2620 if not self.host:
2626 self.host = None
2621 self.host = None
2627 # path of file:///d is /d
2622 # path of file:///d is /d
2628 # path of file:///d:/ is d:/, not /d:/
2623 # path of file:///d:/ is d:/, not /d:/
2629 if path and not hasdriveletter(path):
2624 if path and not hasdriveletter(path):
2630 path = '/' + path
2625 path = '/' + path
2631
2626
2632 if self.host and '@' in self.host:
2627 if self.host and '@' in self.host:
2633 self.user, self.host = self.host.rsplit('@', 1)
2628 self.user, self.host = self.host.rsplit('@', 1)
2634 if ':' in self.user:
2629 if ':' in self.user:
2635 self.user, self.passwd = self.user.split(':', 1)
2630 self.user, self.passwd = self.user.split(':', 1)
2636 if not self.host:
2631 if not self.host:
2637 self.host = None
2632 self.host = None
2638
2633
2639 # Don't split on colons in IPv6 addresses without ports
2634 # Don't split on colons in IPv6 addresses without ports
2640 if (self.host and ':' in self.host and
2635 if (self.host and ':' in self.host and
2641 not (self.host.startswith('[') and self.host.endswith(']'))):
2636 not (self.host.startswith('[') and self.host.endswith(']'))):
2642 self._hostport = self.host
2637 self._hostport = self.host
2643 self.host, self.port = self.host.rsplit(':', 1)
2638 self.host, self.port = self.host.rsplit(':', 1)
2644 if not self.host:
2639 if not self.host:
2645 self.host = None
2640 self.host = None
2646
2641
2647 if (self.host and self.scheme == 'file' and
2642 if (self.host and self.scheme == 'file' and
2648 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2643 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2649 raise Abort(_('file:// URLs can only refer to localhost'))
2644 raise Abort(_('file:// URLs can only refer to localhost'))
2650
2645
2651 self.path = path
2646 self.path = path
2652
2647
2653 # leave the query string escaped
2648 # leave the query string escaped
2654 for a in ('user', 'passwd', 'host', 'port',
2649 for a in ('user', 'passwd', 'host', 'port',
2655 'path', 'fragment'):
2650 'path', 'fragment'):
2656 v = getattr(self, a)
2651 v = getattr(self, a)
2657 if v is not None:
2652 if v is not None:
2658 setattr(self, a, urlreq.unquote(v))
2653 setattr(self, a, urlreq.unquote(v))
2659
2654
2660 def __repr__(self):
2655 def __repr__(self):
2661 attrs = []
2656 attrs = []
2662 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2657 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2663 'query', 'fragment'):
2658 'query', 'fragment'):
2664 v = getattr(self, a)
2659 v = getattr(self, a)
2665 if v is not None:
2660 if v is not None:
2666 attrs.append('%s: %r' % (a, v))
2661 attrs.append('%s: %r' % (a, v))
2667 return '<url %s>' % ', '.join(attrs)
2662 return '<url %s>' % ', '.join(attrs)
2668
2663
2669 def __str__(self):
2664 def __str__(self):
2670 r"""Join the URL's components back into a URL string.
2665 r"""Join the URL's components back into a URL string.
2671
2666
2672 Examples:
2667 Examples:
2673
2668
2674 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2669 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2675 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2670 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2676 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2671 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2677 'http://user:pw@host:80/?foo=bar&baz=42'
2672 'http://user:pw@host:80/?foo=bar&baz=42'
2678 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2673 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2679 'http://user:pw@host:80/?foo=bar%3dbaz'
2674 'http://user:pw@host:80/?foo=bar%3dbaz'
2680 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2675 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2681 'ssh://user:pw@[::1]:2200//home/joe#'
2676 'ssh://user:pw@[::1]:2200//home/joe#'
2682 >>> str(url('http://localhost:80//'))
2677 >>> str(url('http://localhost:80//'))
2683 'http://localhost:80//'
2678 'http://localhost:80//'
2684 >>> str(url('http://localhost:80/'))
2679 >>> str(url('http://localhost:80/'))
2685 'http://localhost:80/'
2680 'http://localhost:80/'
2686 >>> str(url('http://localhost:80'))
2681 >>> str(url('http://localhost:80'))
2687 'http://localhost:80/'
2682 'http://localhost:80/'
2688 >>> str(url('bundle:foo'))
2683 >>> str(url('bundle:foo'))
2689 'bundle:foo'
2684 'bundle:foo'
2690 >>> str(url('bundle://../foo'))
2685 >>> str(url('bundle://../foo'))
2691 'bundle:../foo'
2686 'bundle:../foo'
2692 >>> str(url('path'))
2687 >>> str(url('path'))
2693 'path'
2688 'path'
2694 >>> str(url('file:///tmp/foo/bar'))
2689 >>> str(url('file:///tmp/foo/bar'))
2695 'file:///tmp/foo/bar'
2690 'file:///tmp/foo/bar'
2696 >>> str(url('file:///c:/tmp/foo/bar'))
2691 >>> str(url('file:///c:/tmp/foo/bar'))
2697 'file:///c:/tmp/foo/bar'
2692 'file:///c:/tmp/foo/bar'
2698 >>> print url(r'bundle:foo\bar')
2693 >>> print url(r'bundle:foo\bar')
2699 bundle:foo\bar
2694 bundle:foo\bar
2700 >>> print url(r'file:///D:\data\hg')
2695 >>> print url(r'file:///D:\data\hg')
2701 file:///D:\data\hg
2696 file:///D:\data\hg
2702 """
2697 """
2703 return encoding.strfromlocal(self.__bytes__())
2698 return encoding.strfromlocal(self.__bytes__())
2704
2699
2705 def __bytes__(self):
2700 def __bytes__(self):
2706 if self._localpath:
2701 if self._localpath:
2707 s = self.path
2702 s = self.path
2708 if self.scheme == 'bundle':
2703 if self.scheme == 'bundle':
2709 s = 'bundle:' + s
2704 s = 'bundle:' + s
2710 if self.fragment:
2705 if self.fragment:
2711 s += '#' + self.fragment
2706 s += '#' + self.fragment
2712 return s
2707 return s
2713
2708
2714 s = self.scheme + ':'
2709 s = self.scheme + ':'
2715 if self.user or self.passwd or self.host:
2710 if self.user or self.passwd or self.host:
2716 s += '//'
2711 s += '//'
2717 elif self.scheme and (not self.path or self.path.startswith('/')
2712 elif self.scheme and (not self.path or self.path.startswith('/')
2718 or hasdriveletter(self.path)):
2713 or hasdriveletter(self.path)):
2719 s += '//'
2714 s += '//'
2720 if hasdriveletter(self.path):
2715 if hasdriveletter(self.path):
2721 s += '/'
2716 s += '/'
2722 if self.user:
2717 if self.user:
2723 s += urlreq.quote(self.user, safe=self._safechars)
2718 s += urlreq.quote(self.user, safe=self._safechars)
2724 if self.passwd:
2719 if self.passwd:
2725 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2720 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2726 if self.user or self.passwd:
2721 if self.user or self.passwd:
2727 s += '@'
2722 s += '@'
2728 if self.host:
2723 if self.host:
2729 if not (self.host.startswith('[') and self.host.endswith(']')):
2724 if not (self.host.startswith('[') and self.host.endswith(']')):
2730 s += urlreq.quote(self.host)
2725 s += urlreq.quote(self.host)
2731 else:
2726 else:
2732 s += self.host
2727 s += self.host
2733 if self.port:
2728 if self.port:
2734 s += ':' + urlreq.quote(self.port)
2729 s += ':' + urlreq.quote(self.port)
2735 if self.host:
2730 if self.host:
2736 s += '/'
2731 s += '/'
2737 if self.path:
2732 if self.path:
2738 # TODO: similar to the query string, we should not unescape the
2733 # TODO: similar to the query string, we should not unescape the
2739 # path when we store it, the path might contain '%2f' = '/',
2734 # path when we store it, the path might contain '%2f' = '/',
2740 # which we should *not* escape.
2735 # which we should *not* escape.
2741 s += urlreq.quote(self.path, safe=self._safepchars)
2736 s += urlreq.quote(self.path, safe=self._safepchars)
2742 if self.query:
2737 if self.query:
2743 # we store the query in escaped form.
2738 # we store the query in escaped form.
2744 s += '?' + self.query
2739 s += '?' + self.query
2745 if self.fragment is not None:
2740 if self.fragment is not None:
2746 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2741 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2747 return s
2742 return s
2748
2743
2749 def authinfo(self):
2744 def authinfo(self):
2750 user, passwd = self.user, self.passwd
2745 user, passwd = self.user, self.passwd
2751 try:
2746 try:
2752 self.user, self.passwd = None, None
2747 self.user, self.passwd = None, None
2753 s = str(self)
2748 s = str(self)
2754 finally:
2749 finally:
2755 self.user, self.passwd = user, passwd
2750 self.user, self.passwd = user, passwd
2756 if not self.user:
2751 if not self.user:
2757 return (s, None)
2752 return (s, None)
2758 # authinfo[1] is passed to urllib2 password manager, and its
2753 # authinfo[1] is passed to urllib2 password manager, and its
2759 # URIs must not contain credentials. The host is passed in the
2754 # URIs must not contain credentials. The host is passed in the
2760 # URIs list because Python < 2.4.3 uses only that to search for
2755 # URIs list because Python < 2.4.3 uses only that to search for
2761 # a password.
2756 # a password.
2762 return (s, (None, (s, self.host),
2757 return (s, (None, (s, self.host),
2763 self.user, self.passwd or ''))
2758 self.user, self.passwd or ''))
2764
2759
2765 def isabs(self):
2760 def isabs(self):
2766 if self.scheme and self.scheme != 'file':
2761 if self.scheme and self.scheme != 'file':
2767 return True # remote URL
2762 return True # remote URL
2768 if hasdriveletter(self.path):
2763 if hasdriveletter(self.path):
2769 return True # absolute for our purposes - can't be joined()
2764 return True # absolute for our purposes - can't be joined()
2770 if self.path.startswith(r'\\'):
2765 if self.path.startswith(r'\\'):
2771 return True # Windows UNC path
2766 return True # Windows UNC path
2772 if self.path.startswith('/'):
2767 if self.path.startswith('/'):
2773 return True # POSIX-style
2768 return True # POSIX-style
2774 return False
2769 return False
2775
2770
2776 def localpath(self):
2771 def localpath(self):
2777 if self.scheme == 'file' or self.scheme == 'bundle':
2772 if self.scheme == 'file' or self.scheme == 'bundle':
2778 path = self.path or '/'
2773 path = self.path or '/'
2779 # For Windows, we need to promote hosts containing drive
2774 # For Windows, we need to promote hosts containing drive
2780 # letters to paths with drive letters.
2775 # letters to paths with drive letters.
2781 if hasdriveletter(self._hostport):
2776 if hasdriveletter(self._hostport):
2782 path = self._hostport + '/' + self.path
2777 path = self._hostport + '/' + self.path
2783 elif (self.host is not None and self.path
2778 elif (self.host is not None and self.path
2784 and not hasdriveletter(path)):
2779 and not hasdriveletter(path)):
2785 path = '/' + path
2780 path = '/' + path
2786 return path
2781 return path
2787 return self._origpath
2782 return self._origpath
2788
2783
2789 def islocal(self):
2784 def islocal(self):
2790 '''whether localpath will return something that posixfile can open'''
2785 '''whether localpath will return something that posixfile can open'''
2791 return (not self.scheme or self.scheme == 'file'
2786 return (not self.scheme or self.scheme == 'file'
2792 or self.scheme == 'bundle')
2787 or self.scheme == 'bundle')
2793
2788
2794 def hasscheme(path):
2789 def hasscheme(path):
2795 return bool(url(path).scheme)
2790 return bool(url(path).scheme)
2796
2791
2797 def hasdriveletter(path):
2792 def hasdriveletter(path):
2798 return path and path[1:2] == ':' and path[0:1].isalpha()
2793 return path and path[1:2] == ':' and path[0:1].isalpha()
2799
2794
2800 def urllocalpath(path):
2795 def urllocalpath(path):
2801 return url(path, parsequery=False, parsefragment=False).localpath()
2796 return url(path, parsequery=False, parsefragment=False).localpath()
2802
2797
2803 def hidepassword(u):
2798 def hidepassword(u):
2804 '''hide user credential in a url string'''
2799 '''hide user credential in a url string'''
2805 u = url(u)
2800 u = url(u)
2806 if u.passwd:
2801 if u.passwd:
2807 u.passwd = '***'
2802 u.passwd = '***'
2808 return str(u)
2803 return str(u)
2809
2804
2810 def removeauth(u):
2805 def removeauth(u):
2811 '''remove all authentication information from a url string'''
2806 '''remove all authentication information from a url string'''
2812 u = url(u)
2807 u = url(u)
2813 u.user = u.passwd = None
2808 u.user = u.passwd = None
2814 return str(u)
2809 return str(u)
2815
2810
2816 timecount = unitcountfn(
2811 timecount = unitcountfn(
2817 (1, 1e3, _('%.0f s')),
2812 (1, 1e3, _('%.0f s')),
2818 (100, 1, _('%.1f s')),
2813 (100, 1, _('%.1f s')),
2819 (10, 1, _('%.2f s')),
2814 (10, 1, _('%.2f s')),
2820 (1, 1, _('%.3f s')),
2815 (1, 1, _('%.3f s')),
2821 (100, 0.001, _('%.1f ms')),
2816 (100, 0.001, _('%.1f ms')),
2822 (10, 0.001, _('%.2f ms')),
2817 (10, 0.001, _('%.2f ms')),
2823 (1, 0.001, _('%.3f ms')),
2818 (1, 0.001, _('%.3f ms')),
2824 (100, 0.000001, _('%.1f us')),
2819 (100, 0.000001, _('%.1f us')),
2825 (10, 0.000001, _('%.2f us')),
2820 (10, 0.000001, _('%.2f us')),
2826 (1, 0.000001, _('%.3f us')),
2821 (1, 0.000001, _('%.3f us')),
2827 (100, 0.000000001, _('%.1f ns')),
2822 (100, 0.000000001, _('%.1f ns')),
2828 (10, 0.000000001, _('%.2f ns')),
2823 (10, 0.000000001, _('%.2f ns')),
2829 (1, 0.000000001, _('%.3f ns')),
2824 (1, 0.000000001, _('%.3f ns')),
2830 )
2825 )
2831
2826
2832 _timenesting = [0]
2827 _timenesting = [0]
2833
2828
2834 def timed(func):
2829 def timed(func):
2835 '''Report the execution time of a function call to stderr.
2830 '''Report the execution time of a function call to stderr.
2836
2831
2837 During development, use as a decorator when you need to measure
2832 During development, use as a decorator when you need to measure
2838 the cost of a function, e.g. as follows:
2833 the cost of a function, e.g. as follows:
2839
2834
2840 @util.timed
2835 @util.timed
2841 def foo(a, b, c):
2836 def foo(a, b, c):
2842 pass
2837 pass
2843 '''
2838 '''
2844
2839
2845 def wrapper(*args, **kwargs):
2840 def wrapper(*args, **kwargs):
2846 start = timer()
2841 start = timer()
2847 indent = 2
2842 indent = 2
2848 _timenesting[0] += indent
2843 _timenesting[0] += indent
2849 try:
2844 try:
2850 return func(*args, **kwargs)
2845 return func(*args, **kwargs)
2851 finally:
2846 finally:
2852 elapsed = timer() - start
2847 elapsed = timer() - start
2853 _timenesting[0] -= indent
2848 _timenesting[0] -= indent
2854 stderr.write('%s%s: %s\n' %
2849 stderr.write('%s%s: %s\n' %
2855 (' ' * _timenesting[0], func.__name__,
2850 (' ' * _timenesting[0], func.__name__,
2856 timecount(elapsed)))
2851 timecount(elapsed)))
2857 return wrapper
2852 return wrapper
2858
2853
2859 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2854 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2860 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2855 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2861
2856
2862 def sizetoint(s):
2857 def sizetoint(s):
2863 '''Convert a space specifier to a byte count.
2858 '''Convert a space specifier to a byte count.
2864
2859
2865 >>> sizetoint('30')
2860 >>> sizetoint('30')
2866 30
2861 30
2867 >>> sizetoint('2.2kb')
2862 >>> sizetoint('2.2kb')
2868 2252
2863 2252
2869 >>> sizetoint('6M')
2864 >>> sizetoint('6M')
2870 6291456
2865 6291456
2871 '''
2866 '''
2872 t = s.strip().lower()
2867 t = s.strip().lower()
2873 try:
2868 try:
2874 for k, u in _sizeunits:
2869 for k, u in _sizeunits:
2875 if t.endswith(k):
2870 if t.endswith(k):
2876 return int(float(t[:-len(k)]) * u)
2871 return int(float(t[:-len(k)]) * u)
2877 return int(t)
2872 return int(t)
2878 except ValueError:
2873 except ValueError:
2879 raise error.ParseError(_("couldn't parse size: %s") % s)
2874 raise error.ParseError(_("couldn't parse size: %s") % s)
2880
2875
2881 class hooks(object):
2876 class hooks(object):
2882 '''A collection of hook functions that can be used to extend a
2877 '''A collection of hook functions that can be used to extend a
2883 function's behavior. Hooks are called in lexicographic order,
2878 function's behavior. Hooks are called in lexicographic order,
2884 based on the names of their sources.'''
2879 based on the names of their sources.'''
2885
2880
2886 def __init__(self):
2881 def __init__(self):
2887 self._hooks = []
2882 self._hooks = []
2888
2883
2889 def add(self, source, hook):
2884 def add(self, source, hook):
2890 self._hooks.append((source, hook))
2885 self._hooks.append((source, hook))
2891
2886
2892 def __call__(self, *args):
2887 def __call__(self, *args):
2893 self._hooks.sort(key=lambda x: x[0])
2888 self._hooks.sort(key=lambda x: x[0])
2894 results = []
2889 results = []
2895 for source, hook in self._hooks:
2890 for source, hook in self._hooks:
2896 results.append(hook(*args))
2891 results.append(hook(*args))
2897 return results
2892 return results
2898
2893
2899 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%s', depth=0):
2894 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%s', depth=0):
2900 '''Yields lines for a nicely formatted stacktrace.
2895 '''Yields lines for a nicely formatted stacktrace.
2901 Skips the 'skip' last entries, then return the last 'depth' entries.
2896 Skips the 'skip' last entries, then return the last 'depth' entries.
2902 Each file+linenumber is formatted according to fileline.
2897 Each file+linenumber is formatted according to fileline.
2903 Each line is formatted according to line.
2898 Each line is formatted according to line.
2904 If line is None, it yields:
2899 If line is None, it yields:
2905 length of longest filepath+line number,
2900 length of longest filepath+line number,
2906 filepath+linenumber,
2901 filepath+linenumber,
2907 function
2902 function
2908
2903
2909 Not be used in production code but very convenient while developing.
2904 Not be used in production code but very convenient while developing.
2910 '''
2905 '''
2911 entries = [(fileline % (fn, ln), func)
2906 entries = [(fileline % (fn, ln), func)
2912 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]
2907 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]
2913 ][-depth:]
2908 ][-depth:]
2914 if entries:
2909 if entries:
2915 fnmax = max(len(entry[0]) for entry in entries)
2910 fnmax = max(len(entry[0]) for entry in entries)
2916 for fnln, func in entries:
2911 for fnln, func in entries:
2917 if line is None:
2912 if line is None:
2918 yield (fnmax, fnln, func)
2913 yield (fnmax, fnln, func)
2919 else:
2914 else:
2920 yield line % (fnmax, fnln, func)
2915 yield line % (fnmax, fnln, func)
2921
2916
2922 def debugstacktrace(msg='stacktrace', skip=0,
2917 def debugstacktrace(msg='stacktrace', skip=0,
2923 f=stderr, otherf=stdout, depth=0):
2918 f=stderr, otherf=stdout, depth=0):
2924 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2919 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2925 Skips the 'skip' entries closest to the call, then show 'depth' entries.
2920 Skips the 'skip' entries closest to the call, then show 'depth' entries.
2926 By default it will flush stdout first.
2921 By default it will flush stdout first.
2927 It can be used everywhere and intentionally does not require an ui object.
2922 It can be used everywhere and intentionally does not require an ui object.
2928 Not be used in production code but very convenient while developing.
2923 Not be used in production code but very convenient while developing.
2929 '''
2924 '''
2930 if otherf:
2925 if otherf:
2931 otherf.flush()
2926 otherf.flush()
2932 f.write('%s at:\n' % msg.rstrip())
2927 f.write('%s at:\n' % msg.rstrip())
2933 for line in getstackframes(skip + 1, depth=depth):
2928 for line in getstackframes(skip + 1, depth=depth):
2934 f.write(line)
2929 f.write(line)
2935 f.flush()
2930 f.flush()
2936
2931
2937 class dirs(object):
2932 class dirs(object):
2938 '''a multiset of directory names from a dirstate or manifest'''
2933 '''a multiset of directory names from a dirstate or manifest'''
2939
2934
2940 def __init__(self, map, skip=None):
2935 def __init__(self, map, skip=None):
2941 self._dirs = {}
2936 self._dirs = {}
2942 addpath = self.addpath
2937 addpath = self.addpath
2943 if safehasattr(map, 'iteritems') and skip is not None:
2938 if safehasattr(map, 'iteritems') and skip is not None:
2944 for f, s in map.iteritems():
2939 for f, s in map.iteritems():
2945 if s[0] != skip:
2940 if s[0] != skip:
2946 addpath(f)
2941 addpath(f)
2947 else:
2942 else:
2948 for f in map:
2943 for f in map:
2949 addpath(f)
2944 addpath(f)
2950
2945
2951 def addpath(self, path):
2946 def addpath(self, path):
2952 dirs = self._dirs
2947 dirs = self._dirs
2953 for base in finddirs(path):
2948 for base in finddirs(path):
2954 if base in dirs:
2949 if base in dirs:
2955 dirs[base] += 1
2950 dirs[base] += 1
2956 return
2951 return
2957 dirs[base] = 1
2952 dirs[base] = 1
2958
2953
2959 def delpath(self, path):
2954 def delpath(self, path):
2960 dirs = self._dirs
2955 dirs = self._dirs
2961 for base in finddirs(path):
2956 for base in finddirs(path):
2962 if dirs[base] > 1:
2957 if dirs[base] > 1:
2963 dirs[base] -= 1
2958 dirs[base] -= 1
2964 return
2959 return
2965 del dirs[base]
2960 del dirs[base]
2966
2961
2967 def __iter__(self):
2962 def __iter__(self):
2968 return iter(self._dirs)
2963 return iter(self._dirs)
2969
2964
2970 def __contains__(self, d):
2965 def __contains__(self, d):
2971 return d in self._dirs
2966 return d in self._dirs
2972
2967
2973 if safehasattr(parsers, 'dirs'):
2968 if safehasattr(parsers, 'dirs'):
2974 dirs = parsers.dirs
2969 dirs = parsers.dirs
2975
2970
2976 def finddirs(path):
2971 def finddirs(path):
2977 pos = path.rfind('/')
2972 pos = path.rfind('/')
2978 while pos != -1:
2973 while pos != -1:
2979 yield path[:pos]
2974 yield path[:pos]
2980 pos = path.rfind('/', 0, pos)
2975 pos = path.rfind('/', 0, pos)
2981
2976
2982 class ctxmanager(object):
2977 class ctxmanager(object):
2983 '''A context manager for use in 'with' blocks to allow multiple
2978 '''A context manager for use in 'with' blocks to allow multiple
2984 contexts to be entered at once. This is both safer and more
2979 contexts to be entered at once. This is both safer and more
2985 flexible than contextlib.nested.
2980 flexible than contextlib.nested.
2986
2981
2987 Once Mercurial supports Python 2.7+, this will become mostly
2982 Once Mercurial supports Python 2.7+, this will become mostly
2988 unnecessary.
2983 unnecessary.
2989 '''
2984 '''
2990
2985
2991 def __init__(self, *args):
2986 def __init__(self, *args):
2992 '''Accepts a list of no-argument functions that return context
2987 '''Accepts a list of no-argument functions that return context
2993 managers. These will be invoked at __call__ time.'''
2988 managers. These will be invoked at __call__ time.'''
2994 self._pending = args
2989 self._pending = args
2995 self._atexit = []
2990 self._atexit = []
2996
2991
2997 def __enter__(self):
2992 def __enter__(self):
2998 return self
2993 return self
2999
2994
3000 def enter(self):
2995 def enter(self):
3001 '''Create and enter context managers in the order in which they were
2996 '''Create and enter context managers in the order in which they were
3002 passed to the constructor.'''
2997 passed to the constructor.'''
3003 values = []
2998 values = []
3004 for func in self._pending:
2999 for func in self._pending:
3005 obj = func()
3000 obj = func()
3006 values.append(obj.__enter__())
3001 values.append(obj.__enter__())
3007 self._atexit.append(obj.__exit__)
3002 self._atexit.append(obj.__exit__)
3008 del self._pending
3003 del self._pending
3009 return values
3004 return values
3010
3005
3011 def atexit(self, func, *args, **kwargs):
3006 def atexit(self, func, *args, **kwargs):
3012 '''Add a function to call when this context manager exits. The
3007 '''Add a function to call when this context manager exits. The
3013 ordering of multiple atexit calls is unspecified, save that
3008 ordering of multiple atexit calls is unspecified, save that
3014 they will happen before any __exit__ functions.'''
3009 they will happen before any __exit__ functions.'''
3015 def wrapper(exc_type, exc_val, exc_tb):
3010 def wrapper(exc_type, exc_val, exc_tb):
3016 func(*args, **kwargs)
3011 func(*args, **kwargs)
3017 self._atexit.append(wrapper)
3012 self._atexit.append(wrapper)
3018 return func
3013 return func
3019
3014
3020 def __exit__(self, exc_type, exc_val, exc_tb):
3015 def __exit__(self, exc_type, exc_val, exc_tb):
3021 '''Context managers are exited in the reverse order from which
3016 '''Context managers are exited in the reverse order from which
3022 they were created.'''
3017 they were created.'''
3023 received = exc_type is not None
3018 received = exc_type is not None
3024 suppressed = False
3019 suppressed = False
3025 pending = None
3020 pending = None
3026 self._atexit.reverse()
3021 self._atexit.reverse()
3027 for exitfunc in self._atexit:
3022 for exitfunc in self._atexit:
3028 try:
3023 try:
3029 if exitfunc(exc_type, exc_val, exc_tb):
3024 if exitfunc(exc_type, exc_val, exc_tb):
3030 suppressed = True
3025 suppressed = True
3031 exc_type = None
3026 exc_type = None
3032 exc_val = None
3027 exc_val = None
3033 exc_tb = None
3028 exc_tb = None
3034 except BaseException:
3029 except BaseException:
3035 pending = sys.exc_info()
3030 pending = sys.exc_info()
3036 exc_type, exc_val, exc_tb = pending = sys.exc_info()
3031 exc_type, exc_val, exc_tb = pending = sys.exc_info()
3037 del self._atexit
3032 del self._atexit
3038 if pending:
3033 if pending:
3039 raise exc_val
3034 raise exc_val
3040 return received and suppressed
3035 return received and suppressed
3041
3036
3042 # compression code
3037 # compression code
3043
3038
3044 SERVERROLE = 'server'
3039 SERVERROLE = 'server'
3045 CLIENTROLE = 'client'
3040 CLIENTROLE = 'client'
3046
3041
3047 compewireprotosupport = collections.namedtuple(u'compenginewireprotosupport',
3042 compewireprotosupport = collections.namedtuple(u'compenginewireprotosupport',
3048 (u'name', u'serverpriority',
3043 (u'name', u'serverpriority',
3049 u'clientpriority'))
3044 u'clientpriority'))
3050
3045
3051 class compressormanager(object):
3046 class compressormanager(object):
3052 """Holds registrations of various compression engines.
3047 """Holds registrations of various compression engines.
3053
3048
3054 This class essentially abstracts the differences between compression
3049 This class essentially abstracts the differences between compression
3055 engines to allow new compression formats to be added easily, possibly from
3050 engines to allow new compression formats to be added easily, possibly from
3056 extensions.
3051 extensions.
3057
3052
3058 Compressors are registered against the global instance by calling its
3053 Compressors are registered against the global instance by calling its
3059 ``register()`` method.
3054 ``register()`` method.
3060 """
3055 """
3061 def __init__(self):
3056 def __init__(self):
3062 self._engines = {}
3057 self._engines = {}
3063 # Bundle spec human name to engine name.
3058 # Bundle spec human name to engine name.
3064 self._bundlenames = {}
3059 self._bundlenames = {}
3065 # Internal bundle identifier to engine name.
3060 # Internal bundle identifier to engine name.
3066 self._bundletypes = {}
3061 self._bundletypes = {}
3067 # Revlog header to engine name.
3062 # Revlog header to engine name.
3068 self._revlogheaders = {}
3063 self._revlogheaders = {}
3069 # Wire proto identifier to engine name.
3064 # Wire proto identifier to engine name.
3070 self._wiretypes = {}
3065 self._wiretypes = {}
3071
3066
3072 def __getitem__(self, key):
3067 def __getitem__(self, key):
3073 return self._engines[key]
3068 return self._engines[key]
3074
3069
3075 def __contains__(self, key):
3070 def __contains__(self, key):
3076 return key in self._engines
3071 return key in self._engines
3077
3072
3078 def __iter__(self):
3073 def __iter__(self):
3079 return iter(self._engines.keys())
3074 return iter(self._engines.keys())
3080
3075
3081 def register(self, engine):
3076 def register(self, engine):
3082 """Register a compression engine with the manager.
3077 """Register a compression engine with the manager.
3083
3078
3084 The argument must be a ``compressionengine`` instance.
3079 The argument must be a ``compressionengine`` instance.
3085 """
3080 """
3086 if not isinstance(engine, compressionengine):
3081 if not isinstance(engine, compressionengine):
3087 raise ValueError(_('argument must be a compressionengine'))
3082 raise ValueError(_('argument must be a compressionengine'))
3088
3083
3089 name = engine.name()
3084 name = engine.name()
3090
3085
3091 if name in self._engines:
3086 if name in self._engines:
3092 raise error.Abort(_('compression engine %s already registered') %
3087 raise error.Abort(_('compression engine %s already registered') %
3093 name)
3088 name)
3094
3089
3095 bundleinfo = engine.bundletype()
3090 bundleinfo = engine.bundletype()
3096 if bundleinfo:
3091 if bundleinfo:
3097 bundlename, bundletype = bundleinfo
3092 bundlename, bundletype = bundleinfo
3098
3093
3099 if bundlename in self._bundlenames:
3094 if bundlename in self._bundlenames:
3100 raise error.Abort(_('bundle name %s already registered') %
3095 raise error.Abort(_('bundle name %s already registered') %
3101 bundlename)
3096 bundlename)
3102 if bundletype in self._bundletypes:
3097 if bundletype in self._bundletypes:
3103 raise error.Abort(_('bundle type %s already registered by %s') %
3098 raise error.Abort(_('bundle type %s already registered by %s') %
3104 (bundletype, self._bundletypes[bundletype]))
3099 (bundletype, self._bundletypes[bundletype]))
3105
3100
3106 # No external facing name declared.
3101 # No external facing name declared.
3107 if bundlename:
3102 if bundlename:
3108 self._bundlenames[bundlename] = name
3103 self._bundlenames[bundlename] = name
3109
3104
3110 self._bundletypes[bundletype] = name
3105 self._bundletypes[bundletype] = name
3111
3106
3112 wiresupport = engine.wireprotosupport()
3107 wiresupport = engine.wireprotosupport()
3113 if wiresupport:
3108 if wiresupport:
3114 wiretype = wiresupport.name
3109 wiretype = wiresupport.name
3115 if wiretype in self._wiretypes:
3110 if wiretype in self._wiretypes:
3116 raise error.Abort(_('wire protocol compression %s already '
3111 raise error.Abort(_('wire protocol compression %s already '
3117 'registered by %s') %
3112 'registered by %s') %
3118 (wiretype, self._wiretypes[wiretype]))
3113 (wiretype, self._wiretypes[wiretype]))
3119
3114
3120 self._wiretypes[wiretype] = name
3115 self._wiretypes[wiretype] = name
3121
3116
3122 revlogheader = engine.revlogheader()
3117 revlogheader = engine.revlogheader()
3123 if revlogheader and revlogheader in self._revlogheaders:
3118 if revlogheader and revlogheader in self._revlogheaders:
3124 raise error.Abort(_('revlog header %s already registered by %s') %
3119 raise error.Abort(_('revlog header %s already registered by %s') %
3125 (revlogheader, self._revlogheaders[revlogheader]))
3120 (revlogheader, self._revlogheaders[revlogheader]))
3126
3121
3127 if revlogheader:
3122 if revlogheader:
3128 self._revlogheaders[revlogheader] = name
3123 self._revlogheaders[revlogheader] = name
3129
3124
3130 self._engines[name] = engine
3125 self._engines[name] = engine
3131
3126
3132 @property
3127 @property
3133 def supportedbundlenames(self):
3128 def supportedbundlenames(self):
3134 return set(self._bundlenames.keys())
3129 return set(self._bundlenames.keys())
3135
3130
3136 @property
3131 @property
3137 def supportedbundletypes(self):
3132 def supportedbundletypes(self):
3138 return set(self._bundletypes.keys())
3133 return set(self._bundletypes.keys())
3139
3134
3140 def forbundlename(self, bundlename):
3135 def forbundlename(self, bundlename):
3141 """Obtain a compression engine registered to a bundle name.
3136 """Obtain a compression engine registered to a bundle name.
3142
3137
3143 Will raise KeyError if the bundle type isn't registered.
3138 Will raise KeyError if the bundle type isn't registered.
3144
3139
3145 Will abort if the engine is known but not available.
3140 Will abort if the engine is known but not available.
3146 """
3141 """
3147 engine = self._engines[self._bundlenames[bundlename]]
3142 engine = self._engines[self._bundlenames[bundlename]]
3148 if not engine.available():
3143 if not engine.available():
3149 raise error.Abort(_('compression engine %s could not be loaded') %
3144 raise error.Abort(_('compression engine %s could not be loaded') %
3150 engine.name())
3145 engine.name())
3151 return engine
3146 return engine
3152
3147
3153 def forbundletype(self, bundletype):
3148 def forbundletype(self, bundletype):
3154 """Obtain a compression engine registered to a bundle type.
3149 """Obtain a compression engine registered to a bundle type.
3155
3150
3156 Will raise KeyError if the bundle type isn't registered.
3151 Will raise KeyError if the bundle type isn't registered.
3157
3152
3158 Will abort if the engine is known but not available.
3153 Will abort if the engine is known but not available.
3159 """
3154 """
3160 engine = self._engines[self._bundletypes[bundletype]]
3155 engine = self._engines[self._bundletypes[bundletype]]
3161 if not engine.available():
3156 if not engine.available():
3162 raise error.Abort(_('compression engine %s could not be loaded') %
3157 raise error.Abort(_('compression engine %s could not be loaded') %
3163 engine.name())
3158 engine.name())
3164 return engine
3159 return engine
3165
3160
3166 def supportedwireengines(self, role, onlyavailable=True):
3161 def supportedwireengines(self, role, onlyavailable=True):
3167 """Obtain compression engines that support the wire protocol.
3162 """Obtain compression engines that support the wire protocol.
3168
3163
3169 Returns a list of engines in prioritized order, most desired first.
3164 Returns a list of engines in prioritized order, most desired first.
3170
3165
3171 If ``onlyavailable`` is set, filter out engines that can't be
3166 If ``onlyavailable`` is set, filter out engines that can't be
3172 loaded.
3167 loaded.
3173 """
3168 """
3174 assert role in (SERVERROLE, CLIENTROLE)
3169 assert role in (SERVERROLE, CLIENTROLE)
3175
3170
3176 attr = 'serverpriority' if role == SERVERROLE else 'clientpriority'
3171 attr = 'serverpriority' if role == SERVERROLE else 'clientpriority'
3177
3172
3178 engines = [self._engines[e] for e in self._wiretypes.values()]
3173 engines = [self._engines[e] for e in self._wiretypes.values()]
3179 if onlyavailable:
3174 if onlyavailable:
3180 engines = [e for e in engines if e.available()]
3175 engines = [e for e in engines if e.available()]
3181
3176
3182 def getkey(e):
3177 def getkey(e):
3183 # Sort first by priority, highest first. In case of tie, sort
3178 # Sort first by priority, highest first. In case of tie, sort
3184 # alphabetically. This is arbitrary, but ensures output is
3179 # alphabetically. This is arbitrary, but ensures output is
3185 # stable.
3180 # stable.
3186 w = e.wireprotosupport()
3181 w = e.wireprotosupport()
3187 return -1 * getattr(w, attr), w.name
3182 return -1 * getattr(w, attr), w.name
3188
3183
3189 return list(sorted(engines, key=getkey))
3184 return list(sorted(engines, key=getkey))
3190
3185
3191 def forwiretype(self, wiretype):
3186 def forwiretype(self, wiretype):
3192 engine = self._engines[self._wiretypes[wiretype]]
3187 engine = self._engines[self._wiretypes[wiretype]]
3193 if not engine.available():
3188 if not engine.available():
3194 raise error.Abort(_('compression engine %s could not be loaded') %
3189 raise error.Abort(_('compression engine %s could not be loaded') %
3195 engine.name())
3190 engine.name())
3196 return engine
3191 return engine
3197
3192
3198 def forrevlogheader(self, header):
3193 def forrevlogheader(self, header):
3199 """Obtain a compression engine registered to a revlog header.
3194 """Obtain a compression engine registered to a revlog header.
3200
3195
3201 Will raise KeyError if the revlog header value isn't registered.
3196 Will raise KeyError if the revlog header value isn't registered.
3202 """
3197 """
3203 return self._engines[self._revlogheaders[header]]
3198 return self._engines[self._revlogheaders[header]]
3204
3199
3205 compengines = compressormanager()
3200 compengines = compressormanager()
3206
3201
3207 class compressionengine(object):
3202 class compressionengine(object):
3208 """Base class for compression engines.
3203 """Base class for compression engines.
3209
3204
3210 Compression engines must implement the interface defined by this class.
3205 Compression engines must implement the interface defined by this class.
3211 """
3206 """
3212 def name(self):
3207 def name(self):
3213 """Returns the name of the compression engine.
3208 """Returns the name of the compression engine.
3214
3209
3215 This is the key the engine is registered under.
3210 This is the key the engine is registered under.
3216
3211
3217 This method must be implemented.
3212 This method must be implemented.
3218 """
3213 """
3219 raise NotImplementedError()
3214 raise NotImplementedError()
3220
3215
3221 def available(self):
3216 def available(self):
3222 """Whether the compression engine is available.
3217 """Whether the compression engine is available.
3223
3218
3224 The intent of this method is to allow optional compression engines
3219 The intent of this method is to allow optional compression engines
3225 that may not be available in all installations (such as engines relying
3220 that may not be available in all installations (such as engines relying
3226 on C extensions that may not be present).
3221 on C extensions that may not be present).
3227 """
3222 """
3228 return True
3223 return True
3229
3224
3230 def bundletype(self):
3225 def bundletype(self):
3231 """Describes bundle identifiers for this engine.
3226 """Describes bundle identifiers for this engine.
3232
3227
3233 If this compression engine isn't supported for bundles, returns None.
3228 If this compression engine isn't supported for bundles, returns None.
3234
3229
3235 If this engine can be used for bundles, returns a 2-tuple of strings of
3230 If this engine can be used for bundles, returns a 2-tuple of strings of
3236 the user-facing "bundle spec" compression name and an internal
3231 the user-facing "bundle spec" compression name and an internal
3237 identifier used to denote the compression format within bundles. To
3232 identifier used to denote the compression format within bundles. To
3238 exclude the name from external usage, set the first element to ``None``.
3233 exclude the name from external usage, set the first element to ``None``.
3239
3234
3240 If bundle compression is supported, the class must also implement
3235 If bundle compression is supported, the class must also implement
3241 ``compressstream`` and `decompressorreader``.
3236 ``compressstream`` and `decompressorreader``.
3242 """
3237 """
3243 return None
3238 return None
3244
3239
3245 def wireprotosupport(self):
3240 def wireprotosupport(self):
3246 """Declare support for this compression format on the wire protocol.
3241 """Declare support for this compression format on the wire protocol.
3247
3242
3248 If this compression engine isn't supported for compressing wire
3243 If this compression engine isn't supported for compressing wire
3249 protocol payloads, returns None.
3244 protocol payloads, returns None.
3250
3245
3251 Otherwise, returns ``compenginewireprotosupport`` with the following
3246 Otherwise, returns ``compenginewireprotosupport`` with the following
3252 fields:
3247 fields:
3253
3248
3254 * String format identifier
3249 * String format identifier
3255 * Integer priority for the server
3250 * Integer priority for the server
3256 * Integer priority for the client
3251 * Integer priority for the client
3257
3252
3258 The integer priorities are used to order the advertisement of format
3253 The integer priorities are used to order the advertisement of format
3259 support by server and client. The highest integer is advertised
3254 support by server and client. The highest integer is advertised
3260 first. Integers with non-positive values aren't advertised.
3255 first. Integers with non-positive values aren't advertised.
3261
3256
3262 The priority values are somewhat arbitrary and only used for default
3257 The priority values are somewhat arbitrary and only used for default
3263 ordering. The relative order can be changed via config options.
3258 ordering. The relative order can be changed via config options.
3264
3259
3265 If wire protocol compression is supported, the class must also implement
3260 If wire protocol compression is supported, the class must also implement
3266 ``compressstream`` and ``decompressorreader``.
3261 ``compressstream`` and ``decompressorreader``.
3267 """
3262 """
3268 return None
3263 return None
3269
3264
3270 def revlogheader(self):
3265 def revlogheader(self):
3271 """Header added to revlog chunks that identifies this engine.
3266 """Header added to revlog chunks that identifies this engine.
3272
3267
3273 If this engine can be used to compress revlogs, this method should
3268 If this engine can be used to compress revlogs, this method should
3274 return the bytes used to identify chunks compressed with this engine.
3269 return the bytes used to identify chunks compressed with this engine.
3275 Else, the method should return ``None`` to indicate it does not
3270 Else, the method should return ``None`` to indicate it does not
3276 participate in revlog compression.
3271 participate in revlog compression.
3277 """
3272 """
3278 return None
3273 return None
3279
3274
3280 def compressstream(self, it, opts=None):
3275 def compressstream(self, it, opts=None):
3281 """Compress an iterator of chunks.
3276 """Compress an iterator of chunks.
3282
3277
3283 The method receives an iterator (ideally a generator) of chunks of
3278 The method receives an iterator (ideally a generator) of chunks of
3284 bytes to be compressed. It returns an iterator (ideally a generator)
3279 bytes to be compressed. It returns an iterator (ideally a generator)
3285 of bytes of chunks representing the compressed output.
3280 of bytes of chunks representing the compressed output.
3286
3281
3287 Optionally accepts an argument defining how to perform compression.
3282 Optionally accepts an argument defining how to perform compression.
3288 Each engine treats this argument differently.
3283 Each engine treats this argument differently.
3289 """
3284 """
3290 raise NotImplementedError()
3285 raise NotImplementedError()
3291
3286
3292 def decompressorreader(self, fh):
3287 def decompressorreader(self, fh):
3293 """Perform decompression on a file object.
3288 """Perform decompression on a file object.
3294
3289
3295 Argument is an object with a ``read(size)`` method that returns
3290 Argument is an object with a ``read(size)`` method that returns
3296 compressed data. Return value is an object with a ``read(size)`` that
3291 compressed data. Return value is an object with a ``read(size)`` that
3297 returns uncompressed data.
3292 returns uncompressed data.
3298 """
3293 """
3299 raise NotImplementedError()
3294 raise NotImplementedError()
3300
3295
3301 def revlogcompressor(self, opts=None):
3296 def revlogcompressor(self, opts=None):
3302 """Obtain an object that can be used to compress revlog entries.
3297 """Obtain an object that can be used to compress revlog entries.
3303
3298
3304 The object has a ``compress(data)`` method that compresses binary
3299 The object has a ``compress(data)`` method that compresses binary
3305 data. This method returns compressed binary data or ``None`` if
3300 data. This method returns compressed binary data or ``None`` if
3306 the data could not be compressed (too small, not compressible, etc).
3301 the data could not be compressed (too small, not compressible, etc).
3307 The returned data should have a header uniquely identifying this
3302 The returned data should have a header uniquely identifying this
3308 compression format so decompression can be routed to this engine.
3303 compression format so decompression can be routed to this engine.
3309 This header should be identified by the ``revlogheader()`` return
3304 This header should be identified by the ``revlogheader()`` return
3310 value.
3305 value.
3311
3306
3312 The object has a ``decompress(data)`` method that decompresses
3307 The object has a ``decompress(data)`` method that decompresses
3313 data. The method will only be called if ``data`` begins with
3308 data. The method will only be called if ``data`` begins with
3314 ``revlogheader()``. The method should return the raw, uncompressed
3309 ``revlogheader()``. The method should return the raw, uncompressed
3315 data or raise a ``RevlogError``.
3310 data or raise a ``RevlogError``.
3316
3311
3317 The object is reusable but is not thread safe.
3312 The object is reusable but is not thread safe.
3318 """
3313 """
3319 raise NotImplementedError()
3314 raise NotImplementedError()
3320
3315
3321 class _zlibengine(compressionengine):
3316 class _zlibengine(compressionengine):
3322 def name(self):
3317 def name(self):
3323 return 'zlib'
3318 return 'zlib'
3324
3319
3325 def bundletype(self):
3320 def bundletype(self):
3326 return 'gzip', 'GZ'
3321 return 'gzip', 'GZ'
3327
3322
3328 def wireprotosupport(self):
3323 def wireprotosupport(self):
3329 return compewireprotosupport('zlib', 20, 20)
3324 return compewireprotosupport('zlib', 20, 20)
3330
3325
3331 def revlogheader(self):
3326 def revlogheader(self):
3332 return 'x'
3327 return 'x'
3333
3328
3334 def compressstream(self, it, opts=None):
3329 def compressstream(self, it, opts=None):
3335 opts = opts or {}
3330 opts = opts or {}
3336
3331
3337 z = zlib.compressobj(opts.get('level', -1))
3332 z = zlib.compressobj(opts.get('level', -1))
3338 for chunk in it:
3333 for chunk in it:
3339 data = z.compress(chunk)
3334 data = z.compress(chunk)
3340 # Not all calls to compress emit data. It is cheaper to inspect
3335 # Not all calls to compress emit data. It is cheaper to inspect
3341 # here than to feed empty chunks through generator.
3336 # here than to feed empty chunks through generator.
3342 if data:
3337 if data:
3343 yield data
3338 yield data
3344
3339
3345 yield z.flush()
3340 yield z.flush()
3346
3341
3347 def decompressorreader(self, fh):
3342 def decompressorreader(self, fh):
3348 def gen():
3343 def gen():
3349 d = zlib.decompressobj()
3344 d = zlib.decompressobj()
3350 for chunk in filechunkiter(fh):
3345 for chunk in filechunkiter(fh):
3351 while chunk:
3346 while chunk:
3352 # Limit output size to limit memory.
3347 # Limit output size to limit memory.
3353 yield d.decompress(chunk, 2 ** 18)
3348 yield d.decompress(chunk, 2 ** 18)
3354 chunk = d.unconsumed_tail
3349 chunk = d.unconsumed_tail
3355
3350
3356 return chunkbuffer(gen())
3351 return chunkbuffer(gen())
3357
3352
3358 class zlibrevlogcompressor(object):
3353 class zlibrevlogcompressor(object):
3359 def compress(self, data):
3354 def compress(self, data):
3360 insize = len(data)
3355 insize = len(data)
3361 # Caller handles empty input case.
3356 # Caller handles empty input case.
3362 assert insize > 0
3357 assert insize > 0
3363
3358
3364 if insize < 44:
3359 if insize < 44:
3365 return None
3360 return None
3366
3361
3367 elif insize <= 1000000:
3362 elif insize <= 1000000:
3368 compressed = zlib.compress(data)
3363 compressed = zlib.compress(data)
3369 if len(compressed) < insize:
3364 if len(compressed) < insize:
3370 return compressed
3365 return compressed
3371 return None
3366 return None
3372
3367
3373 # zlib makes an internal copy of the input buffer, doubling
3368 # zlib makes an internal copy of the input buffer, doubling
3374 # memory usage for large inputs. So do streaming compression
3369 # memory usage for large inputs. So do streaming compression
3375 # on large inputs.
3370 # on large inputs.
3376 else:
3371 else:
3377 z = zlib.compressobj()
3372 z = zlib.compressobj()
3378 parts = []
3373 parts = []
3379 pos = 0
3374 pos = 0
3380 while pos < insize:
3375 while pos < insize:
3381 pos2 = pos + 2**20
3376 pos2 = pos + 2**20
3382 parts.append(z.compress(data[pos:pos2]))
3377 parts.append(z.compress(data[pos:pos2]))
3383 pos = pos2
3378 pos = pos2
3384 parts.append(z.flush())
3379 parts.append(z.flush())
3385
3380
3386 if sum(map(len, parts)) < insize:
3381 if sum(map(len, parts)) < insize:
3387 return ''.join(parts)
3382 return ''.join(parts)
3388 return None
3383 return None
3389
3384
3390 def decompress(self, data):
3385 def decompress(self, data):
3391 try:
3386 try:
3392 return zlib.decompress(data)
3387 return zlib.decompress(data)
3393 except zlib.error as e:
3388 except zlib.error as e:
3394 raise error.RevlogError(_('revlog decompress error: %s') %
3389 raise error.RevlogError(_('revlog decompress error: %s') %
3395 str(e))
3390 str(e))
3396
3391
3397 def revlogcompressor(self, opts=None):
3392 def revlogcompressor(self, opts=None):
3398 return self.zlibrevlogcompressor()
3393 return self.zlibrevlogcompressor()
3399
3394
3400 compengines.register(_zlibengine())
3395 compengines.register(_zlibengine())
3401
3396
3402 class _bz2engine(compressionengine):
3397 class _bz2engine(compressionengine):
3403 def name(self):
3398 def name(self):
3404 return 'bz2'
3399 return 'bz2'
3405
3400
3406 def bundletype(self):
3401 def bundletype(self):
3407 return 'bzip2', 'BZ'
3402 return 'bzip2', 'BZ'
3408
3403
3409 # We declare a protocol name but don't advertise by default because
3404 # We declare a protocol name but don't advertise by default because
3410 # it is slow.
3405 # it is slow.
3411 def wireprotosupport(self):
3406 def wireprotosupport(self):
3412 return compewireprotosupport('bzip2', 0, 0)
3407 return compewireprotosupport('bzip2', 0, 0)
3413
3408
3414 def compressstream(self, it, opts=None):
3409 def compressstream(self, it, opts=None):
3415 opts = opts or {}
3410 opts = opts or {}
3416 z = bz2.BZ2Compressor(opts.get('level', 9))
3411 z = bz2.BZ2Compressor(opts.get('level', 9))
3417 for chunk in it:
3412 for chunk in it:
3418 data = z.compress(chunk)
3413 data = z.compress(chunk)
3419 if data:
3414 if data:
3420 yield data
3415 yield data
3421
3416
3422 yield z.flush()
3417 yield z.flush()
3423
3418
3424 def decompressorreader(self, fh):
3419 def decompressorreader(self, fh):
3425 def gen():
3420 def gen():
3426 d = bz2.BZ2Decompressor()
3421 d = bz2.BZ2Decompressor()
3427 for chunk in filechunkiter(fh):
3422 for chunk in filechunkiter(fh):
3428 yield d.decompress(chunk)
3423 yield d.decompress(chunk)
3429
3424
3430 return chunkbuffer(gen())
3425 return chunkbuffer(gen())
3431
3426
3432 compengines.register(_bz2engine())
3427 compengines.register(_bz2engine())
3433
3428
3434 class _truncatedbz2engine(compressionengine):
3429 class _truncatedbz2engine(compressionengine):
3435 def name(self):
3430 def name(self):
3436 return 'bz2truncated'
3431 return 'bz2truncated'
3437
3432
3438 def bundletype(self):
3433 def bundletype(self):
3439 return None, '_truncatedBZ'
3434 return None, '_truncatedBZ'
3440
3435
3441 # We don't implement compressstream because it is hackily handled elsewhere.
3436 # We don't implement compressstream because it is hackily handled elsewhere.
3442
3437
3443 def decompressorreader(self, fh):
3438 def decompressorreader(self, fh):
3444 def gen():
3439 def gen():
3445 # The input stream doesn't have the 'BZ' header. So add it back.
3440 # The input stream doesn't have the 'BZ' header. So add it back.
3446 d = bz2.BZ2Decompressor()
3441 d = bz2.BZ2Decompressor()
3447 d.decompress('BZ')
3442 d.decompress('BZ')
3448 for chunk in filechunkiter(fh):
3443 for chunk in filechunkiter(fh):
3449 yield d.decompress(chunk)
3444 yield d.decompress(chunk)
3450
3445
3451 return chunkbuffer(gen())
3446 return chunkbuffer(gen())
3452
3447
3453 compengines.register(_truncatedbz2engine())
3448 compengines.register(_truncatedbz2engine())
3454
3449
3455 class _noopengine(compressionengine):
3450 class _noopengine(compressionengine):
3456 def name(self):
3451 def name(self):
3457 return 'none'
3452 return 'none'
3458
3453
3459 def bundletype(self):
3454 def bundletype(self):
3460 return 'none', 'UN'
3455 return 'none', 'UN'
3461
3456
3462 # Clients always support uncompressed payloads. Servers don't because
3457 # Clients always support uncompressed payloads. Servers don't because
3463 # unless you are on a fast network, uncompressed payloads can easily
3458 # unless you are on a fast network, uncompressed payloads can easily
3464 # saturate your network pipe.
3459 # saturate your network pipe.
3465 def wireprotosupport(self):
3460 def wireprotosupport(self):
3466 return compewireprotosupport('none', 0, 10)
3461 return compewireprotosupport('none', 0, 10)
3467
3462
3468 # We don't implement revlogheader because it is handled specially
3463 # We don't implement revlogheader because it is handled specially
3469 # in the revlog class.
3464 # in the revlog class.
3470
3465
3471 def compressstream(self, it, opts=None):
3466 def compressstream(self, it, opts=None):
3472 return it
3467 return it
3473
3468
3474 def decompressorreader(self, fh):
3469 def decompressorreader(self, fh):
3475 return fh
3470 return fh
3476
3471
3477 class nooprevlogcompressor(object):
3472 class nooprevlogcompressor(object):
3478 def compress(self, data):
3473 def compress(self, data):
3479 return None
3474 return None
3480
3475
3481 def revlogcompressor(self, opts=None):
3476 def revlogcompressor(self, opts=None):
3482 return self.nooprevlogcompressor()
3477 return self.nooprevlogcompressor()
3483
3478
3484 compengines.register(_noopengine())
3479 compengines.register(_noopengine())
3485
3480
3486 class _zstdengine(compressionengine):
3481 class _zstdengine(compressionengine):
3487 def name(self):
3482 def name(self):
3488 return 'zstd'
3483 return 'zstd'
3489
3484
3490 @propertycache
3485 @propertycache
3491 def _module(self):
3486 def _module(self):
3492 # Not all installs have the zstd module available. So defer importing
3487 # Not all installs have the zstd module available. So defer importing
3493 # until first access.
3488 # until first access.
3494 try:
3489 try:
3495 from . import zstd
3490 from . import zstd
3496 # Force delayed import.
3491 # Force delayed import.
3497 zstd.__version__
3492 zstd.__version__
3498 return zstd
3493 return zstd
3499 except ImportError:
3494 except ImportError:
3500 return None
3495 return None
3501
3496
3502 def available(self):
3497 def available(self):
3503 return bool(self._module)
3498 return bool(self._module)
3504
3499
3505 def bundletype(self):
3500 def bundletype(self):
3506 return 'zstd', 'ZS'
3501 return 'zstd', 'ZS'
3507
3502
3508 def wireprotosupport(self):
3503 def wireprotosupport(self):
3509 return compewireprotosupport('zstd', 50, 50)
3504 return compewireprotosupport('zstd', 50, 50)
3510
3505
3511 def revlogheader(self):
3506 def revlogheader(self):
3512 return '\x28'
3507 return '\x28'
3513
3508
3514 def compressstream(self, it, opts=None):
3509 def compressstream(self, it, opts=None):
3515 opts = opts or {}
3510 opts = opts or {}
3516 # zstd level 3 is almost always significantly faster than zlib
3511 # zstd level 3 is almost always significantly faster than zlib
3517 # while providing no worse compression. It strikes a good balance
3512 # while providing no worse compression. It strikes a good balance
3518 # between speed and compression.
3513 # between speed and compression.
3519 level = opts.get('level', 3)
3514 level = opts.get('level', 3)
3520
3515
3521 zstd = self._module
3516 zstd = self._module
3522 z = zstd.ZstdCompressor(level=level).compressobj()
3517 z = zstd.ZstdCompressor(level=level).compressobj()
3523 for chunk in it:
3518 for chunk in it:
3524 data = z.compress(chunk)
3519 data = z.compress(chunk)
3525 if data:
3520 if data:
3526 yield data
3521 yield data
3527
3522
3528 yield z.flush()
3523 yield z.flush()
3529
3524
3530 def decompressorreader(self, fh):
3525 def decompressorreader(self, fh):
3531 zstd = self._module
3526 zstd = self._module
3532 dctx = zstd.ZstdDecompressor()
3527 dctx = zstd.ZstdDecompressor()
3533 return chunkbuffer(dctx.read_from(fh))
3528 return chunkbuffer(dctx.read_from(fh))
3534
3529
3535 class zstdrevlogcompressor(object):
3530 class zstdrevlogcompressor(object):
3536 def __init__(self, zstd, level=3):
3531 def __init__(self, zstd, level=3):
3537 # Writing the content size adds a few bytes to the output. However,
3532 # Writing the content size adds a few bytes to the output. However,
3538 # it allows decompression to be more optimal since we can
3533 # it allows decompression to be more optimal since we can
3539 # pre-allocate a buffer to hold the result.
3534 # pre-allocate a buffer to hold the result.
3540 self._cctx = zstd.ZstdCompressor(level=level,
3535 self._cctx = zstd.ZstdCompressor(level=level,
3541 write_content_size=True)
3536 write_content_size=True)
3542 self._dctx = zstd.ZstdDecompressor()
3537 self._dctx = zstd.ZstdDecompressor()
3543 self._compinsize = zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE
3538 self._compinsize = zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE
3544 self._decompinsize = zstd.DECOMPRESSION_RECOMMENDED_INPUT_SIZE
3539 self._decompinsize = zstd.DECOMPRESSION_RECOMMENDED_INPUT_SIZE
3545
3540
3546 def compress(self, data):
3541 def compress(self, data):
3547 insize = len(data)
3542 insize = len(data)
3548 # Caller handles empty input case.
3543 # Caller handles empty input case.
3549 assert insize > 0
3544 assert insize > 0
3550
3545
3551 if insize < 50:
3546 if insize < 50:
3552 return None
3547 return None
3553
3548
3554 elif insize <= 1000000:
3549 elif insize <= 1000000:
3555 compressed = self._cctx.compress(data)
3550 compressed = self._cctx.compress(data)
3556 if len(compressed) < insize:
3551 if len(compressed) < insize:
3557 return compressed
3552 return compressed
3558 return None
3553 return None
3559 else:
3554 else:
3560 z = self._cctx.compressobj()
3555 z = self._cctx.compressobj()
3561 chunks = []
3556 chunks = []
3562 pos = 0
3557 pos = 0
3563 while pos < insize:
3558 while pos < insize:
3564 pos2 = pos + self._compinsize
3559 pos2 = pos + self._compinsize
3565 chunk = z.compress(data[pos:pos2])
3560 chunk = z.compress(data[pos:pos2])
3566 if chunk:
3561 if chunk:
3567 chunks.append(chunk)
3562 chunks.append(chunk)
3568 pos = pos2
3563 pos = pos2
3569 chunks.append(z.flush())
3564 chunks.append(z.flush())
3570
3565
3571 if sum(map(len, chunks)) < insize:
3566 if sum(map(len, chunks)) < insize:
3572 return ''.join(chunks)
3567 return ''.join(chunks)
3573 return None
3568 return None
3574
3569
3575 def decompress(self, data):
3570 def decompress(self, data):
3576 insize = len(data)
3571 insize = len(data)
3577
3572
3578 try:
3573 try:
3579 # This was measured to be faster than other streaming
3574 # This was measured to be faster than other streaming
3580 # decompressors.
3575 # decompressors.
3581 dobj = self._dctx.decompressobj()
3576 dobj = self._dctx.decompressobj()
3582 chunks = []
3577 chunks = []
3583 pos = 0
3578 pos = 0
3584 while pos < insize:
3579 while pos < insize:
3585 pos2 = pos + self._decompinsize
3580 pos2 = pos + self._decompinsize
3586 chunk = dobj.decompress(data[pos:pos2])
3581 chunk = dobj.decompress(data[pos:pos2])
3587 if chunk:
3582 if chunk:
3588 chunks.append(chunk)
3583 chunks.append(chunk)
3589 pos = pos2
3584 pos = pos2
3590 # Frame should be exhausted, so no finish() API.
3585 # Frame should be exhausted, so no finish() API.
3591
3586
3592 return ''.join(chunks)
3587 return ''.join(chunks)
3593 except Exception as e:
3588 except Exception as e:
3594 raise error.RevlogError(_('revlog decompress error: %s') %
3589 raise error.RevlogError(_('revlog decompress error: %s') %
3595 str(e))
3590 str(e))
3596
3591
3597 def revlogcompressor(self, opts=None):
3592 def revlogcompressor(self, opts=None):
3598 opts = opts or {}
3593 opts = opts or {}
3599 return self.zstdrevlogcompressor(self._module,
3594 return self.zstdrevlogcompressor(self._module,
3600 level=opts.get('level', 3))
3595 level=opts.get('level', 3))
3601
3596
3602 compengines.register(_zstdengine())
3597 compengines.register(_zstdengine())
3603
3598
3604 # convenient shortcut
3599 # convenient shortcut
3605 dst = debugstacktrace
3600 dst = debugstacktrace
@@ -1,383 +1,383
1 #require hardlink
1 #require hardlink
2
2
3 $ cat > nlinks.py <<EOF
3 $ cat > nlinks.py <<EOF
4 > import sys
4 > import sys
5 > from mercurial import util
5 > from mercurial import util
6 > for f in sorted(sys.stdin.readlines()):
6 > for f in sorted(sys.stdin.readlines()):
7 > f = f[:-1]
7 > f = f[:-1]
8 > print util.nlinks(f), f
8 > print util.nlinks(f), f
9 > EOF
9 > EOF
10
10
11 $ nlinksdir()
11 $ nlinksdir()
12 > {
12 > {
13 > find $1 -type f | python $TESTTMP/nlinks.py
13 > find $1 -type f | python $TESTTMP/nlinks.py
14 > }
14 > }
15
15
16 Some implementations of cp can't create hardlinks (replaces 'cp -al' on Linux):
16 Some implementations of cp can't create hardlinks (replaces 'cp -al' on Linux):
17
17
18 $ cat > linkcp.py <<EOF
18 $ cat > linkcp.py <<EOF
19 > from mercurial import util
19 > from mercurial import util
20 > import sys
20 > import sys
21 > util.copyfiles(sys.argv[1], sys.argv[2], hardlink=True)
21 > util.copyfiles(sys.argv[1], sys.argv[2], hardlink=True)
22 > EOF
22 > EOF
23
23
24 $ linkcp()
24 $ linkcp()
25 > {
25 > {
26 > python $TESTTMP/linkcp.py $1 $2
26 > python $TESTTMP/linkcp.py $1 $2
27 > }
27 > }
28
28
29 Prepare repo r1:
29 Prepare repo r1:
30
30
31 $ hg init r1
31 $ hg init r1
32 $ cd r1
32 $ cd r1
33
33
34 $ echo c1 > f1
34 $ echo c1 > f1
35 $ hg add f1
35 $ hg add f1
36 $ hg ci -m0
36 $ hg ci -m0
37
37
38 $ mkdir d1
38 $ mkdir d1
39 $ cd d1
39 $ cd d1
40 $ echo c2 > f2
40 $ echo c2 > f2
41 $ hg add f2
41 $ hg add f2
42 $ hg ci -m1
42 $ hg ci -m1
43 $ cd ../..
43 $ cd ../..
44
44
45 $ nlinksdir r1/.hg/store
45 $ nlinksdir r1/.hg/store
46 1 r1/.hg/store/00changelog.i
46 1 r1/.hg/store/00changelog.i
47 1 r1/.hg/store/00manifest.i
47 1 r1/.hg/store/00manifest.i
48 1 r1/.hg/store/data/d1/f2.i
48 1 r1/.hg/store/data/d1/f2.i
49 1 r1/.hg/store/data/f1.i
49 1 r1/.hg/store/data/f1.i
50 1 r1/.hg/store/fncache
50 1 r1/.hg/store/fncache
51 1 r1/.hg/store/phaseroots
51 1 r1/.hg/store/phaseroots
52 1 r1/.hg/store/undo
52 1 r1/.hg/store/undo
53 1 r1/.hg/store/undo.backup.fncache
53 1 r1/.hg/store/undo.backup.fncache
54 1 r1/.hg/store/undo.backupfiles
54 1 r1/.hg/store/undo.backupfiles
55 1 r1/.hg/store/undo.phaseroots
55 1 r1/.hg/store/undo.phaseroots
56
56
57
57
58 Create hardlinked clone r2:
58 Create hardlinked clone r2:
59
59
60 $ hg clone -U --debug r1 r2 --config progress.debug=true
60 $ hg clone -U --debug r1 r2 --config progress.debug=true
61 linking: 1
61 linking: 1
62 linking: 2
62 linking: 2
63 linking: 3
63 linking: 3
64 linking: 4
64 linking: 4
65 linking: 5
65 linking: 5
66 linking: 6
66 linking: 6
67 linking: 7
67 linking: 7
68 linked 7 files
68 linked 7 files
69
69
70 Create non-hardlinked clone r3:
70 Create non-hardlinked clone r3:
71
71
72 $ hg clone --pull r1 r3
72 $ hg clone --pull r1 r3
73 requesting all changes
73 requesting all changes
74 adding changesets
74 adding changesets
75 adding manifests
75 adding manifests
76 adding file changes
76 adding file changes
77 added 2 changesets with 2 changes to 2 files
77 added 2 changesets with 2 changes to 2 files
78 updating to branch default
78 updating to branch default
79 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
80
80
81
81
82 Repos r1 and r2 should now contain hardlinked files:
82 Repos r1 and r2 should now contain hardlinked files:
83
83
84 $ nlinksdir r1/.hg/store
84 $ nlinksdir r1/.hg/store
85 2 r1/.hg/store/00changelog.i
85 2 r1/.hg/store/00changelog.i
86 2 r1/.hg/store/00manifest.i
86 2 r1/.hg/store/00manifest.i
87 2 r1/.hg/store/data/d1/f2.i
87 2 r1/.hg/store/data/d1/f2.i
88 2 r1/.hg/store/data/f1.i
88 2 r1/.hg/store/data/f1.i
89 2 r1/.hg/store/fncache
89 2 r1/.hg/store/fncache
90 1 r1/.hg/store/phaseroots
90 1 r1/.hg/store/phaseroots
91 1 r1/.hg/store/undo
91 1 r1/.hg/store/undo
92 1 r1/.hg/store/undo.backup.fncache
92 1 r1/.hg/store/undo.backup.fncache
93 1 r1/.hg/store/undo.backupfiles
93 1 r1/.hg/store/undo.backupfiles
94 1 r1/.hg/store/undo.phaseroots
94 1 r1/.hg/store/undo.phaseroots
95
95
96 $ nlinksdir r2/.hg/store
96 $ nlinksdir r2/.hg/store
97 2 r2/.hg/store/00changelog.i
97 2 r2/.hg/store/00changelog.i
98 2 r2/.hg/store/00manifest.i
98 2 r2/.hg/store/00manifest.i
99 2 r2/.hg/store/data/d1/f2.i
99 2 r2/.hg/store/data/d1/f2.i
100 2 r2/.hg/store/data/f1.i
100 2 r2/.hg/store/data/f1.i
101 2 r2/.hg/store/fncache
101 2 r2/.hg/store/fncache
102
102
103 Repo r3 should not be hardlinked:
103 Repo r3 should not be hardlinked:
104
104
105 $ nlinksdir r3/.hg/store
105 $ nlinksdir r3/.hg/store
106 1 r3/.hg/store/00changelog.i
106 1 r3/.hg/store/00changelog.i
107 1 r3/.hg/store/00manifest.i
107 1 r3/.hg/store/00manifest.i
108 1 r3/.hg/store/data/d1/f2.i
108 1 r3/.hg/store/data/d1/f2.i
109 1 r3/.hg/store/data/f1.i
109 1 r3/.hg/store/data/f1.i
110 1 r3/.hg/store/fncache
110 1 r3/.hg/store/fncache
111 1 r3/.hg/store/phaseroots
111 1 r3/.hg/store/phaseroots
112 1 r3/.hg/store/undo
112 1 r3/.hg/store/undo
113 1 r3/.hg/store/undo.backupfiles
113 1 r3/.hg/store/undo.backupfiles
114 1 r3/.hg/store/undo.phaseroots
114 1 r3/.hg/store/undo.phaseroots
115
115
116
116
117 Create a non-inlined filelog in r3:
117 Create a non-inlined filelog in r3:
118
118
119 $ cd r3/d1
119 $ cd r3/d1
120 >>> f = open('data1', 'wb')
120 >>> f = open('data1', 'wb')
121 >>> for x in range(10000):
121 >>> for x in range(10000):
122 ... f.write("%s\n" % str(x))
122 ... f.write("%s\n" % str(x))
123 >>> f.close()
123 >>> f.close()
124 $ for j in 0 1 2 3 4 5 6 7 8 9; do
124 $ for j in 0 1 2 3 4 5 6 7 8 9; do
125 > cat data1 >> f2
125 > cat data1 >> f2
126 > hg commit -m$j
126 > hg commit -m$j
127 > done
127 > done
128 $ cd ../..
128 $ cd ../..
129
129
130 $ nlinksdir r3/.hg/store
130 $ nlinksdir r3/.hg/store
131 1 r3/.hg/store/00changelog.i
131 1 r3/.hg/store/00changelog.i
132 1 r3/.hg/store/00manifest.i
132 1 r3/.hg/store/00manifest.i
133 1 r3/.hg/store/data/d1/f2.d
133 1 r3/.hg/store/data/d1/f2.d
134 1 r3/.hg/store/data/d1/f2.i
134 1 r3/.hg/store/data/d1/f2.i
135 1 r3/.hg/store/data/f1.i
135 1 r3/.hg/store/data/f1.i
136 1 r3/.hg/store/fncache
136 1 r3/.hg/store/fncache
137 1 r3/.hg/store/phaseroots
137 1 r3/.hg/store/phaseroots
138 1 r3/.hg/store/undo
138 1 r3/.hg/store/undo
139 1 r3/.hg/store/undo.backup.fncache
139 1 r3/.hg/store/undo.backup.fncache
140 1 r3/.hg/store/undo.backup.phaseroots
140 1 r3/.hg/store/undo.backup.phaseroots
141 1 r3/.hg/store/undo.backupfiles
141 1 r3/.hg/store/undo.backupfiles
142 1 r3/.hg/store/undo.phaseroots
142 1 r3/.hg/store/undo.phaseroots
143
143
144 Push to repo r1 should break up most hardlinks in r2:
144 Push to repo r1 should break up most hardlinks in r2:
145
145
146 $ hg -R r2 verify
146 $ hg -R r2 verify
147 checking changesets
147 checking changesets
148 checking manifests
148 checking manifests
149 crosschecking files in changesets and manifests
149 crosschecking files in changesets and manifests
150 checking files
150 checking files
151 2 files, 2 changesets, 2 total revisions
151 2 files, 2 changesets, 2 total revisions
152
152
153 $ cd r3
153 $ cd r3
154 $ hg push
154 $ hg push
155 pushing to $TESTTMP/r1 (glob)
155 pushing to $TESTTMP/r1 (glob)
156 searching for changes
156 searching for changes
157 adding changesets
157 adding changesets
158 adding manifests
158 adding manifests
159 adding file changes
159 adding file changes
160 added 10 changesets with 10 changes to 1 files
160 added 10 changesets with 10 changes to 1 files
161
161
162 $ cd ..
162 $ cd ..
163
163
164 $ nlinksdir r2/.hg/store
164 $ nlinksdir r2/.hg/store
165 1 r2/.hg/store/00changelog.i
165 1 r2/.hg/store/00changelog.i
166 1 r2/.hg/store/00manifest.i
166 1 r2/.hg/store/00manifest.i
167 1 r2/.hg/store/data/d1/f2.i
167 1 r2/.hg/store/data/d1/f2.i
168 2 r2/.hg/store/data/f1.i
168 2 r2/.hg/store/data/f1.i
169 1 r2/.hg/store/fncache
169 [12] r2/\.hg/store/fncache (re)
170
170
171 $ hg -R r2 verify
171 $ hg -R r2 verify
172 checking changesets
172 checking changesets
173 checking manifests
173 checking manifests
174 crosschecking files in changesets and manifests
174 crosschecking files in changesets and manifests
175 checking files
175 checking files
176 2 files, 2 changesets, 2 total revisions
176 2 files, 2 changesets, 2 total revisions
177
177
178
178
179 $ cd r1
179 $ cd r1
180 $ hg up
180 $ hg up
181 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
181 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
182
182
183 Committing a change to f1 in r1 must break up hardlink f1.i in r2:
183 Committing a change to f1 in r1 must break up hardlink f1.i in r2:
184
184
185 $ echo c1c1 >> f1
185 $ echo c1c1 >> f1
186 $ hg ci -m00
186 $ hg ci -m00
187 $ cd ..
187 $ cd ..
188
188
189 $ nlinksdir r2/.hg/store
189 $ nlinksdir r2/.hg/store
190 1 r2/.hg/store/00changelog.i
190 1 r2/.hg/store/00changelog.i
191 1 r2/.hg/store/00manifest.i
191 1 r2/.hg/store/00manifest.i
192 1 r2/.hg/store/data/d1/f2.i
192 1 r2/.hg/store/data/d1/f2.i
193 1 r2/.hg/store/data/f1.i
193 1 r2/.hg/store/data/f1.i
194 1 r2/.hg/store/fncache
194 [12] r2/\.hg/store/fncache (re)
195
195
196
196
197 $ cd r3
197 $ cd r3
198 $ hg tip --template '{rev}:{node|short}\n'
198 $ hg tip --template '{rev}:{node|short}\n'
199 11:a6451b6bc41f
199 11:a6451b6bc41f
200 $ echo bla > f1
200 $ echo bla > f1
201 $ hg ci -m1
201 $ hg ci -m1
202 $ cd ..
202 $ cd ..
203
203
204 Create hardlinked copy r4 of r3 (on Linux, we would call 'cp -al'):
204 Create hardlinked copy r4 of r3 (on Linux, we would call 'cp -al'):
205
205
206 $ linkcp r3 r4
206 $ linkcp r3 r4
207
207
208 r4 has hardlinks in the working dir (not just inside .hg):
208 r4 has hardlinks in the working dir (not just inside .hg):
209
209
210 $ nlinksdir r4
210 $ nlinksdir r4
211 2 r4/.hg/00changelog.i
211 2 r4/.hg/00changelog.i
212 2 r4/.hg/branch
212 2 r4/.hg/branch
213 2 r4/.hg/cache/branch2-served
213 2 r4/.hg/cache/branch2-served
214 2 r4/.hg/cache/checkisexec
214 2 r4/.hg/cache/checkisexec
215 3 r4/.hg/cache/checklink (?)
215 3 r4/.hg/cache/checklink (?)
216 ? r4/.hg/cache/checklink-target (glob)
216 ? r4/.hg/cache/checklink-target (glob)
217 2 r4/.hg/cache/checknoexec
217 2 r4/.hg/cache/checknoexec
218 2 r4/.hg/cache/rbc-names-v1
218 2 r4/.hg/cache/rbc-names-v1
219 2 r4/.hg/cache/rbc-revs-v1
219 2 r4/.hg/cache/rbc-revs-v1
220 2 r4/.hg/dirstate
220 2 r4/.hg/dirstate
221 2 r4/.hg/hgrc
221 2 r4/.hg/hgrc
222 2 r4/.hg/last-message.txt
222 2 r4/.hg/last-message.txt
223 2 r4/.hg/requires
223 2 r4/.hg/requires
224 2 r4/.hg/store/00changelog.i
224 2 r4/.hg/store/00changelog.i
225 2 r4/.hg/store/00manifest.i
225 2 r4/.hg/store/00manifest.i
226 2 r4/.hg/store/data/d1/f2.d
226 2 r4/.hg/store/data/d1/f2.d
227 2 r4/.hg/store/data/d1/f2.i
227 2 r4/.hg/store/data/d1/f2.i
228 2 r4/.hg/store/data/f1.i
228 2 r4/.hg/store/data/f1.i
229 2 r4/.hg/store/fncache
229 2 r4/.hg/store/fncache
230 2 r4/.hg/store/phaseroots
230 2 r4/.hg/store/phaseroots
231 2 r4/.hg/store/undo
231 2 r4/.hg/store/undo
232 2 r4/.hg/store/undo.backup.fncache
232 2 r4/.hg/store/undo.backup.fncache
233 2 r4/.hg/store/undo.backup.phaseroots
233 2 r4/.hg/store/undo.backup.phaseroots
234 2 r4/.hg/store/undo.backupfiles
234 2 r4/.hg/store/undo.backupfiles
235 2 r4/.hg/store/undo.phaseroots
235 2 r4/.hg/store/undo.phaseroots
236 2 r4/.hg/undo.backup.dirstate
236 [24] r4/\.hg/undo\.backup\.dirstate (re)
237 2 r4/.hg/undo.bookmarks
237 2 r4/.hg/undo.bookmarks
238 2 r4/.hg/undo.branch
238 2 r4/.hg/undo.branch
239 2 r4/.hg/undo.desc
239 2 r4/.hg/undo.desc
240 2 r4/.hg/undo.dirstate
240 [24] r4/\.hg/undo\.dirstate (re)
241 2 r4/d1/data1
241 2 r4/d1/data1
242 2 r4/d1/f2
242 2 r4/d1/f2
243 2 r4/f1
243 2 r4/f1
244
244
245 Update back to revision 11 in r4 should break hardlink of file f1:
245 Update back to revision 11 in r4 should break hardlink of file f1:
246
246
247 $ hg -R r4 up 11
247 $ hg -R r4 up 11
248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
249
249
250 $ nlinksdir r4
250 $ nlinksdir r4
251 2 r4/.hg/00changelog.i
251 2 r4/.hg/00changelog.i
252 1 r4/.hg/branch
252 1 r4/.hg/branch
253 2 r4/.hg/cache/branch2-served
253 2 r4/.hg/cache/branch2-served
254 2 r4/.hg/cache/checkisexec
254 2 r4/.hg/cache/checkisexec
255 2 r4/.hg/cache/checklink-target
255 2 r4/.hg/cache/checklink-target
256 2 r4/.hg/cache/checknoexec
256 2 r4/.hg/cache/checknoexec
257 2 r4/.hg/cache/rbc-names-v1
257 2 r4/.hg/cache/rbc-names-v1
258 2 r4/.hg/cache/rbc-revs-v1
258 2 r4/.hg/cache/rbc-revs-v1
259 1 r4/.hg/dirstate
259 1 r4/.hg/dirstate
260 2 r4/.hg/hgrc
260 2 r4/.hg/hgrc
261 2 r4/.hg/last-message.txt
261 2 r4/.hg/last-message.txt
262 2 r4/.hg/requires
262 2 r4/.hg/requires
263 2 r4/.hg/store/00changelog.i
263 2 r4/.hg/store/00changelog.i
264 2 r4/.hg/store/00manifest.i
264 2 r4/.hg/store/00manifest.i
265 2 r4/.hg/store/data/d1/f2.d
265 2 r4/.hg/store/data/d1/f2.d
266 2 r4/.hg/store/data/d1/f2.i
266 2 r4/.hg/store/data/d1/f2.i
267 2 r4/.hg/store/data/f1.i
267 2 r4/.hg/store/data/f1.i
268 2 r4/.hg/store/fncache
268 2 r4/.hg/store/fncache
269 2 r4/.hg/store/phaseroots
269 2 r4/.hg/store/phaseroots
270 2 r4/.hg/store/undo
270 2 r4/.hg/store/undo
271 2 r4/.hg/store/undo.backup.fncache
271 2 r4/.hg/store/undo.backup.fncache
272 2 r4/.hg/store/undo.backup.phaseroots
272 2 r4/.hg/store/undo.backup.phaseroots
273 2 r4/.hg/store/undo.backupfiles
273 2 r4/.hg/store/undo.backupfiles
274 2 r4/.hg/store/undo.phaseroots
274 2 r4/.hg/store/undo.phaseroots
275 2 r4/.hg/undo.backup.dirstate
275 [24] r4/\.hg/undo\.backup\.dirstate (re)
276 2 r4/.hg/undo.bookmarks
276 2 r4/.hg/undo.bookmarks
277 2 r4/.hg/undo.branch
277 2 r4/.hg/undo.branch
278 2 r4/.hg/undo.desc
278 2 r4/.hg/undo.desc
279 2 r4/.hg/undo.dirstate
279 [24] r4/\.hg/undo\.dirstate (re)
280 2 r4/d1/data1
280 2 r4/d1/data1
281 2 r4/d1/f2
281 2 r4/d1/f2
282 1 r4/f1
282 1 r4/f1
283
283
284
284
285 Test hardlinking outside hg:
285 Test hardlinking outside hg:
286
286
287 $ mkdir x
287 $ mkdir x
288 $ echo foo > x/a
288 $ echo foo > x/a
289
289
290 $ linkcp x y
290 $ linkcp x y
291 $ echo bar >> y/a
291 $ echo bar >> y/a
292
292
293 No diff if hardlink:
293 No diff if hardlink:
294
294
295 $ diff x/a y/a
295 $ diff x/a y/a
296
296
297 Test mq hardlinking:
297 Test mq hardlinking:
298
298
299 $ echo "[extensions]" >> $HGRCPATH
299 $ echo "[extensions]" >> $HGRCPATH
300 $ echo "mq=" >> $HGRCPATH
300 $ echo "mq=" >> $HGRCPATH
301
301
302 $ hg init a
302 $ hg init a
303 $ cd a
303 $ cd a
304
304
305 $ hg qimport -n foo - << EOF
305 $ hg qimport -n foo - << EOF
306 > # HG changeset patch
306 > # HG changeset patch
307 > # Date 1 0
307 > # Date 1 0
308 > diff -r 2588a8b53d66 a
308 > diff -r 2588a8b53d66 a
309 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
309 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
310 > +++ b/a Wed Jul 23 15:54:29 2008 +0200
310 > +++ b/a Wed Jul 23 15:54:29 2008 +0200
311 > @@ -0,0 +1,1 @@
311 > @@ -0,0 +1,1 @@
312 > +a
312 > +a
313 > EOF
313 > EOF
314 adding foo to series file
314 adding foo to series file
315
315
316 $ hg qpush
316 $ hg qpush
317 applying foo
317 applying foo
318 now at: foo
318 now at: foo
319
319
320 $ cd ..
320 $ cd ..
321 $ linkcp a b
321 $ linkcp a b
322 $ cd b
322 $ cd b
323
323
324 $ hg qimport -n bar - << EOF
324 $ hg qimport -n bar - << EOF
325 > # HG changeset patch
325 > # HG changeset patch
326 > # Date 2 0
326 > # Date 2 0
327 > diff -r 2588a8b53d66 a
327 > diff -r 2588a8b53d66 a
328 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
328 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
329 > +++ b/b Wed Jul 23 15:54:29 2008 +0200
329 > +++ b/b Wed Jul 23 15:54:29 2008 +0200
330 > @@ -0,0 +1,1 @@
330 > @@ -0,0 +1,1 @@
331 > +b
331 > +b
332 > EOF
332 > EOF
333 adding bar to series file
333 adding bar to series file
334
334
335 $ hg qpush
335 $ hg qpush
336 applying bar
336 applying bar
337 now at: bar
337 now at: bar
338
338
339 $ cat .hg/patches/status
339 $ cat .hg/patches/status
340 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
340 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
341 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c:bar
341 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c:bar
342
342
343 $ cat .hg/patches/series
343 $ cat .hg/patches/series
344 foo
344 foo
345 bar
345 bar
346
346
347 $ cat ../a/.hg/patches/status
347 $ cat ../a/.hg/patches/status
348 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
348 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
349
349
350 $ cat ../a/.hg/patches/series
350 $ cat ../a/.hg/patches/series
351 foo
351 foo
352
352
353 Test tags hardlinking:
353 Test tags hardlinking:
354
354
355 $ hg qdel -r qbase:qtip
355 $ hg qdel -r qbase:qtip
356 patch foo finalized without changeset message
356 patch foo finalized without changeset message
357 patch bar finalized without changeset message
357 patch bar finalized without changeset message
358
358
359 $ hg tag -l lfoo
359 $ hg tag -l lfoo
360 $ hg tag foo
360 $ hg tag foo
361
361
362 $ cd ..
362 $ cd ..
363 $ linkcp b c
363 $ linkcp b c
364 $ cd c
364 $ cd c
365
365
366 $ hg tag -l -r 0 lbar
366 $ hg tag -l -r 0 lbar
367 $ hg tag -r 0 bar
367 $ hg tag -r 0 bar
368
368
369 $ cat .hgtags
369 $ cat .hgtags
370 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
370 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
371 430ed4828a74fa4047bc816a25500f7472ab4bfe bar
371 430ed4828a74fa4047bc816a25500f7472ab4bfe bar
372
372
373 $ cat .hg/localtags
373 $ cat .hg/localtags
374 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
374 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
375 430ed4828a74fa4047bc816a25500f7472ab4bfe lbar
375 430ed4828a74fa4047bc816a25500f7472ab4bfe lbar
376
376
377 $ cat ../b/.hgtags
377 $ cat ../b/.hgtags
378 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
378 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
379
379
380 $ cat ../b/.hg/localtags
380 $ cat ../b/.hg/localtags
381 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
381 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
382
382
383 $ cd ..
383 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now