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