##// END OF EJS Templates
help/dates: use DATE as place-holder in help and abort texts...
Martin Geisler -
r13886:fe48c573 default
parent child Browse files
Show More
@@ -1,36 +1,36
1 Some commands allow the user to specify a date, e.g.:
1 Some commands allow the user to specify a date, e.g.:
2
2
3 - backout, commit, import, tag: Specify the commit date.
3 - backout, commit, import, tag: Specify the commit date.
4 - log, revert, update: Select revision(s) by date.
4 - log, revert, update: Select revision(s) by date.
5
5
6 Many date formats are valid. Here are some examples:
6 Many date formats are valid. Here are some examples:
7
7
8 - ``Wed Dec 6 13:18:29 2006`` (local timezone assumed)
8 - ``Wed Dec 6 13:18:29 2006`` (local timezone assumed)
9 - ``Dec 6 13:18 -0600`` (year assumed, time offset provided)
9 - ``Dec 6 13:18 -0600`` (year assumed, time offset provided)
10 - ``Dec 6 13:18 UTC`` (UTC and GMT are aliases for +0000)
10 - ``Dec 6 13:18 UTC`` (UTC and GMT are aliases for +0000)
11 - ``Dec 6`` (midnight)
11 - ``Dec 6`` (midnight)
12 - ``13:18`` (today assumed)
12 - ``13:18`` (today assumed)
13 - ``3:39`` (3:39AM assumed)
13 - ``3:39`` (3:39AM assumed)
14 - ``3:39pm`` (15:39)
14 - ``3:39pm`` (15:39)
15 - ``2006-12-06 13:18:29`` (ISO 8601 format)
15 - ``2006-12-06 13:18:29`` (ISO 8601 format)
16 - ``2006-12-6 13:18``
16 - ``2006-12-6 13:18``
17 - ``2006-12-6``
17 - ``2006-12-6``
18 - ``12-6``
18 - ``12-6``
19 - ``12/6``
19 - ``12/6``
20 - ``12/6/6`` (Dec 6 2006)
20 - ``12/6/6`` (Dec 6 2006)
21
21
22 Lastly, there is Mercurial's internal format:
22 Lastly, there is Mercurial's internal format:
23
23
24 - ``1165432709 0`` (Wed Dec 6 13:18:29 2006 UTC)
24 - ``1165432709 0`` (Wed Dec 6 13:18:29 2006 UTC)
25
25
26 This is the internal representation format for dates. unixtime is the
26 This is the internal representation format for dates. unixtime is the
27 number of seconds since the epoch (1970-01-01 00:00 UTC). offset is
27 number of seconds since the epoch (1970-01-01 00:00 UTC). offset is
28 the offset of the local timezone, in seconds west of UTC (negative if
28 the offset of the local timezone, in seconds west of UTC (negative if
29 the timezone is east of UTC).
29 the timezone is east of UTC).
30
30
31 The log command also accepts date ranges:
31 The log command also accepts date ranges:
32
32
33 - ``<{datetime}`` - at or before a given date/time
33 - ``<DATE`` - at or before a given date/time
34 - ``>{datetime}`` - on or after a given date/time
34 - ``>DATE`` - on or after a given date/time
35 - ``{datetime} to {datetime}`` - a date range, inclusive
35 - ``DATE to DATE`` - a date range, inclusive
36 - ``-{days}`` - within a given number of days of today
36 - ``-DAYS`` - within a given number of days of today
@@ -1,1540 +1,1540
1 # util.py - Mercurial utility functions and platform specfic implementations
1 # util.py - Mercurial utility functions and platform specfic 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 specfic implementations.
10 """Mercurial utility functions and platform specfic 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 i18n import _
16 from i18n import _
17 import error, osutil, encoding
17 import error, osutil, encoding
18 import errno, re, shutil, sys, tempfile, traceback
18 import errno, re, shutil, sys, tempfile, traceback
19 import os, stat, time, calendar, textwrap, unicodedata, signal
19 import os, stat, time, calendar, textwrap, unicodedata, signal
20 import imp, socket
20 import imp, socket
21
21
22 # Python compatibility
22 # Python compatibility
23
23
24 def sha1(s):
24 def sha1(s):
25 return _fastsha1(s)
25 return _fastsha1(s)
26
26
27 def _fastsha1(s):
27 def _fastsha1(s):
28 # This function will import sha1 from hashlib or sha (whichever is
28 # This function will import sha1 from hashlib or sha (whichever is
29 # available) and overwrite itself with it on the first call.
29 # available) and overwrite itself with it on the first call.
30 # Subsequent calls will go directly to the imported function.
30 # Subsequent calls will go directly to the imported function.
31 if sys.version_info >= (2, 5):
31 if sys.version_info >= (2, 5):
32 from hashlib import sha1 as _sha1
32 from hashlib import sha1 as _sha1
33 else:
33 else:
34 from sha import sha as _sha1
34 from sha import sha as _sha1
35 global _fastsha1, sha1
35 global _fastsha1, sha1
36 _fastsha1 = sha1 = _sha1
36 _fastsha1 = sha1 = _sha1
37 return _sha1(s)
37 return _sha1(s)
38
38
39 import __builtin__
39 import __builtin__
40
40
41 if sys.version_info[0] < 3:
41 if sys.version_info[0] < 3:
42 def fakebuffer(sliceable, offset=0):
42 def fakebuffer(sliceable, offset=0):
43 return sliceable[offset:]
43 return sliceable[offset:]
44 else:
44 else:
45 def fakebuffer(sliceable, offset=0):
45 def fakebuffer(sliceable, offset=0):
46 return memoryview(sliceable)[offset:]
46 return memoryview(sliceable)[offset:]
47 try:
47 try:
48 buffer
48 buffer
49 except NameError:
49 except NameError:
50 __builtin__.buffer = fakebuffer
50 __builtin__.buffer = fakebuffer
51
51
52 import subprocess
52 import subprocess
53 closefds = os.name == 'posix'
53 closefds = os.name == 'posix'
54
54
55 def popen2(cmd, env=None, newlines=False):
55 def popen2(cmd, env=None, newlines=False):
56 # Setting bufsize to -1 lets the system decide the buffer size.
56 # Setting bufsize to -1 lets the system decide the buffer size.
57 # The default for bufsize is 0, meaning unbuffered. This leads to
57 # The default for bufsize is 0, meaning unbuffered. This leads to
58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
60 close_fds=closefds,
60 close_fds=closefds,
61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
62 universal_newlines=newlines,
62 universal_newlines=newlines,
63 env=env)
63 env=env)
64 return p.stdin, p.stdout
64 return p.stdin, p.stdout
65
65
66 def popen3(cmd, env=None, newlines=False):
66 def popen3(cmd, env=None, newlines=False):
67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
68 close_fds=closefds,
68 close_fds=closefds,
69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
70 stderr=subprocess.PIPE,
70 stderr=subprocess.PIPE,
71 universal_newlines=newlines,
71 universal_newlines=newlines,
72 env=env)
72 env=env)
73 return p.stdin, p.stdout, p.stderr
73 return p.stdin, p.stdout, p.stderr
74
74
75 def version():
75 def version():
76 """Return version information if available."""
76 """Return version information if available."""
77 try:
77 try:
78 import __version__
78 import __version__
79 return __version__.version
79 return __version__.version
80 except ImportError:
80 except ImportError:
81 return 'unknown'
81 return 'unknown'
82
82
83 # used by parsedate
83 # used by parsedate
84 defaultdateformats = (
84 defaultdateformats = (
85 '%Y-%m-%d %H:%M:%S',
85 '%Y-%m-%d %H:%M:%S',
86 '%Y-%m-%d %I:%M:%S%p',
86 '%Y-%m-%d %I:%M:%S%p',
87 '%Y-%m-%d %H:%M',
87 '%Y-%m-%d %H:%M',
88 '%Y-%m-%d %I:%M%p',
88 '%Y-%m-%d %I:%M%p',
89 '%Y-%m-%d',
89 '%Y-%m-%d',
90 '%m-%d',
90 '%m-%d',
91 '%m/%d',
91 '%m/%d',
92 '%m/%d/%y',
92 '%m/%d/%y',
93 '%m/%d/%Y',
93 '%m/%d/%Y',
94 '%a %b %d %H:%M:%S %Y',
94 '%a %b %d %H:%M:%S %Y',
95 '%a %b %d %I:%M:%S%p %Y',
95 '%a %b %d %I:%M:%S%p %Y',
96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
97 '%b %d %H:%M:%S %Y',
97 '%b %d %H:%M:%S %Y',
98 '%b %d %I:%M:%S%p %Y',
98 '%b %d %I:%M:%S%p %Y',
99 '%b %d %H:%M:%S',
99 '%b %d %H:%M:%S',
100 '%b %d %I:%M:%S%p',
100 '%b %d %I:%M:%S%p',
101 '%b %d %H:%M',
101 '%b %d %H:%M',
102 '%b %d %I:%M%p',
102 '%b %d %I:%M%p',
103 '%b %d %Y',
103 '%b %d %Y',
104 '%b %d',
104 '%b %d',
105 '%H:%M:%S',
105 '%H:%M:%S',
106 '%I:%M:%S%p',
106 '%I:%M:%S%p',
107 '%H:%M',
107 '%H:%M',
108 '%I:%M%p',
108 '%I:%M%p',
109 )
109 )
110
110
111 extendeddateformats = defaultdateformats + (
111 extendeddateformats = defaultdateformats + (
112 "%Y",
112 "%Y",
113 "%Y-%m",
113 "%Y-%m",
114 "%b",
114 "%b",
115 "%b %Y",
115 "%b %Y",
116 )
116 )
117
117
118 def cachefunc(func):
118 def cachefunc(func):
119 '''cache the result of function calls'''
119 '''cache the result of function calls'''
120 # XXX doesn't handle keywords args
120 # XXX doesn't handle keywords args
121 cache = {}
121 cache = {}
122 if func.func_code.co_argcount == 1:
122 if func.func_code.co_argcount == 1:
123 # we gain a small amount of time because
123 # we gain a small amount of time because
124 # we don't need to pack/unpack the list
124 # we don't need to pack/unpack the list
125 def f(arg):
125 def f(arg):
126 if arg not in cache:
126 if arg not in cache:
127 cache[arg] = func(arg)
127 cache[arg] = func(arg)
128 return cache[arg]
128 return cache[arg]
129 else:
129 else:
130 def f(*args):
130 def f(*args):
131 if args not in cache:
131 if args not in cache:
132 cache[args] = func(*args)
132 cache[args] = func(*args)
133 return cache[args]
133 return cache[args]
134
134
135 return f
135 return f
136
136
137 def lrucachefunc(func):
137 def lrucachefunc(func):
138 '''cache most recent results of function calls'''
138 '''cache most recent results of function calls'''
139 cache = {}
139 cache = {}
140 order = []
140 order = []
141 if func.func_code.co_argcount == 1:
141 if func.func_code.co_argcount == 1:
142 def f(arg):
142 def f(arg):
143 if arg not in cache:
143 if arg not in cache:
144 if len(cache) > 20:
144 if len(cache) > 20:
145 del cache[order.pop(0)]
145 del cache[order.pop(0)]
146 cache[arg] = func(arg)
146 cache[arg] = func(arg)
147 else:
147 else:
148 order.remove(arg)
148 order.remove(arg)
149 order.append(arg)
149 order.append(arg)
150 return cache[arg]
150 return cache[arg]
151 else:
151 else:
152 def f(*args):
152 def f(*args):
153 if args not in cache:
153 if args not in cache:
154 if len(cache) > 20:
154 if len(cache) > 20:
155 del cache[order.pop(0)]
155 del cache[order.pop(0)]
156 cache[args] = func(*args)
156 cache[args] = func(*args)
157 else:
157 else:
158 order.remove(args)
158 order.remove(args)
159 order.append(args)
159 order.append(args)
160 return cache[args]
160 return cache[args]
161
161
162 return f
162 return f
163
163
164 class propertycache(object):
164 class propertycache(object):
165 def __init__(self, func):
165 def __init__(self, func):
166 self.func = func
166 self.func = func
167 self.name = func.__name__
167 self.name = func.__name__
168 def __get__(self, obj, type=None):
168 def __get__(self, obj, type=None):
169 result = self.func(obj)
169 result = self.func(obj)
170 setattr(obj, self.name, result)
170 setattr(obj, self.name, result)
171 return result
171 return result
172
172
173 def pipefilter(s, cmd):
173 def pipefilter(s, cmd):
174 '''filter string S through command CMD, returning its output'''
174 '''filter string S through command CMD, returning its output'''
175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
177 pout, perr = p.communicate(s)
177 pout, perr = p.communicate(s)
178 return pout
178 return pout
179
179
180 def tempfilter(s, cmd):
180 def tempfilter(s, cmd):
181 '''filter string S through a pair of temporary files with CMD.
181 '''filter string S through a pair of temporary files with CMD.
182 CMD is used as a template to create the real command to be run,
182 CMD is used as a template to create the real command to be run,
183 with the strings INFILE and OUTFILE replaced by the real names of
183 with the strings INFILE and OUTFILE replaced by the real names of
184 the temporary files generated.'''
184 the temporary files generated.'''
185 inname, outname = None, None
185 inname, outname = None, None
186 try:
186 try:
187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
188 fp = os.fdopen(infd, 'wb')
188 fp = os.fdopen(infd, 'wb')
189 fp.write(s)
189 fp.write(s)
190 fp.close()
190 fp.close()
191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
192 os.close(outfd)
192 os.close(outfd)
193 cmd = cmd.replace('INFILE', inname)
193 cmd = cmd.replace('INFILE', inname)
194 cmd = cmd.replace('OUTFILE', outname)
194 cmd = cmd.replace('OUTFILE', outname)
195 code = os.system(cmd)
195 code = os.system(cmd)
196 if sys.platform == 'OpenVMS' and code & 1:
196 if sys.platform == 'OpenVMS' and code & 1:
197 code = 0
197 code = 0
198 if code:
198 if code:
199 raise Abort(_("command '%s' failed: %s") %
199 raise Abort(_("command '%s' failed: %s") %
200 (cmd, explain_exit(code)))
200 (cmd, explain_exit(code)))
201 fp = open(outname, 'rb')
201 fp = open(outname, 'rb')
202 r = fp.read()
202 r = fp.read()
203 fp.close()
203 fp.close()
204 return r
204 return r
205 finally:
205 finally:
206 try:
206 try:
207 if inname:
207 if inname:
208 os.unlink(inname)
208 os.unlink(inname)
209 except:
209 except:
210 pass
210 pass
211 try:
211 try:
212 if outname:
212 if outname:
213 os.unlink(outname)
213 os.unlink(outname)
214 except:
214 except:
215 pass
215 pass
216
216
217 filtertable = {
217 filtertable = {
218 'tempfile:': tempfilter,
218 'tempfile:': tempfilter,
219 'pipe:': pipefilter,
219 'pipe:': pipefilter,
220 }
220 }
221
221
222 def filter(s, cmd):
222 def filter(s, cmd):
223 "filter a string through a command that transforms its input to its output"
223 "filter a string through a command that transforms its input to its output"
224 for name, fn in filtertable.iteritems():
224 for name, fn in filtertable.iteritems():
225 if cmd.startswith(name):
225 if cmd.startswith(name):
226 return fn(s, cmd[len(name):].lstrip())
226 return fn(s, cmd[len(name):].lstrip())
227 return pipefilter(s, cmd)
227 return pipefilter(s, cmd)
228
228
229 def binary(s):
229 def binary(s):
230 """return true if a string is binary data"""
230 """return true if a string is binary data"""
231 return bool(s and '\0' in s)
231 return bool(s and '\0' in s)
232
232
233 def increasingchunks(source, min=1024, max=65536):
233 def increasingchunks(source, min=1024, max=65536):
234 '''return no less than min bytes per chunk while data remains,
234 '''return no less than min bytes per chunk while data remains,
235 doubling min after each chunk until it reaches max'''
235 doubling min after each chunk until it reaches max'''
236 def log2(x):
236 def log2(x):
237 if not x:
237 if not x:
238 return 0
238 return 0
239 i = 0
239 i = 0
240 while x:
240 while x:
241 x >>= 1
241 x >>= 1
242 i += 1
242 i += 1
243 return i - 1
243 return i - 1
244
244
245 buf = []
245 buf = []
246 blen = 0
246 blen = 0
247 for chunk in source:
247 for chunk in source:
248 buf.append(chunk)
248 buf.append(chunk)
249 blen += len(chunk)
249 blen += len(chunk)
250 if blen >= min:
250 if blen >= min:
251 if min < max:
251 if min < max:
252 min = min << 1
252 min = min << 1
253 nmin = 1 << log2(blen)
253 nmin = 1 << log2(blen)
254 if nmin > min:
254 if nmin > min:
255 min = nmin
255 min = nmin
256 if min > max:
256 if min > max:
257 min = max
257 min = max
258 yield ''.join(buf)
258 yield ''.join(buf)
259 blen = 0
259 blen = 0
260 buf = []
260 buf = []
261 if buf:
261 if buf:
262 yield ''.join(buf)
262 yield ''.join(buf)
263
263
264 Abort = error.Abort
264 Abort = error.Abort
265
265
266 def always(fn):
266 def always(fn):
267 return True
267 return True
268
268
269 def never(fn):
269 def never(fn):
270 return False
270 return False
271
271
272 def pathto(root, n1, n2):
272 def pathto(root, n1, n2):
273 '''return the relative path from one place to another.
273 '''return the relative path from one place to another.
274 root should use os.sep to separate directories
274 root should use os.sep to separate directories
275 n1 should use os.sep to separate directories
275 n1 should use os.sep to separate directories
276 n2 should use "/" to separate directories
276 n2 should use "/" to separate directories
277 returns an os.sep-separated path.
277 returns an os.sep-separated path.
278
278
279 If n1 is a relative path, it's assumed it's
279 If n1 is a relative path, it's assumed it's
280 relative to root.
280 relative to root.
281 n2 should always be relative to root.
281 n2 should always be relative to root.
282 '''
282 '''
283 if not n1:
283 if not n1:
284 return localpath(n2)
284 return localpath(n2)
285 if os.path.isabs(n1):
285 if os.path.isabs(n1):
286 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
286 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
287 return os.path.join(root, localpath(n2))
287 return os.path.join(root, localpath(n2))
288 n2 = '/'.join((pconvert(root), n2))
288 n2 = '/'.join((pconvert(root), n2))
289 a, b = splitpath(n1), n2.split('/')
289 a, b = splitpath(n1), n2.split('/')
290 a.reverse()
290 a.reverse()
291 b.reverse()
291 b.reverse()
292 while a and b and a[-1] == b[-1]:
292 while a and b and a[-1] == b[-1]:
293 a.pop()
293 a.pop()
294 b.pop()
294 b.pop()
295 b.reverse()
295 b.reverse()
296 return os.sep.join((['..'] * len(a)) + b) or '.'
296 return os.sep.join((['..'] * len(a)) + b) or '.'
297
297
298 def canonpath(root, cwd, myname, auditor=None):
298 def canonpath(root, cwd, myname, auditor=None):
299 """return the canonical path of myname, given cwd and root"""
299 """return the canonical path of myname, given cwd and root"""
300 if endswithsep(root):
300 if endswithsep(root):
301 rootsep = root
301 rootsep = root
302 else:
302 else:
303 rootsep = root + os.sep
303 rootsep = root + os.sep
304 name = myname
304 name = myname
305 if not os.path.isabs(name):
305 if not os.path.isabs(name):
306 name = os.path.join(root, cwd, name)
306 name = os.path.join(root, cwd, name)
307 name = os.path.normpath(name)
307 name = os.path.normpath(name)
308 if auditor is None:
308 if auditor is None:
309 auditor = path_auditor(root)
309 auditor = path_auditor(root)
310 if name != rootsep and name.startswith(rootsep):
310 if name != rootsep and name.startswith(rootsep):
311 name = name[len(rootsep):]
311 name = name[len(rootsep):]
312 auditor(name)
312 auditor(name)
313 return pconvert(name)
313 return pconvert(name)
314 elif name == root:
314 elif name == root:
315 return ''
315 return ''
316 else:
316 else:
317 # Determine whether `name' is in the hierarchy at or beneath `root',
317 # Determine whether `name' is in the hierarchy at or beneath `root',
318 # by iterating name=dirname(name) until that causes no change (can't
318 # by iterating name=dirname(name) until that causes no change (can't
319 # check name == '/', because that doesn't work on windows). For each
319 # check name == '/', because that doesn't work on windows). For each
320 # `name', compare dev/inode numbers. If they match, the list `rel'
320 # `name', compare dev/inode numbers. If they match, the list `rel'
321 # holds the reversed list of components making up the relative file
321 # holds the reversed list of components making up the relative file
322 # name we want.
322 # name we want.
323 root_st = os.stat(root)
323 root_st = os.stat(root)
324 rel = []
324 rel = []
325 while True:
325 while True:
326 try:
326 try:
327 name_st = os.stat(name)
327 name_st = os.stat(name)
328 except OSError:
328 except OSError:
329 break
329 break
330 if samestat(name_st, root_st):
330 if samestat(name_st, root_st):
331 if not rel:
331 if not rel:
332 # name was actually the same as root (maybe a symlink)
332 # name was actually the same as root (maybe a symlink)
333 return ''
333 return ''
334 rel.reverse()
334 rel.reverse()
335 name = os.path.join(*rel)
335 name = os.path.join(*rel)
336 auditor(name)
336 auditor(name)
337 return pconvert(name)
337 return pconvert(name)
338 dirname, basename = os.path.split(name)
338 dirname, basename = os.path.split(name)
339 rel.append(basename)
339 rel.append(basename)
340 if dirname == name:
340 if dirname == name:
341 break
341 break
342 name = dirname
342 name = dirname
343
343
344 raise Abort('%s not under root' % myname)
344 raise Abort('%s not under root' % myname)
345
345
346 _hgexecutable = None
346 _hgexecutable = None
347
347
348 def main_is_frozen():
348 def main_is_frozen():
349 """return True if we are a frozen executable.
349 """return True if we are a frozen executable.
350
350
351 The code supports py2exe (most common, Windows only) and tools/freeze
351 The code supports py2exe (most common, Windows only) and tools/freeze
352 (portable, not much used).
352 (portable, not much used).
353 """
353 """
354 return (hasattr(sys, "frozen") or # new py2exe
354 return (hasattr(sys, "frozen") or # new py2exe
355 hasattr(sys, "importers") or # old py2exe
355 hasattr(sys, "importers") or # old py2exe
356 imp.is_frozen("__main__")) # tools/freeze
356 imp.is_frozen("__main__")) # tools/freeze
357
357
358 def hgexecutable():
358 def hgexecutable():
359 """return location of the 'hg' executable.
359 """return location of the 'hg' executable.
360
360
361 Defaults to $HG or 'hg' in the search path.
361 Defaults to $HG or 'hg' in the search path.
362 """
362 """
363 if _hgexecutable is None:
363 if _hgexecutable is None:
364 hg = os.environ.get('HG')
364 hg = os.environ.get('HG')
365 if hg:
365 if hg:
366 set_hgexecutable(hg)
366 set_hgexecutable(hg)
367 elif main_is_frozen():
367 elif main_is_frozen():
368 set_hgexecutable(sys.executable)
368 set_hgexecutable(sys.executable)
369 else:
369 else:
370 exe = find_exe('hg') or os.path.basename(sys.argv[0])
370 exe = find_exe('hg') or os.path.basename(sys.argv[0])
371 set_hgexecutable(exe)
371 set_hgexecutable(exe)
372 return _hgexecutable
372 return _hgexecutable
373
373
374 def set_hgexecutable(path):
374 def set_hgexecutable(path):
375 """set location of the 'hg' executable"""
375 """set location of the 'hg' executable"""
376 global _hgexecutable
376 global _hgexecutable
377 _hgexecutable = path
377 _hgexecutable = path
378
378
379 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
379 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
380 '''enhanced shell command execution.
380 '''enhanced shell command execution.
381 run with environment maybe modified, maybe in different dir.
381 run with environment maybe modified, maybe in different dir.
382
382
383 if command fails and onerr is None, return status. if ui object,
383 if command fails and onerr is None, return status. if ui object,
384 print error message and return status, else raise onerr object as
384 print error message and return status, else raise onerr object as
385 exception.
385 exception.
386
386
387 if out is specified, it is assumed to be a file-like object that has a
387 if out is specified, it is assumed to be a file-like object that has a
388 write() method. stdout and stderr will be redirected to out.'''
388 write() method. stdout and stderr will be redirected to out.'''
389 try:
389 try:
390 sys.stdout.flush()
390 sys.stdout.flush()
391 except Exception:
391 except Exception:
392 pass
392 pass
393 def py2shell(val):
393 def py2shell(val):
394 'convert python object into string that is useful to shell'
394 'convert python object into string that is useful to shell'
395 if val is None or val is False:
395 if val is None or val is False:
396 return '0'
396 return '0'
397 if val is True:
397 if val is True:
398 return '1'
398 return '1'
399 return str(val)
399 return str(val)
400 origcmd = cmd
400 origcmd = cmd
401 cmd = quotecommand(cmd)
401 cmd = quotecommand(cmd)
402 env = dict(os.environ)
402 env = dict(os.environ)
403 env.update((k, py2shell(v)) for k, v in environ.iteritems())
403 env.update((k, py2shell(v)) for k, v in environ.iteritems())
404 env['HG'] = hgexecutable()
404 env['HG'] = hgexecutable()
405 if out is None:
405 if out is None:
406 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
406 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
407 env=env, cwd=cwd)
407 env=env, cwd=cwd)
408 else:
408 else:
409 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
409 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
410 env=env, cwd=cwd, stdout=subprocess.PIPE,
410 env=env, cwd=cwd, stdout=subprocess.PIPE,
411 stderr=subprocess.STDOUT)
411 stderr=subprocess.STDOUT)
412 for line in proc.stdout:
412 for line in proc.stdout:
413 out.write(line)
413 out.write(line)
414 proc.wait()
414 proc.wait()
415 rc = proc.returncode
415 rc = proc.returncode
416 if sys.platform == 'OpenVMS' and rc & 1:
416 if sys.platform == 'OpenVMS' and rc & 1:
417 rc = 0
417 rc = 0
418 if rc and onerr:
418 if rc and onerr:
419 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
419 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
420 explain_exit(rc)[0])
420 explain_exit(rc)[0])
421 if errprefix:
421 if errprefix:
422 errmsg = '%s: %s' % (errprefix, errmsg)
422 errmsg = '%s: %s' % (errprefix, errmsg)
423 try:
423 try:
424 onerr.warn(errmsg + '\n')
424 onerr.warn(errmsg + '\n')
425 except AttributeError:
425 except AttributeError:
426 raise onerr(errmsg)
426 raise onerr(errmsg)
427 return rc
427 return rc
428
428
429 def checksignature(func):
429 def checksignature(func):
430 '''wrap a function with code to check for calling errors'''
430 '''wrap a function with code to check for calling errors'''
431 def check(*args, **kwargs):
431 def check(*args, **kwargs):
432 try:
432 try:
433 return func(*args, **kwargs)
433 return func(*args, **kwargs)
434 except TypeError:
434 except TypeError:
435 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
435 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
436 raise error.SignatureError
436 raise error.SignatureError
437 raise
437 raise
438
438
439 return check
439 return check
440
440
441 def makedir(path, notindexed):
441 def makedir(path, notindexed):
442 os.mkdir(path)
442 os.mkdir(path)
443
443
444 def unlinkpath(f):
444 def unlinkpath(f):
445 """unlink and remove the directory if it is empty"""
445 """unlink and remove the directory if it is empty"""
446 os.unlink(f)
446 os.unlink(f)
447 # try removing directories that might now be empty
447 # try removing directories that might now be empty
448 try:
448 try:
449 os.removedirs(os.path.dirname(f))
449 os.removedirs(os.path.dirname(f))
450 except OSError:
450 except OSError:
451 pass
451 pass
452
452
453 def copyfile(src, dest):
453 def copyfile(src, dest):
454 "copy a file, preserving mode and atime/mtime"
454 "copy a file, preserving mode and atime/mtime"
455 if os.path.islink(src):
455 if os.path.islink(src):
456 try:
456 try:
457 os.unlink(dest)
457 os.unlink(dest)
458 except:
458 except:
459 pass
459 pass
460 os.symlink(os.readlink(src), dest)
460 os.symlink(os.readlink(src), dest)
461 else:
461 else:
462 try:
462 try:
463 shutil.copyfile(src, dest)
463 shutil.copyfile(src, dest)
464 shutil.copymode(src, dest)
464 shutil.copymode(src, dest)
465 except shutil.Error, inst:
465 except shutil.Error, inst:
466 raise Abort(str(inst))
466 raise Abort(str(inst))
467
467
468 def copyfiles(src, dst, hardlink=None):
468 def copyfiles(src, dst, hardlink=None):
469 """Copy a directory tree using hardlinks if possible"""
469 """Copy a directory tree using hardlinks if possible"""
470
470
471 if hardlink is None:
471 if hardlink is None:
472 hardlink = (os.stat(src).st_dev ==
472 hardlink = (os.stat(src).st_dev ==
473 os.stat(os.path.dirname(dst)).st_dev)
473 os.stat(os.path.dirname(dst)).st_dev)
474
474
475 num = 0
475 num = 0
476 if os.path.isdir(src):
476 if os.path.isdir(src):
477 os.mkdir(dst)
477 os.mkdir(dst)
478 for name, kind in osutil.listdir(src):
478 for name, kind in osutil.listdir(src):
479 srcname = os.path.join(src, name)
479 srcname = os.path.join(src, name)
480 dstname = os.path.join(dst, name)
480 dstname = os.path.join(dst, name)
481 hardlink, n = copyfiles(srcname, dstname, hardlink)
481 hardlink, n = copyfiles(srcname, dstname, hardlink)
482 num += n
482 num += n
483 else:
483 else:
484 if hardlink:
484 if hardlink:
485 try:
485 try:
486 os_link(src, dst)
486 os_link(src, dst)
487 except (IOError, OSError):
487 except (IOError, OSError):
488 hardlink = False
488 hardlink = False
489 shutil.copy(src, dst)
489 shutil.copy(src, dst)
490 else:
490 else:
491 shutil.copy(src, dst)
491 shutil.copy(src, dst)
492 num += 1
492 num += 1
493
493
494 return hardlink, num
494 return hardlink, num
495
495
496 class path_auditor(object):
496 class path_auditor(object):
497 '''ensure that a filesystem path contains no banned components.
497 '''ensure that a filesystem path contains no banned components.
498 the following properties of a path are checked:
498 the following properties of a path are checked:
499
499
500 - ends with a directory separator
500 - ends with a directory separator
501 - under top-level .hg
501 - under top-level .hg
502 - starts at the root of a windows drive
502 - starts at the root of a windows drive
503 - contains ".."
503 - contains ".."
504 - traverses a symlink (e.g. a/symlink_here/b)
504 - traverses a symlink (e.g. a/symlink_here/b)
505 - inside a nested repository (a callback can be used to approve
505 - inside a nested repository (a callback can be used to approve
506 some nested repositories, e.g., subrepositories)
506 some nested repositories, e.g., subrepositories)
507 '''
507 '''
508
508
509 def __init__(self, root, callback=None):
509 def __init__(self, root, callback=None):
510 self.audited = set()
510 self.audited = set()
511 self.auditeddir = set()
511 self.auditeddir = set()
512 self.root = root
512 self.root = root
513 self.callback = callback
513 self.callback = callback
514
514
515 def __call__(self, path):
515 def __call__(self, path):
516 if path in self.audited:
516 if path in self.audited:
517 return
517 return
518 # AIX ignores "/" at end of path, others raise EISDIR.
518 # AIX ignores "/" at end of path, others raise EISDIR.
519 if endswithsep(path):
519 if endswithsep(path):
520 raise Abort(_("path ends in directory separator: %s") % path)
520 raise Abort(_("path ends in directory separator: %s") % path)
521 normpath = os.path.normcase(path)
521 normpath = os.path.normcase(path)
522 parts = splitpath(normpath)
522 parts = splitpath(normpath)
523 if (os.path.splitdrive(path)[0]
523 if (os.path.splitdrive(path)[0]
524 or parts[0].lower() in ('.hg', '.hg.', '')
524 or parts[0].lower() in ('.hg', '.hg.', '')
525 or os.pardir in parts):
525 or os.pardir in parts):
526 raise Abort(_("path contains illegal component: %s") % path)
526 raise Abort(_("path contains illegal component: %s") % path)
527 if '.hg' in path.lower():
527 if '.hg' in path.lower():
528 lparts = [p.lower() for p in parts]
528 lparts = [p.lower() for p in parts]
529 for p in '.hg', '.hg.':
529 for p in '.hg', '.hg.':
530 if p in lparts[1:]:
530 if p in lparts[1:]:
531 pos = lparts.index(p)
531 pos = lparts.index(p)
532 base = os.path.join(*parts[:pos])
532 base = os.path.join(*parts[:pos])
533 raise Abort(_('path %r is inside repo %r') % (path, base))
533 raise Abort(_('path %r is inside repo %r') % (path, base))
534 def check(prefix):
534 def check(prefix):
535 curpath = os.path.join(self.root, prefix)
535 curpath = os.path.join(self.root, prefix)
536 try:
536 try:
537 st = os.lstat(curpath)
537 st = os.lstat(curpath)
538 except OSError, err:
538 except OSError, err:
539 # EINVAL can be raised as invalid path syntax under win32.
539 # EINVAL can be raised as invalid path syntax under win32.
540 # They must be ignored for patterns can be checked too.
540 # They must be ignored for patterns can be checked too.
541 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
541 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
542 raise
542 raise
543 else:
543 else:
544 if stat.S_ISLNK(st.st_mode):
544 if stat.S_ISLNK(st.st_mode):
545 raise Abort(_('path %r traverses symbolic link %r') %
545 raise Abort(_('path %r traverses symbolic link %r') %
546 (path, prefix))
546 (path, prefix))
547 elif (stat.S_ISDIR(st.st_mode) and
547 elif (stat.S_ISDIR(st.st_mode) and
548 os.path.isdir(os.path.join(curpath, '.hg'))):
548 os.path.isdir(os.path.join(curpath, '.hg'))):
549 if not self.callback or not self.callback(curpath):
549 if not self.callback or not self.callback(curpath):
550 raise Abort(_('path %r is inside repo %r') %
550 raise Abort(_('path %r is inside repo %r') %
551 (path, prefix))
551 (path, prefix))
552 parts.pop()
552 parts.pop()
553 prefixes = []
553 prefixes = []
554 while parts:
554 while parts:
555 prefix = os.sep.join(parts)
555 prefix = os.sep.join(parts)
556 if prefix in self.auditeddir:
556 if prefix in self.auditeddir:
557 break
557 break
558 check(prefix)
558 check(prefix)
559 prefixes.append(prefix)
559 prefixes.append(prefix)
560 parts.pop()
560 parts.pop()
561
561
562 self.audited.add(path)
562 self.audited.add(path)
563 # only add prefixes to the cache after checking everything: we don't
563 # only add prefixes to the cache after checking everything: we don't
564 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
564 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
565 self.auditeddir.update(prefixes)
565 self.auditeddir.update(prefixes)
566
566
567 def lookup_reg(key, name=None, scope=None):
567 def lookup_reg(key, name=None, scope=None):
568 return None
568 return None
569
569
570 def hidewindow():
570 def hidewindow():
571 """Hide current shell window.
571 """Hide current shell window.
572
572
573 Used to hide the window opened when starting asynchronous
573 Used to hide the window opened when starting asynchronous
574 child process under Windows, unneeded on other systems.
574 child process under Windows, unneeded on other systems.
575 """
575 """
576 pass
576 pass
577
577
578 if os.name == 'nt':
578 if os.name == 'nt':
579 from windows import *
579 from windows import *
580 else:
580 else:
581 from posix import *
581 from posix import *
582
582
583 def makelock(info, pathname):
583 def makelock(info, pathname):
584 try:
584 try:
585 return os.symlink(info, pathname)
585 return os.symlink(info, pathname)
586 except OSError, why:
586 except OSError, why:
587 if why.errno == errno.EEXIST:
587 if why.errno == errno.EEXIST:
588 raise
588 raise
589 except AttributeError: # no symlink in os
589 except AttributeError: # no symlink in os
590 pass
590 pass
591
591
592 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
592 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
593 os.write(ld, info)
593 os.write(ld, info)
594 os.close(ld)
594 os.close(ld)
595
595
596 def readlock(pathname):
596 def readlock(pathname):
597 try:
597 try:
598 return os.readlink(pathname)
598 return os.readlink(pathname)
599 except OSError, why:
599 except OSError, why:
600 if why.errno not in (errno.EINVAL, errno.ENOSYS):
600 if why.errno not in (errno.EINVAL, errno.ENOSYS):
601 raise
601 raise
602 except AttributeError: # no symlink in os
602 except AttributeError: # no symlink in os
603 pass
603 pass
604 fp = posixfile(pathname)
604 fp = posixfile(pathname)
605 r = fp.read()
605 r = fp.read()
606 fp.close()
606 fp.close()
607 return r
607 return r
608
608
609 def fstat(fp):
609 def fstat(fp):
610 '''stat file object that may not have fileno method.'''
610 '''stat file object that may not have fileno method.'''
611 try:
611 try:
612 return os.fstat(fp.fileno())
612 return os.fstat(fp.fileno())
613 except AttributeError:
613 except AttributeError:
614 return os.stat(fp.name)
614 return os.stat(fp.name)
615
615
616 # File system features
616 # File system features
617
617
618 def checkcase(path):
618 def checkcase(path):
619 """
619 """
620 Check whether the given path is on a case-sensitive filesystem
620 Check whether the given path is on a case-sensitive filesystem
621
621
622 Requires a path (like /foo/.hg) ending with a foldable final
622 Requires a path (like /foo/.hg) ending with a foldable final
623 directory component.
623 directory component.
624 """
624 """
625 s1 = os.stat(path)
625 s1 = os.stat(path)
626 d, b = os.path.split(path)
626 d, b = os.path.split(path)
627 p2 = os.path.join(d, b.upper())
627 p2 = os.path.join(d, b.upper())
628 if path == p2:
628 if path == p2:
629 p2 = os.path.join(d, b.lower())
629 p2 = os.path.join(d, b.lower())
630 try:
630 try:
631 s2 = os.stat(p2)
631 s2 = os.stat(p2)
632 if s2 == s1:
632 if s2 == s1:
633 return False
633 return False
634 return True
634 return True
635 except:
635 except:
636 return True
636 return True
637
637
638 _fspathcache = {}
638 _fspathcache = {}
639 def fspath(name, root):
639 def fspath(name, root):
640 '''Get name in the case stored in the filesystem
640 '''Get name in the case stored in the filesystem
641
641
642 The name is either relative to root, or it is an absolute path starting
642 The name is either relative to root, or it is an absolute path starting
643 with root. Note that this function is unnecessary, and should not be
643 with root. Note that this function is unnecessary, and should not be
644 called, for case-sensitive filesystems (simply because it's expensive).
644 called, for case-sensitive filesystems (simply because it's expensive).
645 '''
645 '''
646 # If name is absolute, make it relative
646 # If name is absolute, make it relative
647 if name.lower().startswith(root.lower()):
647 if name.lower().startswith(root.lower()):
648 l = len(root)
648 l = len(root)
649 if name[l] == os.sep or name[l] == os.altsep:
649 if name[l] == os.sep or name[l] == os.altsep:
650 l = l + 1
650 l = l + 1
651 name = name[l:]
651 name = name[l:]
652
652
653 if not os.path.lexists(os.path.join(root, name)):
653 if not os.path.lexists(os.path.join(root, name)):
654 return None
654 return None
655
655
656 seps = os.sep
656 seps = os.sep
657 if os.altsep:
657 if os.altsep:
658 seps = seps + os.altsep
658 seps = seps + os.altsep
659 # Protect backslashes. This gets silly very quickly.
659 # Protect backslashes. This gets silly very quickly.
660 seps.replace('\\','\\\\')
660 seps.replace('\\','\\\\')
661 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
661 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
662 dir = os.path.normcase(os.path.normpath(root))
662 dir = os.path.normcase(os.path.normpath(root))
663 result = []
663 result = []
664 for part, sep in pattern.findall(name):
664 for part, sep in pattern.findall(name):
665 if sep:
665 if sep:
666 result.append(sep)
666 result.append(sep)
667 continue
667 continue
668
668
669 if dir not in _fspathcache:
669 if dir not in _fspathcache:
670 _fspathcache[dir] = os.listdir(dir)
670 _fspathcache[dir] = os.listdir(dir)
671 contents = _fspathcache[dir]
671 contents = _fspathcache[dir]
672
672
673 lpart = part.lower()
673 lpart = part.lower()
674 lenp = len(part)
674 lenp = len(part)
675 for n in contents:
675 for n in contents:
676 if lenp == len(n) and n.lower() == lpart:
676 if lenp == len(n) and n.lower() == lpart:
677 result.append(n)
677 result.append(n)
678 break
678 break
679 else:
679 else:
680 # Cannot happen, as the file exists!
680 # Cannot happen, as the file exists!
681 result.append(part)
681 result.append(part)
682 dir = os.path.join(dir, lpart)
682 dir = os.path.join(dir, lpart)
683
683
684 return ''.join(result)
684 return ''.join(result)
685
685
686 def checklink(path):
686 def checklink(path):
687 """check whether the given path is on a symlink-capable filesystem"""
687 """check whether the given path is on a symlink-capable filesystem"""
688 # mktemp is not racy because symlink creation will fail if the
688 # mktemp is not racy because symlink creation will fail if the
689 # file already exists
689 # file already exists
690 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
690 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
691 try:
691 try:
692 os.symlink(".", name)
692 os.symlink(".", name)
693 os.unlink(name)
693 os.unlink(name)
694 return True
694 return True
695 except (OSError, AttributeError):
695 except (OSError, AttributeError):
696 return False
696 return False
697
697
698 def checknlink(testfile):
698 def checknlink(testfile):
699 '''check whether hardlink count reporting works properly'''
699 '''check whether hardlink count reporting works properly'''
700
700
701 # testfile may be open, so we need a separate file for checking to
701 # testfile may be open, so we need a separate file for checking to
702 # work around issue2543 (or testfile may get lost on Samba shares)
702 # work around issue2543 (or testfile may get lost on Samba shares)
703 f1 = testfile + ".hgtmp1"
703 f1 = testfile + ".hgtmp1"
704 if os.path.lexists(f1):
704 if os.path.lexists(f1):
705 return False
705 return False
706 try:
706 try:
707 posixfile(f1, 'w').close()
707 posixfile(f1, 'w').close()
708 except IOError:
708 except IOError:
709 return False
709 return False
710
710
711 f2 = testfile + ".hgtmp2"
711 f2 = testfile + ".hgtmp2"
712 fd = None
712 fd = None
713 try:
713 try:
714 try:
714 try:
715 os_link(f1, f2)
715 os_link(f1, f2)
716 except OSError:
716 except OSError:
717 return False
717 return False
718
718
719 # nlinks() may behave differently for files on Windows shares if
719 # nlinks() may behave differently for files on Windows shares if
720 # the file is open.
720 # the file is open.
721 fd = posixfile(f2)
721 fd = posixfile(f2)
722 return nlinks(f2) > 1
722 return nlinks(f2) > 1
723 finally:
723 finally:
724 if fd is not None:
724 if fd is not None:
725 fd.close()
725 fd.close()
726 for f in (f1, f2):
726 for f in (f1, f2):
727 try:
727 try:
728 os.unlink(f)
728 os.unlink(f)
729 except OSError:
729 except OSError:
730 pass
730 pass
731
731
732 return False
732 return False
733
733
734 def endswithsep(path):
734 def endswithsep(path):
735 '''Check path ends with os.sep or os.altsep.'''
735 '''Check path ends with os.sep or os.altsep.'''
736 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
736 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
737
737
738 def splitpath(path):
738 def splitpath(path):
739 '''Split path by os.sep.
739 '''Split path by os.sep.
740 Note that this function does not use os.altsep because this is
740 Note that this function does not use os.altsep because this is
741 an alternative of simple "xxx.split(os.sep)".
741 an alternative of simple "xxx.split(os.sep)".
742 It is recommended to use os.path.normpath() before using this
742 It is recommended to use os.path.normpath() before using this
743 function if need.'''
743 function if need.'''
744 return path.split(os.sep)
744 return path.split(os.sep)
745
745
746 def gui():
746 def gui():
747 '''Are we running in a GUI?'''
747 '''Are we running in a GUI?'''
748 if sys.platform == 'darwin':
748 if sys.platform == 'darwin':
749 if 'SSH_CONNECTION' in os.environ:
749 if 'SSH_CONNECTION' in os.environ:
750 # handle SSH access to a box where the user is logged in
750 # handle SSH access to a box where the user is logged in
751 return False
751 return False
752 elif getattr(osutil, 'isgui', None):
752 elif getattr(osutil, 'isgui', None):
753 # check if a CoreGraphics session is available
753 # check if a CoreGraphics session is available
754 return osutil.isgui()
754 return osutil.isgui()
755 else:
755 else:
756 # pure build; use a safe default
756 # pure build; use a safe default
757 return True
757 return True
758 else:
758 else:
759 return os.name == "nt" or os.environ.get("DISPLAY")
759 return os.name == "nt" or os.environ.get("DISPLAY")
760
760
761 def mktempcopy(name, emptyok=False, createmode=None):
761 def mktempcopy(name, emptyok=False, createmode=None):
762 """Create a temporary file with the same contents from name
762 """Create a temporary file with the same contents from name
763
763
764 The permission bits are copied from the original file.
764 The permission bits are copied from the original file.
765
765
766 If the temporary file is going to be truncated immediately, you
766 If the temporary file is going to be truncated immediately, you
767 can use emptyok=True as an optimization.
767 can use emptyok=True as an optimization.
768
768
769 Returns the name of the temporary file.
769 Returns the name of the temporary file.
770 """
770 """
771 d, fn = os.path.split(name)
771 d, fn = os.path.split(name)
772 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
772 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
773 os.close(fd)
773 os.close(fd)
774 # Temporary files are created with mode 0600, which is usually not
774 # Temporary files are created with mode 0600, which is usually not
775 # what we want. If the original file already exists, just copy
775 # what we want. If the original file already exists, just copy
776 # its mode. Otherwise, manually obey umask.
776 # its mode. Otherwise, manually obey umask.
777 try:
777 try:
778 st_mode = os.lstat(name).st_mode & 0777
778 st_mode = os.lstat(name).st_mode & 0777
779 except OSError, inst:
779 except OSError, inst:
780 if inst.errno != errno.ENOENT:
780 if inst.errno != errno.ENOENT:
781 raise
781 raise
782 st_mode = createmode
782 st_mode = createmode
783 if st_mode is None:
783 if st_mode is None:
784 st_mode = ~umask
784 st_mode = ~umask
785 st_mode &= 0666
785 st_mode &= 0666
786 os.chmod(temp, st_mode)
786 os.chmod(temp, st_mode)
787 if emptyok:
787 if emptyok:
788 return temp
788 return temp
789 try:
789 try:
790 try:
790 try:
791 ifp = posixfile(name, "rb")
791 ifp = posixfile(name, "rb")
792 except IOError, inst:
792 except IOError, inst:
793 if inst.errno == errno.ENOENT:
793 if inst.errno == errno.ENOENT:
794 return temp
794 return temp
795 if not getattr(inst, 'filename', None):
795 if not getattr(inst, 'filename', None):
796 inst.filename = name
796 inst.filename = name
797 raise
797 raise
798 ofp = posixfile(temp, "wb")
798 ofp = posixfile(temp, "wb")
799 for chunk in filechunkiter(ifp):
799 for chunk in filechunkiter(ifp):
800 ofp.write(chunk)
800 ofp.write(chunk)
801 ifp.close()
801 ifp.close()
802 ofp.close()
802 ofp.close()
803 except:
803 except:
804 try: os.unlink(temp)
804 try: os.unlink(temp)
805 except: pass
805 except: pass
806 raise
806 raise
807 return temp
807 return temp
808
808
809 class atomictempfile(object):
809 class atomictempfile(object):
810 """file-like object that atomically updates a file
810 """file-like object that atomically updates a file
811
811
812 All writes will be redirected to a temporary copy of the original
812 All writes will be redirected to a temporary copy of the original
813 file. When rename is called, the copy is renamed to the original
813 file. When rename is called, the copy is renamed to the original
814 name, making the changes visible.
814 name, making the changes visible.
815 """
815 """
816 def __init__(self, name, mode='w+b', createmode=None):
816 def __init__(self, name, mode='w+b', createmode=None):
817 self.__name = name
817 self.__name = name
818 self._fp = None
818 self._fp = None
819 self.temp = mktempcopy(name, emptyok=('w' in mode),
819 self.temp = mktempcopy(name, emptyok=('w' in mode),
820 createmode=createmode)
820 createmode=createmode)
821 self._fp = posixfile(self.temp, mode)
821 self._fp = posixfile(self.temp, mode)
822
822
823 def __getattr__(self, name):
823 def __getattr__(self, name):
824 return getattr(self._fp, name)
824 return getattr(self._fp, name)
825
825
826 def rename(self):
826 def rename(self):
827 if not self._fp.closed:
827 if not self._fp.closed:
828 self._fp.close()
828 self._fp.close()
829 rename(self.temp, localpath(self.__name))
829 rename(self.temp, localpath(self.__name))
830
830
831 def close(self):
831 def close(self):
832 if not self._fp:
832 if not self._fp:
833 return
833 return
834 if not self._fp.closed:
834 if not self._fp.closed:
835 try:
835 try:
836 os.unlink(self.temp)
836 os.unlink(self.temp)
837 except: pass
837 except: pass
838 self._fp.close()
838 self._fp.close()
839
839
840 def __del__(self):
840 def __del__(self):
841 self.close()
841 self.close()
842
842
843 def makedirs(name, mode=None):
843 def makedirs(name, mode=None):
844 """recursive directory creation with parent mode inheritance"""
844 """recursive directory creation with parent mode inheritance"""
845 parent = os.path.abspath(os.path.dirname(name))
845 parent = os.path.abspath(os.path.dirname(name))
846 try:
846 try:
847 os.mkdir(name)
847 os.mkdir(name)
848 if mode is not None:
848 if mode is not None:
849 os.chmod(name, mode)
849 os.chmod(name, mode)
850 return
850 return
851 except OSError, err:
851 except OSError, err:
852 if err.errno == errno.EEXIST:
852 if err.errno == errno.EEXIST:
853 return
853 return
854 if not name or parent == name or err.errno != errno.ENOENT:
854 if not name or parent == name or err.errno != errno.ENOENT:
855 raise
855 raise
856 makedirs(parent, mode)
856 makedirs(parent, mode)
857 makedirs(name, mode)
857 makedirs(name, mode)
858
858
859 class opener(object):
859 class opener(object):
860 """Open files relative to a base directory
860 """Open files relative to a base directory
861
861
862 This class is used to hide the details of COW semantics and
862 This class is used to hide the details of COW semantics and
863 remote file access from higher level code.
863 remote file access from higher level code.
864 """
864 """
865 def __init__(self, base, audit=True):
865 def __init__(self, base, audit=True):
866 self.base = base
866 self.base = base
867 if audit:
867 if audit:
868 self.auditor = path_auditor(base)
868 self.auditor = path_auditor(base)
869 else:
869 else:
870 self.auditor = always
870 self.auditor = always
871 self.createmode = None
871 self.createmode = None
872 self._trustnlink = None
872 self._trustnlink = None
873
873
874 @propertycache
874 @propertycache
875 def _can_symlink(self):
875 def _can_symlink(self):
876 return checklink(self.base)
876 return checklink(self.base)
877
877
878 def _fixfilemode(self, name):
878 def _fixfilemode(self, name):
879 if self.createmode is None:
879 if self.createmode is None:
880 return
880 return
881 os.chmod(name, self.createmode & 0666)
881 os.chmod(name, self.createmode & 0666)
882
882
883 def __call__(self, path, mode="r", text=False, atomictemp=False):
883 def __call__(self, path, mode="r", text=False, atomictemp=False):
884 self.auditor(path)
884 self.auditor(path)
885 f = os.path.join(self.base, path)
885 f = os.path.join(self.base, path)
886
886
887 if not text and "b" not in mode:
887 if not text and "b" not in mode:
888 mode += "b" # for that other OS
888 mode += "b" # for that other OS
889
889
890 nlink = -1
890 nlink = -1
891 dirname, basename = os.path.split(f)
891 dirname, basename = os.path.split(f)
892 # If basename is empty, then the path is malformed because it points
892 # If basename is empty, then the path is malformed because it points
893 # to a directory. Let the posixfile() call below raise IOError.
893 # to a directory. Let the posixfile() call below raise IOError.
894 if basename and mode not in ('r', 'rb'):
894 if basename and mode not in ('r', 'rb'):
895 if atomictemp:
895 if atomictemp:
896 if not os.path.isdir(dirname):
896 if not os.path.isdir(dirname):
897 makedirs(dirname, self.createmode)
897 makedirs(dirname, self.createmode)
898 return atomictempfile(f, mode, self.createmode)
898 return atomictempfile(f, mode, self.createmode)
899 try:
899 try:
900 if 'w' in mode:
900 if 'w' in mode:
901 unlink(f)
901 unlink(f)
902 nlink = 0
902 nlink = 0
903 else:
903 else:
904 # nlinks() may behave differently for files on Windows
904 # nlinks() may behave differently for files on Windows
905 # shares if the file is open.
905 # shares if the file is open.
906 fd = posixfile(f)
906 fd = posixfile(f)
907 nlink = nlinks(f)
907 nlink = nlinks(f)
908 if nlink < 1:
908 if nlink < 1:
909 nlink = 2 # force mktempcopy (issue1922)
909 nlink = 2 # force mktempcopy (issue1922)
910 fd.close()
910 fd.close()
911 except (OSError, IOError), e:
911 except (OSError, IOError), e:
912 if e.errno != errno.ENOENT:
912 if e.errno != errno.ENOENT:
913 raise
913 raise
914 nlink = 0
914 nlink = 0
915 if not os.path.isdir(dirname):
915 if not os.path.isdir(dirname):
916 makedirs(dirname, self.createmode)
916 makedirs(dirname, self.createmode)
917 if nlink > 0:
917 if nlink > 0:
918 if self._trustnlink is None:
918 if self._trustnlink is None:
919 self._trustnlink = nlink > 1 or checknlink(f)
919 self._trustnlink = nlink > 1 or checknlink(f)
920 if nlink > 1 or not self._trustnlink:
920 if nlink > 1 or not self._trustnlink:
921 rename(mktempcopy(f), f)
921 rename(mktempcopy(f), f)
922 fp = posixfile(f, mode)
922 fp = posixfile(f, mode)
923 if nlink == 0:
923 if nlink == 0:
924 self._fixfilemode(f)
924 self._fixfilemode(f)
925 return fp
925 return fp
926
926
927 def symlink(self, src, dst):
927 def symlink(self, src, dst):
928 self.auditor(dst)
928 self.auditor(dst)
929 linkname = os.path.join(self.base, dst)
929 linkname = os.path.join(self.base, dst)
930 try:
930 try:
931 os.unlink(linkname)
931 os.unlink(linkname)
932 except OSError:
932 except OSError:
933 pass
933 pass
934
934
935 dirname = os.path.dirname(linkname)
935 dirname = os.path.dirname(linkname)
936 if not os.path.exists(dirname):
936 if not os.path.exists(dirname):
937 makedirs(dirname, self.createmode)
937 makedirs(dirname, self.createmode)
938
938
939 if self._can_symlink:
939 if self._can_symlink:
940 try:
940 try:
941 os.symlink(src, linkname)
941 os.symlink(src, linkname)
942 except OSError, err:
942 except OSError, err:
943 raise OSError(err.errno, _('could not symlink to %r: %s') %
943 raise OSError(err.errno, _('could not symlink to %r: %s') %
944 (src, err.strerror), linkname)
944 (src, err.strerror), linkname)
945 else:
945 else:
946 f = self(dst, "w")
946 f = self(dst, "w")
947 f.write(src)
947 f.write(src)
948 f.close()
948 f.close()
949 self._fixfilemode(dst)
949 self._fixfilemode(dst)
950
950
951 class chunkbuffer(object):
951 class chunkbuffer(object):
952 """Allow arbitrary sized chunks of data to be efficiently read from an
952 """Allow arbitrary sized chunks of data to be efficiently read from an
953 iterator over chunks of arbitrary size."""
953 iterator over chunks of arbitrary size."""
954
954
955 def __init__(self, in_iter):
955 def __init__(self, in_iter):
956 """in_iter is the iterator that's iterating over the input chunks.
956 """in_iter is the iterator that's iterating over the input chunks.
957 targetsize is how big a buffer to try to maintain."""
957 targetsize is how big a buffer to try to maintain."""
958 def splitbig(chunks):
958 def splitbig(chunks):
959 for chunk in chunks:
959 for chunk in chunks:
960 if len(chunk) > 2**20:
960 if len(chunk) > 2**20:
961 pos = 0
961 pos = 0
962 while pos < len(chunk):
962 while pos < len(chunk):
963 end = pos + 2 ** 18
963 end = pos + 2 ** 18
964 yield chunk[pos:end]
964 yield chunk[pos:end]
965 pos = end
965 pos = end
966 else:
966 else:
967 yield chunk
967 yield chunk
968 self.iter = splitbig(in_iter)
968 self.iter = splitbig(in_iter)
969 self._queue = []
969 self._queue = []
970
970
971 def read(self, l):
971 def read(self, l):
972 """Read L bytes of data from the iterator of chunks of data.
972 """Read L bytes of data from the iterator of chunks of data.
973 Returns less than L bytes if the iterator runs dry."""
973 Returns less than L bytes if the iterator runs dry."""
974 left = l
974 left = l
975 buf = ''
975 buf = ''
976 queue = self._queue
976 queue = self._queue
977 while left > 0:
977 while left > 0:
978 # refill the queue
978 # refill the queue
979 if not queue:
979 if not queue:
980 target = 2**18
980 target = 2**18
981 for chunk in self.iter:
981 for chunk in self.iter:
982 queue.append(chunk)
982 queue.append(chunk)
983 target -= len(chunk)
983 target -= len(chunk)
984 if target <= 0:
984 if target <= 0:
985 break
985 break
986 if not queue:
986 if not queue:
987 break
987 break
988
988
989 chunk = queue.pop(0)
989 chunk = queue.pop(0)
990 left -= len(chunk)
990 left -= len(chunk)
991 if left < 0:
991 if left < 0:
992 queue.insert(0, chunk[left:])
992 queue.insert(0, chunk[left:])
993 buf += chunk[:left]
993 buf += chunk[:left]
994 else:
994 else:
995 buf += chunk
995 buf += chunk
996
996
997 return buf
997 return buf
998
998
999 def filechunkiter(f, size=65536, limit=None):
999 def filechunkiter(f, size=65536, limit=None):
1000 """Create a generator that produces the data in the file size
1000 """Create a generator that produces the data in the file size
1001 (default 65536) bytes at a time, up to optional limit (default is
1001 (default 65536) bytes at a time, up to optional limit (default is
1002 to read all data). Chunks may be less than size bytes if the
1002 to read all data). Chunks may be less than size bytes if the
1003 chunk is the last chunk in the file, or the file is a socket or
1003 chunk is the last chunk in the file, or the file is a socket or
1004 some other type of file that sometimes reads less data than is
1004 some other type of file that sometimes reads less data than is
1005 requested."""
1005 requested."""
1006 assert size >= 0
1006 assert size >= 0
1007 assert limit is None or limit >= 0
1007 assert limit is None or limit >= 0
1008 while True:
1008 while True:
1009 if limit is None:
1009 if limit is None:
1010 nbytes = size
1010 nbytes = size
1011 else:
1011 else:
1012 nbytes = min(limit, size)
1012 nbytes = min(limit, size)
1013 s = nbytes and f.read(nbytes)
1013 s = nbytes and f.read(nbytes)
1014 if not s:
1014 if not s:
1015 break
1015 break
1016 if limit:
1016 if limit:
1017 limit -= len(s)
1017 limit -= len(s)
1018 yield s
1018 yield s
1019
1019
1020 def makedate():
1020 def makedate():
1021 lt = time.localtime()
1021 lt = time.localtime()
1022 if lt[8] == 1 and time.daylight:
1022 if lt[8] == 1 and time.daylight:
1023 tz = time.altzone
1023 tz = time.altzone
1024 else:
1024 else:
1025 tz = time.timezone
1025 tz = time.timezone
1026 t = time.mktime(lt)
1026 t = time.mktime(lt)
1027 if t < 0:
1027 if t < 0:
1028 hint = _("check your clock")
1028 hint = _("check your clock")
1029 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1029 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1030 return t, tz
1030 return t, tz
1031
1031
1032 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1032 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1033 """represent a (unixtime, offset) tuple as a localized time.
1033 """represent a (unixtime, offset) tuple as a localized time.
1034 unixtime is seconds since the epoch, and offset is the time zone's
1034 unixtime is seconds since the epoch, and offset is the time zone's
1035 number of seconds away from UTC. if timezone is false, do not
1035 number of seconds away from UTC. if timezone is false, do not
1036 append time zone to string."""
1036 append time zone to string."""
1037 t, tz = date or makedate()
1037 t, tz = date or makedate()
1038 if t < 0:
1038 if t < 0:
1039 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1039 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1040 tz = 0
1040 tz = 0
1041 if "%1" in format or "%2" in format:
1041 if "%1" in format or "%2" in format:
1042 sign = (tz > 0) and "-" or "+"
1042 sign = (tz > 0) and "-" or "+"
1043 minutes = abs(tz) // 60
1043 minutes = abs(tz) // 60
1044 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1044 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1045 format = format.replace("%2", "%02d" % (minutes % 60))
1045 format = format.replace("%2", "%02d" % (minutes % 60))
1046 s = time.strftime(format, time.gmtime(float(t) - tz))
1046 s = time.strftime(format, time.gmtime(float(t) - tz))
1047 return s
1047 return s
1048
1048
1049 def shortdate(date=None):
1049 def shortdate(date=None):
1050 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1050 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1051 return datestr(date, format='%Y-%m-%d')
1051 return datestr(date, format='%Y-%m-%d')
1052
1052
1053 def strdate(string, format, defaults=[]):
1053 def strdate(string, format, defaults=[]):
1054 """parse a localized time string and return a (unixtime, offset) tuple.
1054 """parse a localized time string and return a (unixtime, offset) tuple.
1055 if the string cannot be parsed, ValueError is raised."""
1055 if the string cannot be parsed, ValueError is raised."""
1056 def timezone(string):
1056 def timezone(string):
1057 tz = string.split()[-1]
1057 tz = string.split()[-1]
1058 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1058 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1059 sign = (tz[0] == "+") and 1 or -1
1059 sign = (tz[0] == "+") and 1 or -1
1060 hours = int(tz[1:3])
1060 hours = int(tz[1:3])
1061 minutes = int(tz[3:5])
1061 minutes = int(tz[3:5])
1062 return -sign * (hours * 60 + minutes) * 60
1062 return -sign * (hours * 60 + minutes) * 60
1063 if tz == "GMT" or tz == "UTC":
1063 if tz == "GMT" or tz == "UTC":
1064 return 0
1064 return 0
1065 return None
1065 return None
1066
1066
1067 # NOTE: unixtime = localunixtime + offset
1067 # NOTE: unixtime = localunixtime + offset
1068 offset, date = timezone(string), string
1068 offset, date = timezone(string), string
1069 if offset is not None:
1069 if offset is not None:
1070 date = " ".join(string.split()[:-1])
1070 date = " ".join(string.split()[:-1])
1071
1071
1072 # add missing elements from defaults
1072 # add missing elements from defaults
1073 usenow = False # default to using biased defaults
1073 usenow = False # default to using biased defaults
1074 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1074 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1075 found = [True for p in part if ("%"+p) in format]
1075 found = [True for p in part if ("%"+p) in format]
1076 if not found:
1076 if not found:
1077 date += "@" + defaults[part][usenow]
1077 date += "@" + defaults[part][usenow]
1078 format += "@%" + part[0]
1078 format += "@%" + part[0]
1079 else:
1079 else:
1080 # We've found a specific time element, less specific time
1080 # We've found a specific time element, less specific time
1081 # elements are relative to today
1081 # elements are relative to today
1082 usenow = True
1082 usenow = True
1083
1083
1084 timetuple = time.strptime(date, format)
1084 timetuple = time.strptime(date, format)
1085 localunixtime = int(calendar.timegm(timetuple))
1085 localunixtime = int(calendar.timegm(timetuple))
1086 if offset is None:
1086 if offset is None:
1087 # local timezone
1087 # local timezone
1088 unixtime = int(time.mktime(timetuple))
1088 unixtime = int(time.mktime(timetuple))
1089 offset = unixtime - localunixtime
1089 offset = unixtime - localunixtime
1090 else:
1090 else:
1091 unixtime = localunixtime + offset
1091 unixtime = localunixtime + offset
1092 return unixtime, offset
1092 return unixtime, offset
1093
1093
1094 def parsedate(date, formats=None, bias={}):
1094 def parsedate(date, formats=None, bias={}):
1095 """parse a localized date/time and return a (unixtime, offset) tuple.
1095 """parse a localized date/time and return a (unixtime, offset) tuple.
1096
1096
1097 The date may be a "unixtime offset" string or in one of the specified
1097 The date may be a "unixtime offset" string or in one of the specified
1098 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1098 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1099 """
1099 """
1100 if not date:
1100 if not date:
1101 return 0, 0
1101 return 0, 0
1102 if isinstance(date, tuple) and len(date) == 2:
1102 if isinstance(date, tuple) and len(date) == 2:
1103 return date
1103 return date
1104 if not formats:
1104 if not formats:
1105 formats = defaultdateformats
1105 formats = defaultdateformats
1106 date = date.strip()
1106 date = date.strip()
1107 try:
1107 try:
1108 when, offset = map(int, date.split(' '))
1108 when, offset = map(int, date.split(' '))
1109 except ValueError:
1109 except ValueError:
1110 # fill out defaults
1110 # fill out defaults
1111 now = makedate()
1111 now = makedate()
1112 defaults = {}
1112 defaults = {}
1113 nowmap = {}
1113 nowmap = {}
1114 for part in ("d", "mb", "yY", "HI", "M", "S"):
1114 for part in ("d", "mb", "yY", "HI", "M", "S"):
1115 # this piece is for rounding the specific end of unknowns
1115 # this piece is for rounding the specific end of unknowns
1116 b = bias.get(part)
1116 b = bias.get(part)
1117 if b is None:
1117 if b is None:
1118 if part[0] in "HMS":
1118 if part[0] in "HMS":
1119 b = "00"
1119 b = "00"
1120 else:
1120 else:
1121 b = "0"
1121 b = "0"
1122
1122
1123 # this piece is for matching the generic end to today's date
1123 # this piece is for matching the generic end to today's date
1124 n = datestr(now, "%" + part[0])
1124 n = datestr(now, "%" + part[0])
1125
1125
1126 defaults[part] = (b, n)
1126 defaults[part] = (b, n)
1127
1127
1128 for format in formats:
1128 for format in formats:
1129 try:
1129 try:
1130 when, offset = strdate(date, format, defaults)
1130 when, offset = strdate(date, format, defaults)
1131 except (ValueError, OverflowError):
1131 except (ValueError, OverflowError):
1132 pass
1132 pass
1133 else:
1133 else:
1134 break
1134 break
1135 else:
1135 else:
1136 raise Abort(_('invalid date: %r') % date)
1136 raise Abort(_('invalid date: %r') % date)
1137 # validate explicit (probably user-specified) date and
1137 # validate explicit (probably user-specified) date and
1138 # time zone offset. values must fit in signed 32 bits for
1138 # time zone offset. values must fit in signed 32 bits for
1139 # current 32-bit linux runtimes. timezones go from UTC-12
1139 # current 32-bit linux runtimes. timezones go from UTC-12
1140 # to UTC+14
1140 # to UTC+14
1141 if abs(when) > 0x7fffffff:
1141 if abs(when) > 0x7fffffff:
1142 raise Abort(_('date exceeds 32 bits: %d') % when)
1142 raise Abort(_('date exceeds 32 bits: %d') % when)
1143 if when < 0:
1143 if when < 0:
1144 raise Abort(_('negative date value: %d') % when)
1144 raise Abort(_('negative date value: %d') % when)
1145 if offset < -50400 or offset > 43200:
1145 if offset < -50400 or offset > 43200:
1146 raise Abort(_('impossible time zone offset: %d') % offset)
1146 raise Abort(_('impossible time zone offset: %d') % offset)
1147 return when, offset
1147 return when, offset
1148
1148
1149 def matchdate(date):
1149 def matchdate(date):
1150 """Return a function that matches a given date match specifier
1150 """Return a function that matches a given date match specifier
1151
1151
1152 Formats include:
1152 Formats include:
1153
1153
1154 '{date}' match a given date to the accuracy provided
1154 '{date}' match a given date to the accuracy provided
1155
1155
1156 '<{date}' on or before a given date
1156 '<{date}' on or before a given date
1157
1157
1158 '>{date}' on or after a given date
1158 '>{date}' on or after a given date
1159
1159
1160 >>> p1 = parsedate("10:29:59")
1160 >>> p1 = parsedate("10:29:59")
1161 >>> p2 = parsedate("10:30:00")
1161 >>> p2 = parsedate("10:30:00")
1162 >>> p3 = parsedate("10:30:59")
1162 >>> p3 = parsedate("10:30:59")
1163 >>> p4 = parsedate("10:31:00")
1163 >>> p4 = parsedate("10:31:00")
1164 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1164 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1165 >>> f = matchdate("10:30")
1165 >>> f = matchdate("10:30")
1166 >>> f(p1[0])
1166 >>> f(p1[0])
1167 False
1167 False
1168 >>> f(p2[0])
1168 >>> f(p2[0])
1169 True
1169 True
1170 >>> f(p3[0])
1170 >>> f(p3[0])
1171 True
1171 True
1172 >>> f(p4[0])
1172 >>> f(p4[0])
1173 False
1173 False
1174 >>> f(p5[0])
1174 >>> f(p5[0])
1175 False
1175 False
1176 """
1176 """
1177
1177
1178 def lower(date):
1178 def lower(date):
1179 d = dict(mb="1", d="1")
1179 d = dict(mb="1", d="1")
1180 return parsedate(date, extendeddateformats, d)[0]
1180 return parsedate(date, extendeddateformats, d)[0]
1181
1181
1182 def upper(date):
1182 def upper(date):
1183 d = dict(mb="12", HI="23", M="59", S="59")
1183 d = dict(mb="12", HI="23", M="59", S="59")
1184 for days in ("31", "30", "29"):
1184 for days in ("31", "30", "29"):
1185 try:
1185 try:
1186 d["d"] = days
1186 d["d"] = days
1187 return parsedate(date, extendeddateformats, d)[0]
1187 return parsedate(date, extendeddateformats, d)[0]
1188 except:
1188 except:
1189 pass
1189 pass
1190 d["d"] = "28"
1190 d["d"] = "28"
1191 return parsedate(date, extendeddateformats, d)[0]
1191 return parsedate(date, extendeddateformats, d)[0]
1192
1192
1193 date = date.strip()
1193 date = date.strip()
1194
1194
1195 if not date:
1195 if not date:
1196 raise Abort(_("dates cannot consist entirely of whitespace"))
1196 raise Abort(_("dates cannot consist entirely of whitespace"))
1197 elif date[0] == "<":
1197 elif date[0] == "<":
1198 if not date[1:]:
1198 if not date[1:]:
1199 raise Abort(_("invalid day spec. use '<{datetime}' "))
1199 raise Abort(_("invalid day spec, use '<DATE'"))
1200 when = upper(date[1:])
1200 when = upper(date[1:])
1201 return lambda x: x <= when
1201 return lambda x: x <= when
1202 elif date[0] == ">":
1202 elif date[0] == ">":
1203 if not date[1:]:
1203 if not date[1:]:
1204 raise Abort(_("invalid day spec. use '>{datetime}' "))
1204 raise Abort(_("invalid day spec, use '>DATE'"))
1205 when = lower(date[1:])
1205 when = lower(date[1:])
1206 return lambda x: x >= when
1206 return lambda x: x >= when
1207 elif date[0] == "-":
1207 elif date[0] == "-":
1208 try:
1208 try:
1209 days = int(date[1:])
1209 days = int(date[1:])
1210 except ValueError:
1210 except ValueError:
1211 raise Abort(_("invalid day spec: %s") % date[1:])
1211 raise Abort(_("invalid day spec: %s") % date[1:])
1212 when = makedate()[0] - days * 3600 * 24
1212 when = makedate()[0] - days * 3600 * 24
1213 return lambda x: x >= when
1213 return lambda x: x >= when
1214 elif " to " in date:
1214 elif " to " in date:
1215 a, b = date.split(" to ")
1215 a, b = date.split(" to ")
1216 start, stop = lower(a), upper(b)
1216 start, stop = lower(a), upper(b)
1217 return lambda x: x >= start and x <= stop
1217 return lambda x: x >= start and x <= stop
1218 else:
1218 else:
1219 start, stop = lower(date), upper(date)
1219 start, stop = lower(date), upper(date)
1220 return lambda x: x >= start and x <= stop
1220 return lambda x: x >= start and x <= stop
1221
1221
1222 def shortuser(user):
1222 def shortuser(user):
1223 """Return a short representation of a user name or email address."""
1223 """Return a short representation of a user name or email address."""
1224 f = user.find('@')
1224 f = user.find('@')
1225 if f >= 0:
1225 if f >= 0:
1226 user = user[:f]
1226 user = user[:f]
1227 f = user.find('<')
1227 f = user.find('<')
1228 if f >= 0:
1228 if f >= 0:
1229 user = user[f + 1:]
1229 user = user[f + 1:]
1230 f = user.find(' ')
1230 f = user.find(' ')
1231 if f >= 0:
1231 if f >= 0:
1232 user = user[:f]
1232 user = user[:f]
1233 f = user.find('.')
1233 f = user.find('.')
1234 if f >= 0:
1234 if f >= 0:
1235 user = user[:f]
1235 user = user[:f]
1236 return user
1236 return user
1237
1237
1238 def email(author):
1238 def email(author):
1239 '''get email of author.'''
1239 '''get email of author.'''
1240 r = author.find('>')
1240 r = author.find('>')
1241 if r == -1:
1241 if r == -1:
1242 r = None
1242 r = None
1243 return author[author.find('<') + 1:r]
1243 return author[author.find('<') + 1:r]
1244
1244
1245 def _ellipsis(text, maxlength):
1245 def _ellipsis(text, maxlength):
1246 if len(text) <= maxlength:
1246 if len(text) <= maxlength:
1247 return text, False
1247 return text, False
1248 else:
1248 else:
1249 return "%s..." % (text[:maxlength - 3]), True
1249 return "%s..." % (text[:maxlength - 3]), True
1250
1250
1251 def ellipsis(text, maxlength=400):
1251 def ellipsis(text, maxlength=400):
1252 """Trim string to at most maxlength (default: 400) characters."""
1252 """Trim string to at most maxlength (default: 400) characters."""
1253 try:
1253 try:
1254 # use unicode not to split at intermediate multi-byte sequence
1254 # use unicode not to split at intermediate multi-byte sequence
1255 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1255 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1256 maxlength)
1256 maxlength)
1257 if not truncated:
1257 if not truncated:
1258 return text
1258 return text
1259 return utext.encode(encoding.encoding)
1259 return utext.encode(encoding.encoding)
1260 except (UnicodeDecodeError, UnicodeEncodeError):
1260 except (UnicodeDecodeError, UnicodeEncodeError):
1261 return _ellipsis(text, maxlength)[0]
1261 return _ellipsis(text, maxlength)[0]
1262
1262
1263 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1263 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1264 '''yield every hg repository under path, recursively.'''
1264 '''yield every hg repository under path, recursively.'''
1265 def errhandler(err):
1265 def errhandler(err):
1266 if err.filename == path:
1266 if err.filename == path:
1267 raise err
1267 raise err
1268 if followsym and hasattr(os.path, 'samestat'):
1268 if followsym and hasattr(os.path, 'samestat'):
1269 def _add_dir_if_not_there(dirlst, dirname):
1269 def _add_dir_if_not_there(dirlst, dirname):
1270 match = False
1270 match = False
1271 samestat = os.path.samestat
1271 samestat = os.path.samestat
1272 dirstat = os.stat(dirname)
1272 dirstat = os.stat(dirname)
1273 for lstdirstat in dirlst:
1273 for lstdirstat in dirlst:
1274 if samestat(dirstat, lstdirstat):
1274 if samestat(dirstat, lstdirstat):
1275 match = True
1275 match = True
1276 break
1276 break
1277 if not match:
1277 if not match:
1278 dirlst.append(dirstat)
1278 dirlst.append(dirstat)
1279 return not match
1279 return not match
1280 else:
1280 else:
1281 followsym = False
1281 followsym = False
1282
1282
1283 if (seen_dirs is None) and followsym:
1283 if (seen_dirs is None) and followsym:
1284 seen_dirs = []
1284 seen_dirs = []
1285 _add_dir_if_not_there(seen_dirs, path)
1285 _add_dir_if_not_there(seen_dirs, path)
1286 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1286 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1287 dirs.sort()
1287 dirs.sort()
1288 if '.hg' in dirs:
1288 if '.hg' in dirs:
1289 yield root # found a repository
1289 yield root # found a repository
1290 qroot = os.path.join(root, '.hg', 'patches')
1290 qroot = os.path.join(root, '.hg', 'patches')
1291 if os.path.isdir(os.path.join(qroot, '.hg')):
1291 if os.path.isdir(os.path.join(qroot, '.hg')):
1292 yield qroot # we have a patch queue repo here
1292 yield qroot # we have a patch queue repo here
1293 if recurse:
1293 if recurse:
1294 # avoid recursing inside the .hg directory
1294 # avoid recursing inside the .hg directory
1295 dirs.remove('.hg')
1295 dirs.remove('.hg')
1296 else:
1296 else:
1297 dirs[:] = [] # don't descend further
1297 dirs[:] = [] # don't descend further
1298 elif followsym:
1298 elif followsym:
1299 newdirs = []
1299 newdirs = []
1300 for d in dirs:
1300 for d in dirs:
1301 fname = os.path.join(root, d)
1301 fname = os.path.join(root, d)
1302 if _add_dir_if_not_there(seen_dirs, fname):
1302 if _add_dir_if_not_there(seen_dirs, fname):
1303 if os.path.islink(fname):
1303 if os.path.islink(fname):
1304 for hgname in walkrepos(fname, True, seen_dirs):
1304 for hgname in walkrepos(fname, True, seen_dirs):
1305 yield hgname
1305 yield hgname
1306 else:
1306 else:
1307 newdirs.append(d)
1307 newdirs.append(d)
1308 dirs[:] = newdirs
1308 dirs[:] = newdirs
1309
1309
1310 _rcpath = None
1310 _rcpath = None
1311
1311
1312 def os_rcpath():
1312 def os_rcpath():
1313 '''return default os-specific hgrc search path'''
1313 '''return default os-specific hgrc search path'''
1314 path = system_rcpath()
1314 path = system_rcpath()
1315 path.extend(user_rcpath())
1315 path.extend(user_rcpath())
1316 path = [os.path.normpath(f) for f in path]
1316 path = [os.path.normpath(f) for f in path]
1317 return path
1317 return path
1318
1318
1319 def rcpath():
1319 def rcpath():
1320 '''return hgrc search path. if env var HGRCPATH is set, use it.
1320 '''return hgrc search path. if env var HGRCPATH is set, use it.
1321 for each item in path, if directory, use files ending in .rc,
1321 for each item in path, if directory, use files ending in .rc,
1322 else use item.
1322 else use item.
1323 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1323 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1324 if no HGRCPATH, use default os-specific path.'''
1324 if no HGRCPATH, use default os-specific path.'''
1325 global _rcpath
1325 global _rcpath
1326 if _rcpath is None:
1326 if _rcpath is None:
1327 if 'HGRCPATH' in os.environ:
1327 if 'HGRCPATH' in os.environ:
1328 _rcpath = []
1328 _rcpath = []
1329 for p in os.environ['HGRCPATH'].split(os.pathsep):
1329 for p in os.environ['HGRCPATH'].split(os.pathsep):
1330 if not p:
1330 if not p:
1331 continue
1331 continue
1332 p = expandpath(p)
1332 p = expandpath(p)
1333 if os.path.isdir(p):
1333 if os.path.isdir(p):
1334 for f, kind in osutil.listdir(p):
1334 for f, kind in osutil.listdir(p):
1335 if f.endswith('.rc'):
1335 if f.endswith('.rc'):
1336 _rcpath.append(os.path.join(p, f))
1336 _rcpath.append(os.path.join(p, f))
1337 else:
1337 else:
1338 _rcpath.append(p)
1338 _rcpath.append(p)
1339 else:
1339 else:
1340 _rcpath = os_rcpath()
1340 _rcpath = os_rcpath()
1341 return _rcpath
1341 return _rcpath
1342
1342
1343 def bytecount(nbytes):
1343 def bytecount(nbytes):
1344 '''return byte count formatted as readable string, with units'''
1344 '''return byte count formatted as readable string, with units'''
1345
1345
1346 units = (
1346 units = (
1347 (100, 1 << 30, _('%.0f GB')),
1347 (100, 1 << 30, _('%.0f GB')),
1348 (10, 1 << 30, _('%.1f GB')),
1348 (10, 1 << 30, _('%.1f GB')),
1349 (1, 1 << 30, _('%.2f GB')),
1349 (1, 1 << 30, _('%.2f GB')),
1350 (100, 1 << 20, _('%.0f MB')),
1350 (100, 1 << 20, _('%.0f MB')),
1351 (10, 1 << 20, _('%.1f MB')),
1351 (10, 1 << 20, _('%.1f MB')),
1352 (1, 1 << 20, _('%.2f MB')),
1352 (1, 1 << 20, _('%.2f MB')),
1353 (100, 1 << 10, _('%.0f KB')),
1353 (100, 1 << 10, _('%.0f KB')),
1354 (10, 1 << 10, _('%.1f KB')),
1354 (10, 1 << 10, _('%.1f KB')),
1355 (1, 1 << 10, _('%.2f KB')),
1355 (1, 1 << 10, _('%.2f KB')),
1356 (1, 1, _('%.0f bytes')),
1356 (1, 1, _('%.0f bytes')),
1357 )
1357 )
1358
1358
1359 for multiplier, divisor, format in units:
1359 for multiplier, divisor, format in units:
1360 if nbytes >= divisor * multiplier:
1360 if nbytes >= divisor * multiplier:
1361 return format % (nbytes / float(divisor))
1361 return format % (nbytes / float(divisor))
1362 return units[-1][2] % nbytes
1362 return units[-1][2] % nbytes
1363
1363
1364 def uirepr(s):
1364 def uirepr(s):
1365 # Avoid double backslash in Windows path repr()
1365 # Avoid double backslash in Windows path repr()
1366 return repr(s).replace('\\\\', '\\')
1366 return repr(s).replace('\\\\', '\\')
1367
1367
1368 # delay import of textwrap
1368 # delay import of textwrap
1369 def MBTextWrapper(**kwargs):
1369 def MBTextWrapper(**kwargs):
1370 class tw(textwrap.TextWrapper):
1370 class tw(textwrap.TextWrapper):
1371 """
1371 """
1372 Extend TextWrapper for double-width characters.
1372 Extend TextWrapper for double-width characters.
1373
1373
1374 Some Asian characters use two terminal columns instead of one.
1374 Some Asian characters use two terminal columns instead of one.
1375 A good example of this behavior can be seen with u'\u65e5\u672c',
1375 A good example of this behavior can be seen with u'\u65e5\u672c',
1376 the two Japanese characters for "Japan":
1376 the two Japanese characters for "Japan":
1377 len() returns 2, but when printed to a terminal, they eat 4 columns.
1377 len() returns 2, but when printed to a terminal, they eat 4 columns.
1378
1378
1379 (Note that this has nothing to do whatsoever with unicode
1379 (Note that this has nothing to do whatsoever with unicode
1380 representation, or encoding of the underlying string)
1380 representation, or encoding of the underlying string)
1381 """
1381 """
1382 def __init__(self, **kwargs):
1382 def __init__(self, **kwargs):
1383 textwrap.TextWrapper.__init__(self, **kwargs)
1383 textwrap.TextWrapper.__init__(self, **kwargs)
1384
1384
1385 def _cutdown(self, str, space_left):
1385 def _cutdown(self, str, space_left):
1386 l = 0
1386 l = 0
1387 ucstr = unicode(str, encoding.encoding)
1387 ucstr = unicode(str, encoding.encoding)
1388 colwidth = unicodedata.east_asian_width
1388 colwidth = unicodedata.east_asian_width
1389 for i in xrange(len(ucstr)):
1389 for i in xrange(len(ucstr)):
1390 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1390 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1391 if space_left < l:
1391 if space_left < l:
1392 return (ucstr[:i].encode(encoding.encoding),
1392 return (ucstr[:i].encode(encoding.encoding),
1393 ucstr[i:].encode(encoding.encoding))
1393 ucstr[i:].encode(encoding.encoding))
1394 return str, ''
1394 return str, ''
1395
1395
1396 # overriding of base class
1396 # overriding of base class
1397 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1397 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1398 space_left = max(width - cur_len, 1)
1398 space_left = max(width - cur_len, 1)
1399
1399
1400 if self.break_long_words:
1400 if self.break_long_words:
1401 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1401 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1402 cur_line.append(cut)
1402 cur_line.append(cut)
1403 reversed_chunks[-1] = res
1403 reversed_chunks[-1] = res
1404 elif not cur_line:
1404 elif not cur_line:
1405 cur_line.append(reversed_chunks.pop())
1405 cur_line.append(reversed_chunks.pop())
1406
1406
1407 global MBTextWrapper
1407 global MBTextWrapper
1408 MBTextWrapper = tw
1408 MBTextWrapper = tw
1409 return tw(**kwargs)
1409 return tw(**kwargs)
1410
1410
1411 def wrap(line, width, initindent='', hangindent=''):
1411 def wrap(line, width, initindent='', hangindent=''):
1412 maxindent = max(len(hangindent), len(initindent))
1412 maxindent = max(len(hangindent), len(initindent))
1413 if width <= maxindent:
1413 if width <= maxindent:
1414 # adjust for weird terminal size
1414 # adjust for weird terminal size
1415 width = max(78, maxindent + 1)
1415 width = max(78, maxindent + 1)
1416 wrapper = MBTextWrapper(width=width,
1416 wrapper = MBTextWrapper(width=width,
1417 initial_indent=initindent,
1417 initial_indent=initindent,
1418 subsequent_indent=hangindent)
1418 subsequent_indent=hangindent)
1419 return wrapper.fill(line)
1419 return wrapper.fill(line)
1420
1420
1421 def iterlines(iterator):
1421 def iterlines(iterator):
1422 for chunk in iterator:
1422 for chunk in iterator:
1423 for line in chunk.splitlines():
1423 for line in chunk.splitlines():
1424 yield line
1424 yield line
1425
1425
1426 def expandpath(path):
1426 def expandpath(path):
1427 return os.path.expanduser(os.path.expandvars(path))
1427 return os.path.expanduser(os.path.expandvars(path))
1428
1428
1429 def hgcmd():
1429 def hgcmd():
1430 """Return the command used to execute current hg
1430 """Return the command used to execute current hg
1431
1431
1432 This is different from hgexecutable() because on Windows we want
1432 This is different from hgexecutable() because on Windows we want
1433 to avoid things opening new shell windows like batch files, so we
1433 to avoid things opening new shell windows like batch files, so we
1434 get either the python call or current executable.
1434 get either the python call or current executable.
1435 """
1435 """
1436 if main_is_frozen():
1436 if main_is_frozen():
1437 return [sys.executable]
1437 return [sys.executable]
1438 return gethgcmd()
1438 return gethgcmd()
1439
1439
1440 def rundetached(args, condfn):
1440 def rundetached(args, condfn):
1441 """Execute the argument list in a detached process.
1441 """Execute the argument list in a detached process.
1442
1442
1443 condfn is a callable which is called repeatedly and should return
1443 condfn is a callable which is called repeatedly and should return
1444 True once the child process is known to have started successfully.
1444 True once the child process is known to have started successfully.
1445 At this point, the child process PID is returned. If the child
1445 At this point, the child process PID is returned. If the child
1446 process fails to start or finishes before condfn() evaluates to
1446 process fails to start or finishes before condfn() evaluates to
1447 True, return -1.
1447 True, return -1.
1448 """
1448 """
1449 # Windows case is easier because the child process is either
1449 # Windows case is easier because the child process is either
1450 # successfully starting and validating the condition or exiting
1450 # successfully starting and validating the condition or exiting
1451 # on failure. We just poll on its PID. On Unix, if the child
1451 # on failure. We just poll on its PID. On Unix, if the child
1452 # process fails to start, it will be left in a zombie state until
1452 # process fails to start, it will be left in a zombie state until
1453 # the parent wait on it, which we cannot do since we expect a long
1453 # the parent wait on it, which we cannot do since we expect a long
1454 # running process on success. Instead we listen for SIGCHLD telling
1454 # running process on success. Instead we listen for SIGCHLD telling
1455 # us our child process terminated.
1455 # us our child process terminated.
1456 terminated = set()
1456 terminated = set()
1457 def handler(signum, frame):
1457 def handler(signum, frame):
1458 terminated.add(os.wait())
1458 terminated.add(os.wait())
1459 prevhandler = None
1459 prevhandler = None
1460 if hasattr(signal, 'SIGCHLD'):
1460 if hasattr(signal, 'SIGCHLD'):
1461 prevhandler = signal.signal(signal.SIGCHLD, handler)
1461 prevhandler = signal.signal(signal.SIGCHLD, handler)
1462 try:
1462 try:
1463 pid = spawndetached(args)
1463 pid = spawndetached(args)
1464 while not condfn():
1464 while not condfn():
1465 if ((pid in terminated or not testpid(pid))
1465 if ((pid in terminated or not testpid(pid))
1466 and not condfn()):
1466 and not condfn()):
1467 return -1
1467 return -1
1468 time.sleep(0.1)
1468 time.sleep(0.1)
1469 return pid
1469 return pid
1470 finally:
1470 finally:
1471 if prevhandler is not None:
1471 if prevhandler is not None:
1472 signal.signal(signal.SIGCHLD, prevhandler)
1472 signal.signal(signal.SIGCHLD, prevhandler)
1473
1473
1474 try:
1474 try:
1475 any, all = any, all
1475 any, all = any, all
1476 except NameError:
1476 except NameError:
1477 def any(iterable):
1477 def any(iterable):
1478 for i in iterable:
1478 for i in iterable:
1479 if i:
1479 if i:
1480 return True
1480 return True
1481 return False
1481 return False
1482
1482
1483 def all(iterable):
1483 def all(iterable):
1484 for i in iterable:
1484 for i in iterable:
1485 if not i:
1485 if not i:
1486 return False
1486 return False
1487 return True
1487 return True
1488
1488
1489 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1489 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1490 """Return the result of interpolating items in the mapping into string s.
1490 """Return the result of interpolating items in the mapping into string s.
1491
1491
1492 prefix is a single character string, or a two character string with
1492 prefix is a single character string, or a two character string with
1493 a backslash as the first character if the prefix needs to be escaped in
1493 a backslash as the first character if the prefix needs to be escaped in
1494 a regular expression.
1494 a regular expression.
1495
1495
1496 fn is an optional function that will be applied to the replacement text
1496 fn is an optional function that will be applied to the replacement text
1497 just before replacement.
1497 just before replacement.
1498
1498
1499 escape_prefix is an optional flag that allows using doubled prefix for
1499 escape_prefix is an optional flag that allows using doubled prefix for
1500 its escaping.
1500 its escaping.
1501 """
1501 """
1502 fn = fn or (lambda s: s)
1502 fn = fn or (lambda s: s)
1503 patterns = '|'.join(mapping.keys())
1503 patterns = '|'.join(mapping.keys())
1504 if escape_prefix:
1504 if escape_prefix:
1505 patterns += '|' + prefix
1505 patterns += '|' + prefix
1506 if len(prefix) > 1:
1506 if len(prefix) > 1:
1507 prefix_char = prefix[1:]
1507 prefix_char = prefix[1:]
1508 else:
1508 else:
1509 prefix_char = prefix
1509 prefix_char = prefix
1510 mapping[prefix_char] = prefix_char
1510 mapping[prefix_char] = prefix_char
1511 r = re.compile(r'%s(%s)' % (prefix, patterns))
1511 r = re.compile(r'%s(%s)' % (prefix, patterns))
1512 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1512 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1513
1513
1514 def getport(port):
1514 def getport(port):
1515 """Return the port for a given network service.
1515 """Return the port for a given network service.
1516
1516
1517 If port is an integer, it's returned as is. If it's a string, it's
1517 If port is an integer, it's returned as is. If it's a string, it's
1518 looked up using socket.getservbyname(). If there's no matching
1518 looked up using socket.getservbyname(). If there's no matching
1519 service, util.Abort is raised.
1519 service, util.Abort is raised.
1520 """
1520 """
1521 try:
1521 try:
1522 return int(port)
1522 return int(port)
1523 except ValueError:
1523 except ValueError:
1524 pass
1524 pass
1525
1525
1526 try:
1526 try:
1527 return socket.getservbyname(port)
1527 return socket.getservbyname(port)
1528 except socket.error:
1528 except socket.error:
1529 raise Abort(_("no port number associated with service '%s'") % port)
1529 raise Abort(_("no port number associated with service '%s'") % port)
1530
1530
1531 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1531 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1532 '0': False, 'no': False, 'false': False, 'off': False,
1532 '0': False, 'no': False, 'false': False, 'off': False,
1533 'never': False}
1533 'never': False}
1534
1534
1535 def parsebool(s):
1535 def parsebool(s):
1536 """Parse s into a boolean.
1536 """Parse s into a boolean.
1537
1537
1538 If s is not a valid boolean, returns None.
1538 If s is not a valid boolean, returns None.
1539 """
1539 """
1540 return _booleans.get(s.lower(), None)
1540 return _booleans.get(s.lower(), None)
@@ -1,1135 +1,1135
1 $ hg init a
1 $ hg init a
2
2
3 $ cd a
3 $ cd a
4 $ echo a > a
4 $ echo a > a
5 $ hg ci -Ama -d '1 0'
5 $ hg ci -Ama -d '1 0'
6 adding a
6 adding a
7
7
8 $ hg cp a b
8 $ hg cp a b
9 $ hg ci -mb -d '2 0'
9 $ hg ci -mb -d '2 0'
10
10
11 $ mkdir dir
11 $ mkdir dir
12 $ hg mv b dir
12 $ hg mv b dir
13 $ hg ci -mc -d '3 0'
13 $ hg ci -mc -d '3 0'
14
14
15 $ hg mv a b
15 $ hg mv a b
16 $ echo a > d
16 $ echo a > d
17 $ hg add d
17 $ hg add d
18 $ hg ci -md -d '4 0'
18 $ hg ci -md -d '4 0'
19
19
20 $ hg mv dir/b e
20 $ hg mv dir/b e
21 $ hg ci -me -d '5 0'
21 $ hg ci -me -d '5 0'
22
22
23 $ hg log a
23 $ hg log a
24 changeset: 0:8580ff50825a
24 changeset: 0:8580ff50825a
25 user: test
25 user: test
26 date: Thu Jan 01 00:00:01 1970 +0000
26 date: Thu Jan 01 00:00:01 1970 +0000
27 summary: a
27 summary: a
28
28
29
29
30 -f, directory
30 -f, directory
31
31
32 $ hg log -f dir
32 $ hg log -f dir
33 abort: cannot follow nonexistent file: "dir"
33 abort: cannot follow nonexistent file: "dir"
34 [255]
34 [255]
35
35
36 -f, but no args
36 -f, but no args
37
37
38 $ hg log -f
38 $ hg log -f
39 changeset: 4:66c1345dc4f9
39 changeset: 4:66c1345dc4f9
40 tag: tip
40 tag: tip
41 user: test
41 user: test
42 date: Thu Jan 01 00:00:05 1970 +0000
42 date: Thu Jan 01 00:00:05 1970 +0000
43 summary: e
43 summary: e
44
44
45 changeset: 3:7c6c671bb7cc
45 changeset: 3:7c6c671bb7cc
46 user: test
46 user: test
47 date: Thu Jan 01 00:00:04 1970 +0000
47 date: Thu Jan 01 00:00:04 1970 +0000
48 summary: d
48 summary: d
49
49
50 changeset: 2:41dd4284081e
50 changeset: 2:41dd4284081e
51 user: test
51 user: test
52 date: Thu Jan 01 00:00:03 1970 +0000
52 date: Thu Jan 01 00:00:03 1970 +0000
53 summary: c
53 summary: c
54
54
55 changeset: 1:784de7cef101
55 changeset: 1:784de7cef101
56 user: test
56 user: test
57 date: Thu Jan 01 00:00:02 1970 +0000
57 date: Thu Jan 01 00:00:02 1970 +0000
58 summary: b
58 summary: b
59
59
60 changeset: 0:8580ff50825a
60 changeset: 0:8580ff50825a
61 user: test
61 user: test
62 date: Thu Jan 01 00:00:01 1970 +0000
62 date: Thu Jan 01 00:00:01 1970 +0000
63 summary: a
63 summary: a
64
64
65
65
66 one rename
66 one rename
67
67
68 $ hg log -vf a
68 $ hg log -vf a
69 changeset: 0:8580ff50825a
69 changeset: 0:8580ff50825a
70 user: test
70 user: test
71 date: Thu Jan 01 00:00:01 1970 +0000
71 date: Thu Jan 01 00:00:01 1970 +0000
72 files: a
72 files: a
73 description:
73 description:
74 a
74 a
75
75
76
76
77
77
78 many renames
78 many renames
79
79
80 $ hg log -vf e
80 $ hg log -vf e
81 changeset: 4:66c1345dc4f9
81 changeset: 4:66c1345dc4f9
82 tag: tip
82 tag: tip
83 user: test
83 user: test
84 date: Thu Jan 01 00:00:05 1970 +0000
84 date: Thu Jan 01 00:00:05 1970 +0000
85 files: dir/b e
85 files: dir/b e
86 description:
86 description:
87 e
87 e
88
88
89
89
90 changeset: 2:41dd4284081e
90 changeset: 2:41dd4284081e
91 user: test
91 user: test
92 date: Thu Jan 01 00:00:03 1970 +0000
92 date: Thu Jan 01 00:00:03 1970 +0000
93 files: b dir/b
93 files: b dir/b
94 description:
94 description:
95 c
95 c
96
96
97
97
98 changeset: 1:784de7cef101
98 changeset: 1:784de7cef101
99 user: test
99 user: test
100 date: Thu Jan 01 00:00:02 1970 +0000
100 date: Thu Jan 01 00:00:02 1970 +0000
101 files: b
101 files: b
102 description:
102 description:
103 b
103 b
104
104
105
105
106 changeset: 0:8580ff50825a
106 changeset: 0:8580ff50825a
107 user: test
107 user: test
108 date: Thu Jan 01 00:00:01 1970 +0000
108 date: Thu Jan 01 00:00:01 1970 +0000
109 files: a
109 files: a
110 description:
110 description:
111 a
111 a
112
112
113
113
114
114
115
115
116 log -pf dir/b
116 log -pf dir/b
117
117
118 $ hg log -pf dir/b
118 $ hg log -pf dir/b
119 changeset: 2:41dd4284081e
119 changeset: 2:41dd4284081e
120 user: test
120 user: test
121 date: Thu Jan 01 00:00:03 1970 +0000
121 date: Thu Jan 01 00:00:03 1970 +0000
122 summary: c
122 summary: c
123
123
124 diff -r 784de7cef101 -r 41dd4284081e dir/b
124 diff -r 784de7cef101 -r 41dd4284081e dir/b
125 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
125 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
126 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
126 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
127 @@ -0,0 +1,1 @@
127 @@ -0,0 +1,1 @@
128 +a
128 +a
129
129
130 changeset: 1:784de7cef101
130 changeset: 1:784de7cef101
131 user: test
131 user: test
132 date: Thu Jan 01 00:00:02 1970 +0000
132 date: Thu Jan 01 00:00:02 1970 +0000
133 summary: b
133 summary: b
134
134
135 diff -r 8580ff50825a -r 784de7cef101 b
135 diff -r 8580ff50825a -r 784de7cef101 b
136 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
136 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
137 +++ b/b Thu Jan 01 00:00:02 1970 +0000
137 +++ b/b Thu Jan 01 00:00:02 1970 +0000
138 @@ -0,0 +1,1 @@
138 @@ -0,0 +1,1 @@
139 +a
139 +a
140
140
141 changeset: 0:8580ff50825a
141 changeset: 0:8580ff50825a
142 user: test
142 user: test
143 date: Thu Jan 01 00:00:01 1970 +0000
143 date: Thu Jan 01 00:00:01 1970 +0000
144 summary: a
144 summary: a
145
145
146 diff -r 000000000000 -r 8580ff50825a a
146 diff -r 000000000000 -r 8580ff50825a a
147 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
147 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
148 +++ b/a Thu Jan 01 00:00:01 1970 +0000
148 +++ b/a Thu Jan 01 00:00:01 1970 +0000
149 @@ -0,0 +1,1 @@
149 @@ -0,0 +1,1 @@
150 +a
150 +a
151
151
152
152
153 log -vf dir/b
153 log -vf dir/b
154
154
155 $ hg log -vf dir/b
155 $ hg log -vf dir/b
156 changeset: 2:41dd4284081e
156 changeset: 2:41dd4284081e
157 user: test
157 user: test
158 date: Thu Jan 01 00:00:03 1970 +0000
158 date: Thu Jan 01 00:00:03 1970 +0000
159 files: b dir/b
159 files: b dir/b
160 description:
160 description:
161 c
161 c
162
162
163
163
164 changeset: 1:784de7cef101
164 changeset: 1:784de7cef101
165 user: test
165 user: test
166 date: Thu Jan 01 00:00:02 1970 +0000
166 date: Thu Jan 01 00:00:02 1970 +0000
167 files: b
167 files: b
168 description:
168 description:
169 b
169 b
170
170
171
171
172 changeset: 0:8580ff50825a
172 changeset: 0:8580ff50825a
173 user: test
173 user: test
174 date: Thu Jan 01 00:00:01 1970 +0000
174 date: Thu Jan 01 00:00:01 1970 +0000
175 files: a
175 files: a
176 description:
176 description:
177 a
177 a
178
178
179
179
180
180
181
181
182 log copies with --copies
182 log copies with --copies
183
183
184 $ hg log -vC --template '{rev} {file_copies}\n'
184 $ hg log -vC --template '{rev} {file_copies}\n'
185 4 e (dir/b)
185 4 e (dir/b)
186 3 b (a)
186 3 b (a)
187 2 dir/b (b)
187 2 dir/b (b)
188 1 b (a)
188 1 b (a)
189 0
189 0
190
190
191 log copies switch without --copies, with old filecopy template
191 log copies switch without --copies, with old filecopy template
192
192
193 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
193 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
194 4
194 4
195 3
195 3
196 2
196 2
197 1
197 1
198 0
198 0
199
199
200 log copies switch with --copies
200 log copies switch with --copies
201
201
202 $ hg log -vC --template '{rev} {file_copies_switch}\n'
202 $ hg log -vC --template '{rev} {file_copies_switch}\n'
203 4 e (dir/b)
203 4 e (dir/b)
204 3 b (a)
204 3 b (a)
205 2 dir/b (b)
205 2 dir/b (b)
206 1 b (a)
206 1 b (a)
207 0
207 0
208
208
209
209
210 log copies with hardcoded style and with --style=default
210 log copies with hardcoded style and with --style=default
211
211
212 $ hg log -vC -r4
212 $ hg log -vC -r4
213 changeset: 4:66c1345dc4f9
213 changeset: 4:66c1345dc4f9
214 tag: tip
214 tag: tip
215 user: test
215 user: test
216 date: Thu Jan 01 00:00:05 1970 +0000
216 date: Thu Jan 01 00:00:05 1970 +0000
217 files: dir/b e
217 files: dir/b e
218 copies: e (dir/b)
218 copies: e (dir/b)
219 description:
219 description:
220 e
220 e
221
221
222
222
223 $ hg log -vC -r4 --style=default
223 $ hg log -vC -r4 --style=default
224 changeset: 4:66c1345dc4f9
224 changeset: 4:66c1345dc4f9
225 tag: tip
225 tag: tip
226 user: test
226 user: test
227 date: Thu Jan 01 00:00:05 1970 +0000
227 date: Thu Jan 01 00:00:05 1970 +0000
228 files: dir/b e
228 files: dir/b e
229 copies: e (dir/b)
229 copies: e (dir/b)
230 description:
230 description:
231 e
231 e
232
232
233
233
234
234
235
235
236 log copies, non-linear manifest
236 log copies, non-linear manifest
237
237
238 $ hg up -C 3
238 $ hg up -C 3
239 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
239 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
240 $ hg mv dir/b e
240 $ hg mv dir/b e
241 $ echo foo > foo
241 $ echo foo > foo
242 $ hg ci -Ame2 -d '6 0'
242 $ hg ci -Ame2 -d '6 0'
243 adding foo
243 adding foo
244 created new head
244 created new head
245 $ hg log -v --template '{rev} {file_copies}\n' -r 5
245 $ hg log -v --template '{rev} {file_copies}\n' -r 5
246 5 e (dir/b)
246 5 e (dir/b)
247
247
248
248
249 log copies, execute bit set
249 log copies, execute bit set
250
250
251 $ chmod +x e
251 $ chmod +x e
252 $ hg ci -me3 -d '7 0'
252 $ hg ci -me3 -d '7 0'
253 $ hg log -v --template '{rev} {file_copies}\n' -r 6
253 $ hg log -v --template '{rev} {file_copies}\n' -r 6
254 6
254 6
255
255
256
256
257 log -p d
257 log -p d
258
258
259 $ hg log -pv d
259 $ hg log -pv d
260 changeset: 3:7c6c671bb7cc
260 changeset: 3:7c6c671bb7cc
261 user: test
261 user: test
262 date: Thu Jan 01 00:00:04 1970 +0000
262 date: Thu Jan 01 00:00:04 1970 +0000
263 files: a b d
263 files: a b d
264 description:
264 description:
265 d
265 d
266
266
267
267
268 diff -r 41dd4284081e -r 7c6c671bb7cc d
268 diff -r 41dd4284081e -r 7c6c671bb7cc d
269 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
269 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
270 +++ b/d Thu Jan 01 00:00:04 1970 +0000
270 +++ b/d Thu Jan 01 00:00:04 1970 +0000
271 @@ -0,0 +1,1 @@
271 @@ -0,0 +1,1 @@
272 +a
272 +a
273
273
274
274
275
275
276 log --removed file
276 log --removed file
277
277
278 $ hg log --removed -v a
278 $ hg log --removed -v a
279 changeset: 3:7c6c671bb7cc
279 changeset: 3:7c6c671bb7cc
280 user: test
280 user: test
281 date: Thu Jan 01 00:00:04 1970 +0000
281 date: Thu Jan 01 00:00:04 1970 +0000
282 files: a b d
282 files: a b d
283 description:
283 description:
284 d
284 d
285
285
286
286
287 changeset: 0:8580ff50825a
287 changeset: 0:8580ff50825a
288 user: test
288 user: test
289 date: Thu Jan 01 00:00:01 1970 +0000
289 date: Thu Jan 01 00:00:01 1970 +0000
290 files: a
290 files: a
291 description:
291 description:
292 a
292 a
293
293
294
294
295
295
296 log --removed revrange file
296 log --removed revrange file
297
297
298 $ hg log --removed -v -r0:2 a
298 $ hg log --removed -v -r0:2 a
299 changeset: 0:8580ff50825a
299 changeset: 0:8580ff50825a
300 user: test
300 user: test
301 date: Thu Jan 01 00:00:01 1970 +0000
301 date: Thu Jan 01 00:00:01 1970 +0000
302 files: a
302 files: a
303 description:
303 description:
304 a
304 a
305
305
306
306
307
307
308
308
309 log --follow tests
309 log --follow tests
310
310
311 $ hg init ../follow
311 $ hg init ../follow
312 $ cd ../follow
312 $ cd ../follow
313
313
314 $ echo base > base
314 $ echo base > base
315 $ hg ci -Ambase -d '1 0'
315 $ hg ci -Ambase -d '1 0'
316 adding base
316 adding base
317
317
318 $ echo r1 >> base
318 $ echo r1 >> base
319 $ hg ci -Amr1 -d '1 0'
319 $ hg ci -Amr1 -d '1 0'
320 $ echo r2 >> base
320 $ echo r2 >> base
321 $ hg ci -Amr2 -d '1 0'
321 $ hg ci -Amr2 -d '1 0'
322
322
323 $ hg up -C 1
323 $ hg up -C 1
324 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
324 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
325 $ echo b1 > b1
325 $ echo b1 > b1
326 $ hg ci -Amb1 -d '1 0'
326 $ hg ci -Amb1 -d '1 0'
327 adding b1
327 adding b1
328 created new head
328 created new head
329
329
330
330
331 log -f
331 log -f
332
332
333 $ hg log -f
333 $ hg log -f
334 changeset: 3:e62f78d544b4
334 changeset: 3:e62f78d544b4
335 tag: tip
335 tag: tip
336 parent: 1:3d5bf5654eda
336 parent: 1:3d5bf5654eda
337 user: test
337 user: test
338 date: Thu Jan 01 00:00:01 1970 +0000
338 date: Thu Jan 01 00:00:01 1970 +0000
339 summary: b1
339 summary: b1
340
340
341 changeset: 1:3d5bf5654eda
341 changeset: 1:3d5bf5654eda
342 user: test
342 user: test
343 date: Thu Jan 01 00:00:01 1970 +0000
343 date: Thu Jan 01 00:00:01 1970 +0000
344 summary: r1
344 summary: r1
345
345
346 changeset: 0:67e992f2c4f3
346 changeset: 0:67e992f2c4f3
347 user: test
347 user: test
348 date: Thu Jan 01 00:00:01 1970 +0000
348 date: Thu Jan 01 00:00:01 1970 +0000
349 summary: base
349 summary: base
350
350
351
351
352
352
353 log -f -r 1:tip
353 log -f -r 1:tip
354
354
355 $ hg up -C 0
355 $ hg up -C 0
356 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
356 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
357 $ echo b2 > b2
357 $ echo b2 > b2
358 $ hg ci -Amb2 -d '1 0'
358 $ hg ci -Amb2 -d '1 0'
359 adding b2
359 adding b2
360 created new head
360 created new head
361 $ hg log -f -r 1:tip
361 $ hg log -f -r 1:tip
362 changeset: 1:3d5bf5654eda
362 changeset: 1:3d5bf5654eda
363 user: test
363 user: test
364 date: Thu Jan 01 00:00:01 1970 +0000
364 date: Thu Jan 01 00:00:01 1970 +0000
365 summary: r1
365 summary: r1
366
366
367 changeset: 2:60c670bf5b30
367 changeset: 2:60c670bf5b30
368 user: test
368 user: test
369 date: Thu Jan 01 00:00:01 1970 +0000
369 date: Thu Jan 01 00:00:01 1970 +0000
370 summary: r2
370 summary: r2
371
371
372 changeset: 3:e62f78d544b4
372 changeset: 3:e62f78d544b4
373 parent: 1:3d5bf5654eda
373 parent: 1:3d5bf5654eda
374 user: test
374 user: test
375 date: Thu Jan 01 00:00:01 1970 +0000
375 date: Thu Jan 01 00:00:01 1970 +0000
376 summary: b1
376 summary: b1
377
377
378
378
379
379
380 log -r . with two parents
380 log -r . with two parents
381
381
382 $ hg up -C 3
382 $ hg up -C 3
383 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
383 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
384 $ hg merge tip
384 $ hg merge tip
385 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
385 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
386 (branch merge, don't forget to commit)
386 (branch merge, don't forget to commit)
387 $ hg log -r .
387 $ hg log -r .
388 changeset: 3:e62f78d544b4
388 changeset: 3:e62f78d544b4
389 parent: 1:3d5bf5654eda
389 parent: 1:3d5bf5654eda
390 user: test
390 user: test
391 date: Thu Jan 01 00:00:01 1970 +0000
391 date: Thu Jan 01 00:00:01 1970 +0000
392 summary: b1
392 summary: b1
393
393
394
394
395
395
396 log -r . with one parent
396 log -r . with one parent
397
397
398 $ hg ci -mm12 -d '1 0'
398 $ hg ci -mm12 -d '1 0'
399 $ hg log -r .
399 $ hg log -r .
400 changeset: 5:302e9dd6890d
400 changeset: 5:302e9dd6890d
401 tag: tip
401 tag: tip
402 parent: 3:e62f78d544b4
402 parent: 3:e62f78d544b4
403 parent: 4:ddb82e70d1a1
403 parent: 4:ddb82e70d1a1
404 user: test
404 user: test
405 date: Thu Jan 01 00:00:01 1970 +0000
405 date: Thu Jan 01 00:00:01 1970 +0000
406 summary: m12
406 summary: m12
407
407
408
408
409 $ echo postm >> b1
409 $ echo postm >> b1
410 $ hg ci -Amb1.1 -d'1 0'
410 $ hg ci -Amb1.1 -d'1 0'
411
411
412
412
413 log --follow-first
413 log --follow-first
414
414
415 $ hg log --follow-first
415 $ hg log --follow-first
416 changeset: 6:2404bbcab562
416 changeset: 6:2404bbcab562
417 tag: tip
417 tag: tip
418 user: test
418 user: test
419 date: Thu Jan 01 00:00:01 1970 +0000
419 date: Thu Jan 01 00:00:01 1970 +0000
420 summary: b1.1
420 summary: b1.1
421
421
422 changeset: 5:302e9dd6890d
422 changeset: 5:302e9dd6890d
423 parent: 3:e62f78d544b4
423 parent: 3:e62f78d544b4
424 parent: 4:ddb82e70d1a1
424 parent: 4:ddb82e70d1a1
425 user: test
425 user: test
426 date: Thu Jan 01 00:00:01 1970 +0000
426 date: Thu Jan 01 00:00:01 1970 +0000
427 summary: m12
427 summary: m12
428
428
429 changeset: 3:e62f78d544b4
429 changeset: 3:e62f78d544b4
430 parent: 1:3d5bf5654eda
430 parent: 1:3d5bf5654eda
431 user: test
431 user: test
432 date: Thu Jan 01 00:00:01 1970 +0000
432 date: Thu Jan 01 00:00:01 1970 +0000
433 summary: b1
433 summary: b1
434
434
435 changeset: 1:3d5bf5654eda
435 changeset: 1:3d5bf5654eda
436 user: test
436 user: test
437 date: Thu Jan 01 00:00:01 1970 +0000
437 date: Thu Jan 01 00:00:01 1970 +0000
438 summary: r1
438 summary: r1
439
439
440 changeset: 0:67e992f2c4f3
440 changeset: 0:67e992f2c4f3
441 user: test
441 user: test
442 date: Thu Jan 01 00:00:01 1970 +0000
442 date: Thu Jan 01 00:00:01 1970 +0000
443 summary: base
443 summary: base
444
444
445
445
446
446
447 log -P 2
447 log -P 2
448
448
449 $ hg log -P 2
449 $ hg log -P 2
450 changeset: 6:2404bbcab562
450 changeset: 6:2404bbcab562
451 tag: tip
451 tag: tip
452 user: test
452 user: test
453 date: Thu Jan 01 00:00:01 1970 +0000
453 date: Thu Jan 01 00:00:01 1970 +0000
454 summary: b1.1
454 summary: b1.1
455
455
456 changeset: 5:302e9dd6890d
456 changeset: 5:302e9dd6890d
457 parent: 3:e62f78d544b4
457 parent: 3:e62f78d544b4
458 parent: 4:ddb82e70d1a1
458 parent: 4:ddb82e70d1a1
459 user: test
459 user: test
460 date: Thu Jan 01 00:00:01 1970 +0000
460 date: Thu Jan 01 00:00:01 1970 +0000
461 summary: m12
461 summary: m12
462
462
463 changeset: 4:ddb82e70d1a1
463 changeset: 4:ddb82e70d1a1
464 parent: 0:67e992f2c4f3
464 parent: 0:67e992f2c4f3
465 user: test
465 user: test
466 date: Thu Jan 01 00:00:01 1970 +0000
466 date: Thu Jan 01 00:00:01 1970 +0000
467 summary: b2
467 summary: b2
468
468
469 changeset: 3:e62f78d544b4
469 changeset: 3:e62f78d544b4
470 parent: 1:3d5bf5654eda
470 parent: 1:3d5bf5654eda
471 user: test
471 user: test
472 date: Thu Jan 01 00:00:01 1970 +0000
472 date: Thu Jan 01 00:00:01 1970 +0000
473 summary: b1
473 summary: b1
474
474
475
475
476
476
477 log -r tip -p --git
477 log -r tip -p --git
478
478
479 $ hg log -r tip -p --git
479 $ hg log -r tip -p --git
480 changeset: 6:2404bbcab562
480 changeset: 6:2404bbcab562
481 tag: tip
481 tag: tip
482 user: test
482 user: test
483 date: Thu Jan 01 00:00:01 1970 +0000
483 date: Thu Jan 01 00:00:01 1970 +0000
484 summary: b1.1
484 summary: b1.1
485
485
486 diff --git a/b1 b/b1
486 diff --git a/b1 b/b1
487 --- a/b1
487 --- a/b1
488 +++ b/b1
488 +++ b/b1
489 @@ -1,1 +1,2 @@
489 @@ -1,1 +1,2 @@
490 b1
490 b1
491 +postm
491 +postm
492
492
493
493
494
494
495 log -r ""
495 log -r ""
496
496
497 $ hg log -r ''
497 $ hg log -r ''
498 hg: parse error: empty query
498 hg: parse error: empty query
499 [255]
499 [255]
500
500
501 log -r <some unknown node id>
501 log -r <some unknown node id>
502
502
503 $ hg log -r 1000000000000000000000000000000000000000
503 $ hg log -r 1000000000000000000000000000000000000000
504 abort: unknown revision '1000000000000000000000000000000000000000'!
504 abort: unknown revision '1000000000000000000000000000000000000000'!
505 [255]
505 [255]
506
506
507 log -k r1
507 log -k r1
508
508
509 $ hg log -k r1
509 $ hg log -k r1
510 changeset: 1:3d5bf5654eda
510 changeset: 1:3d5bf5654eda
511 user: test
511 user: test
512 date: Thu Jan 01 00:00:01 1970 +0000
512 date: Thu Jan 01 00:00:01 1970 +0000
513 summary: r1
513 summary: r1
514
514
515 log -d " " (whitespaces only)
515 log -d " " (whitespaces only)
516
516
517 $ hg log -d " "
517 $ hg log -d " "
518 abort: dates cannot consist entirely of whitespace
518 abort: dates cannot consist entirely of whitespace
519 [255]
519 [255]
520
520
521 log -d -1
521 log -d -1
522
522
523 $ hg log -d -1
523 $ hg log -d -1
524
524
525 log -d ">"
525 log -d ">"
526
526
527 $ hg log -d ">"
527 $ hg log -d ">"
528 abort: invalid day spec. use '>{datetime}'
528 abort: invalid day spec, use '>DATE'
529 [255]
529 [255]
530
530
531 log -d "<"
531 log -d "<"
532
532
533 $ hg log -d "<"
533 $ hg log -d "<"
534 abort: invalid day spec. use '<{datetime}'
534 abort: invalid day spec, use '<DATE'
535 [255]
535 [255]
536
536
537
537
538 log -p -l2 --color=always
538 log -p -l2 --color=always
539
539
540 $ hg --config extensions.color= --config color.mode=ansi \
540 $ hg --config extensions.color= --config color.mode=ansi \
541 > log -p -l2 --color=always
541 > log -p -l2 --color=always
542 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
542 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
543 tag: tip
543 tag: tip
544 user: test
544 user: test
545 date: Thu Jan 01 00:00:01 1970 +0000
545 date: Thu Jan 01 00:00:01 1970 +0000
546 summary: b1.1
546 summary: b1.1
547
547
548 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
548 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
549 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
549 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
550 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
550 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
551 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
551 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
552 b1
552 b1
553 \x1b[0;32m+postm\x1b[0m (esc)
553 \x1b[0;32m+postm\x1b[0m (esc)
554
554
555 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
555 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
556 parent: 3:e62f78d544b4
556 parent: 3:e62f78d544b4
557 parent: 4:ddb82e70d1a1
557 parent: 4:ddb82e70d1a1
558 user: test
558 user: test
559 date: Thu Jan 01 00:00:01 1970 +0000
559 date: Thu Jan 01 00:00:01 1970 +0000
560 summary: m12
560 summary: m12
561
561
562 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
562 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
563 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
563 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
564 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
564 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
565 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
565 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
566 \x1b[0;32m+b2\x1b[0m (esc)
566 \x1b[0;32m+b2\x1b[0m (esc)
567
567
568
568
569
569
570 log -r tip --stat
570 log -r tip --stat
571
571
572 $ hg log -r tip --stat
572 $ hg log -r tip --stat
573 changeset: 6:2404bbcab562
573 changeset: 6:2404bbcab562
574 tag: tip
574 tag: tip
575 user: test
575 user: test
576 date: Thu Jan 01 00:00:01 1970 +0000
576 date: Thu Jan 01 00:00:01 1970 +0000
577 summary: b1.1
577 summary: b1.1
578
578
579 b1 | 1 +
579 b1 | 1 +
580 1 files changed, 1 insertions(+), 0 deletions(-)
580 1 files changed, 1 insertions(+), 0 deletions(-)
581
581
582
582
583 $ cd ..
583 $ cd ..
584
584
585 $ hg init usertest
585 $ hg init usertest
586 $ cd usertest
586 $ cd usertest
587
587
588 $ echo a > a
588 $ echo a > a
589 $ hg ci -A -m "a" -u "User One <user1@example.org>"
589 $ hg ci -A -m "a" -u "User One <user1@example.org>"
590 adding a
590 adding a
591 $ echo b > b
591 $ echo b > b
592 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
592 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
593 adding b
593 adding b
594
594
595 $ hg log -u "User One <user1@example.org>"
595 $ hg log -u "User One <user1@example.org>"
596 changeset: 0:29a4c94f1924
596 changeset: 0:29a4c94f1924
597 user: User One <user1@example.org>
597 user: User One <user1@example.org>
598 date: Thu Jan 01 00:00:00 1970 +0000
598 date: Thu Jan 01 00:00:00 1970 +0000
599 summary: a
599 summary: a
600
600
601 $ hg log -u "user1" -u "user2"
601 $ hg log -u "user1" -u "user2"
602 changeset: 1:e834b5e69c0e
602 changeset: 1:e834b5e69c0e
603 tag: tip
603 tag: tip
604 user: User Two <user2@example.org>
604 user: User Two <user2@example.org>
605 date: Thu Jan 01 00:00:00 1970 +0000
605 date: Thu Jan 01 00:00:00 1970 +0000
606 summary: b
606 summary: b
607
607
608 changeset: 0:29a4c94f1924
608 changeset: 0:29a4c94f1924
609 user: User One <user1@example.org>
609 user: User One <user1@example.org>
610 date: Thu Jan 01 00:00:00 1970 +0000
610 date: Thu Jan 01 00:00:00 1970 +0000
611 summary: a
611 summary: a
612
612
613 $ hg log -u "user3"
613 $ hg log -u "user3"
614
614
615 $ cd ..
615 $ cd ..
616
616
617 $ hg init branches
617 $ hg init branches
618 $ cd branches
618 $ cd branches
619
619
620 $ echo a > a
620 $ echo a > a
621 $ hg ci -A -m "commit on default"
621 $ hg ci -A -m "commit on default"
622 adding a
622 adding a
623 $ hg branch test
623 $ hg branch test
624 marked working directory as branch test
624 marked working directory as branch test
625 $ echo b > b
625 $ echo b > b
626 $ hg ci -A -m "commit on test"
626 $ hg ci -A -m "commit on test"
627 adding b
627 adding b
628
628
629 $ hg up default
629 $ hg up default
630 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
630 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
631 $ echo c > c
631 $ echo c > c
632 $ hg ci -A -m "commit on default"
632 $ hg ci -A -m "commit on default"
633 adding c
633 adding c
634 $ hg up test
634 $ hg up test
635 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
635 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
636 $ echo c > c
636 $ echo c > c
637 $ hg ci -A -m "commit on test"
637 $ hg ci -A -m "commit on test"
638 adding c
638 adding c
639
639
640
640
641 log -b default
641 log -b default
642
642
643 $ hg log -b default
643 $ hg log -b default
644 changeset: 2:c3a4f03cc9a7
644 changeset: 2:c3a4f03cc9a7
645 parent: 0:24427303d56f
645 parent: 0:24427303d56f
646 user: test
646 user: test
647 date: Thu Jan 01 00:00:00 1970 +0000
647 date: Thu Jan 01 00:00:00 1970 +0000
648 summary: commit on default
648 summary: commit on default
649
649
650 changeset: 0:24427303d56f
650 changeset: 0:24427303d56f
651 user: test
651 user: test
652 date: Thu Jan 01 00:00:00 1970 +0000
652 date: Thu Jan 01 00:00:00 1970 +0000
653 summary: commit on default
653 summary: commit on default
654
654
655
655
656
656
657 log -b test
657 log -b test
658
658
659 $ hg log -b test
659 $ hg log -b test
660 changeset: 3:f5d8de11c2e2
660 changeset: 3:f5d8de11c2e2
661 branch: test
661 branch: test
662 tag: tip
662 tag: tip
663 parent: 1:d32277701ccb
663 parent: 1:d32277701ccb
664 user: test
664 user: test
665 date: Thu Jan 01 00:00:00 1970 +0000
665 date: Thu Jan 01 00:00:00 1970 +0000
666 summary: commit on test
666 summary: commit on test
667
667
668 changeset: 1:d32277701ccb
668 changeset: 1:d32277701ccb
669 branch: test
669 branch: test
670 user: test
670 user: test
671 date: Thu Jan 01 00:00:00 1970 +0000
671 date: Thu Jan 01 00:00:00 1970 +0000
672 summary: commit on test
672 summary: commit on test
673
673
674
674
675
675
676 log -b dummy
676 log -b dummy
677
677
678 $ hg log -b dummy
678 $ hg log -b dummy
679 abort: unknown revision 'dummy'!
679 abort: unknown revision 'dummy'!
680 [255]
680 [255]
681
681
682
682
683 log -b .
683 log -b .
684
684
685 $ hg log -b .
685 $ hg log -b .
686 changeset: 3:f5d8de11c2e2
686 changeset: 3:f5d8de11c2e2
687 branch: test
687 branch: test
688 tag: tip
688 tag: tip
689 parent: 1:d32277701ccb
689 parent: 1:d32277701ccb
690 user: test
690 user: test
691 date: Thu Jan 01 00:00:00 1970 +0000
691 date: Thu Jan 01 00:00:00 1970 +0000
692 summary: commit on test
692 summary: commit on test
693
693
694 changeset: 1:d32277701ccb
694 changeset: 1:d32277701ccb
695 branch: test
695 branch: test
696 user: test
696 user: test
697 date: Thu Jan 01 00:00:00 1970 +0000
697 date: Thu Jan 01 00:00:00 1970 +0000
698 summary: commit on test
698 summary: commit on test
699
699
700
700
701
701
702 log -b default -b test
702 log -b default -b test
703
703
704 $ hg log -b default -b test
704 $ hg log -b default -b test
705 changeset: 3:f5d8de11c2e2
705 changeset: 3:f5d8de11c2e2
706 branch: test
706 branch: test
707 tag: tip
707 tag: tip
708 parent: 1:d32277701ccb
708 parent: 1:d32277701ccb
709 user: test
709 user: test
710 date: Thu Jan 01 00:00:00 1970 +0000
710 date: Thu Jan 01 00:00:00 1970 +0000
711 summary: commit on test
711 summary: commit on test
712
712
713 changeset: 2:c3a4f03cc9a7
713 changeset: 2:c3a4f03cc9a7
714 parent: 0:24427303d56f
714 parent: 0:24427303d56f
715 user: test
715 user: test
716 date: Thu Jan 01 00:00:00 1970 +0000
716 date: Thu Jan 01 00:00:00 1970 +0000
717 summary: commit on default
717 summary: commit on default
718
718
719 changeset: 1:d32277701ccb
719 changeset: 1:d32277701ccb
720 branch: test
720 branch: test
721 user: test
721 user: test
722 date: Thu Jan 01 00:00:00 1970 +0000
722 date: Thu Jan 01 00:00:00 1970 +0000
723 summary: commit on test
723 summary: commit on test
724
724
725 changeset: 0:24427303d56f
725 changeset: 0:24427303d56f
726 user: test
726 user: test
727 date: Thu Jan 01 00:00:00 1970 +0000
727 date: Thu Jan 01 00:00:00 1970 +0000
728 summary: commit on default
728 summary: commit on default
729
729
730
730
731
731
732 log -b default -b .
732 log -b default -b .
733
733
734 $ hg log -b default -b .
734 $ hg log -b default -b .
735 changeset: 3:f5d8de11c2e2
735 changeset: 3:f5d8de11c2e2
736 branch: test
736 branch: test
737 tag: tip
737 tag: tip
738 parent: 1:d32277701ccb
738 parent: 1:d32277701ccb
739 user: test
739 user: test
740 date: Thu Jan 01 00:00:00 1970 +0000
740 date: Thu Jan 01 00:00:00 1970 +0000
741 summary: commit on test
741 summary: commit on test
742
742
743 changeset: 2:c3a4f03cc9a7
743 changeset: 2:c3a4f03cc9a7
744 parent: 0:24427303d56f
744 parent: 0:24427303d56f
745 user: test
745 user: test
746 date: Thu Jan 01 00:00:00 1970 +0000
746 date: Thu Jan 01 00:00:00 1970 +0000
747 summary: commit on default
747 summary: commit on default
748
748
749 changeset: 1:d32277701ccb
749 changeset: 1:d32277701ccb
750 branch: test
750 branch: test
751 user: test
751 user: test
752 date: Thu Jan 01 00:00:00 1970 +0000
752 date: Thu Jan 01 00:00:00 1970 +0000
753 summary: commit on test
753 summary: commit on test
754
754
755 changeset: 0:24427303d56f
755 changeset: 0:24427303d56f
756 user: test
756 user: test
757 date: Thu Jan 01 00:00:00 1970 +0000
757 date: Thu Jan 01 00:00:00 1970 +0000
758 summary: commit on default
758 summary: commit on default
759
759
760
760
761
761
762 log -b . -b test
762 log -b . -b test
763
763
764 $ hg log -b . -b test
764 $ hg log -b . -b test
765 changeset: 3:f5d8de11c2e2
765 changeset: 3:f5d8de11c2e2
766 branch: test
766 branch: test
767 tag: tip
767 tag: tip
768 parent: 1:d32277701ccb
768 parent: 1:d32277701ccb
769 user: test
769 user: test
770 date: Thu Jan 01 00:00:00 1970 +0000
770 date: Thu Jan 01 00:00:00 1970 +0000
771 summary: commit on test
771 summary: commit on test
772
772
773 changeset: 1:d32277701ccb
773 changeset: 1:d32277701ccb
774 branch: test
774 branch: test
775 user: test
775 user: test
776 date: Thu Jan 01 00:00:00 1970 +0000
776 date: Thu Jan 01 00:00:00 1970 +0000
777 summary: commit on test
777 summary: commit on test
778
778
779
779
780
780
781 log -b 2
781 log -b 2
782
782
783 $ hg log -b 2
783 $ hg log -b 2
784 changeset: 2:c3a4f03cc9a7
784 changeset: 2:c3a4f03cc9a7
785 parent: 0:24427303d56f
785 parent: 0:24427303d56f
786 user: test
786 user: test
787 date: Thu Jan 01 00:00:00 1970 +0000
787 date: Thu Jan 01 00:00:00 1970 +0000
788 summary: commit on default
788 summary: commit on default
789
789
790 changeset: 0:24427303d56f
790 changeset: 0:24427303d56f
791 user: test
791 user: test
792 date: Thu Jan 01 00:00:00 1970 +0000
792 date: Thu Jan 01 00:00:00 1970 +0000
793 summary: commit on default
793 summary: commit on default
794
794
795
795
796
796
797 log -p --cwd dir (in subdir)
797 log -p --cwd dir (in subdir)
798
798
799 $ mkdir dir
799 $ mkdir dir
800 $ hg log -p --cwd dir
800 $ hg log -p --cwd dir
801 changeset: 3:f5d8de11c2e2
801 changeset: 3:f5d8de11c2e2
802 branch: test
802 branch: test
803 tag: tip
803 tag: tip
804 parent: 1:d32277701ccb
804 parent: 1:d32277701ccb
805 user: test
805 user: test
806 date: Thu Jan 01 00:00:00 1970 +0000
806 date: Thu Jan 01 00:00:00 1970 +0000
807 summary: commit on test
807 summary: commit on test
808
808
809 diff -r d32277701ccb -r f5d8de11c2e2 c
809 diff -r d32277701ccb -r f5d8de11c2e2 c
810 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
810 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
811 +++ b/c Thu Jan 01 00:00:00 1970 +0000
811 +++ b/c Thu Jan 01 00:00:00 1970 +0000
812 @@ -0,0 +1,1 @@
812 @@ -0,0 +1,1 @@
813 +c
813 +c
814
814
815 changeset: 2:c3a4f03cc9a7
815 changeset: 2:c3a4f03cc9a7
816 parent: 0:24427303d56f
816 parent: 0:24427303d56f
817 user: test
817 user: test
818 date: Thu Jan 01 00:00:00 1970 +0000
818 date: Thu Jan 01 00:00:00 1970 +0000
819 summary: commit on default
819 summary: commit on default
820
820
821 diff -r 24427303d56f -r c3a4f03cc9a7 c
821 diff -r 24427303d56f -r c3a4f03cc9a7 c
822 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
822 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
823 +++ b/c Thu Jan 01 00:00:00 1970 +0000
823 +++ b/c Thu Jan 01 00:00:00 1970 +0000
824 @@ -0,0 +1,1 @@
824 @@ -0,0 +1,1 @@
825 +c
825 +c
826
826
827 changeset: 1:d32277701ccb
827 changeset: 1:d32277701ccb
828 branch: test
828 branch: test
829 user: test
829 user: test
830 date: Thu Jan 01 00:00:00 1970 +0000
830 date: Thu Jan 01 00:00:00 1970 +0000
831 summary: commit on test
831 summary: commit on test
832
832
833 diff -r 24427303d56f -r d32277701ccb b
833 diff -r 24427303d56f -r d32277701ccb b
834 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
834 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
835 +++ b/b Thu Jan 01 00:00:00 1970 +0000
835 +++ b/b Thu Jan 01 00:00:00 1970 +0000
836 @@ -0,0 +1,1 @@
836 @@ -0,0 +1,1 @@
837 +b
837 +b
838
838
839 changeset: 0:24427303d56f
839 changeset: 0:24427303d56f
840 user: test
840 user: test
841 date: Thu Jan 01 00:00:00 1970 +0000
841 date: Thu Jan 01 00:00:00 1970 +0000
842 summary: commit on default
842 summary: commit on default
843
843
844 diff -r 000000000000 -r 24427303d56f a
844 diff -r 000000000000 -r 24427303d56f a
845 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
845 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
846 +++ b/a Thu Jan 01 00:00:00 1970 +0000
846 +++ b/a Thu Jan 01 00:00:00 1970 +0000
847 @@ -0,0 +1,1 @@
847 @@ -0,0 +1,1 @@
848 +a
848 +a
849
849
850
850
851
851
852 log -p -R repo
852 log -p -R repo
853
853
854 $ cd dir
854 $ cd dir
855 $ hg log -p -R .. ../a
855 $ hg log -p -R .. ../a
856 changeset: 0:24427303d56f
856 changeset: 0:24427303d56f
857 user: test
857 user: test
858 date: Thu Jan 01 00:00:00 1970 +0000
858 date: Thu Jan 01 00:00:00 1970 +0000
859 summary: commit on default
859 summary: commit on default
860
860
861 diff -r 000000000000 -r 24427303d56f a
861 diff -r 000000000000 -r 24427303d56f a
862 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
862 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
863 +++ b/a Thu Jan 01 00:00:00 1970 +0000
863 +++ b/a Thu Jan 01 00:00:00 1970 +0000
864 @@ -0,0 +1,1 @@
864 @@ -0,0 +1,1 @@
865 +a
865 +a
866
866
867
867
868
868
869 $ cd ..
869 $ cd ..
870 $ hg init follow2
870 $ hg init follow2
871 $ cd follow2
871 $ cd follow2
872
872
873
873
874 # Build the following history:
874 # Build the following history:
875 # tip - o - x - o - x - x
875 # tip - o - x - o - x - x
876 # \ /
876 # \ /
877 # o - o - o - x
877 # o - o - o - x
878 # \ /
878 # \ /
879 # o
879 # o
880 #
880 #
881 # Where "o" is a revision containing "foo" and
881 # Where "o" is a revision containing "foo" and
882 # "x" is a revision without "foo"
882 # "x" is a revision without "foo"
883
883
884 $ touch init
884 $ touch init
885 $ hg ci -A -m "init, unrelated"
885 $ hg ci -A -m "init, unrelated"
886 adding init
886 adding init
887 $ echo 'foo' > init
887 $ echo 'foo' > init
888 $ hg ci -m "change, unrelated"
888 $ hg ci -m "change, unrelated"
889 $ echo 'foo' > foo
889 $ echo 'foo' > foo
890 $ hg ci -A -m "add unrelated old foo"
890 $ hg ci -A -m "add unrelated old foo"
891 adding foo
891 adding foo
892 $ hg rm foo
892 $ hg rm foo
893 $ hg ci -m "delete foo, unrelated"
893 $ hg ci -m "delete foo, unrelated"
894 $ echo 'related' > foo
894 $ echo 'related' > foo
895 $ hg ci -A -m "add foo, related"
895 $ hg ci -A -m "add foo, related"
896 adding foo
896 adding foo
897
897
898 $ hg up 0
898 $ hg up 0
899 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
899 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
900 $ touch branch
900 $ touch branch
901 $ hg ci -A -m "first branch, unrelated"
901 $ hg ci -A -m "first branch, unrelated"
902 adding branch
902 adding branch
903 created new head
903 created new head
904 $ touch foo
904 $ touch foo
905 $ hg ci -A -m "create foo, related"
905 $ hg ci -A -m "create foo, related"
906 adding foo
906 adding foo
907 $ echo 'change' > foo
907 $ echo 'change' > foo
908 $ hg ci -m "change foo, related"
908 $ hg ci -m "change foo, related"
909
909
910 $ hg up 6
910 $ hg up 6
911 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
911 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
912 $ echo 'change foo in branch' > foo
912 $ echo 'change foo in branch' > foo
913 $ hg ci -m "change foo in branch, related"
913 $ hg ci -m "change foo in branch, related"
914 created new head
914 created new head
915 $ hg merge 7
915 $ hg merge 7
916 merging foo
916 merging foo
917 warning: conflicts during merge.
917 warning: conflicts during merge.
918 merging foo failed!
918 merging foo failed!
919 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
919 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
920 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
920 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
921 [1]
921 [1]
922 $ echo 'merge 1' > foo
922 $ echo 'merge 1' > foo
923 $ hg resolve -m foo
923 $ hg resolve -m foo
924 $ hg ci -m "First merge, related"
924 $ hg ci -m "First merge, related"
925
925
926 $ hg merge 4
926 $ hg merge 4
927 merging foo
927 merging foo
928 warning: conflicts during merge.
928 warning: conflicts during merge.
929 merging foo failed!
929 merging foo failed!
930 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
930 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
931 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
931 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
932 [1]
932 [1]
933 $ echo 'merge 2' > foo
933 $ echo 'merge 2' > foo
934 $ hg resolve -m foo
934 $ hg resolve -m foo
935 $ hg ci -m "Last merge, related"
935 $ hg ci -m "Last merge, related"
936
936
937 $ hg --config "extensions.graphlog=" glog
937 $ hg --config "extensions.graphlog=" glog
938 @ changeset: 10:4dae8563d2c5
938 @ changeset: 10:4dae8563d2c5
939 |\ tag: tip
939 |\ tag: tip
940 | | parent: 9:7b35701b003e
940 | | parent: 9:7b35701b003e
941 | | parent: 4:88176d361b69
941 | | parent: 4:88176d361b69
942 | | user: test
942 | | user: test
943 | | date: Thu Jan 01 00:00:00 1970 +0000
943 | | date: Thu Jan 01 00:00:00 1970 +0000
944 | | summary: Last merge, related
944 | | summary: Last merge, related
945 | |
945 | |
946 | o changeset: 9:7b35701b003e
946 | o changeset: 9:7b35701b003e
947 | |\ parent: 8:e5416ad8a855
947 | |\ parent: 8:e5416ad8a855
948 | | | parent: 7:87fe3144dcfa
948 | | | parent: 7:87fe3144dcfa
949 | | | user: test
949 | | | user: test
950 | | | date: Thu Jan 01 00:00:00 1970 +0000
950 | | | date: Thu Jan 01 00:00:00 1970 +0000
951 | | | summary: First merge, related
951 | | | summary: First merge, related
952 | | |
952 | | |
953 | | o changeset: 8:e5416ad8a855
953 | | o changeset: 8:e5416ad8a855
954 | | | parent: 6:dc6c325fe5ee
954 | | | parent: 6:dc6c325fe5ee
955 | | | user: test
955 | | | user: test
956 | | | date: Thu Jan 01 00:00:00 1970 +0000
956 | | | date: Thu Jan 01 00:00:00 1970 +0000
957 | | | summary: change foo in branch, related
957 | | | summary: change foo in branch, related
958 | | |
958 | | |
959 | o | changeset: 7:87fe3144dcfa
959 | o | changeset: 7:87fe3144dcfa
960 | |/ user: test
960 | |/ user: test
961 | | date: Thu Jan 01 00:00:00 1970 +0000
961 | | date: Thu Jan 01 00:00:00 1970 +0000
962 | | summary: change foo, related
962 | | summary: change foo, related
963 | |
963 | |
964 | o changeset: 6:dc6c325fe5ee
964 | o changeset: 6:dc6c325fe5ee
965 | | user: test
965 | | user: test
966 | | date: Thu Jan 01 00:00:00 1970 +0000
966 | | date: Thu Jan 01 00:00:00 1970 +0000
967 | | summary: create foo, related
967 | | summary: create foo, related
968 | |
968 | |
969 | o changeset: 5:73db34516eb9
969 | o changeset: 5:73db34516eb9
970 | | parent: 0:e87515fd044a
970 | | parent: 0:e87515fd044a
971 | | user: test
971 | | user: test
972 | | date: Thu Jan 01 00:00:00 1970 +0000
972 | | date: Thu Jan 01 00:00:00 1970 +0000
973 | | summary: first branch, unrelated
973 | | summary: first branch, unrelated
974 | |
974 | |
975 o | changeset: 4:88176d361b69
975 o | changeset: 4:88176d361b69
976 | | user: test
976 | | user: test
977 | | date: Thu Jan 01 00:00:00 1970 +0000
977 | | date: Thu Jan 01 00:00:00 1970 +0000
978 | | summary: add foo, related
978 | | summary: add foo, related
979 | |
979 | |
980 o | changeset: 3:dd78ae4afb56
980 o | changeset: 3:dd78ae4afb56
981 | | user: test
981 | | user: test
982 | | date: Thu Jan 01 00:00:00 1970 +0000
982 | | date: Thu Jan 01 00:00:00 1970 +0000
983 | | summary: delete foo, unrelated
983 | | summary: delete foo, unrelated
984 | |
984 | |
985 o | changeset: 2:c4c64aedf0f7
985 o | changeset: 2:c4c64aedf0f7
986 | | user: test
986 | | user: test
987 | | date: Thu Jan 01 00:00:00 1970 +0000
987 | | date: Thu Jan 01 00:00:00 1970 +0000
988 | | summary: add unrelated old foo
988 | | summary: add unrelated old foo
989 | |
989 | |
990 o | changeset: 1:e5faa7440653
990 o | changeset: 1:e5faa7440653
991 |/ user: test
991 |/ user: test
992 | date: Thu Jan 01 00:00:00 1970 +0000
992 | date: Thu Jan 01 00:00:00 1970 +0000
993 | summary: change, unrelated
993 | summary: change, unrelated
994 |
994 |
995 o changeset: 0:e87515fd044a
995 o changeset: 0:e87515fd044a
996 user: test
996 user: test
997 date: Thu Jan 01 00:00:00 1970 +0000
997 date: Thu Jan 01 00:00:00 1970 +0000
998 summary: init, unrelated
998 summary: init, unrelated
999
999
1000
1000
1001 $ hg --traceback log -f foo
1001 $ hg --traceback log -f foo
1002 changeset: 10:4dae8563d2c5
1002 changeset: 10:4dae8563d2c5
1003 tag: tip
1003 tag: tip
1004 parent: 9:7b35701b003e
1004 parent: 9:7b35701b003e
1005 parent: 4:88176d361b69
1005 parent: 4:88176d361b69
1006 user: test
1006 user: test
1007 date: Thu Jan 01 00:00:00 1970 +0000
1007 date: Thu Jan 01 00:00:00 1970 +0000
1008 summary: Last merge, related
1008 summary: Last merge, related
1009
1009
1010 changeset: 9:7b35701b003e
1010 changeset: 9:7b35701b003e
1011 parent: 8:e5416ad8a855
1011 parent: 8:e5416ad8a855
1012 parent: 7:87fe3144dcfa
1012 parent: 7:87fe3144dcfa
1013 user: test
1013 user: test
1014 date: Thu Jan 01 00:00:00 1970 +0000
1014 date: Thu Jan 01 00:00:00 1970 +0000
1015 summary: First merge, related
1015 summary: First merge, related
1016
1016
1017 changeset: 8:e5416ad8a855
1017 changeset: 8:e5416ad8a855
1018 parent: 6:dc6c325fe5ee
1018 parent: 6:dc6c325fe5ee
1019 user: test
1019 user: test
1020 date: Thu Jan 01 00:00:00 1970 +0000
1020 date: Thu Jan 01 00:00:00 1970 +0000
1021 summary: change foo in branch, related
1021 summary: change foo in branch, related
1022
1022
1023 changeset: 7:87fe3144dcfa
1023 changeset: 7:87fe3144dcfa
1024 user: test
1024 user: test
1025 date: Thu Jan 01 00:00:00 1970 +0000
1025 date: Thu Jan 01 00:00:00 1970 +0000
1026 summary: change foo, related
1026 summary: change foo, related
1027
1027
1028 changeset: 6:dc6c325fe5ee
1028 changeset: 6:dc6c325fe5ee
1029 user: test
1029 user: test
1030 date: Thu Jan 01 00:00:00 1970 +0000
1030 date: Thu Jan 01 00:00:00 1970 +0000
1031 summary: create foo, related
1031 summary: create foo, related
1032
1032
1033 changeset: 4:88176d361b69
1033 changeset: 4:88176d361b69
1034 user: test
1034 user: test
1035 date: Thu Jan 01 00:00:00 1970 +0000
1035 date: Thu Jan 01 00:00:00 1970 +0000
1036 summary: add foo, related
1036 summary: add foo, related
1037
1037
1038
1038
1039 Also check when maxrev < lastrevfilelog
1039 Also check when maxrev < lastrevfilelog
1040
1040
1041 $ hg --traceback log -f -r4 foo
1041 $ hg --traceback log -f -r4 foo
1042 changeset: 4:88176d361b69
1042 changeset: 4:88176d361b69
1043 user: test
1043 user: test
1044 date: Thu Jan 01 00:00:00 1970 +0000
1044 date: Thu Jan 01 00:00:00 1970 +0000
1045 summary: add foo, related
1045 summary: add foo, related
1046
1046
1047
1047
1048 Issue2383: hg log showing _less_ differences than hg diff
1048 Issue2383: hg log showing _less_ differences than hg diff
1049
1049
1050 $ hg init issue2383
1050 $ hg init issue2383
1051 $ cd issue2383
1051 $ cd issue2383
1052
1052
1053 Create a test repo:
1053 Create a test repo:
1054
1054
1055 $ echo a > a
1055 $ echo a > a
1056 $ hg ci -Am0
1056 $ hg ci -Am0
1057 adding a
1057 adding a
1058 $ echo b > b
1058 $ echo b > b
1059 $ hg ci -Am1
1059 $ hg ci -Am1
1060 adding b
1060 adding b
1061 $ hg co 0
1061 $ hg co 0
1062 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1062 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1063 $ echo b > a
1063 $ echo b > a
1064 $ hg ci -m2
1064 $ hg ci -m2
1065 created new head
1065 created new head
1066
1066
1067 Merge:
1067 Merge:
1068
1068
1069 $ hg merge
1069 $ hg merge
1070 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1070 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1071 (branch merge, don't forget to commit)
1071 (branch merge, don't forget to commit)
1072
1072
1073 Make sure there's a file listed in the merge to trigger the bug:
1073 Make sure there's a file listed in the merge to trigger the bug:
1074
1074
1075 $ echo c > a
1075 $ echo c > a
1076 $ hg ci -m3
1076 $ hg ci -m3
1077
1077
1078 Two files shown here in diff:
1078 Two files shown here in diff:
1079
1079
1080 $ hg diff --rev 2:3
1080 $ hg diff --rev 2:3
1081 diff -r b09be438c43a -r 8e07aafe1edc a
1081 diff -r b09be438c43a -r 8e07aafe1edc a
1082 --- a/a Thu Jan 01 00:00:00 1970 +0000
1082 --- a/a Thu Jan 01 00:00:00 1970 +0000
1083 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1083 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1084 @@ -1,1 +1,1 @@
1084 @@ -1,1 +1,1 @@
1085 -b
1085 -b
1086 +c
1086 +c
1087 diff -r b09be438c43a -r 8e07aafe1edc b
1087 diff -r b09be438c43a -r 8e07aafe1edc b
1088 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1088 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1089 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1089 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1090 @@ -0,0 +1,1 @@
1090 @@ -0,0 +1,1 @@
1091 +b
1091 +b
1092
1092
1093 Diff here should be the same:
1093 Diff here should be the same:
1094
1094
1095 $ hg log -vpr 3
1095 $ hg log -vpr 3
1096 changeset: 3:8e07aafe1edc
1096 changeset: 3:8e07aafe1edc
1097 tag: tip
1097 tag: tip
1098 parent: 2:b09be438c43a
1098 parent: 2:b09be438c43a
1099 parent: 1:925d80f479bb
1099 parent: 1:925d80f479bb
1100 user: test
1100 user: test
1101 date: Thu Jan 01 00:00:00 1970 +0000
1101 date: Thu Jan 01 00:00:00 1970 +0000
1102 files: a
1102 files: a
1103 description:
1103 description:
1104 3
1104 3
1105
1105
1106
1106
1107 diff -r b09be438c43a -r 8e07aafe1edc a
1107 diff -r b09be438c43a -r 8e07aafe1edc a
1108 --- a/a Thu Jan 01 00:00:00 1970 +0000
1108 --- a/a Thu Jan 01 00:00:00 1970 +0000
1109 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1109 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1110 @@ -1,1 +1,1 @@
1110 @@ -1,1 +1,1 @@
1111 -b
1111 -b
1112 +c
1112 +c
1113 diff -r b09be438c43a -r 8e07aafe1edc b
1113 diff -r b09be438c43a -r 8e07aafe1edc b
1114 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1114 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1115 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1115 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1116 @@ -0,0 +1,1 @@
1116 @@ -0,0 +1,1 @@
1117 +b
1117 +b
1118
1118
1119 $ cd ..
1119 $ cd ..
1120
1120
1121 'hg log -r rev fn' when last(filelog(fn)) != rev
1121 'hg log -r rev fn' when last(filelog(fn)) != rev
1122
1122
1123 $ hg init simplelog; cd simplelog
1123 $ hg init simplelog; cd simplelog
1124 $ echo f > a
1124 $ echo f > a
1125 $ hg ci -Am'a' -d '0 0'
1125 $ hg ci -Am'a' -d '0 0'
1126 adding a
1126 adding a
1127 $ echo f >> a
1127 $ echo f >> a
1128 $ hg ci -Am'a bis' -d '1 0'
1128 $ hg ci -Am'a bis' -d '1 0'
1129
1129
1130 $ hg log -r0 a
1130 $ hg log -r0 a
1131 changeset: 0:9f758d63dcde
1131 changeset: 0:9f758d63dcde
1132 user: test
1132 user: test
1133 date: Thu Jan 01 00:00:00 1970 +0000
1133 date: Thu Jan 01 00:00:00 1970 +0000
1134 summary: a
1134 summary: a
1135
1135
@@ -1,220 +1,220
1 This runs with TZ="GMT"
1 This runs with TZ="GMT"
2
2
3 $ hg init
3 $ hg init
4 $ echo "test-parse-date" > a
4 $ echo "test-parse-date" > a
5 $ hg add a
5 $ hg add a
6 $ hg ci -d "2006-02-01 13:00:30" -m "rev 0"
6 $ hg ci -d "2006-02-01 13:00:30" -m "rev 0"
7 $ echo "hi!" >> a
7 $ echo "hi!" >> a
8 $ hg ci -d "2006-02-01 13:00:30 -0500" -m "rev 1"
8 $ hg ci -d "2006-02-01 13:00:30 -0500" -m "rev 1"
9 $ hg tag -d "2006-04-15 13:30" "Hi"
9 $ hg tag -d "2006-04-15 13:30" "Hi"
10 $ hg backout --merge -d "2006-04-15 13:30 +0200" -m "rev 3" 1
10 $ hg backout --merge -d "2006-04-15 13:30 +0200" -m "rev 3" 1
11 reverting a
11 reverting a
12 created new head
12 created new head
13 changeset 3:107ce1ee2b43 backs out changeset 1:25a1420a55f8
13 changeset 3:107ce1ee2b43 backs out changeset 1:25a1420a55f8
14 merging with changeset 3:107ce1ee2b43
14 merging with changeset 3:107ce1ee2b43
15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 (branch merge, don't forget to commit)
16 (branch merge, don't forget to commit)
17 $ hg ci -d "1150000000 14400" -m "rev 4 (merge)"
17 $ hg ci -d "1150000000 14400" -m "rev 4 (merge)"
18 $ echo "fail" >> a
18 $ echo "fail" >> a
19 $ hg ci -d "should fail" -m "fail"
19 $ hg ci -d "should fail" -m "fail"
20 abort: invalid date: 'should fail'
20 abort: invalid date: 'should fail'
21 [255]
21 [255]
22 $ hg ci -d "100000000000000000 1400" -m "fail"
22 $ hg ci -d "100000000000000000 1400" -m "fail"
23 abort: date exceeds 32 bits: 100000000000000000
23 abort: date exceeds 32 bits: 100000000000000000
24 [255]
24 [255]
25 $ hg ci -d "100000 1400000" -m "fail"
25 $ hg ci -d "100000 1400000" -m "fail"
26 abort: impossible time zone offset: 1400000
26 abort: impossible time zone offset: 1400000
27 [255]
27 [255]
28
28
29 Check with local timezone other than GMT and with DST
29 Check with local timezone other than GMT and with DST
30
30
31 $ TZ="PST+8PDT"
31 $ TZ="PST+8PDT"
32 $ export TZ
32 $ export TZ
33
33
34 PST=UTC-8 / PDT=UTC-7
34 PST=UTC-8 / PDT=UTC-7
35
35
36 $ hg debugrebuildstate
36 $ hg debugrebuildstate
37 $ echo "a" > a
37 $ echo "a" > a
38 $ hg ci -d "2006-07-15 13:30" -m "summer@UTC-7"
38 $ hg ci -d "2006-07-15 13:30" -m "summer@UTC-7"
39 $ hg debugrebuildstate
39 $ hg debugrebuildstate
40 $ echo "b" > a
40 $ echo "b" > a
41 $ hg ci -d "2006-07-15 13:30 +0500" -m "summer@UTC+5"
41 $ hg ci -d "2006-07-15 13:30 +0500" -m "summer@UTC+5"
42 $ hg debugrebuildstate
42 $ hg debugrebuildstate
43 $ echo "c" > a
43 $ echo "c" > a
44 $ hg ci -d "2006-01-15 13:30" -m "winter@UTC-8"
44 $ hg ci -d "2006-01-15 13:30" -m "winter@UTC-8"
45 $ hg debugrebuildstate
45 $ hg debugrebuildstate
46 $ echo "d" > a
46 $ echo "d" > a
47 $ hg ci -d "2006-01-15 13:30 +0500" -m "winter@UTC+5"
47 $ hg ci -d "2006-01-15 13:30 +0500" -m "winter@UTC+5"
48 $ hg log --template '{date|date}\n'
48 $ hg log --template '{date|date}\n'
49 Sun Jan 15 13:30:00 2006 +0500
49 Sun Jan 15 13:30:00 2006 +0500
50 Sun Jan 15 13:30:00 2006 -0800
50 Sun Jan 15 13:30:00 2006 -0800
51 Sat Jul 15 13:30:00 2006 +0500
51 Sat Jul 15 13:30:00 2006 +0500
52 Sat Jul 15 13:30:00 2006 -0700
52 Sat Jul 15 13:30:00 2006 -0700
53 Sun Jun 11 00:26:40 2006 -0400
53 Sun Jun 11 00:26:40 2006 -0400
54 Sat Apr 15 13:30:00 2006 +0200
54 Sat Apr 15 13:30:00 2006 +0200
55 Sat Apr 15 13:30:00 2006 +0000
55 Sat Apr 15 13:30:00 2006 +0000
56 Wed Feb 01 13:00:30 2006 -0500
56 Wed Feb 01 13:00:30 2006 -0500
57 Wed Feb 01 13:00:30 2006 +0000
57 Wed Feb 01 13:00:30 2006 +0000
58
58
59 Test issue1014 (fractional timezones)
59 Test issue1014 (fractional timezones)
60
60
61 $ hg debugdate "1000000000 -16200" # 0430
61 $ hg debugdate "1000000000 -16200" # 0430
62 internal: 1000000000 -16200
62 internal: 1000000000 -16200
63 standard: Sun Sep 09 06:16:40 2001 +0430
63 standard: Sun Sep 09 06:16:40 2001 +0430
64 $ hg debugdate "1000000000 -15300" # 0415
64 $ hg debugdate "1000000000 -15300" # 0415
65 internal: 1000000000 -15300
65 internal: 1000000000 -15300
66 standard: Sun Sep 09 06:01:40 2001 +0415
66 standard: Sun Sep 09 06:01:40 2001 +0415
67 $ hg debugdate "1000000000 -14400" # 0400
67 $ hg debugdate "1000000000 -14400" # 0400
68 internal: 1000000000 -14400
68 internal: 1000000000 -14400
69 standard: Sun Sep 09 05:46:40 2001 +0400
69 standard: Sun Sep 09 05:46:40 2001 +0400
70 $ hg debugdate "1000000000 0" # GMT
70 $ hg debugdate "1000000000 0" # GMT
71 internal: 1000000000 0
71 internal: 1000000000 0
72 standard: Sun Sep 09 01:46:40 2001 +0000
72 standard: Sun Sep 09 01:46:40 2001 +0000
73 $ hg debugdate "1000000000 14400" # -0400
73 $ hg debugdate "1000000000 14400" # -0400
74 internal: 1000000000 14400
74 internal: 1000000000 14400
75 standard: Sat Sep 08 21:46:40 2001 -0400
75 standard: Sat Sep 08 21:46:40 2001 -0400
76 $ hg debugdate "1000000000 15300" # -0415
76 $ hg debugdate "1000000000 15300" # -0415
77 internal: 1000000000 15300
77 internal: 1000000000 15300
78 standard: Sat Sep 08 21:31:40 2001 -0415
78 standard: Sat Sep 08 21:31:40 2001 -0415
79 $ hg debugdate "1000000000 16200" # -0430
79 $ hg debugdate "1000000000 16200" # -0430
80 internal: 1000000000 16200
80 internal: 1000000000 16200
81 standard: Sat Sep 08 21:16:40 2001 -0430
81 standard: Sat Sep 08 21:16:40 2001 -0430
82 $ hg debugdate "Sat Sep 08 21:16:40 2001 +0430"
82 $ hg debugdate "Sat Sep 08 21:16:40 2001 +0430"
83 internal: 999967600 -16200
83 internal: 999967600 -16200
84 standard: Sat Sep 08 21:16:40 2001 +0430
84 standard: Sat Sep 08 21:16:40 2001 +0430
85 $ hg debugdate "Sat Sep 08 21:16:40 2001 -0430"
85 $ hg debugdate "Sat Sep 08 21:16:40 2001 -0430"
86 internal: 1000000000 16200
86 internal: 1000000000 16200
87 standard: Sat Sep 08 21:16:40 2001 -0430
87 standard: Sat Sep 08 21:16:40 2001 -0430
88
88
89 Test 12-hours times
89 Test 12-hours times
90
90
91 $ hg debugdate "2006-02-01 1:00:30PM +0000"
91 $ hg debugdate "2006-02-01 1:00:30PM +0000"
92 internal: 1138798830 0
92 internal: 1138798830 0
93 standard: Wed Feb 01 13:00:30 2006 +0000
93 standard: Wed Feb 01 13:00:30 2006 +0000
94 $ hg debugdate "1:00:30PM" > /dev/null
94 $ hg debugdate "1:00:30PM" > /dev/null
95
95
96 Test date formats with '>' or '<' accompanied by space characters
96 Test date formats with '>' or '<' accompanied by space characters
97
97
98 $ hg log -d '>' --template '{date|date}\n'
98 $ hg log -d '>' --template '{date|date}\n'
99 abort: invalid day spec. use '>{datetime}'
99 abort: invalid day spec, use '>DATE'
100 [255]
100 [255]
101 $ hg log -d '<' hg log -d '>' --template '{date|date}\n'
101 $ hg log -d '<' hg log -d '>' --template '{date|date}\n'
102 abort: invalid day spec. use '>{datetime}'
102 abort: invalid day spec, use '>DATE'
103 [255]
103 [255]
104
104
105 $ hg log -d ' >' --template '{date|date}\n'
105 $ hg log -d ' >' --template '{date|date}\n'
106 abort: invalid day spec. use '>{datetime}'
106 abort: invalid day spec, use '>DATE'
107 [255]
107 [255]
108 $ hg log -d ' <' --template '{date|date}\n'
108 $ hg log -d ' <' --template '{date|date}\n'
109 abort: invalid day spec. use '<{datetime}'
109 abort: invalid day spec, use '<DATE'
110 [255]
110 [255]
111
111
112 $ hg log -d '> ' --template '{date|date}\n'
112 $ hg log -d '> ' --template '{date|date}\n'
113 abort: invalid day spec. use '>{datetime}'
113 abort: invalid day spec, use '>DATE'
114 [255]
114 [255]
115 $ hg log -d '< ' --template '{date|date}\n'
115 $ hg log -d '< ' --template '{date|date}\n'
116 abort: invalid day spec. use '<{datetime}'
116 abort: invalid day spec, use '<DATE'
117 [255]
117 [255]
118
118
119 $ hg log -d ' > ' --template '{date|date}\n'
119 $ hg log -d ' > ' --template '{date|date}\n'
120 abort: invalid day spec. use '>{datetime}'
120 abort: invalid day spec, use '>DATE'
121 [255]
121 [255]
122 $ hg log -d ' < ' --template '{date|date}\n'
122 $ hg log -d ' < ' --template '{date|date}\n'
123 abort: invalid day spec. use '<{datetime}'
123 abort: invalid day spec, use '<DATE'
124 [255]
124 [255]
125
125
126 $ hg log -d '>02/01' --template '{date|date}\n'
126 $ hg log -d '>02/01' --template '{date|date}\n'
127 $ hg log -d '<02/01' --template '{date|date}\n'
127 $ hg log -d '<02/01' --template '{date|date}\n'
128 Sun Jan 15 13:30:00 2006 +0500
128 Sun Jan 15 13:30:00 2006 +0500
129 Sun Jan 15 13:30:00 2006 -0800
129 Sun Jan 15 13:30:00 2006 -0800
130 Sat Jul 15 13:30:00 2006 +0500
130 Sat Jul 15 13:30:00 2006 +0500
131 Sat Jul 15 13:30:00 2006 -0700
131 Sat Jul 15 13:30:00 2006 -0700
132 Sun Jun 11 00:26:40 2006 -0400
132 Sun Jun 11 00:26:40 2006 -0400
133 Sat Apr 15 13:30:00 2006 +0200
133 Sat Apr 15 13:30:00 2006 +0200
134 Sat Apr 15 13:30:00 2006 +0000
134 Sat Apr 15 13:30:00 2006 +0000
135 Wed Feb 01 13:00:30 2006 -0500
135 Wed Feb 01 13:00:30 2006 -0500
136 Wed Feb 01 13:00:30 2006 +0000
136 Wed Feb 01 13:00:30 2006 +0000
137
137
138 $ hg log -d ' >02/01' --template '{date|date}\n'
138 $ hg log -d ' >02/01' --template '{date|date}\n'
139 $ hg log -d ' <02/01' --template '{date|date}\n'
139 $ hg log -d ' <02/01' --template '{date|date}\n'
140 Sun Jan 15 13:30:00 2006 +0500
140 Sun Jan 15 13:30:00 2006 +0500
141 Sun Jan 15 13:30:00 2006 -0800
141 Sun Jan 15 13:30:00 2006 -0800
142 Sat Jul 15 13:30:00 2006 +0500
142 Sat Jul 15 13:30:00 2006 +0500
143 Sat Jul 15 13:30:00 2006 -0700
143 Sat Jul 15 13:30:00 2006 -0700
144 Sun Jun 11 00:26:40 2006 -0400
144 Sun Jun 11 00:26:40 2006 -0400
145 Sat Apr 15 13:30:00 2006 +0200
145 Sat Apr 15 13:30:00 2006 +0200
146 Sat Apr 15 13:30:00 2006 +0000
146 Sat Apr 15 13:30:00 2006 +0000
147 Wed Feb 01 13:00:30 2006 -0500
147 Wed Feb 01 13:00:30 2006 -0500
148 Wed Feb 01 13:00:30 2006 +0000
148 Wed Feb 01 13:00:30 2006 +0000
149
149
150 $ hg log -d '> 02/01' --template '{date|date}\n'
150 $ hg log -d '> 02/01' --template '{date|date}\n'
151 $ hg log -d '< 02/01' --template '{date|date}\n'
151 $ hg log -d '< 02/01' --template '{date|date}\n'
152 Sun Jan 15 13:30:00 2006 +0500
152 Sun Jan 15 13:30:00 2006 +0500
153 Sun Jan 15 13:30:00 2006 -0800
153 Sun Jan 15 13:30:00 2006 -0800
154 Sat Jul 15 13:30:00 2006 +0500
154 Sat Jul 15 13:30:00 2006 +0500
155 Sat Jul 15 13:30:00 2006 -0700
155 Sat Jul 15 13:30:00 2006 -0700
156 Sun Jun 11 00:26:40 2006 -0400
156 Sun Jun 11 00:26:40 2006 -0400
157 Sat Apr 15 13:30:00 2006 +0200
157 Sat Apr 15 13:30:00 2006 +0200
158 Sat Apr 15 13:30:00 2006 +0000
158 Sat Apr 15 13:30:00 2006 +0000
159 Wed Feb 01 13:00:30 2006 -0500
159 Wed Feb 01 13:00:30 2006 -0500
160 Wed Feb 01 13:00:30 2006 +0000
160 Wed Feb 01 13:00:30 2006 +0000
161
161
162 $ hg log -d ' > 02/01' --template '{date|date}\n'
162 $ hg log -d ' > 02/01' --template '{date|date}\n'
163 $ hg log -d ' < 02/01' --template '{date|date}\n'
163 $ hg log -d ' < 02/01' --template '{date|date}\n'
164 Sun Jan 15 13:30:00 2006 +0500
164 Sun Jan 15 13:30:00 2006 +0500
165 Sun Jan 15 13:30:00 2006 -0800
165 Sun Jan 15 13:30:00 2006 -0800
166 Sat Jul 15 13:30:00 2006 +0500
166 Sat Jul 15 13:30:00 2006 +0500
167 Sat Jul 15 13:30:00 2006 -0700
167 Sat Jul 15 13:30:00 2006 -0700
168 Sun Jun 11 00:26:40 2006 -0400
168 Sun Jun 11 00:26:40 2006 -0400
169 Sat Apr 15 13:30:00 2006 +0200
169 Sat Apr 15 13:30:00 2006 +0200
170 Sat Apr 15 13:30:00 2006 +0000
170 Sat Apr 15 13:30:00 2006 +0000
171 Wed Feb 01 13:00:30 2006 -0500
171 Wed Feb 01 13:00:30 2006 -0500
172 Wed Feb 01 13:00:30 2006 +0000
172 Wed Feb 01 13:00:30 2006 +0000
173
173
174 $ hg log -d '>02/01 ' --template '{date|date}\n'
174 $ hg log -d '>02/01 ' --template '{date|date}\n'
175 $ hg log -d '<02/01 ' --template '{date|date}\n'
175 $ hg log -d '<02/01 ' --template '{date|date}\n'
176 Sun Jan 15 13:30:00 2006 +0500
176 Sun Jan 15 13:30:00 2006 +0500
177 Sun Jan 15 13:30:00 2006 -0800
177 Sun Jan 15 13:30:00 2006 -0800
178 Sat Jul 15 13:30:00 2006 +0500
178 Sat Jul 15 13:30:00 2006 +0500
179 Sat Jul 15 13:30:00 2006 -0700
179 Sat Jul 15 13:30:00 2006 -0700
180 Sun Jun 11 00:26:40 2006 -0400
180 Sun Jun 11 00:26:40 2006 -0400
181 Sat Apr 15 13:30:00 2006 +0200
181 Sat Apr 15 13:30:00 2006 +0200
182 Sat Apr 15 13:30:00 2006 +0000
182 Sat Apr 15 13:30:00 2006 +0000
183 Wed Feb 01 13:00:30 2006 -0500
183 Wed Feb 01 13:00:30 2006 -0500
184 Wed Feb 01 13:00:30 2006 +0000
184 Wed Feb 01 13:00:30 2006 +0000
185
185
186 $ hg log -d ' >02/01 ' --template '{date|date}\n'
186 $ hg log -d ' >02/01 ' --template '{date|date}\n'
187 $ hg log -d ' <02/01 ' --template '{date|date}\n'
187 $ hg log -d ' <02/01 ' --template '{date|date}\n'
188 Sun Jan 15 13:30:00 2006 +0500
188 Sun Jan 15 13:30:00 2006 +0500
189 Sun Jan 15 13:30:00 2006 -0800
189 Sun Jan 15 13:30:00 2006 -0800
190 Sat Jul 15 13:30:00 2006 +0500
190 Sat Jul 15 13:30:00 2006 +0500
191 Sat Jul 15 13:30:00 2006 -0700
191 Sat Jul 15 13:30:00 2006 -0700
192 Sun Jun 11 00:26:40 2006 -0400
192 Sun Jun 11 00:26:40 2006 -0400
193 Sat Apr 15 13:30:00 2006 +0200
193 Sat Apr 15 13:30:00 2006 +0200
194 Sat Apr 15 13:30:00 2006 +0000
194 Sat Apr 15 13:30:00 2006 +0000
195 Wed Feb 01 13:00:30 2006 -0500
195 Wed Feb 01 13:00:30 2006 -0500
196 Wed Feb 01 13:00:30 2006 +0000
196 Wed Feb 01 13:00:30 2006 +0000
197
197
198 $ hg log -d '> 02/01 ' --template '{date|date}\n'
198 $ hg log -d '> 02/01 ' --template '{date|date}\n'
199 $ hg log -d '< 02/01 ' --template '{date|date}\n'
199 $ hg log -d '< 02/01 ' --template '{date|date}\n'
200 Sun Jan 15 13:30:00 2006 +0500
200 Sun Jan 15 13:30:00 2006 +0500
201 Sun Jan 15 13:30:00 2006 -0800
201 Sun Jan 15 13:30:00 2006 -0800
202 Sat Jul 15 13:30:00 2006 +0500
202 Sat Jul 15 13:30:00 2006 +0500
203 Sat Jul 15 13:30:00 2006 -0700
203 Sat Jul 15 13:30:00 2006 -0700
204 Sun Jun 11 00:26:40 2006 -0400
204 Sun Jun 11 00:26:40 2006 -0400
205 Sat Apr 15 13:30:00 2006 +0200
205 Sat Apr 15 13:30:00 2006 +0200
206 Sat Apr 15 13:30:00 2006 +0000
206 Sat Apr 15 13:30:00 2006 +0000
207 Wed Feb 01 13:00:30 2006 -0500
207 Wed Feb 01 13:00:30 2006 -0500
208 Wed Feb 01 13:00:30 2006 +0000
208 Wed Feb 01 13:00:30 2006 +0000
209
209
210 $ hg log -d ' > 02/01 ' --template '{date|date}\n'
210 $ hg log -d ' > 02/01 ' --template '{date|date}\n'
211 $ hg log -d ' < 02/01 ' --template '{date|date}\n'
211 $ hg log -d ' < 02/01 ' --template '{date|date}\n'
212 Sun Jan 15 13:30:00 2006 +0500
212 Sun Jan 15 13:30:00 2006 +0500
213 Sun Jan 15 13:30:00 2006 -0800
213 Sun Jan 15 13:30:00 2006 -0800
214 Sat Jul 15 13:30:00 2006 +0500
214 Sat Jul 15 13:30:00 2006 +0500
215 Sat Jul 15 13:30:00 2006 -0700
215 Sat Jul 15 13:30:00 2006 -0700
216 Sun Jun 11 00:26:40 2006 -0400
216 Sun Jun 11 00:26:40 2006 -0400
217 Sat Apr 15 13:30:00 2006 +0200
217 Sat Apr 15 13:30:00 2006 +0200
218 Sat Apr 15 13:30:00 2006 +0000
218 Sat Apr 15 13:30:00 2006 +0000
219 Wed Feb 01 13:00:30 2006 -0500
219 Wed Feb 01 13:00:30 2006 -0500
220 Wed Feb 01 13:00:30 2006 +0000
220 Wed Feb 01 13:00:30 2006 +0000
General Comments 0
You need to be logged in to leave comments. Login now